7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

mac+eGPU で機械学習の環境構築からVison+CoreMLでの画像認識までの一通りの動作確認

Last updated at Posted at 2021-01-24

 macに機械学習の環境をつくり、画像分類を学習させたモデルの作成とVison+CoreMLで画像を分類をするまでを試したのでそのメモ。

この記事で参考になるかもしれない情報

 この記事は初心者が初めて機械学習をやってみた、という内容なので参考になるところはほとんどないと思いますが!

  • tensorflow-macos を使えばintel+eGPU環境でもGPUを使ってトレーニング時間は短縮できるっぽい。
eGPU
(Radeon RX 580)
接続
mlcompute
.set_mlc_device(device_name="gpu")
トレーニング
時間
✔️ 41.2s
231.5s
✔️ 606.9s
ERROR:root:Constant Propagation pass failed: invalid version number '2.4.0-rc0'

動作確認環境

  • macOS Big Sur 11.1
  • Mac mini(2018) 3.2GHz 6コア Intel Core i7 メモリ 16GB
  • 内蔵GPU Intel UHD Graphics 630
  • eGPU Radeon RX 580 8GB (+ Sonnet eGFX Breakaway Box 550)

動作確認の流れ

    1. トレーニング環境構築
    1. 学習用画像の準備
    1. 学習用と検証用のデータの作成
    1. トレーニング
    1. mlmodel に変換
    1. Vison+CoreMLで推論

1. トレーニング環境構築

1-1) tensorflow-macos インストール

 手順は参考にさせていただいたこちらの記事 【Apple Silicon M1 でtensorflow-macosを実行したらめちゃくちゃ速かった。】 と同じ。

Keras@mini2018 ~ % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/apple/tensorflow_macos/master/scripts/download_and_install.sh)"

Installation script for pre-release tensorflow_macos 0.1alpha1.  Please visit https://github.com/apple/tensorflow_macos 
for instructions and license information.

This script will download tensorflow_macos 0.1alpha1 and needed binary dependencies, then install them into a new 
or existing Python 3.8 virtual enviornoment.
Continue [y/N]? y

(略)

./install_venv.sh will perform the following operations:
-> Create new python 3.8 virtual environment at /Users/Keras/tensorflow_macos_venv.
-> Install tensorflow_macos 0.1a1 into created virtual environment /Users/Keras/tensorflow_macos_venv.
-> Install bundled binary wheels for numpy-1.18.5, grpcio-1.33.2, h5py-2.10.0, scipy-1.5.4, tensorflow_addons-0.11.2+mlcompute into /Users/Keras/tensorflow_macos_venv.

Confirm [y/N]? y

(略)

TensorFlow and TensorFlow Addons with ML Compute for macOS 11.0 successfully installed.

To begin, activate the virtual environment:

   . "/Users/Keras/tensorflow_macos_venv/bin/activate" 

~

1-2) tensorflow-macosの仮想環境をアクティベート

Keras@mini2018 ~ % source /Users/Keras/tensorflow_macos_venv/bin/activate
(tensorflow_macos_venv) Keras@mini2018 ~ % 

1-3) pillow のインストール

引き続きコマンドラインから python で画像を扱うライブラリ pillow をインストールする。

(tensorflow_macos_venv) Keras@mini2018 ~ % pip install pillow

1-4) scikit-learn のインストール

Pythonの機械学習ライブラリの scikit-learn をインストールする。

(tensorflow_macos_venv) Keras@mini2018 ~ % pip install scikit-learn

1-5) keras のインストール

TensorFlowのラッパーライブラリ keras をインストールする。

(tensorflow_macos_venv) Keras@mini2018 ~ % pip install keras

1-6) flickrapi のインストール

機械学習用の画像を入手するため、写真投稿コミュニティのflickr を利用する。Pythonから flickr API を利用するためのライブラリがあるので、これをインストールする。提供元はこちらのサイト

(tensorflow_macos_venv) Keras@mini2018 ~ % pip install flickrapi

2. 学習用画像の準備

機械学習に使う画像を 写真投稿コミュニティのflickr から入手する。

画像取得までの流れ

  • flickr API key と secret を入手
  • Python を使って画像を入手

2-1) flickr にアクセスして Sign Up する

アカウントがなければここで作成する。
flickr.png

2-2) App Garden にアクセスする

The App Gardenにアクセスすると、Flickrが提供するAPIの一覧を確認できる。
appgarden.png

ここから Flickr APIを利用するための Key と Secret を入手する。

2-3) App Garden のページの左上の「Create an App」をクリック

create_app.png

2-4) Get your API Key をクリック

create_app_2.png

2-5) 非商用か商用かを選択する。今回は非商用を選択

create_app_3.png

2-6) アプリ名とアプリの目的を入力し「SUBMIT」をクリック。

