Transfer Learning & API Azure
Transfer Learning(転移学習)を用いたオリジナル画像認識AIを、
アプリケーションに組み込むためのAPI開発と実装 (CNTK)
日本マイクロソフト株式会社
アプリケーション ソリューション アーキテクト
服部 佑樹(Yuki Hattori)
目的とゴール
✓CNTKが ”使える” ようになる = CNTKの “実装力” をつける
✓アプリケーションに人工知能 (AI) を組み込めるようにする
✓継続的にアプリとモデルのメンテナンスができるようになる
➡ Deep Learningの概要を学ぶためには「Deep Learning 理論編」
を参照ください。
➡ Deep Learning (CNTK)に適した環境構築は「深層学習 環境構築 Azure」
を参照ください
➡ Deep Learningの概要が理解できたらこの資料を参考に、アプリ
ケーションに人工知能を組み込みましょう。
2
問題
Deep Learningは理論概要を理解することと、実際にプログラムとし
て実装することでは技術的な難易度の違いがあります。
3
実装 理論概要 数学
インターネットに
多く解説が存在
多くが論文に掲載
インターネットにも多く存在
コードは存在するが
解説=理論概要
になっているものが多い
GAP GAP
完成図
本セッションを通して、システムに人口知能APIを実装できるようになりましょう。
今回はPythonでモデルをトレーニングし、
.NET (C#) でモデルを用いた人工知能APIをデプロイする方法を学びます。
4
ユーザー 既存のシステム 人工知能API
CNTK
CNTK
今回の開発対象
トレーニングの流れ
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
Transfer Learningについて
6
Transfer Learning(転移学習)とは
新規タスクの効果的な仮説を効率的に見つけ出すために、一つ以上の別のタ
スクで学習された知識を得て、それを適用する問題
=1つのタスクを学習したネットの重みを、別のタスクに転用する。
下位層:ベーシックな特徴を学習。汎用的に使われ、流用される。
上位層:特定の特徴のみを再学習。一般的に小さなデータセットで十分。
7
ソースデータ
(ImageNetなど)
ソースモデル
ソースラベル
ターゲット
データ
ソースモデル
ターゲット
ラベル
ナレッジを転移学習大量のデータ 少量のデータ
Transfer Learningにおけるモデルの変更箇所
8http://cs231n.stanford.edu/slides/2017/cs231n_2017_lecture7.pdf
汎用的なモデル
特定の用途のモデル
Transfer Learningでは、ベーシックな特徴を学習したモデルの下位
層が再利用されます。そしてそのモデルをベースに特定の特徴量が
検出できるモデルに変更される処理がなされます。
再利用する部分 識別層やアウトプットラベルを置き換え
入力層を置き換え(ピクセル数)
用語説明
conv : 畳み込み層
pool : プーリング層
fc : 全結合層
Neural Network – classification
例えば、今回のように野球選手の画像からコスモス/ひまわり/チューリップを分類しようと
すると、下記のようなネットワーク構造が考えられます。ただし、カラー画像に関しては1
点注意が必要です。
9
入力データ Input Layer Hidden Layer
(複数)
Output Layer
チューリップ
コスモス
ひまわり
0.7
0.2
0.1
出力値
Transfer Learningのモデル学習方法
畳み込みニューラルネットワークをゼロから学
習させることはあまりありません。
学習に使えるデータの量が限られていたり、学
習に時間がかかるからです。
小さなデータセット向け
• CNNを特徴抽出器として利用します
• 最後の全結合層を取り除き、その他の部分を
特徴抽出器として扱います。
比較的大きなデータセット向け
• 畳み込み層を学習し直します。
• 学習済みの重みの値を初期値として、ニュー
ラルネットワーク全体で学習し直すことがで
きます。
• ランダムに初期化した場合よりもより早く学
習が収束することが期待できます。
• 新しく付けた層だけを学習させ、それ以外の
層をフリーズさせておくこともできますが、
余計に計算時間がかかる可能性があります。
10
https://elix-tech.github.io/ja/2016/06/22/transfer-learning-ja.html
http://www.kamishima.net/archive/2010-s-jsai_tl.pdf
http://www.nlab.ci.i.u-tokyo.ac.jp/pdf/CNN_survey.pdf
用語説明
conv : 畳み込み層
pool : プーリング層
fc : 全結合層
• 単純に層を深くすると性能が悪化することが報告されていたが、ResNetのアイデアはシンプルで、
「ある層で求める最適な出力を学習するのではなく、層の入力を参照した残差関数を学習する」 こ
とで最適化しやすくしています。
今回使う “ResNet” について
https://deepage.net/deep_learning/2016/11/30/resnet.html
https://github.com/Microsoft/CNTK/tree/master/Examples/Image/Classification/ResNet
トレーニングの流れ
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
コードリーディングと理解
まずはプロジェクトの理解から
13
データ用意からデプロイまで
14
データの用意
• トレーニングデータの用意
• テストデータの用意
• マップファイルの用意
TransferLearning_Extended.py
を利用
• ファイル読み込みの設定、データ
やパラメータの定義など
• 内部的に TransferLeaning.py を
呼び出す
デプロイするために
C#.NET のプロジェクトを作成
• プログラムファイルの改修
• モデルの配置
• デプロイ
TransferLearning.py
= TransferLearningをCNTKで
行う際の本体
✓ 学習方法・ロジックが定義
されているファイル
✓ 学習とモデルの作成を行う
TransferLearning_Extended.py
✓ パラメータやファイルの
処理などが記載
✓ TransferLearning.pyを
読み出す
人工知能API on Azure
TransferLearning.py を利用する際のファイル構成
TransferLearning
install_data_and_model.py
TransferLearning.py
TransferLearning_Extended.py
Output
TransferLearning.model
predictions.txt
PretrainedModels
ResNet_18.model
TransferLearning.model
DataSets
Flowers
6k_img_map.txt
1k_img_map.txt
jpg
image_00001.jpg
image_00002.jpg
install_flowers.py
Animals
image_00001.jpg
Test
Tiger
image_10001.jpg
Train
Tiger
15
転移学習の
スクリプト
スクリプト実行後の
アウトプット
転移学習に使う
ベースモデル
サンプルのデータ
花の画像データ
野球データ
画像とラベルの
マップファイル
今回はGitHubにあるCNTKのTransfer Learningのトレーニングサンプルプログラムを見ていきます。
利用する主要なファイルは以下になります。
✓ https://github.com/Microsoft/CNTK/tree/master/Examples/Image
データとモデルを
ダウンロードする
TransferLearning_Extended.pyでなにをやっているか
TransferLearning_Extended.py は、TransferLearningを使うための実行ファイルです。
このプログラムでは最初に TransferLearning.py をロードしています。そのため、TransferLearning.py で定
義されている関数をすべて使うことが出来ます。このファイルでは主に以下の機能が備わっています。
マップファイル作成:create_map_file_from_folder()
✓ マップファイルをフォルダ名・ファイルパスから作成する。
クラス作成:create_class_mapping_from_folder()
✓ フォルダ名から、クラスを作る (例:ラベルが3種類の場合はそれぞれのラベルの配列を作る)
操作ログ出力:format_output_line()
✓ それぞれのトレーニング結果などのデータを出力するための関数
16
TransferLearning.py でなにをやっているか
ミニバッチを作成:create_mb_source()
✓ マップファイル (ImageとLabelの対応テキスト)を読み込んでミニバッチ実行のためのハコ(クラス)を作る
ベースモデルを作成:create_model()
✓ ベースとなるモデルから必要な部分(転移させる部分)を抽出し、そこに新しい学習のための定義情報などを付与し、今
回アウトプットするモデルのベース部分を作る
モデルをトレーニング:train_model()
✓ 作成したベースのモデルをトレーニングして、トレーニング済みの新しいモデルを作成する
一つのイメージを評価:eval_single_image()
✓ 一つのデータイメージでモデルを評価する。このプロセスはAPIを作って1枚1枚画像を処理したいときに有用
イメージのデータセットの評価:eval_test_images()
✓ 複数のデータセットでモデルを評価する。このプロセスはトレーニングしたモデルの精度について調べるときに有用
実行部分:Main
✓ このファイルに含まれる実行部分(関数ではないです)
17
エポック(Epoch)とミニバッチ(Mini-batch)
✓ 10000枚の学習用画像があるとします。
✓ 本来であれば、1枚ごとに損失関数をもとめ、重みを更新し、という処理を10000回繰り返したいところですが、重みの更新処理
が非常に重く、計算時間が膨大になります。
✓ そのため、10000枚から100枚ずつ選び出して、100枚の1枚1枚に損失関数を求め、それを足して、100で割り、平均の損失関数
を求めます。それをもとに重みの更新処理を1回行います。このように部分的なサンプルで重みを更新することをミニバッチと言
います。
✓ 10000枚の訓練データに対して100枚のミニバッチで学習する場合、重みの更新を100回繰り返すとすべての訓練データを見たこ
とになるので、100回=1エポックとします。つまり、エポックとは、全画像データ(10000枚)をすべて処理したことを表す単位
です。
18
サマリー:全体のながれ
フォルダ構造からマップファイルを作成
分析のパラメータを計算、初期値をロード
ミニバッチを作成ベースモデルを作成
トレーニングデータからモデルをトレーニング
テストデータでモデルの精度を評価
19
TransferLearning.py
TransferLearning_Extended.py
= TransferLearningをCNTKで
行う際の本体
✓ 学習方法・ロジックが定義
されているファイル
✓ 学習とモデルの作成を行う
✓ パラメータやファイルの
処理などが記載
✓ TransferLearning.pyを
読み出す
TransferLearning.py の
コードリーディングと理解
20
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
処
理
の
実
態
定義
実行
その前に:CNTKプログラミングの基本
22
Train
変数・関数・クラスなどを用意していき➡最後にガバっと実行
初期パラメータ
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"
初期パラメータはそれぞれ以下に設定されています。
✓ ファイルパスの定義:データセットやモデルへのパス
✓ モデルを読み込む際の設定:モデルにはパラメータやノードの定義がされています。今回操作する際にどのような処理を
するのか定義しています。
✓ 抽出するモデルの定義に関する設定:インプットデータの次元やクラス数が定義されています。
✓ ラーニングのパラメータ:学習の際の数式に与えるパラメータが定義されています。
[参考] 試用する 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の機能はそれぞれ以下を使っています。
全体の流れ
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
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のモデルがダウンロードされます。
初期設定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()のパラメータ
全体の流れ
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
処
理
の
実
態
定義
実行
エポックとミニバッチ
✓ 10000枚の学習用画像があるとします。本来であれば、1枚ごとに損失関数をもとめ、重みを更新し、という処理を10000回繰り
返したいところですが、重みの更新処理が非常に重く、計算時間が膨大になります。
✓ そのため、10000枚から100枚ずつ選び出して、100枚の1枚1枚に損失関数を求め、それを足して、100で割り、平均の損失関数
を求めます。それをもとに重みの更新処理を1回行います。このように部分的なサンプルで重みを更新することをミニバッチと言
います。
✓ 10000枚の訓練データに対して100枚のミニバッチで学習する場合、重みの更新を100回繰り返すとすべての訓練データを見たこ
とになるので、100回=1エポックとします。つまり、エポックとは、全画像データ(10000枚)をすべて処理したことを表す単位
です。
29
モデルをトレーニング : 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
パラメータ
エポック数を指定する理由
エポック数とは、一回のトレーニングデータセットを何回繰り返して学習させるかの数のことです。
深層学習だとパラメータが多いので、トレーニング用のデータセットを何回も繰り返し学習させないと、
うまく学習してくれないといった問題があります。
ミニバッチを用意するにあたり用意するインプットデータ
今回は、ベースモデルであるResNetの入力層とアウトプットレイヤーの値を入れ替
えるためインプットデータを2つ用意します
31
入力データ(次元xピクセル) Input Layer Hidden Layer(複数) Output Layer
チューリップ
コスモス
ひまわり
N
層
の
隠
れ
層
出力データ(ラベル)
モデルをトレーニング
ミニバッチソースを作成 : 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
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
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の渡され方
ここまでで下準備は完了です。イメージのファイルを抽出する際の定義などが完了しました。
全体の流れ
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
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
Transfer Learningにおけるモデルの変更箇所
37参考:http://cs231n.stanford.edu/slides/2017/cs231n_2017_lecture7.pdf
汎用的なモデル
特定の用途のモデル
Transfer Learningでは、ベーシックな特徴を学習したモデルの下位
層が再利用されます。そしてそのモデルをベースに特定の特徴量が
検出できるモデルに変更される処理がなされます。
今回再利用する部分 識別層を置き換え(ラベル数)
入力層を置き換え(ピクセル数)
用語説明
conv : 畳み込み層
pool : プーリング層
fc : 全結合層
ベースのモデルを作成
ベースモデルから、再利用する部分を抽出する
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
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を接続する処理をしていま
す。
複製箇所
クラスに合致するように
置き換え
プレースホルダーを設置
後から挿入
全体の流れ
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
処
理
の
実
態
定義
実行
The definition of optimization in Neural Network
ニューラルネットワークにおける学習とは、モデルの予測値と実際の値
との誤差から、パラメータ(重み)を適切に更新すること
目的関数
探索最適化対象
• 各層間の「重み」
• 平均二乗誤差
(Mean Squared Error /
Classification Error)
• 交差エントロピー誤差
(Cross-Entropy Error)
• 勾配法
(Gradient method)
最適化対象
(Optimization Target)
目的関数
(別名:損失関数 - loss function)
探索手法
(別名:Optimizer、Learner)
重みの最適化と
インプットとアウトプットの差を計算します。エポックの数だけ処理をまわし、重
みを更新していきます。
42
入力データ(次元xピクセル) Input Layer Hidden Layer
(複数)
Output Layer
3:チューリップ
1:コスモス
2:ひまわり
N
層
の
隠
れ
層
出力データ(ラベル)
0.1
0.7
0.2
②クラス(種類)の違いを
把握し、値をフィードバック
③重みを更新
①1つのエポックをまわす
④学習した重みで次のエポックをまわす➡…
学習用のための準備
クロスエントロピー : cross_entropy_with_softmax()
43
ce = cross_entropy_with_softmax(tl_model, label_input)
クロスエントロピー
ある数値の集団1と、ある数値の集団2がどれくらい異なるかを表す概念です。
今回はこちらをloss関数として使います。
エントロピー
エントロピーとは乱雑さの度合いです。この「エントロピー」という概念は様々
な理工学分野で用いられます。上下に分離したドレッシングの瓶をシャカシャカ
振ってよく混ざった状態にすると「エントロピーが増えた」となります。
これを機械学習でいうと「学習データの出力と、実際のニューラルネットワーク
の学習結果としての出力がどれくらい異なるか」を表します。
このクロスエントロピーを用いる際に比較対象となる「数値の軍団」は正規化さ
れている必要があり、内部的にソフトマックス関数で正規化しています。
参考:http://qiita.com/MATS_ElectricBlueIndustries/items/db85de80c72be28fc60b
学習用のための準備
平均二乗誤差(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/
探索手法の種類
勾配法は各重みが損失関数に対してどの程度影響度があるかを求め、それに応じて
学習率をベースに重みを調整する方法です。
勾配法にも種類がありますが、SGDとMomentum SGDだけ覚えれば結構です。
45
SGD
MomentumSGD
• 色々な勾配法
- Gradient Descent
- SGD
- MomentumSGD
- AdaGrad
- AdaDelta
- Adam
- RMSpropGraves
- NesterovAG
学習率
(Learning Rate)
学習率 モーメンタム
(Momentum)
SGDには「学習率」をハイ
パーパラメータとして指定
しなければならない
MomentumSGDには「学習
率」と「モーメンタム」を
ハイパーパラメータとして
指定しなければならない
勾配降下法
「ある教師データを読み込ませたときの出力がどれくら
い期待外れだったか?」を先ほどのクロスエントロピー
と平均二乗誤差の関数で計算しました。
アルゴリズム
✓ この学習においてはその差をフィードバックし、エポックの
回数をこなすことでモデルの精度(ノードの重み・パラメー
タ)を更新していきます。
✓ 今回はその調整にMomentumSGDというその値を調整するア
ルゴリズムを用います。
調整
✓このアルゴリズムでは学習率とモーメンタムをハイパー
パラメータとして指定します。
✓この値は出力されるモデルの精度をもとにトライアンド
エラーで調整する必要があります。
46参考情報:http://hokuts.com/2015/11/25/ml2_perceptron/
学習率 モメンタム
(Momentum)
学習のための準備
学習のスケジュールの設定
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()
学習のための準備
勾配降下法学習 / トレーナー インスタンス
全体の流れ
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
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 スケジュールを作成します
最後にバッチ単位でエポック数分トレーニングを回していきます。
精度とトレーニング
50
似ているデータセット 異なるデータセット
少ないデータ トップレイヤーでの線形分類をしましょう。
データが小さいため、ConvNetを過剰適合に対応するため
に調整することは良いアイデアとは言えません。
データは元のデータと似ているため、ConvNetの上位レベ
ルの機能もこのデータセットに関連すると考えられます。
したがって、最良のアイデアは、CNNコード上で線形分類
器を訓練することです。
過剰適合:訓練データに対して学習されているが、未知
データ(テストデータ)に対しては適応できていない状態
線形分類器を訓練することが最もよいと
思われます。
データセット固有の機能が多く含まれて
いるネットワークの上部にある分類子を
訓練するのが最良ではない場合がありま
す。
その代わりに、SVMクラシファイアを
ネットワークの以前のどこかのアクティ
ベーションから訓練するほうが効果的か
もしれません。
大量のデータ 少ないレイヤーの微調整で大丈夫でしょう。
より多くのデータがあるので、ネットワークをフルで
チューニングしても、過剰適合にはならないだろうと言え
ます。
多くのレイヤーを調整しましょう。
あらかじめトレーニングされたモデルか
らウェイトを使用して初期化することも
有益です。
微調整では以下の以下2つが重要です。以下の4つのシナリオのケースごとに見ていきましょう。
✓新しいデータセットのサイズ
✓元のデータセットとの類似性
特定
汎用的
TransferLearning_Extended.py
コードリーディングと理解
51
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
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']
ファイルパスの変更
これらの値を変えましょう。テストデータとトレーニングデータが入っているフォルダーのパスを記載します。
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
マップファイルをフォルダ名・ファイルパスから作成する。
マップファイルをフォルダ名・ファイルパスから作成します
TransferLearning.py をもっと便利に使う
クラスを作成します
57
def create_class_mapping_from_folder(root_folder):
classes = []
for _, directories, _ in os.walk(root_folder):
for directory in directories:
classes.append(directory)
return np.asarray(classes)
フォルダ名からクラスを作る
フォルダ名からクラスを作ります。
Flower
image_00001.jpg
Test
Cosmos
image_10001.jpg
Sunflower
image_20001.jpg
Turip
3クラス
[“Cosmos”,”Sunflower”,”Turip”]
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
それぞれのトレーニング結果などのデータを出力するための関数
✓それぞれのトレーニング結果などのデータを出力するための関数です。
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() 関数を利用し、モデルをトレーニングします。
Webデプロイチュートリアル
コードリーディングと理解
62
GitHubでプログラムをClone
✓トレーニングしたデータをデプロイするためのC#のWeb APIサンプルです。
https://github.com/hat22u/CNTKAzureTutorial01withV2API
✓ベースのシステムはこちらですので、環境構築はこちらを参考にしてください
63
←作成したモデルを配置します。
←このファイルがAPIの実態なので、こちらを編集していきます。
Web APIの処理
64
①
モデルをロードし、インプット/アウトプット値を抽出
②
URLから画像を抽出し、モデルが求めるインプットサイズに変換
③
変換した値をデータマップ変数に追加
④
画像を評価し、返り値をjsonでかえす
C#を使ってデプロイをしていきます。C#ではCNTK Library APIを使ってモデルの評価をします。
このAPIは評価に重点を置いており、トレーニングはできません。
具体的な手順は以下です。
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()でモデルを抽出し、配列からモデルが持つインプット値、アウトプット値を取り出します。
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)です。
✓リサイズしたら、それぞれのパラメータを浮動小数点のリストとして変数に代入します。
✓今回の分類をするうえでは、画像が横に伸びていても大きな問題はありません。
イメージのサイズ
✓学習の際には画像データを引き延ばしたり反転したりしてモデルを
学習させていきます。
✓そのため、例えば評価の際のイメージサイズが無理矢理224x224の
大きさに統一されたとしても、精度に大きな影響はないと言えます。
67
元データ ずらしたり 広げたり 回転させたり さらに回転させたり !?
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つある場合
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で渡すと配列がレスポンスとして返ってきます。
最後に:出来上がったモデルの精度を上げるには
モデルの精度を上げるためには、トレーニングデータセットを見直しましょう。
トライ&エラーで、確率的勾配降下法などのハイパーパラメータを調整するのも
モデルの精度を高める一つの手法ですが、それよりもまずはデータセットの以下
の点を見直しましょう。
✓データセットの数は多すぎないか?
➡過学習を避けるために、質の良いデータを適量集めましょう。
✓「お手本のデータ」ばかりになっていませんか?
➡人間にも機械にもわかりやすいデータだけでなく、
バランスの良いデータを入れましょう。
70
 本書に記載した情報は、本書各項目に関する発行日現在の Microsoft の見解を表明するものです。Microsoftは絶えず変化する市場に対応しなければならないため、ここに記載した情報に対していかなる責務を負うものではなく、
提示された情報の信憑性については保証できません。
 本書は情報提供のみを目的としています。 Microsoft は、明示的または暗示的を問わず、本書にいかなる保証も与えるものではありません。
 すべての当該著作権法を遵守することはお客様の責務です。Microsoftの書面による明確な許可なく、本書の如何なる部分についても、転載や検索システムへの格納または挿入を行うことは、どのような形式または手段(電子的、
機械的、複写、レコーディング、その他)、および目的であっても禁じられています。
これらは著作権保護された権利を制限するものではありません。
 Microsoftは、本書の内容を保護する特許、特許出願書、商標、著作権、またはその他の知的財産権を保有する場合があります。Microsoftから書面によるライセンス契約が明確に供給される場合を除いて、本書の提供はこれら
の特許、商標、著作権、またはその他の知的財産へのライセンスを与えるものではありません。
© 2017 Microsoft Corporation. All rights reserved.
Microsoft, Windows, その他本文中に登場した各製品名は、Microsoft Corporation の米国およびその他の国における登録商標または商標です。
その他、記載されている会社名および製品名は、一般に各社の商標です。

Transfer Learning & API Azure