本書はCore ML Toolsの実践入門書です。KerasやTensorFlowを用いて構築した機械学習/ディープラーニングのモデルをiOSで利用するために必要な変換ツールである「Core ML Tools」(Pythonパッケージ名としてはcoremltools
)の利用方法をさまざまなモデルをつくりながら学んでいきます。
最初はわずか2行のコードで変換することからはじめてCore ML Toolsに入門しつつ、もっと高度な変換方法や、モデルのサイズを縮小する方法、パイプラインを構築する歩法、オンデバイスで学習できるようにする方法等も解説していきます。
また巻末には「逆引きリファレンス」も収録しています。開発の現場で「どう書くんだっけ?」となったときに大いに役立つことでしょう。
筆者はiOSエンジニアであり、基本的にその目線で書いてあります。したがって本書のiOSパートはXcodeやSwiftやUIKitについては習熟していることが前提になります。coremltools
パートはiOSの知識は必要ないものの、やはりPythonの構文等については解説していません。また「機械学習とは」「ディープラーニングとは」といった内容や、ニューラルネットワークの各種レイヤーや関数の意味や効果についても、既に多くの良書があるため本書では解説していません。
iOSの基礎と機械学習の概念は理解しており、Core MLモデルを自作する方法について学びたい、という場合には本書は役立つでしょう。
本書は以下の環境に準拠しています。
本書のサンプルコードはhttps://github.com/shu223/iOSMLBook
で公開しています。
はじめに
第1章 準備
第2章 Core ML Toolsはじめの一歩
第3章 Core MLモデル作成の基礎
第4章 オンデバイス学習 - UpdatableなCore MLモデルの作成
第5章 オンデバイス学習 - iOSで学習
第6章 TensorFlowモデルの変換 - 基礎編
第7章 TensorFlowモデルの変換 - 画風変換モデル
第8章 Flexible Shape - 超解像モデル
第9章 Core MLモデルのサイズを小さくする
第10章 パイプラインモデルとリンクモデル(Linked Model)
第11章 モデルの可視化
第12章 mlmodelファイルフォーマット
付録A coremltools逆引きリファレンス
付録B Keras入門
参考文献
Core ML Toolsは、さまざまな機械学習フレームワークで作成されたモデルをCore MLモデルフォーマット(.mlmodel
)に変換するツールです。
GitHubのAppleアカウント配下でオープンソースとして運用されています。
2020年3月現在、coremltools
の最新リリースバージョンは3.3
で、以下のフォーマットをサポートしています*1。
.prototxt
, .caffemodel
format).h5
format).pb
frozen graph def format).h5
and SavedModel formats)developer.apple.com
では主に"Core ML Tools"と表記され、GitHubのリポジトリやAPIドキュメント(https://apple.github.io/coremltools/)では主に"coremltools"と表記されており正式な表記に迷いますが、本書ではツール名称としては前者、Pythonパッケージとしての文脈では後者の表記を用いることにします。
環境構築手順を解説します。本書ではCore ML Toolsの入門として、Keras, TensorFlowで作成したモデルの変換を行うので、それらもインストールします*2。なお前提として、筆者はAnacondaでPython 3.7の仮想環境を用意しました*3。
まずはcoremltools
をインストールします。
$ pip install -U coremltools
手順としてはこれだけです。次のようにPythonコードを実行すると、利用中のCore ML Toolsのバージョンが確認できます。
$ python
>>> import coremltools
>>> print(coremltools.__version__) # 3.3
最新リリース版にアップデートする際にはまた同様に-U
/--upgrade
オプションをつけてpip install
を実行してください。
$ pip install -U coremltools
インストールしたCore ML Toolsバージョンに合うTensorFlowバージョンの番号を指定してインストールを実行します。
$ pip install tensorflow==1.14.0
こちらも次のようにバージョン確認できます。
$ python
>>> import tensorflow
>>> print(tensorflow.__version__) # 1.14.0
サンプルコード: coremltools/keras_helloworld.ipynb
Kerasが利用可能となっているはずなので、確認としてバージョンを出力してみましょう。
import keras
print('Hello, Keras. version:', keras.__version__)
上記のコードをJupyter Notebookに打ち込んで、実行してください。以下のような出力が得られます。
Hello, Keras. version: 2.2.4
Kerasが利用可能となっていることがわかります。またバージョンとしてもCore ML Toolsがサポートしているものとなっていることが確認できます。
なお、本書では使用しませんが、TensorFlow同梱版のKerasを利用する場合はtensorflow.keras
をインポートします。
import tensorflow as tf
import tensorflow.keras as keras
print(keras.__version__) # 2.2.4-tf
[*2] Kerasについては付録B「Keras入門」で基礎から解説しています。
[*3] 本書ではサンプル実行にJupyter Notebookを利用するため、仮想環境にはJupyterもインストールしています。Anaconda環境であれば、conda install jupyter
でインストールできます。
まずはKerasに用意されている学習済みモデルをCore MLモデルに変換してみるところから始めてみましょう。モデルを構築するところが省けるので非常にシンプルなコードでKerasやCore ML Toolsがどういうものかを学べますし、また既存モデルの取り扱いは今後ファインチューニングや転移学習を行う際にも必要となってきます。
サンプルコード: coremltools/keras_firststep.ipynb
Jupyter Notebookで新規ノートブックを作成し、次のコードを書いてください。
# VGG16をインポート
from keras.applications.vgg16 import VGG16
# VGG16オブジェクトを生成
model = VGG16()
たったこれだけのコードで、Kerasが持っている学習済みモデルのひとつVGG16
を使用する準備ができました。確認のため、summary()
メソッドを用いてモデルのサマリを出力してみましょう。
model.summary()
実行すると以下のような出力が得られます。
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) (None, 224, 224, 3) 0
_________________________________________________________________
block1_conv1 (Conv2D) (None, 224, 224, 64) 1792
_________________________________________________________________
block1_conv2 (Conv2D) (None, 224, 224, 64) 36928
_________________________________________________________________
block1_pool (MaxPooling2D) (None, 112, 112, 64) 0
_________________________________________________________________
block2_conv1 (Conv2D) (None, 112, 112, 128) 73856
_________________________________________________________________
block2_conv2 (Conv2D) (None, 112, 112, 128) 147584
_________________________________________________________________
block2_pool (MaxPooling2D) (None, 56, 56, 128) 0
_________________________________________________________________
(中略)
_________________________________________________________________
flatten (Flatten) (None, 25088) 0
_________________________________________________________________
fc1 (Dense) (None, 4096) 102764544
_________________________________________________________________
fc2 (Dense) (None, 4096) 16781312
_________________________________________________________________
predictions (Dense) (None, 1000) 4097000
=================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
_________________________________________________________________
VGG16*1のネットワークが得られていることが確認できます。
この学習済みモデルをCore ML ToolsでCore MLモデルに変換してみましょう。
KerasのモデルをCore ML形式に変換するには、coremltools.converters.keras
のconvert
メソッドを使用します。第1引数にはKerasモデルのオブジェクトを渡します*2。
import coremltools
mlmodel = coremltools.converters.keras.convert(model)
上記コードを実行すると次のようなログが出力されます。
0 : input_1, <keras.engine.input_layer.InputLayer object at ...>
1 : block1_conv1, <keras.layers.convolutional.Conv2D object at ...>
2 : block1_conv1__activation__, <keras.layers.core.Activation object at ...>
3 : block1_conv2, <keras.layers.convolutional.Conv2D object at ...>
4 : block1_conv2__activation__, <keras.layers.core.Activation object at ...>
5 : block1_pool, <keras.layers.pooling.MaxPooling2D object at ...>
(中略)
32 : flatten, <keras.layers.core.Flatten object at ...>
33 : fc1, <keras.layers.core.Dense object at ...>
34 : fc1__activation__, <keras.layers.core.Activation object at ...>
35 : fc2, <keras.layers.core.Dense object at ...>
36 : fc2__activation__, <keras.layers.core.Activation object at ...>
37 : predictions, <keras.layers.core.Dense object at ...>
38 : predictions__activation__, <keras.layers.core.Activation object at ...>
前項で使用したconvert
メソッドでは、返り値としてMLModel
クラスのオブジェクトが得られます。同クラスが持つsave
メソッドを用いてCore MLモデルを.mlmodel
ファイルとして保存することができます。
mlmodel.save('./VGG16.mlmodel')
作成した.mlmodel
ファイルがちゃんとCore MLモデルファイルになっているか確認してみましょう。.mlmodel
ファイルをXcodeで開いてください。
しっかりCore MLモデルとして生成できていることがわかります。
さて、ここまで正味のコードはimport
を入れてもわずか5行です。たったこれだけのコードでKerasからモデルを読み込み、Core ML Toolsで変換し、iOSで使えるCore MLモデルファイルを出力できたことになります。
# VGG16モデルをロード
from keras.applications.vgg16 import VGG16
model = VGG16()
# Core MLモデル形式に変換
import coremltools
mlmodel = coremltools.converters.keras.convert(model)
# 変換したモデルを.mlmodelファイルとして保存
mlmodel.save('./VGG16.mlmodel')
[*2] coremltools.converters.keras.convert
には他にも多くの引数がありますが、第1引数以外はすべてoptionalです。そのうちいくつかの引数については以降の章で使用します。
本章では前章からは一歩進んで、Core MLモデルにクラスラベルの情報を持たせたり、入出力の型を変更する方法について解説していきます。いずれもCore MLモデル作成において必須の知識となります。
サンプルコード: coremltools/mnist_cnn_convert.ipynb
なお、本章で用いる学習済みモデルは筆者がKerasで独自に構築・学習したモデルです。Kerasを用いたカスタムモデルの作成方法の基礎については付録B「Keras入門」で解説しています。
Kerasで作成したHDF5形式(.h5
)のモデルファイルを読み込むには、keras.models.load_model(filepath)
メソッドを使用します。
from keras.models import load_model
keras_model = load_model('./KerasMnist.h5')
coremltools.converters.keras.convert
メソッドを用いて変換を行います。
from coremltools.converters import keras as converter
class_labels = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
mlmodel = converter.convert(keras_model,
input_names=['image'],
output_names=['digitProbabilities'],
class_labels=class_labels,
predicted_feature_name='digit')
前章とは違い、第1引数にKerasモデルオブジェクトを渡しているだけではなく、他のさまざまな引数を用いている点に注目してください。
引数class_labels
にはその名の通りクラスラベルを渡します。上のコード例のように文字列の配列で渡すか、クラスラベルファイル*1のパスを文字列で渡します。
引数input_names
には、Core MLモデルの入力の名前のリストを渡します。何も指定しなければ名前は[input1, input2, …, inputN]
となります。引数output_names
は同様にCore MLモデルの出力の名前を与えるもので、指定しなければ[output1, output2,
…, outputN]
となります。第2章でconvert
を実行した際にはこれらを指定しなかったため、デフォルトの名前が付いていました。
引数predicted_feature_name
には、Core MLモデルのクラスラベル出力の名前を与えます。デフォルトではclassLabel
となります。
戻り値の型はMLModel
です。Core ML形式のモデルを表すクラスです。
ではconvert
メソッドを実行してみましょう。次のようなログが出力されます。
0 : conv2d_1_input, <keras.engine.input_layer.InputLayer object at ...>
1 : conv2d_1, <keras.layers.convolutional.Conv2D object at ...>
2 : conv2d_1__activation__, <keras.layers.core.Activation object at ...>
...
変換したCore MLモデルを.mlmodel
ファイルとして保存します。第2章と同様にsave
メソッドを使用します。
coreml_model_path = './MNISTDigitClassifier.mlmodel'
mlmodel.save(coreml_model_path)
保存した.mlmodel
ファイルをXcodeで開いてプレビューしてみましょう。
Core ML Toolsでの変換時に指定した各名前がきちんと反映されていますね。
しかし、入力の型が"MultiArray (Double 1x28x28)"となっている点に注目してください。このCore MLモデルを用いてiOSで推論処理を実装しようとすると、VNCoreMLModel
を初期化しようとするところで、
let model = try! VNCoreMLModel(for: MNISTDigitClassifier().model)
次のような実行時エラーになってしまいます。
Error Domain=com.apple.vis Code=15 "The model does not have a valid input feature of type image"
Visionを用いてCore MLモデルを利用する実装を行う場合は、入力の型がMultiArray(MLMultiArray
)ではなくCVPixelBuffer
等の画像型である必要があります。
画像を入力とするCore MLモデルをiOSで使用する場合は、上位レイヤーであるVisionフレームワークを用いて実装したほうが(Core MLフレームワーク単体で実装するよりも)簡単になります。Visionを使用できるよう、Core MLモデルの入力の型をMultiArrayからImageにCore ML Toolsで変更します。
まず、作成したモデルの入力形式をcoremltools
を用いて確認してみましょう。
coremltools.models.utils
のload_spec
メソッドを用いてモデルの仕様を表すModel_pb2.Model
オブジェクト(以下specオブジェクト)を取得します。引数にはファイルパスを渡します。
import coremltools
spec = coremltools.utils.load_spec(coreml_model_path)
次にcoremltools.models.neural_network.builder.NeuralNetworkBuilder
クラスを初期化します。spec
引数には取得したspecオブジェクトを渡します。
builder = coremltools.models.neural_network.NeuralNetworkBuilder(spec=spec)
NeuralNetworkBuilder
はCore MLモデルを構築するためのクラスです。inspect_input_features
メソッドでモデルの入力に関する情報を出力できます。
builder.inspect_input_features()
(出力)
[Id: 0] Name: image
Type: multiArrayType {
shape: 1
shape: 28
shape: 28
dataType: DOUBLE
}
MNISTDigitClassifier.mlmodel
をXcodeでプレビューした際に入力形式が"MultiArray (Double 1x28x28)"となっていたことと一致します。
NeuralNetworkBuilder
を用いて入力の型を28x28のグレースケール画像に変更します。モデルの入力を表すオブジェクトへはbuilder.spec.description.input[{インデックス番号}]
のようにアクセスできます*2。
from coremltools.proto import FeatureTypes_pb2 as ft
grayscale = ft.ImageFeatureType.ColorSpace.Value('GRAYSCALE')
input_image_type = builder.spec.description.input[0].type.imageType
input_image_type.width = 28
input_image_type.height = 28
input_image_type.colorSpace = grayscale
変更されたか確認してみましょう。
builder.inspect_input_features()
(出力)
[Id: 0] Name: image
Type: imageType {
width: 28
height: 28
colorSpace: GRAYSCALE
}
"Type: imageType"となり、以前は"dataType: DOUBLE"だったところが"colorSpace: GRAYSCALE"となりました。
specからMLModel
オブジェクトを改めて生成し、save
メソッドで保存します。
mlmodel_modified = coremltools.models.MLModel(spec)
mlmodel_modified.save('./ModifiedMNISTDigitClassifier.mlmodel')
保存した.mlmodel
をXcodeでプレビューしてみましょう。
入力の型が"Image (Grayscale 28x28)"となっていることが確認できます。
.mlmodel
ファイルをXcodeプロジェクトに追加すると、Swiftコードが自動生成されます。自動生成されたSwiftコードは、プロジェクト内で.mlmodel
ファイルを選択したときに表示されるプレビュー内の、[Model Class]欄にある矢印ボタンを押すと見ることができます。
これらを見ると、coremltools
のconvert
メソッドの各種引数に渡した値がどうCore MLモデルに影響したかをより具体的に知ることができます。
たとえば本書で作成したModifiedMNISTDigitClassifier.mlmodel
からは次のようなクラスの実装が自動生成されます(いずれも一部を抜粋)。
- モデルを表すクラス。MLModel
型のmodel
プロパティを持つ。
class ModifiedMNISTDigitClassifier {
var model: MLModel
...
}
- モデルへの入力を表すクラス。coremltools
で与えた入力の名前や型が反映されている。
class ModifiedMNISTDigitClassifierInput : MLFeatureProvider {
var image: CVPixelBuffer
var featureNames: Set<String> {
get {
return ["image"]
}
}
...
init(image: CVPixelBuffer) {
self.image = image
}
}
- モデルの出力を表すクラス。coremltools
で与えた名前がインターフェースに反映されている。
class ModifiedMNISTDigitClassifierOutput : MLFeatureProvider {
lazy var digitProbabilities: [String : Double] = ...
lazy var digit: String = ...
...
init(digitProbabilities: [String : Double], digit: String) {
...
}
...
}
前節までに作成した手書き数字認識のCore MLモデルファイルを用いて、iOSで推論を行います。Visionフレームワークを使用します。
サンプルコード: iOS/MNISTSample
.mlmodel
ファイルをXcodeプロジェクトに追加します。モデルが正しく生成されていれば、Xcodeが必要なSwiftコードを自動生成してくれます。
まず、VNCoreMLModel
オブジェクトを生成します。引数にはCore MLから生成されたクラスのmodel
プロパティからMLModel
オブジェクトを渡します。
private let model: VNCoreMLModel
model = try! VNCoreMLModel(for: ModifiedMNISTDigitClassifier().model)
次に、VNCoreMLRequest
オブジェクトを生成します。イニシャライザの引数にはVNCoreMLModel
オブジェクトと、推論処理完了時に実行するクロージャを渡します。
let request =
VNCoreMLRequest(model: model, completionHandler: { request, error in
// 推論処理完了後に行う処理
...
})
VNImageRequestHandler
オブジェクトを生成します。イニシャライザの引数には入力画像データをCGImage
型やCVPixelBuffer
型で渡せませす。
let handler = VNImageRequestHandler(cgImage: inputImage)
そしてVNImageRequestHandler
のperform
メソッドを呼ぶと推論処理が開始します。
try! handler.perform([request])
引数にはVNRequest
オブジェクトの配列を渡します。VNCoreMLRequest
クラスはVNRequest
クラスを継承しています。
推論処理が完了すると、VNCoreMLRequest
のイニシャライザで第2引数に渡したクロージャが実行されます。クロージャの型はVNRequestCompletionHandler
で、次のように定義されています。
public typealias VNRequestCompletionHandler = (VNRequest, Error?) -> Void
この定義の通り、引数には実行したVNRequest
のオブジェクトが入ってくるので、そのresults
プロパティから推論処理の結果が取り出せます。ただし、次のようにその型はAny
の配列となっています。VNRequest
自体はVisionフレームワークにおけるさまざまなリクエストの基底クラスとなるもので、そのリクエストの種類に応じて結果の型も変わるためです。
var results: [Any]?
今回のモデルは画像の分類(classification)を行うものなので、このresults
はVNClassificationObservation
型の配列となります。
let request =
VNCoreMLRequest(model: model, completionHandler: { request, error in
let observations = request.results as! [VNClassificationObservation]
...
})
VNClassificationObservation
のidentifier
プロパティからはクラスラベルがString
型で取得でき、
var identifier: String
また当該クラスの信頼度がconfidence
プロパティよりVNConfidence
型で取得できます*3。
var confidence: VNConfidence { get }
次の画像は作成したCore MLモデルを使って実装した手書き数字認識デモアプリです。
手書きビューの下にあるラベルが認識結果を示しています。実際に正しく手書き数字を認識できていることがわかります。
Visionは次の場合、VNCoreMLRequest
に渡されたMLModel
が画像分類モデルであると判断します*4。
MLModel
のmodelDescription
プロパティから得られるMLModelDescription
オブジェクトのpredictedFeatureName
がnil
ではない値を持つこと。// MLModelのプロパティ
var modelDescription: MLModelDescription { get }
// MLModelDescriptionのプロパティ
var predictedFeatureName: String? { get }
[*1] 1行ごとに各クラスラベルの文字列が入ったファイル。
[*2] 具体的には、{NeuralNetworkBuilderオブジェクト}.spec
でModel_pb2.Model
オブジェクトを取得しており、*.proto
の定義に従ってその内容をカスタマイズしています。詳しくは第12章「mlmodelファイルフォーマット」を参照してください。
[*3] confidence
プロパティはVNClassificationObservation
の親クラスであるVNObservation
クラスで定義されています。
Core ML 3で追加された待望の新機能が、iOSデバイス側(エッジ側)で追加のデータを与えて学習を行い、モデルを更新できるというものです。
True-Depthカメラを利用したFace IDでは以前からこのオンデバイス学習によるモデルのパーソナライゼーションを行っていたと考えられていますが、ついにこの機能をサードパーティのiOSデベロッパも利用できるようになったわけです。最初から汎用的なモデルを作成することは困難なケースもあるため、この機能はCore MLのユースケースを大きく広げるでしょう。
本章ではそのUpdatableなCore MLモデルの作成方法を解説します。具体的には、付録B「Keras入門」および第3章「Core MLモデル作成の基礎」で作成した手書き数字認識と同様のモデルを、UpdatableなCore MLモデルに変換します。
サンプルコード: coremltools/mnist_cnn_make_updatable.ipynb
なお、UpdatableなCore MLモデルの作成にはCore ML Tools 3.0以上が必要で、モデルの利用(推論処理の実行)にはCore ML 3以上(iOS 13以上)が必要です。