More Related Content Similar to Transfer Learning & API Azure (20) More from Yuki Hattori (8) Transfer Learning & API Azure1. Transfer Learning & API Azure
Transfer Learning(転移学習)を用いたオリジナル画像認識AIを、
アプリケーションに組み込むためのAPI開発と実装 (CNTK)
日本マイクロソフト株式会社
アプリケーション ソリューション アーキテクト
服部 佑樹(Yuki Hattori)
2. 目的とゴール
✓CNTKが ”使える” ようになる = CNTKの “実装力” をつける
✓アプリケーションに人工知能 (AI) を組み込めるようにする
✓継続的にアプリとモデルのメンテナンスができるようになる
➡ Deep Learningの概要を学ぶためには「Deep Learning 理論編」
を参照ください。
➡ Deep Learning (CNTK)に適した環境構築は「深層学習 環境構築 Azure」
を参照ください
➡ Deep Learningの概要が理解できたらこの資料を参考に、アプリ
ケーションに人工知能を組み込みましょう。
2
5. トレーニングの流れ
Transfer Learning (転移学習) を理解する
✓ まずTransfer Learningの仕組みを理解します
モデルをトレーニングする
Pythonサンプルコードの TransferLearning.py を理解する
✓ CNTKでTransfer Learningを利用する場合、Pythonを使うことができます。
✓ TransferLearning.pyがGitHubに公開されているので読み解いていきましょう
Pythonサンプルコードの TransferLearning_Extended.py を理解する
✓ TransferLearning.pyをラップして使いやすくしたのがTransferLearning_Extended.py です。こちらもGitHubに公開されています。
✓ このプログラムによりマップファイル(画像Pathとラベルの対応ファイル)の作成や実行が多少容易になります。
✓ Transfer Learningを利用するにあたりこちらも学習しましょう。
モデルをデプロイする
✓ C#を使い、Webにデプロイしましょう。
精度を高めていく
5
9. Neural Network – classification
例えば、今回のように野球選手の画像からコスモス/ひまわり/チューリップを分類しようと
すると、下記のようなネットワーク構造が考えられます。ただし、カラー画像に関しては1
点注意が必要です。
9
入力データ Input Layer Hidden Layer
(複数)
Output Layer
チューリップ
コスモス
ひまわり
0.7
0.2
0.1
出力値
12. トレーニングの流れ
Transfer Learning (転移学習) を理解する
✓ まずTransfer Learningの仕組みを理解します
モデルをトレーニングする
Pythonサンプルコードの TransferLearning.py を理解する
✓ CNTKでTransfer Learningを利用する場合、Pythonを使うことができます。
✓ TransferLearning.pyがGitHubに公開されているので読み解いていきましょう
Pythonサンプルコードの TransferLearning_Extended.py を理解する
✓ TransferLearning.pyをラップして使いやすくしたのがTransferLearning_Extended.py です。こちらもGitHubに公開されています。
✓ このプログラムによりマップファイル(画像Pathとラベルの対応ファイル)の作成や実行が多少容易になります。
✓ Transfer Learningを利用するにあたりこちらも学習しましょう。
モデルをデプロイする
✓ C#を使い、Webにデプロイしましょう。
精度を高めていく
12
14. データ用意からデプロイまで
14
データの用意
• トレーニングデータの用意
• テストデータの用意
• マップファイルの用意
TransferLearning_Extended.py
を利用
• ファイル読み込みの設定、データ
やパラメータの定義など
• 内部的に TransferLeaning.py を
呼び出す
デプロイするために
C#.NET のプロジェクトを作成
• プログラムファイルの改修
• モデルの配置
• デプロイ
TransferLearning.py
= TransferLearningをCNTKで
行う際の本体
✓ 学習方法・ロジックが定義
されているファイル
✓ 学習とモデルの作成を行う
TransferLearning_Extended.py
✓ パラメータやファイルの
処理などが記載
✓ TransferLearning.pyを
読み出す
人工知能API on Azure
17. TransferLearning.py でなにをやっているか
ミニバッチを作成:create_mb_source()
✓ マップファイル (ImageとLabelの対応テキスト)を読み込んでミニバッチ実行のためのハコ(クラス)を作る
ベースモデルを作成:create_model()
✓ ベースとなるモデルから必要な部分(転移させる部分)を抽出し、そこに新しい学習のための定義情報などを付与し、今
回アウトプットするモデルのベース部分を作る
モデルをトレーニング:train_model()
✓ 作成したベースのモデルをトレーニングして、トレーニング済みの新しいモデルを作成する
一つのイメージを評価:eval_single_image()
✓ 一つのデータイメージでモデルを評価する。このプロセスはAPIを作って1枚1枚画像を処理したいときに有用
イメージのデータセットの評価:eval_test_images()
✓ 複数のデータセットでモデルを評価する。このプロセスはトレーニングしたモデルの精度について調べるときに有用
実行部分:Main
✓ このファイルに含まれる実行部分(関数ではないです)
17
21. TransferLearning.py 全体の流れ
1 GPUかCPUかの設定
2 モデルをトレーニング : train_model()
2-1 エポックのサイズを指定
2-2 ミニバッチソースを作成 : create_mb_source()
2-2-1 ミニバッチ一つのサイズを設定
2-2-2 処理するイメージのサイズや次元数を定義
2-2-3 画像と対応するラベルを読み取るイメージリーダを設定(イメージとラベルの対応ファイル=mapfile)
2-3 トレーニングするモデルに渡すためのインプット情報を定義する
2-4 ベースのモデルを作成 : create_model()
2-4-1 ベースモデルを読み込む
2-4-2 最後の隠しノードとフィーチャーノード(入力)を抽出
2-4-3 クラスラベルを予測する最終密集denseレイヤーを切り捨て、再利用する部分をクローン
2-4-4 クローンしたモデルの最終密集レイヤーを新しいものに置き換える
2-5 クロスエントロピーのオブジェクトを作成 : cross_entropy_with_softmax() 乱雑さの度合いを計算する関数
2-6 クラス分類エラーのオブジェクトを作成 : classification_error() モデルでの分類後にどれだけ本当の値とのギャップがあったかを計算する関数
2-7 ミニバッチ単位の学習率スケジュールを作成 : learning_rate_schedule()
2-8 ミニバッチ単位の運動量スケジュールを作成 : momentum_schedule()
2-9 それらのパラメータを知るためにMomentum SGDのラーナーインスタンスを作成 : momentum_sgd() 上記二つのスケジュールとモデルのパラメータを用いて作成
2-10 ログ出力のための準備 : ProgressPrinter()
2-11 計算された勾配と指定されたデータセットを使用してモデルのパラメータを更新するために訓練するクラス
2-12 ★ ミニバッチ単位がなくなるまで、指定されたエポックの数、モデルのトレーニングを繰り返す : trainer.train_minibatch()
3 モデルを保存 : trained_model.save()
4 出来上がったモデルを評価する : eval_test_images()
21
処
理
の
実
態
定義
実行
23. 初期パラメータ
23
ファイルパスの定義
_data_folder os.path.join(base_folder, "..", "DataSets", "Flowers")
_train_map_file os.path.join(_data_folder, "6k_img_map.txt")
_test_map_file os.path.join(_data_folder, "1k_img_map.txt")
_base_model_file os.path.join(base_folder, "..", "PretrainedModels", "ResNet_18.model")
base_folder os.path.dirname(os.path.abspath(__file__))
tl_model_file os.path.join(base_folder, "Output", "TransferLearning.model")
output_file os.path.join(base_folder, "Output", "predOutput.txt")
抽出するモデルの定義に関する設定
_feature_node_name "features"
_last_hidden_node_name "z.x"
_image_height 224
_image_width 224
_num_channels 3
_num_classes 102
ラーニングのパラメータ
max_epochs 20
mb_size 50
lr_per_mb [0.2]*10 + [0.1]
momentum_per_mb 0.9
l2_reg_weight 0.0005
モデルを操作する際の設定
make_mode False
freeze_weights False
features_stream_name 'features'
label_stream_name 'labels'
new_output_node_name "prediction"
初期パラメータはそれぞれ以下に設定されています。
✓ ファイルパスの定義:データセットやモデルへのパス
✓ モデルを読み込む際の設定:モデルにはパラメータやノードの定義がされています。今回操作する際にどのような処理を
するのか定義しています。
✓ 抽出するモデルの定義に関する設定:インプットデータの次元やクラス数が定義されています。
✓ ラーニングのパラメータ:学習の際の数式に与えるパラメータが定義されています。
24. [参考] 試用する Python のライブラリ
24
ロードするライブラリ
from pprint import pprint
from PIL import Image Pillow は、Python の画像処理ライブラリ
import numpy as np NumPy は Python で多次元配列や行列の計算を高速に行うためのライブラリ。
from cntk.device import try_set_default_device, gpu デバイス関連の情報を扱うGPUやCPUなど
from cntk import load_model, placeholder, Constant モデルをロードするための関数など
from cntk import Trainer, UnitType
from cntk.logging.graph import find_by_name, get_node_outputs モデルファイルから情報を読む
from cntk.io import MinibatchSource, ImageDeserializer, StreamDefs, StreamDef ミニバッチソースを作るために主に使う、イメージをデシリアライズする機能など
import cntk.io.transforms as xforms 行列を操作する
from cntk.layers import Dense 最終密集レイヤの関数
from cntk.learners import momentum_sgd, learning_rate_schedule, momentum_schedule 学習する際のスケジューリングなどの関数
from cntk.ops import input, combine, softmax ソフトマックス計算など
from cntk.ops.functions import CloneMethod ベースモデルを部分的にコピーする際に使う
from cntk.losses import cross_entropy_with_softmax クロスエントロピーを正規化(softmax)した形で返す
from cntk.metrics import classification_error クラス判別エラーの関数
from cntk.logging import log_number_of_parameters, ProgressPrinter プログレスプリンターなど、主にログの用途で使う
CNTKの機能はそれぞれ以下を使っています。
25. 全体の流れ
1 GPUかCPUかの設定
2 モデルをトレーニング : train_model()
2-1 エポックのサイズを指定
2-2 ミニバッチソースを作成 : create_mb_source()
2-2-1 ミニバッチ一つのサイズを設定
2-2-2 処理するイメージのサイズや次元数を定義
2-2-3 画像と対応するラベルを読み取るイメージリーダを設定(イメージとラベルの対応ファイル=mapfile)
2-3 トレーニングするモデルに渡すためのインプット情報を定義する
2-4 ベースのモデルを作成 : create_model()
2-4-1 ベースモデルを読み込む
2-4-2 最後の隠しノードとフィーチャーノード(入力)を抽出
2-4-3 クラスラベルを予測する最終密集denseレイヤーを切り捨て、再利用する部分をクローン
2-4-4 クローンしたモデルの最終密集レイヤーを新しいものに置き換える
2-5 クロスエントロピーのオブジェクトを作成 : cross_entropy_with_softmax() 乱雑さの度合いを計算する関数
2-6 クラス分類エラーのオブジェクトを作成 : classification_error() モデルでの分類後にどれだけ本当の値とのギャップがあったかを計算する関数
2-7 ミニバッチ単位の学習率スケジュールを作成 : learning_rate_schedule()
2-8 ミニバッチ単位の運動量スケジュールを作成 : momentum_schedule()
2-9 それらのパラメータを知るためにMomentum SGDのラーナーインスタンスを作成 : momentum_sgd() 上記二つのスケジュールとモデルのパラメータを用いて作成
2-10 ログ出力のための準備 : ProgressPrinter()
2-11 計算された勾配と指定されたデータセットを使用してモデルのパラメータを更新するために訓練するクラス
2-12 ミニバッチ単位がなくなるまで、指定されたエポックの数、モデルのトレーニングを繰り返す : trainer.train_minibatch()
3 モデルを保存 : trained_model.save()
4 出来上がったモデルを評価する : eval_test_images()
25
処
理
の
実
態
定義
実行
26. 初期設定
26
try_set_default_device(gpu(0))
デバイス設定部分
✓ CPUを利用するかGPUを利用するかを決められます。CPUの場合は cpu()とします。
✓ ただし、このコードには現状GPUのみに対応しているコードが含まれているため、GPUでないと実行できません。
if not (os.path.exists(_base_model_file) and os.path.exists(_train_map_file) and os.path.exists(_test_map_file)):
print("Please run 'python install_data_and_model.py' first to get the required data and model.")
exit(0)
ベースのモデルの有無を確認
✓ モデルとデータの有無を確認します。
✓ ファイルが存在しない場合はユーザーに python install_data_and_model.py を実行するように促します。
✓ install_data_and_model.py を実行すると花などのサンプル写真がダウンロードされ、また今回のモデルのベースにな
るResNet18のモデルがダウンロードされます。
27. 初期設定2
27
if os.path.exists(tl_model_file) and make_mode:
print("Loading existing model from %s" % tl_model_file)
trained_model = load_model(tl_model_file)
else:
trained_model = train_model(_base_model_file, _feature_node_name, _last_hidden_node_name,
_image_width, _image_height, _num_channels, _num_classes, _train_map_file,
max_epochs, freeze=freeze_weights)
trained_model.save(tl_model_file)
トレーニングされたモデルがすでに存在するか否かで処理を変更
✓ 出力モデルが存在する場合、trained_model変数には既存のモ
デルをロードして代入します。
✓ 出力モデルが存在しない場合、またはmake_modeがFalseに設定
されている場合にのみトレーニングします。
✓ trained_model変数にはtrain_modelでトレーニングされた値が
代入します。
✓ トレーニングがおわったモデルはファイルに出力されます。
_base_model_file = 今回ベースにする"ResNet_18.model"までのパス
_feature_node_name = "features"
_last_hidden_node_name = "z.x"
_image_width = 224
_image_height = 224
_num_channels = 3
_num_classes = 102
_train_map_file = os.path.join(_data_folder, "6k_img_map.txt")
max_epochs = 20
train_model()のパラメータ
28. 全体の流れ
1 GPUかCPUかの設定
2 モデルをトレーニング : train_model()
2-1 エポックのサイズを指定
2-2 ミニバッチソースを作成 : create_mb_source()
2-2-1 ミニバッチ一つのサイズを設定
2-2-2 処理するイメージのサイズや次元数を定義
2-2-3 画像と対応するラベルを読み取るイメージリーダを設定(イメージとラベルの対応ファイル=mapfile)
2-3 トレーニングするモデルに渡すためのインプット情報を定義する
2-4 ベースのモデルを作成 : create_model()
2-4-1 ベースモデルを読み込む
2-4-2 最後の隠しノードとフィーチャーノード(入力)を抽出
2-4-3 クラスラベルを予測する最終密集denseレイヤーを切り捨て、再利用する部分をクローン
2-4-4 クローンしたモデルの最終密集レイヤーを新しいものに置き換える
2-5 クロスエントロピーのオブジェクトを作成 : cross_entropy_with_softmax() 乱雑さの度合いを計算する関数
2-6 クラス分類エラーのオブジェクトを作成 : classification_error() モデルでの分類後にどれだけ本当の値とのギャップがあったかを計算する関数
2-7 ミニバッチ単位の学習率スケジュールを作成 : learning_rate_schedule()
2-8 ミニバッチ単位の運動量スケジュールを作成 : momentum_schedule()
2-9 それらのパラメータを知るためにMomentum SGDのラーナーインスタンスを作成 : momentum_sgd() 上記二つのスケジュールとモデルのパラメータを用いて作成
2-10 ログ出力のための準備 : ProgressPrinter()
2-11 計算された勾配と指定されたデータセットを使用してモデルのパラメータを更新するために訓練するクラス
2-12 ミニバッチ単位がなくなるまで、指定されたエポックの数、モデルのトレーニングを繰り返す : trainer.train_minibatch()
3 モデルを保存 : trained_model.save()
4 出来上がったモデルを評価する : eval_test_images()
28
処
理
の
実
態
定義
実行
30. モデルをトレーニング : train_model()
エポックのサイズを指定
30
epoch_size = sum(1 for line in open(train_map_file))
if max_images > 0:
epoch_size = min(epoch_size, max_images)
エポック数を決定
✓ エポックのサイズを決めます。サンプルのデフォルトは20にしてあります。サンプル
プログラムではtrain_map_file は、6k(6149)件の画像URLとラベルデータのセットの
羅列があるので、エポックのサイズは6149になります。
✓ 引数でmax_imagesが渡され、その数が画像の件数より小さかった場合にはエポック
のサイズはそちらになります。
✓ 今回の場合。エポックのサイズ(epoch_size)は6149が入ることになります。
epoch_size = 6149
max_images = -1
num_epochs = 20
パラメータ
エポック数を指定する理由
エポック数とは、一回のトレーニングデータセットを何回繰り返して学習させるかの数のことです。
深層学習だとパラメータが多いので、トレーニング用のデータセットを何回も繰り返し学習させないと、
うまく学習してくれないといった問題があります。
32. モデルをトレーニング
ミニバッチソースを作成 : create_mb_source()
32
minibatch_source = create_mb_source(train_map_file, image_width, image_height, num_channels, num_classes)
image_input = input((num_channels, image_height, image_width))
label_input = input(num_classes)
ミニバッチソースを作成
✓ ミニバッチソースと入力変数を作成します。
✓ このプログラムで行っているのは、確率的勾配降下法(SGD =
Stochastic Gradient Descent) です。この手法では訓練データ
をミニバッチに分割して学習させます。
minibatch_source : ミニバッチのクラスインスタンス
image_input : Input('Input3', [#], [3 x 224 x 224])
label_input : Input('Input4', [#], [102])
各変数の値
ミニバッチで学習を行う理由
✓ ミニバッチ学習とは、ネットワークのパラメータ学習に対して全学習サンプルを一度に使うのではなく、全学習サンプ
ルから一定数のサンプルを抽出したサブセットを使って誤差計算と重み更新を行う方法です。
✓ ひとつのサンプルごとに重みを更新するオンラインの方法と比較すると高速に処理を行うことができ、バッチと比べて
良い精度を得られることが、ミニバッチの利点です。
✓ ディープラーニングでミニバッチを使用する際には、よくエポックごとにミニバッチの学習サンプルがシャッフルされ、
ランダムに抽出されます。
✓ 勾配降下法では局所解に陥ったら抜け出せませんが、確率的勾配降下法では毎回違う訓練データを使うので局所解から
抜け出せます。またミニバッチ学習では勾配がより安定的になります。(学習が停滞状態に陥ったのを脱出させるため
に、モーメントを使うことができます。)
33. モデルをトレーニング ミニバッチソースを作成
ミニバッチのイメージリーダを作成
33
def create_mb_source(map_file, image_width, image_height, num_channels, num_classes, randomize=True):
transforms = [xforms.scale(width=image_width, height=image_height, channels=num_channels, interpolations='linear’)]
return MinibatchSource(ImageDeserializer(map_file, StreamDefs(
features = StreamDef(field='image', transforms=transforms),
labels = StreamDef(field='label', shape=num_classes))),
randomize=randomize)
イメージリーダを作成
関数説明
xforms.scale()
✓ データ拡大のため、map_features
に渡すために使用できるオブジェ
クトにスケール変換をします。
✓ 戻り値としてはスケール変換を記
述する辞書のようなオブジェクト
なオブジェクトが返されます。
map_file : イメージのパスとそれぞれのラベルがマップされているファイルへのパス
image_height : 処理の対象になるイメージの高さ。このプログラムのデフォルトでは224
image_width : 処理の対象になるイメージの横の長さ。このプログラムのデフォルトでは224
num_channels : チャネル数。ここではRGBの3色なので3になっている
num_classes : このサンプルではnum_classesには102 が入っています。
randomize : ミニバッチの抽出の仕方の指定、今回はTrue
{
'features': {'defines_mb_size': False, 'is_sparse': False, 'stream_alias': 'image', 'transforms’: [<transformObject>]},
'labels': {'defines_mb_size': False, 'is_sparse': False, 'dim': 102, 'stream_alias': 'label’}
}
各引数の値
StreamDefsの返り値
StreamDef()
ビルトインデシリアライザで使用するストリームの設定をします。
- field : 今回使うImageDeserializerの場合受け入れ可能な名前は”image”または”label”です。
- shape : このストリームの寸法。今回のサンプルデータのケースでは102種類の花の画像があるため102が代入されます
- trasform : デシリアライザによって適用される変換のリスト。現在ImageDeserializerのみが変換をサポートしています
StreamDefs() : StreamDef() をまとめたディクショナリを作成します。
ImageDeserializer() : ファイルから画像と対応するラベルを読み取るイメージリーダを設定します。
34. モデルをトレーニング
ネットワーク入力へのマッピングを定義する
34
input_map = {
image_input: minibatch_source[features_stream_name],
label_input: minibatch_source[label_stream_name]
}
リーダーストリームからネットワーク入力へのマッピングを定義する
リーダーストリームからネットワーク入力へのマッピングを定義します。
minibatch_sourceのクラスインスタンスには "features" と "labels" の値が存在するので、その値を指定し、
create_mb_source()で作成したクラスの中から、以下を抽出します。
✓ “features”という入力の定義がされている部分
✓ labelsというラベルの定義がされている部分
[参考]こちらは、トレーニングの際に、入力値のマップ情報として渡されます。
data = minibatch_source.next_minibatch(min(mb_size, epoch_size-sample_count), input_map=input_map)
参考:input_mapの渡され方
ここまでで下準備は完了です。イメージのファイルを抽出する際の定義などが完了しました。
35. 全体の流れ
1 GPUかCPUかの設定
2 モデルをトレーニング : train_model()
2-1 エポックのサイズを指定
2-2 ミニバッチソースを作成 : create_mb_source()
2-2-1 ミニバッチ一つのサイズを設定
2-2-2 処理するイメージのサイズや次元数を定義
2-2-3 画像と対応するラベルを読み取るイメージリーダを設定(イメージとラベルの対応ファイル=mapfile)
2-3 トレーニングするモデルに渡すためのインプット情報を定義する
2-4 ベースのモデルを作成 : create_model()
2-4-1 ベースモデルを読み込む
2-4-2 最後の隠しノードとフィーチャーノード(入力)を抽出
2-4-3 クラスラベルを予測する最終密集denseレイヤーを切り捨て、再利用する部分をクローン
2-4-4 クローンしたモデルの最終密集レイヤーを新しいものに置き換える
2-5 クロスエントロピーのオブジェクトを作成 : cross_entropy_with_softmax() 乱雑さの度合いを計算する関数
2-6 クラス分類エラーのオブジェクトを作成 : classification_error() モデルでの分類後にどれだけ本当の値とのギャップがあったかを計算する関数
2-7 ミニバッチ単位の学習率スケジュールを作成 : learning_rate_schedule()
2-8 ミニバッチ単位の運動量スケジュールを作成 : momentum_schedule()
2-9 それらのパラメータを知るためにMomentum SGDのラーナーインスタンスを作成 : momentum_sgd() 上記二つのスケジュールとモデルのパラメータを用いて作成
2-10 ログ出力のための準備 : ProgressPrinter()
2-11 計算された勾配と指定されたデータセットを使用してモデルのパラメータを更新するために訓練するクラス
2-12 ミニバッチ単位がなくなるまで、指定されたエポックの数、モデルのトレーニングを繰り返す : trainer.train_minibatch()
3 モデルを保存 : trained_model.save()
4 出来上がったモデルを評価する : eval_test_images()
35
処
理
の
実
態
定義
実行
36. ベースのモデルを作成
転移学習のネットワークモデルを作成
36
tl_model = create_model(base_model_file, feature_node_name, last_hidden_node_name, num_classes, image_input, freeze)
def create_model(base_model_file, feature_node_name, last_hidden_node_name, num_classes, input_features, freeze=False):
base_model = load_model(base_model_file)
feature_node = find_by_name(base_model, feature_node_name)
last_node = find_by_name(base_model, last_hidden_node_name)
転移学習のネットワークモデルを作成
base_model_file : ベースモデル“ResNet_18.model” へのパス
feature_node_name : "features"
last_hidden_node_name : "z.x"
num_classes : 102 (サンプルでは102種類の花の画像のデータ
セットが含まれるため)
image_input : こちらは、イメージのサイズなどが書かれた定
義が入っています。Input('Input3', [#], [3 x 224 x 224])
Freeze : False (サンプルのデフォルト値)
各引数の値
事前にトレーニングされた分類ネットをロードし、それを元に転移学習のネットワー
クモデルを作成します。今回のベースモデルは”ResNet_18.model” です。
”.model”拡張子のファイルの実態は、ノードの情報、インプット/アウトプットの情報、
各特徴量の重みなど、学習済みのパラメータが定義されているバイナリファイルです。
✓ load_model()を利用し、ResNetのモデルをロードしプログラムから利用できるよ
うにします。
✓ find_by_name() はノードを名前から見つける関数です。ロードしたモデルから、
フィーチャーノード、ラストノードの値を変数に代入しています。
- feature_node : フィーチャーは入力ノードを表します。
- last_node : 最後の隠しノードの名前が設定されています。
参考資料
ResNetに関する参考PDF(英語):http://image-net.org/challenges/talks/ilsvrc2015_deep_residual_learning_kaiminghe.pdf
フィーチャーノードの考え方に関する参考ページ(BrainScript):https://msdn.microsoft.com/ja-jp/magazine/mt791798.aspx
38. ベースのモデルを作成
ベースモデルから、再利用する部分を抽出する
38
cloned_layers = combine([last_node.owner]).clone(
CloneMethod.freeze if freeze else CloneMethod.clone,
{feature_node: placeholder(name='features')})
ベースモデルから、再利用する部分を抽出する
combine()
✓ ノードが記述されている関数インスタンスを作成します。
✓ ccombine([last_node.owner]) を実行すると Input(‘features’, [#, ], [3 x 224 x 224]) -> Output(‘z.x’, [#, ], [512 x 1 x 1]) の値が返っ
てきますが、それが今回の複製対象となります。
clone()
✓ 関数をクローンし、パラメータを新しい値で置き換え初期化します。しかしこの段階ではまだパラメータは入れ替えません。
✓ 今回のサンプルにおける、インプットの値(feature)は [3x224x224] ですが、このインプットは変わるかもしれません。
✓ そのためこの段階ではクローン方法とインプットの値のハコを定義します。
クローン方法
✓ CNTKでは以下の方法で複製できます。
clone : 新しい学習可能パラメータは、複製される関数の対応するパラメータの現在の値で作成および初期化されます
freeze : パラメータは複製され、イミュータブルになります。新しいクローン内の定数(例えば、固定された特徴抽出器として使用するため)
share : パラメータは、クローンされるファンクションと新しいクローンとの間で共有されます。
インプットの値のハコを定義:placeholder()
✓ プレースホルダーはデータが格納される予定地です。インプットの値はTrainingをするときにまとめて実行関数に渡されますが、その時
にインプットの値をこのモデルに反映できるように、外からアクセス(代入)できる’feature’というハコを用意しておきます
last_node.owner : Output('z.x.x.r', [#, ], [512 x 7 x 7]) -> Output('z.x', [#, ], [512 x 1 x 1])
39. ベースのモデルを作成
最終密集レイヤーを新しいものに置き換える
39
feat_norm = input_features - Constant(114)
cloned_out = cloned_layers(feat_norm)
z = Dense(num_classes, activation=None, name=new_output_node_name) (cloned_out)
return z
一定の重みで目的のレイヤーを複製する
cloned_layers(feat_norm)
✓ ノードが記述されている関数インスタンスを作成します。
✓ cfeat_norm には Composite(Minus): Input(‘Input3’, [#], [3 x 224 x 224]) -> Output(‘Minus914_Output_0’, [#], [3 x 224 x 224]) の
値が入っています。
✓ この処理で、先ほど作成したcloned_layerから “Input3”のフィーチャーノード(インプット)を取り除く処理をしています。この”Input3”
は最後に処理を走らせる段階で新しいものが定義され、先ほど定義した “features”のプレースホルダーに挿入されます。
Dense()
✓ Denseはニューラルネットワークの全結合層を表す機能です。
✓ ここでは、クラスラベルを予測する最終密集(dense)レイヤーを切り捨て、新しいnum_classesサイズのDenseを接続する処理をしていま
す。
複製箇所
クラスに合致するように
置き換え
プレースホルダーを設置
後から挿入
40. 全体の流れ
1 GPUかCPUかの設定
2 モデルをトレーニング : train_model()
2-1 エポックのサイズを指定
2-2 ミニバッチソースを作成 : create_mb_source()
2-2-1 ミニバッチ一つのサイズを設定
2-2-2 処理するイメージのサイズや次元数を定義
2-2-3 画像と対応するラベルを読み取るイメージリーダを設定(イメージとラベルの対応ファイル=mapfile)
2-3 トレーニングするモデルに渡すためのインプット情報を定義する
2-4 ベースのモデルを作成 : create_model()
2-4-1 ベースモデルを読み込む
2-4-2 最後の隠しノードとフィーチャーノード(入力)を抽出
2-4-3 クラスラベルを予測する最終密集denseレイヤーを切り捨て、再利用する部分をクローン
2-4-4 クローンしたモデルの最終密集レイヤーを新しいものに置き換える
2-5 クロスエントロピーのオブジェクトを作成 : cross_entropy_with_softmax() 乱雑さの度合いを計算する関数
2-6 クラス分類エラーのオブジェクトを作成 : classification_error() モデルでの分類後にどれだけ本当の値とのギャップがあったかを計算する関数
2-7 ミニバッチ単位の学習率スケジュールを作成 : learning_rate_schedule()
2-8 ミニバッチ単位の運動量スケジュールを作成 : momentum_schedule()
2-9 それらのパラメータを知るためにMomentum SGDのラーナーインスタンスを作成 : momentum_sgd() 上記二つのスケジュールとモデルのパラメータを用いて作成
2-10 ログ出力のための準備 : ProgressPrinter()
2-11 計算された勾配と指定されたデータセットを使用してモデルのパラメータを更新するために訓練するクラス
2-12 ミニバッチ単位がなくなるまで、指定されたエポックの数、モデルのトレーニングを繰り返す : trainer.train_minibatch()
3 モデルを保存 : trained_model.save()
4 出来上がったモデルを評価する : eval_test_images()
40
処
理
の
実
態
定義
実行
41. The definition of optimization in Neural Network
ニューラルネットワークにおける学習とは、モデルの予測値と実際の値
との誤差から、パラメータ(重み)を適切に更新すること
目的関数
探索最適化対象
• 各層間の「重み」
• 平均二乗誤差
(Mean Squared Error /
Classification Error)
• 交差エントロピー誤差
(Cross-Entropy Error)
• 勾配法
(Gradient method)
最適化対象
(Optimization Target)
目的関数
(別名:損失関数 - loss function)
探索手法
(別名:Optimizer、Learner)
43. 学習用のための準備
クロスエントロピー : cross_entropy_with_softmax()
43
ce = cross_entropy_with_softmax(tl_model, label_input)
クロスエントロピー
ある数値の集団1と、ある数値の集団2がどれくらい異なるかを表す概念です。
今回はこちらをloss関数として使います。
エントロピー
エントロピーとは乱雑さの度合いです。この「エントロピー」という概念は様々
な理工学分野で用いられます。上下に分離したドレッシングの瓶をシャカシャカ
振ってよく混ざった状態にすると「エントロピーが増えた」となります。
これを機械学習でいうと「学習データの出力と、実際のニューラルネットワーク
の学習結果としての出力がどれくらい異なるか」を表します。
このクロスエントロピーを用いる際に比較対象となる「数値の軍団」は正規化さ
れている必要があり、内部的にソフトマックス関数で正規化しています。
参考:http://qiita.com/MATS_ElectricBlueIndustries/items/db85de80c72be28fc60b
44. 学習用のための準備
平均二乗誤差(MSE) : classification_error()
44
pe = classification_error(tl_model, label_input)
クラス分類エラー
分類エラーを計算するために設定します。今回はこちらを評価関数として利用します。
この関数ではoutput_vectorのインデックスの最高値と実際のラベルを比較します。
クロスエントロピーと平均二乗誤差
✓ セグメンテーション
ネットワークの出力とグランド真理値ラベルとの間のクロスエントロピーを計算し、バイナリおよびカテゴリ出力に
使用することができます(ホットワンエンコーディングを使用)
✓ 回帰
二乗誤差/平均二乗誤差は、回帰問題の一般的な選択肢です
✓ セグメンテーション
IoU(Intersection over union)は、画像と予測の重複領域を比較するのに適した損失関数ですが、非重複領域に対して
0を返すため、DNNにはあまり適しません。MSE(平均二乗誤差)はより良い選択です
参考:https://chaosmail.github.io/deeplearning/2016/10/22/intro-to-deep-learning-for-computer-vision/
47. 学習のための準備
学習のスケジュールの設定
47
lr_schedule = learning_rate_schedule(lr_per_mb, unit=UnitType.minibatch)
学習率スケジュールを作成します : learning_rate_schedule()
mm_schedule = momentum_schedule(momentum_per_mb)
minibatch単位の momentum スケジュールを作成します : momentum_schedule()
参考情報:http://hokuts.com/2015/11/25/ml2_perceptron/
learner = momentum_sgd(tl_model.parameters, lr_schedule, mm_schedule, l2_regularization_weight=l2_reg_weight)
# パラメータを知るためにMomentum SGD学習者インスタンスを作成します。
# 学習アルゴリズムの定義、モデルやモメンタムのスケジュール
勾配降下法ラーナーインスタンスを作成します : momentum_sgd()
trainer = Trainer(tl_model, (ce, pe), learner, progress_printer)
# 計算された勾配を使用してモデルのパラメータを更新するため、
# 指定されたセットを使用して、モデルの指定された損失関数のモデルパラメータを訓練するためのクラス。
# オプションの指定されたメトリック関数(微分不可能)は、訓練されたモデルの品質を追跡するために使用できます。
トレーナーを作成します: Trainer()
学習のための準備
勾配降下法学習 / トレーナー インスタンス
48. 全体の流れ
1 GPUかCPUかの設定
2 モデルをトレーニング : train_model()
2-1 エポックのサイズを指定
2-2 ミニバッチソースを作成 : create_mb_source()
2-2-1 ミニバッチ一つのサイズを設定
2-2-2 処理するイメージのサイズや次元数を定義
2-2-3 画像と対応するラベルを読み取るイメージリーダを設定(イメージとラベルの対応ファイル=mapfile)
2-3 トレーニングするモデルに渡すためのインプット情報を定義する
2-4 ベースのモデルを作成 : create_model()
2-4-1 ベースモデルを読み込む
2-4-2 最後の隠しノードとフィーチャーノード(入力)を抽出
2-4-3 クラスラベルを予測する最終密集denseレイヤーを切り捨て、再利用する部分をクローン
2-4-4 クローンしたモデルの最終密集レイヤーを新しいものに置き換える
2-5 クロスエントロピーのオブジェクトを作成 : cross_entropy_with_softmax() 乱雑さの度合いを計算する関数
2-6 クラス分類エラーのオブジェクトを作成 : classification_error() モデルでの分類後にどれだけ本当の値とのギャップがあったかを計算する関数
2-7 ミニバッチ単位の学習率スケジュールを作成 : learning_rate_schedule()
2-8 ミニバッチ単位の運動量スケジュールを作成 : momentum_schedule()
2-9 それらのパラメータを知るためにMomentum SGDのラーナーインスタンスを作成 : momentum_sgd() 上記二つのスケジュールとモデルのパラメータを用いて作成
2-10 ログ出力のための準備 : ProgressPrinter()
2-11 計算された勾配と指定されたデータセットを使用してモデルのパラメータを更新するために訓練するクラス
2-12 ミニバッチ単位がなくなるまで、指定されたエポックの数、モデルのトレーニングを繰り返す : trainer.train_minibatch()
3 モデルを保存 : trained_model.save()
4 出来上がったモデルを評価する : eval_test_images()
48
処
理
の
実
態
定義
実行
49. 学習部分
バッチ単位でトレーニングをします
49
for epoch in range(num_epochs): # エポックのループ
sample_count = 0
while sample_count < epoch_size: # エポック内のbodyのバッチのループ
print(".")
# dataにバッチソースからデータを取り出して入れます
data = minibatch_source.next_minibatch(min(mb_size, epoch_size-sample_count), input_map=input_map)
# trainerでtrainします
trainer.train_minibatch(data) # モデルを更新する
sample_count += trainer.previous_minibatch_sample_count # 処理されたサンプル数をカウントする
if sample_count % (100 * mb_size) == 0:
print ("Processed {0} samples".format(sample_count))
trainer.summarize_training_progress()
return tl_model
minibatch単位の momentum スケジュールを作成します
最後にバッチ単位でエポック数分トレーニングを回していきます。
52. TransferLearning_Extended.py に含まれる内容
TransferLearning_Extended.py は、先ほどまでの TransferLearning_Extended.pyをうまく使うための実行
ファイルです。
このプログラムでは最初に TransferLearning.py をロードしています。そのため、TransferLearning.py で定
義されている関数をすべて使うことが出来ます。このファイルでは主に以下の機能が備わっています。
create_map_file_from_folder()
✓ マップファイルをフォルダ名・ファイルパスから作成する。
create_class_mapping_from_folder()
✓ フォルダ名から、クラスを作る (例:ラベルが3種類の場合はそれぞれのラベルの配列を作る)
format_output_line()
✓ それぞれのトレーニング結果などのデータを出力するための関数
52
53. TransferLearning.py をもっと便利に使う
初期パラメータを確認します
54
base_folder = os.path.dirname(os.path.abspath(__file__))
base_model_file = os.path.join(base_folder, "..", "PretrainedModels", "ResNet_18.model")
feature_node_name = "features"
last_hidden_node_name = "z.x"
image_height = 224
image_width = 224
num_channels = 3
初期パラメータ
初期値は基本的に変わりません。
# define data location and characteristics
train_image_folder = os.path.join(base_folder, "..", "DataSets", "Animals", "Train")
test_image_folder = os.path.join(base_folder, "..", "DataSets", "Animals", "Test")
file_endings = ['.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG']
ファイルパスの変更
これらの値を変えましょう。テストデータとトレーニングデータが入っているフォルダーのパスを記載します。
54. TransferLearning.py をもっと便利に使う
マップファイルを作成します
55
def create_map_file_from_folder(root_folder, class_mapping, include_unknown=False):
map_file_name = os.path.join(root_folder, "map.txt")
lines = []
for class_id in range(0, len(class_mapping)):
folder = os.path.join(root_folder, class_mapping[class_id])
if os.path.exists(folder):
for entry in os.listdir(folder):
filename = os.path.join(folder, entry)
if os.path.isfile(filename) and os.path.splitext(filename)[1] in file_endings:
lines.append("{0}¥t{1}¥n".format(filename, class_id))
if include_unknown:
for entry in os.listdir(root_folder):
filename = os.path.join(root_folder, entry)
if os.path.isfile(filename) and os.path.splitext(filename)[1] in file_endings:
lines.append("{0}¥t-1¥n".format(filename))
lines.sort()
with open(map_file_name , 'w') as map_file:
for line in lines:
map_file.write(line)
return map_file_name
マップファイルをフォルダ名・ファイルパスから作成する。
マップファイルをフォルダ名・ファイルパスから作成します
56. TransferLearning.py をもっと便利に使う
結果データ(処理のログ)を出力します
58
def format_output_line(img_name, true_class, probs, class_mapping, top_n=3):
class_probs = np.column_stack((probs, class_mapping)).tolist()
class_probs.sort(key=lambda x: float(x[0]), reverse=True)
top_n = min(top_n, len(class_mapping)) if top_n > 0 else len(class_mapping)
global total_correct
true_class_name = class_mapping[true_class] if true_class >= 0 else 'unknown'
line = '[{"class": "%s", "predictions": {' % true_class_name
for i in range(0, top_n):
line = '%s"%s":%.3f, ' % (line, class_probs[i][1], float(class_probs[i][0]))
if (true_class_name == class_probs[0][1]):
total_correct = total_correct + 1
line = '%s}, "image": "%s"}]¥n' % (line[:-2], img_name.replace('¥¥', '/'))
return line
それぞれのトレーニング結果などのデータを出力するための関数
✓それぞれのトレーニング結果などのデータを出力するための関数です。
57. TransferLearning.py をもっと便利に使う
トレーニング (抜粋)
59
def train_and_eval(_base_model_file, _train_image_folder, _test_image_folder, _results_file, _new_model_file, tes
ting = False):
class_mapping = create_class_mapping_from_folder(_train_image_folder)
train_map_file = create_map_file_from_folder(_train_image_folder, class_mapping)
test_map_file = create_map_file_from_folder(_test_image_folder, class_mapping, include_unknown=True)
# train
trained_model = train_model(_base_model_file, feature_node_name, last_hidden_node_name,
image_width, image_height, num_channels,
len(class_mapping), train_map_file, num_epochs=30, freeze=True)
# evaluate test images
with open(_results_file, 'w') as output_file:
with open(test_map_file, "r") as input_file:
for line in input_file:
tokens = line.rstrip().split('¥t')
img_file = tokens[0]
true_label = int(tokens[1])
probs = eval_single_image(trained_model, img_file, image_width, image_height)
formatted_line = format_output_line(img_file, true_label, probs, class_mapping)
output_file.write(formatted_line)
実行部分
✓クラスの配列を取得し、トレーニングとテストそれぞれのマップファイルを作ります。
✓上記をもとにTransferLearning.pyの train_model() 関数を利用し、モデルをトレーニングします。
61. TransferLearning.model をデプロイ
モデルをロードし、インプット/アウトプット値を抽出
65
DeviceDescriptor device = DeviceDescriptor.CPUDevice;
Function modelFunc = Function.Load(modelFilePath, device);
// Get input variable. The model has only one single input.
// The same way described above for output variable can be used here to get input variable by name.
Variable inputVar = modelFunc.Arguments.Single();
// Get shape data for the input variable
NDShape inputShape = inputVar.Shape;
int imageWidth = inputShape[0];
int imageHeight = inputShape[1];
int imageChannels = inputShape[2];
int imageSize = inputShape.TotalSize;
// The model has only one output.
// If the model have more than one output, use the following way to get output variable by name.
// Variable outputVar = modelFunc.Outputs.Where(variable => string.Equals(variable.Name, outputName)).Single();
Variable outputVar = modelFunc.Output;
var inputDataMap = new Dictionary<Variable, Value>();
var outputDataMap = new Dictionary<Variable, Value>();
実行部分
✓デバイスをGPUかCPUか指定します。
✓Function.load()でモデルを抽出し、配列からモデルが持つインプット値、アウトプット値を取り出します。
62. TransferLearning.model をデプロイ
画像を抽出し、モデルが求めるインプットサイズに変換
66
// Image preprocessing to match input requirements of the model.
// This program uses images from the CIFAR-10 dataset for evaluation.
// Please see README.md in <CNTK>/Examples/Image/DataSets/CIFAR-10 about how to download the CIFAR-10 dataset.
// Transform the image
System.Net.Http.HttpClient httpClient = new HttpClient();
Stream imageStream = await httpClient.GetStreamAsync(imageUrl);
Bitmap bmp = new Bitmap(Bitmap.FromStream(imageStream));
var resized = bmp.Resize((int)imageWidth, (int)imageHeight, true);
List<float> resizedCHW = resized.ParallelExtractCHW();
画像抽出とリサイズ
✓URLのイメージをリサイズします。
✓今回求められているインプットは 3(RGB) x 224(width) x 224(height)です。
✓リサイズしたら、それぞれのパラメータを浮動小数点のリストとして変数に代入します。
✓今回の分類をするうえでは、画像が横に伸びていても大きな問題はありません。
64. TransferLearning.model をデプロイ
画像を抽出し、モデルが求めるインプットサイズに変換
68
// Create input data map
var inputVal = Value.CreateBatch(inputVar.Shape, resizedCHW, device);
inputDataMap.Add(inputVar, inputVal);
// Create ouput data map. Using null as Value to indicate using system allocated memory.
// Alternatively, create a Value object and add it to the data map.
outputDataMap.Add(outputVar, null);
インプット/アウトプットデータマップを定義
✓インプットの値を指定します。今回作成したResNetベースのTransferLearningの分類モデルで要求されるインプットは1つなので
CreateBatch()は1回行い、inputDataMapの配列も1つのインプット値セットで大丈夫です。
✓例として、GitHubのCNTK公式リポジトリにあるFast R-CNNのサンプルのように2個のインプットが必要な際には、インプットの値は以下
の様にする必要があります。
Variable inputVar_1 = modelFunc.Arguments.First();
Variable inputVar_2 = modelFunc.Arguments.Last();
var inputVal_1 = Value.CreateBatch(inputVar_1.Shape, resizedCHW, device);
inputDataMap.Add(inputVar_1, inputVal_1);
var inputVal_2 = Value.CreateBatch(inputVar_2.Shape, your_data, device);
inputDataMap.Add(inputVar_2, inputVal_2);
例:インプットが2つある場合
65. TransferLearning.model をデプロイ
画像を抽出し、モデルが求めるインプットサイズに変換
69
// Start evaluation on the device
modelFunc.Evaluate(inputDataMap, outputDataMap, device);
// Get evaluate result as dense output
var outputVal = outputDataMap[outputVar];
var outputData = outputVal.GetDenseData<float>(outputVar);
string json = JsonConvert.SerializeObject(outputData);
return json;
評価
✓最後にEvaluate()で関数を評価し、json変換をして値を返します。
デプロイ
このプログラムをデプロイし、
http://localhost:3469/api/values
{“url”:”http://hogehoge.com/hoge.jpg”}
のjsonをPOSTで渡すと配列がレスポンスとして返ってきます。
67. 本書に記載した情報は、本書各項目に関する発行日現在の Microsoft の見解を表明するものです。Microsoftは絶えず変化する市場に対応しなければならないため、ここに記載した情報に対していかなる責務を負うものではなく、
提示された情報の信憑性については保証できません。
本書は情報提供のみを目的としています。 Microsoft は、明示的または暗示的を問わず、本書にいかなる保証も与えるものではありません。
すべての当該著作権法を遵守することはお客様の責務です。Microsoftの書面による明確な許可なく、本書の如何なる部分についても、転載や検索システムへの格納または挿入を行うことは、どのような形式または手段(電子的、
機械的、複写、レコーディング、その他)、および目的であっても禁じられています。
これらは著作権保護された権利を制限するものではありません。
Microsoftは、本書の内容を保護する特許、特許出願書、商標、著作権、またはその他の知的財産権を保有する場合があります。Microsoftから書面によるライセンス契約が明確に供給される場合を除いて、本書の提供はこれら
の特許、商標、著作権、またはその他の知的財産へのライセンスを与えるものではありません。
© 2017 Microsoft Corporation. All rights reserved.
Microsoft, Windows, その他本文中に登場した各製品名は、Microsoft Corporation の米国およびその他の国における登録商標または商標です。
その他、記載されている会社名および製品名は、一般に各社の商標です。