appname.png

2-7) KeyとSecretが表示されるのでメモる

key_and_secret 2.png

2-8) 欲しい画像のキーワードを引数にとって画像をダウンロードするプログラムを準備

downloader.py
import urllib.request
import flickrapi
import time, os, sys

# ダウンロードしたい画像のキーワード
keyword = sys.argv[1]
# flickr アクセス用
api_key = "★★★入手した key をここに指定★★★"
api_secret = "★★★入手した secret をここに指定★★★"

# ダウンロードした画像の格納フォルダを作成
folder = keyword
if not os.path.exists(folder):
    os.mkdir(folder)
    
# Flickr API ライブラリ インスタンス化
flickr = flickrapi.FlickrAPI(api_key, api_secret, format='parsed-json')
# flickrからキーワードで写真一覧を取得
response = flickr.photos.search(
    text = keyword,     # 取得する写真のキーワード
    media = 'photos',   # 写真のみ
    safe_search = 1,    # 不適切なコンテンツを除くための指定。1が一番安全。
    per_page = 500,     # 欲しい画像数を指定(1ページあたり最大500枚まで)
    extras = 'url_q'    # 150x150の画像ファイルURLをresponseに含める指定。
)

photos = response['photos']
print('response pages:' + str(photos['pages']) + ' perpage:' + str(photos['perpage']))

for photo in photos['photo']:
    image_url = photo['url_q']
    print(image_url)
    filename = image_url.rsplit('/', 1)[1]
    filepath = folder + '/' + filename
    # URLからファイルをダウンロード
    urllib.request.urlretrieve(image_url, filepath)
    # スパム判定されないようにウェイトを入れておく
    time.sleep(0.5)

参考:

2-9) 画像をダウンロードする

入手したい画像のキーワードを指定して画像をダウンロードする。

(tensorflow_macos_venv) Keras@mini2018 BotanicalPB % python downloader.py lilium
response pages:117 perpage:500
https://live.staticflickr.com/65535/50863926903_730d70a26d_q.jpg
https://live.staticflickr.com/1914/45868122311_b85a29090c_q.jpg
...

2-10) 不適切な画像を削除する。

ダウンロードした画像ファイルの中には、想定した画像とは異なるものが含まれるので、これを手作業で削除する。

3. 学習用と検証用のデータの作成

入手した画像を学習用と検証用のデータに加工する。

3-1) Numpy形式に加工するプログラムを準備

画像を読み込み、学習用と検証用のデータに分類し、それぞれnumpy形式に変換後、ファイルに保存するプログラムを準備。
※この記事ではマーガレット、スターチス、ゆりの花の画像の分類を行うため、それぞれの画像を240枚準備済みの前提。

data_generator.py
from PIL import Image
from sklearn import model_selection
import os
import glob
import numpy as np

# マーガレット、スターチス、ゆり の画像分類
classes = ["marguerite", "statice", "lilium"]
# 全画像ファイル数
file_count = 240
# 検証用ファイル数
test_file_count = 40
# 50x50pxの画像で学習させる
image_size = 50
# 学習用のデータと検証用のデータの器
X_train = []    # 学習用画像
X_test = []     # 学習用のラベル
Y_train = []    # 検証用画像
Y_test = []     # 検証用のラベル

for class_index, class_label in enumerate(classes):
    # 分類ごとの画像ファイルを取得
    files = glob.glob("./" + class_label + "/*.jpg")
    files = files[:file_count]
    print("Label: " + class_label + " File Count: " + str(len(files)))
    
    for file_index, file in enumerate(files):
        # ファイル読み込み
        image = Image.open(file)
        # RGB(8bit x 3)に変換
        image = image.convert("RGB")
        # リサイズ
        image = image.resize((image_size, image_size))
        # 配列として扱えるようにする
        data = np.asarray(image)
        # 学習用と検証用に画像を振り分ける
        if file_index < test_file_count:
            X_test.append(data)
            Y_test.append(class_index)
        else:
            X_train.append(data)
            Y_train.append(class_index)

# 画像データをnumpy形式で保存する
X_train = np.array(X_train)
X_test = np.array(X_test)
y_train = np.array(Y_train)
y_test = np.array(Y_test)
# タプルで保存
xy = (X_train, X_test, y_train, y_test)
np.save("./botanical.npy", xy)

3-2) Numpy形式に加工する

プログラムを実行する。

(tensorflow_macos_venv) Keras@mini2018 BotanicalPB % python data_generator.py 
Label: marguerite File Count: 240
Label: statice File Count: 240
Label: lilium File Count: 240

4. トレーニング

4-1) トレーニングプログラムの準備

画像分類の学習済みモデルを作成するため、KerasのCIFAR-10のコードを流用したプログラムを準備する。

※この記事を書いている途中(2021/1月中旬くらい)にKerasのGithubから CIFAR-10のコードが削除(移動?)されてしまった模様。

botanical_cnn.py
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.utils import np_utils
import keras, time
import numpy as np

classes = ["marguerite", "statice", "lilium"]

def main():
    x_train, x_test, y_train, y_test = np.load("./botanical.npy", allow_pickle=True)
    # データの正規化。0〜255 -> 0〜1に変換
    x_train = x_train.astype("float") / 256
    x_test = x_test.astype("float") / 256
    # one-hotベクトルを作る
    y_train = np_utils.to_categorical(y_train, len(classes))
    y_test = np_utils.to_categorical(y_test, len(classes))
    # モデルを作る
    model = generate_model()
    model.summary()
    # トレーニング
    start = time.perf_counter()
    model.fit(x_train, y_train, batch_size=32, epochs=100)
    end = time.perf_counter()
    print("Elapsed time : {0} s.".format(end-start))
    # モデルの精度を測る
    test_loss, test_acc = model.evaluate(x_test, y_test, verbose=1)
    print('test_loss: ', test_loss)
    print('test_acc: ', test_acc)
    # モデルの保存
    model.save('./botanical_cnn.h5')

# モデルを作る(CIFAR-10)
def generate_model():

    model = Sequential()
    model.add(Conv2D(32, (3, 3), padding='same', input_shape=(50,50,3)))
    model.add(Activation('relu'))
    model.add(Conv2D(32, (3, 3)))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))

    model.add(Conv2D(64, (3, 3), padding='same'))
    model.add(Activation('relu'))
    model.add(Conv2D(64, (3, 3)))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))
    
    model.add(Flatten())
    model.add(Dense(512))
    model.add(Activation('relu'))
    model.add(Dropout(0.5))
    # 出力は"marguerite", "statice", "lilium" の3つ
    model.add(Dense(3))
    model.add(Activation('softmax'))

    opt = keras.optimizers.RMSprop(lr=0.0001, decay=1e-6)
    model.compile(loss='categorical_crossentropy',
                  optimizer=opt,
                  metrics=['accuracy'])
    return model

if __name__ == '__main__':
    main()

4-2) トレーニング実行

(tensorflow_macos_venv) Keras@mini2018 BotanicalPB % python botanical_cnn.py 
  • CPUでのトレーニング結果
Elapsed time : 231.572187988 s.
4/4 [==============================] - 0s 12ms/step - loss: 2.6364 - accuracy: 0.7833
test_loss:  2.6363558769226074
test_acc:  0.7833333611488342
  • eGPU接続状態でのトレーニング結果
Elapsed time : 41.286664788 s.
4/4 [==============================] - 0s 11ms/step - loss: 0.7714 - accuracy: 0.8750
test_loss:  0.7714245915412903
test_acc:  0.875

 下図はトレーニング時のGPU負荷の状況。eGPUに負荷がかかっていることがわかる。
eGPU.png

  • eGPU未接続・GPUトレーニング有効化

 参考にさせていただいたこちらの記事 【Apple Silicon M1 でtensorflow-macosを実行したらめちゃくちゃ速かった。】 にあるようにtensorflowを動作させるデバイスを次のようにすることで選択できるので、gpu を指定して内蔵GPUでトレーニングさせる。

python
from tensorflow.python.compiler.mlcompute import mlcompute
mlcompute.set_mlc_device(device_name="gpu")

 結果はやたら遅い。

Elapsed time : 606.924531516 s.
4/4 [==============================] - 1s 113ms/step - loss: 0.7534 - accuracy: 0.8500
test_loss:  0.7534401416778564
test_acc:  0.8500000238418579

 内蔵GPUに負荷はかかっている。
internal_gpu.png
internal_gpu_per.png

5. mlmodel に変換

 学習済みのモデルを Vison+CoreML から利用できるように mlmodel 形式に変換する。
変換はAppleが提供している coremltools を利用する。

で、tensorflow-macos(0.1alpha1) + coremltools4.0 環境では変換実行時にエラーになる。

(tensorflow_macos_venv) Keras@mini2018 BotanicalPB % pip list | grep coremltools
coremltools            4.0
(tensorflow_macos_venv) Keras@mini2018 BotanicalPB % python convert_h5.py 
WARNING:root:scikit-learn version 0.24.1 is not supported. Minimum required version: 0.17. Maximum required version: 0.19.2. Disabling scikit-learn conversion API.
WARNING:root:TensorFlow version 2.4.0-rc0 detected. Last version known to be fully compatible is 2.3.1 .
WARNING:root:Keras version 2.4.3 detected. Last version known to be fully compatible of Keras is 2.2.4 .
Running TensorFlow Graph Passes:   0%|                   | 0/5 [00:00<?, ? passes/s]ERROR:root:Constant Propagation pass failed: invalid version number '2.4.0-rc0'
Traceback (most recent call last):
  File "/Users/Keras/tensorflow_macos_venv/lib/python3.8/site-packages/coremltools/converters/mil/frontend/tensorflow/tf_graph_pass/constant_propagation.py", line 79, in _constant_propagation
    if tf.__version__ < _StrictVersion("1.13.1"):
  File "/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/distutils/version.py", line 64, in __gt__
    c = self._cmp(other)
  File "/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/distutils/version.py", line 168, in _cmp
    other = StrictVersion(other)
  File "/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/distutils/version.py", line 40, in __init__
    self.parse(vstring)
  File "/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/distutils/version.py", line 137, in parse
    raise ValueError("invalid version number '%s'" % vstring)
ValueError: invalid version number '2.4.0-rc0'

 この問題はすでにcoremltools側で修正されているようだが 、リリースされていないので、ここでは、tensorflow-macos を使わない環境を別に用意する。

5-1) coremltools利用環境を作る

新たにPythonの仮想環境を作る。

Keras@mini2018 BotanicalPB % python3 -m venv env

Keras@mini2018 BotanicalPB % source env/bin/activate

(env) Keras@mini2018 BotanicalPB % pip install tensorflow

(env) Keras@mini2018 BotanicalPB % pip install keras 

(env) Keras@mini2018 BotanicalPB % pip install coremltools

(env) Keras@mini2018 BotanicalPB % pip install pillow

5-2) 変換プログラムの準備

convert_h5.py
import coremltools as ct
from PIL import Image

# ラベル定義
class_labels = ['marguerite', 'statice', 'lilium']
classifier_config = ct.ClassifierConfig(class_labels)

# mlmodelに変換
coreml_model = ct.converters.convert('botanical_cnn.h5',
    inputs=[ct.ImageType()],
    classifier_config=classifier_config,
    source='TensorFlow',)

# mlmodelを保存
coreml_model.save('./botanical_cnn.mlmodel')

# 推論して mlmodel に正しく変換できたか確認
# https://coremltools.readme.io/docs/model-prediction
test_image = Image.open("lily2.jpg").resize((50, 50))
data = {'conv2d_input': test_image}
out_dict = coreml_model.predict(data)
print(out_dict['classLabel'])

 classifier_config=classifier_config でラベルの配列を指定しないと、Vision+CoreML の実行時にエラーになるので重要。この部分、coremltools4.0での解決方法が見つからなくてcoremltoolsのドキュメント を眺めていて解決。
 もう一点、推論に与える画像の指定で'conv2d_input' はmlmodel変換後にXcodeで確認できる(Xcodeで確認するなら、Preview タブで推論の動作確認できちゃうのでmlmodel変換時に推論確認する意義は小さいかも)。

inputname.png

5-3) 変換の実施

(env) Keras@mini2018 BotanicalPB % python convert_h5.py
WARNING:root:TensorFlow version 2.4.1 detected. Last version known to be fully compatible is 2.3.1 .
WARNING:root:Keras version 2.4.3 detected. Last version known to be fully compatible of Keras is 2.2.4 .
Running TensorFlow Graph Passes: 100%|███████| 5/5 [00:00<00:00, 16.11 passes/s]
Converting Frontend ==> MIL Ops: 100%|███████| 36/36 [00:00<00:00, 566.06 ops/s]
Running MIL optimization passes: 100%|████| 17/17 [00:00<00:00, 404.85 passes/s]
Translating MIL ==> MLModel Ops: 100%|███████| 53/53 [00:00<00:00, 113.73 ops/s]
marguerite

ワーニングが出まくっているが、一応変換はできている。
mlmodel変換後の推論もできている(マーガレットの画像を与えて分類できている)。

6. Vison+CoreMLで推論

変換したmlmodelを使って、任意の画像の推論をする。
Playgroundを使って試してみた結果は次の通り。

  • mlmodelを読み込んで推論するコード
import UIKit
import CoreML
import Vision

guard let model = try? VNCoreMLModel(for: botanical_cnn(configuration: MLModelConfiguration()).model) else {
    fatalError()
}

let request = VNCoreMLRequest(model: model) { request, error in
    guard let results = request.results as? [VNClassificationObservation],
          let result = results.first else {
        fatalError()
    }
    
    print("Confidence: \(result.confidence * 100) % Label: \(result.identifier)")
}

guard let image = UIImage(named: "lily.jpg"),
      let cgimage = image.cgImage else { fatalError() }
let handler = VNImageRequestHandler(cgImage: cgimage, options: [:])

try? handler.perform([request])

  • Playground の設定

変換した mlmodel と分類したい画像(ここでは「ゆり」)を組み込んでおく。
playground.png

  • 実行結果

result.png

画像分類ができた。

参考にさせていただいた記事

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?