TensorFlow のチュートリアル(Convolutional Neural Networks)
http://www.tensorflow.org/tutorials/deep_cnn/index.html#convolutional-neural-networks
の翻訳です。
翻訳の誤りなどあればご指摘お待ちしております。
注:このチュートリアルは TensorFlow の上級ユーザーを対象としており、機械学習の専門知識と経験を前提としています。
##概説
CIFAR-10 分類は、機械学習における一般的なベンチマーク問題です。問題は、RGB 32×32 ピクセルの画像を10カテゴリーに分類するものです:飛行機、自動車、鳥、猫、鹿、犬、カエル、馬、船、およびトラック
詳細については CIFAR-10ページ と Alex Krizhevsky による技術レポートを参照してください。
###目標
このチュートリアルの目標は、画像認識のための比較的小さな畳み込みニューラルネットワーク(CNN)を構築することです。このチュートリアルの手順では:
- ネットワークアーキテクチャ、トレーニング、評価のための標準的な構成をハイライトします。
- より大きく、より洗練されたモデルを構築するためのテンプレートを提供します。
CIFAR-10 を選択した理由は、TensorFlow の大規模なモデルにスケールする能力の多くを演習するために、十分複雑であるためです。同時に、モデルは十分に小さく、高速に訓練できます。そのため、新しいアイデアを試し、新しい技術を実験するには理想的です。
###チュートリアルのハイライト
この CIFAR-10 のチュートリアルは、TensorFlow でより大きく、より洗練されたモデルを設計するための、いくつかの重要な構造を示します:
- 畳み込み、ReLU 活性化、最大プーリングと局所的応答正規化を含む、中心的な数学的コンポーネント
- 入力画像、損失、活性化と勾配の分布を含む、訓練中のネットワーク活動の可視化
- 学習済みパラメータの移動平均を計算し、予測性能を高めるために、評価の際にこれらの平均値を使用するためのルーチン
- 体系的に時間とともに減少させる、学習率スケジュールの実装
- ディスクの待ち時間および高価な画像前処理から、モデルを分離するための、入力データのキューのプリフェッチ
また、以下を説明する、マルチ GPU バージョンのモデルを提供します:
- 同時に複数の GPU カードで訓練するための、モデルの設定。
- 複数の GPU 間での変数の共有と更新。
このチュートリアルが、TensorFlow での画像タスクの、より大きな CNN 構築の出発点となることを願っています。
###モデル・アーキテクチャ
この CIFAR-10 チュートリアルのモデルは、畳み込みと非線形性が交互に現れる多層アーキテクチャです。これらの層に、ソフトマックス分類器へ続く全結合層が続きます。モデルは、Alex Krizhevsky が述べたアーキテクチャに、上位数層のいくつかの違いを除き、従います。
このモデルは、GPU 上での訓練で、数時間以内で約86%の精度の最大性能を実現しています。詳細については、以下およびコードを参照してください。それは 1,068,298 個の学習可能なパラメータで構成され、単一画像の推論を計算するためにはおよそ1950万回の乗算、加算が必要です。
##コード構成
このチュートリアルのコードは tensorflow/models/image/cifar10/ にあります。
ファイル | 目的 |
---|---|
cifar10_input.py | CIFAR-10 バイナリファイル形式を読み込み |
cifar10.py | CIFAR-10 モデルを構築 |
cifar10_train.py | CPU または GPU で CIFAR-10 モデルを訓練 |
cifar10_multi_gpu_train.py | 複数の GPU で CIFAR-10 モデルを訓練 |
cifar10_eval.py | CIFAR-10 モデルの予測性能を評価 |
##CIFAR-10モデル
CIFAR-10 ネットワークは、主に cifar10.py に含まれています。完全な訓練グラフには、およそ765の操作が含まれています。以下のモジュールでグラフを構築することにより、コードをほとんど再利用可能にできることが分かります:
- モデル入力:inputs() と distorted_inputs() は、評価および訓練のために、CIFAR 画像を読み、また、前処理する操作を追加します
- モデル予測:inference() は指定された画像に対して推定を実行、すなわち分類する操作を追加します
- モデルの訓練:loss() と train() は損失、勾配、変数の更新、可視化の要約を計算する操作を追加します
###モデル入力
モデルの入力部分は CIFAR-10 バイナリデータファイルから画像を読み込む関数 inputs() と distorted_inputs() によって構築されています。これらのファイルは、固定バイト長のレコードを含むので、tf.FixedLengthRecordReader を使用しています。Reader クラスの詳細は、データの読込みを参照してください。
画像は次のように処理されます:
訓練において、人工的にデータセットを増加させるために、一連のランダムな歪みを適用します:
利用可能な歪みのリストについては、画像のページをご覧ください。TensorBoard でそれらを視覚化できるように、画像に image_summary を添付します。入力データが正しく作成されていることを確認することをお勧めします。
ディスクから画像を読み込み、歪める処理には、多少時間がかかる場合があります。この操作が訓練を遅くすることを防ぐために、継続的に TensorFlow のキューを満たす16の別々のスレッドでこれを実行します。
###モデル予測
モデルの予測部は、予測のロジットを計算する操作を追加する、inference() 関数で構成されています。モデルのこの部分は次のように構成されます。
層名 | 説明 |
---|---|
conv1 | 畳み込みと ReLU 活性化 |
pool1 | 最大プーリング |
norm1 | 局所的応答正規化 |
conv2 | 畳み込みと ReLU 活性化 |
norm2 | 局所的応答正規化 |
pool2 | 最大プーリング |
local3 | ReLU 活性化を伴う全結合層 |
local4 | ReLU 活性化を伴う全結合層 |
softmax_linear | ロジットを生成する線形変換 |
TensorBoard により生成された推論操作のグラフは、次のとおりです。
演習:推論の出力は正規化されていないロジットです。ネットワーク・アーキテクチャを編集し、tf.softmax() を使用して正規化された予測を返すようにしてください。
inputs() と inference() 関数は、モデルの評価を行うために必要なすべてのコンポーネントを提供します。次は、モデルを訓練する操作の構築に焦点をシフトしましょう。
演習:inference() のモデル・アーキテクチャは、cuda-convnet で定義された CIFAR-10 のモデルとは若干異なります。具体的には、Alex の元モデルの最上位層は部分結合であり、全結合ではありません。最上位層が部分結合になるようにアーキテクチャを編集してみてください。
###モデルの訓練
Nクラスの分類を行うネットワークを訓練する通常の方法は、多項ロジスティック回帰、別名ソフトマックス回帰です。ソフトマックス回帰は、ネットワークの出力にソフトマックス非線形性を適用し、正規化された予測と、ラベルの1-ホット符号化の、交差エントロピーを計算します。また、正則化のため、すべての学習済み変数に通常の重み減衰(weight decay)損失を適用します。loss() 関数によって返されるモデルの目的関数は、交差エントロピー損失と、これらすべての重み減衰項の和です。
TensorBoard で scalar_summary を用いて、これを可視化します:
標準的な勾配降下アルゴリズム(他の方法のための訓練を参照)を使用して、時間に対して指数関数的に減衰する学習率で、モデルを訓練します。
train() 関数は、勾配を計算し学習変数を更新(詳細については GradientDescentOptimizer を参照してください)することにより目的関数を最小化するために必要な操作を、追加します。この関数は、画像の1バッチに対しモデルを訓練し更新するために必要な全ての計算を実行する操作を、返します。
##モデルの起動と訓練
モデルの構築はこれで完了です。スクリプト cifar10_train.py により、モデルを起動し、訓練する操作を実行してみましょう。
python cifar10_train.py
注:CIFAR-10 チュートリアルの任意の対象を最初に実行するときは、CIFAR-10 データセットが自動的にダウンロードされます。データセットは約160MBありますので、最初の実行時は、コーヒーでも飲んでいてください。
次のように出力されるはずです:
Filling queue with 20000 CIFAR images before starting to train. This will take a few minutes.
2015-11-04 11:45:45.927302: step 0, loss = 4.68 (2.0 examples/sec; 64.221 sec/batch)
2015-11-04 11:45:49.133065: step 10, loss = 4.66 (533.8 examples/sec; 0.240 sec/batch)
2015-11-04 11:45:51.397710: step 20, loss = 4.64 (597.4 examples/sec; 0.214 sec/batch)
2015-11-04 11:45:54.446850: step 30, loss = 4.62 (391.0 examples/sec; 0.327 sec/batch)
2015-11-04 11:45:57.152676: step 40, loss = 4.61 (430.2 examples/sec; 0.298 sec/batch)
2015-11-04 11:46:00.437717: step 50, loss = 4.59 (406.4 examples/sec; 0.315 sec/batch)
...
スクリプトは、10ステップごとに損失合計をレポートするだけでなく、データの最後のバッチが処理された時点での速度をレポートします。いくつかのコメント:
-
データの最初のバッチは異常に遅い(例えば、数分)場合があります。これは、前処理スレッドが 20,000 の処理された CIFAR 画像で、シャッフルされるキューを埋めるためです
-
レポートされた損失は、最新のバッチの平均損失です。この損失は、交差エントロピーと全ての重み減衰項の和であることに注意してください
-
バッチの処理速度に注目してください。上に示した数字は、テスラK40c で得ました。CPU で実行した場合、パフォーマンス低下が予期されます
演習:実験時、最初の訓練ステップに時間がかかりすぎることを、しばしば忌々しく感じるかもしれません。最初にキューを埋める画像の数を減らしてみてください。cifar10.py で NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN を検索してください。
cifar10_train.py は、定期的にチェックポイント・ファイルにすべてのモデルパラメータを保存しますが、モデルの評価は行いません。チェックポイント・ファイルは cifar10_eval.py によって予測性能を測定(下記モデルの評価を参照)するために使用されます。
先の手順を実行すると、CIFAR-10 モデルの訓練が開始されます。おめでとう!
cifar10_train.py から返される端末のテキストは、モデルがどのように訓練されているかについて、最小限の洞察を提供します。訓練中にもっとモデルの洞察を得たいです:
- 損失は本当に減少しているか、またはそれはただのノイズか?
- モデルは、適切な画像を提供されているか?
- 勾配、アクティベーションと重みは妥当か?
- 学習率は現在いくらか?
TensorBoard は cifar10_train.py から SummaryWriter を経て定期的にエクスポートされたデータを表示することにより、この機能を提供します。
例えば、local3 フィーチャーにおけるアクティベーションとスパース度の分布が訓練中にどのように進展するかを見ることができます:
個々の損失関数を、全損失と同様に、時間の経過とともに追跡することは特に興味深いです。しかし、損失は、訓練に用いられる小さなバッチサイズによるかなりの量のノイズを示します。実践的には、生の値に加えて、その移動平均を可視化することが非常に有効です。この目的のためにスクリプトが ExponentialMovingAverage を使用している方法を参照してください。
##モデルの評価
それでは、訓練されたモデルが提供されたデータセットでどれくらいよく機能するか評価しましょう。モデルは、スクリプト cifar10_eval.py によって評価されます。これは、inference() 関数を使用してモデルを構築し、CIFAR-10 の評価セット内の全1万画像を使用します。それは、1の精度、すなわち、トップ予測が画像の真のラベルに一致する頻度、を計算します。
訓練中のモデルの改善状況を監視するために、cifar10_train.py によって作成された最新のチェックポイント・ファイルに対して評価スクリプトが定期的に実行されます。
python cifar10_eval.py
評価と訓練のバイナリを同じ GPU で実行しないように注意してください。そうでなければメモリが不足するでしょう。利用可能であれば別の GPU で評価を実行するか、同じ GPU の場合には訓練バイナリを一時停止することを検討してください。
次のように出力されるはずです:
2015-11-06 08:30:44.391206: precision @ 1 = 0.860
...
スクリプトは、単に定期的に精度@ 1を返します、この場合は86%の精度です。cifar10_eval.py も TensorBoard で視覚化できる要約をエクスポートします。これらの要約は、評価中にモデルに追加の洞察を提供します。
訓練スクリプトは、すべての学習済み変数の移動平均版を計算します。評価スクリプトは、学習済みモデル・パラメータを移動平均版で置き換えます。この置換は、評価時のモデルの性能を向上させます。
演習:モデルの平均化パラメータを使用することにより精度@ 1によって測定される予測性能を約3%高めることができます。cifar10_eval.py を平均化パラメータを使用しないように編集し、予測性能が低下することを確認してください。
##複数 GPU カードを使用したモデルの訓練
現在のワークステーションは、科学技術計算のための複数の GPU が含まれている場合があります。 TensorFlow は、複数のカード全体で同時に訓練操作を実行することで、この環境を活用することができます。
モデルの並列、分散方式による訓練は、訓練プロセスのコーディネートを必要とします。後のために、データのサブセットにより訓練されたモデルの一つのコピーを、モデル・レプリカと呼びます。
単純にモデル・パラメータの非同期更新を採用した場合、
個々のモデル・レプリカがモデル・パラメータの古いコピーで訓練される可能性があるため、最善の訓練パフォーマンスを下回ります。逆に、完全に同期更新を採用した場合、最も遅いモデル・レプリカと同じくらい遅くなります。
複数の GPU カードを搭載したワークステーションでは、それぞれの GPU が同じような速さを持っており、全 CIFAR-10 モデルを実行するのに十分なメモリが含まれています。したがって、以下のように訓練システムを設計することにします:
- 各 GPU に個々のモデル・レプリカを配置
- 全 GPU のバッチ・データの処理終了を待ち、モデル・パラメータを同期更新
このモデルのダイアグラムは以下です:
各 GPU は推定だけでなく、一意のバッチ・データの勾配を計算することに注意してください。この設定では、データの大きなバッチを効率的に GPU 間で分割することが可能となります。
この設定は、すべての GPU がモデル・パラメータを共有することを必要とします。よく知られているように、GPU へ、また GPU からのデータ転送は非常に遅いです。このような理由から、すべてのモデル・パラメータを CPU(緑色のボックスを参照)に格納し、更新することにしました。データの新しいバッチがすべての GPU によって処理された後、新たなモデル・パラメータ・セットが GPU に転送されます。
GPU は、操作において同期化されます。GPU からすべての勾配が蓄積され、平均化されます(緑色のボックスを参照)。モデル・パラメータは全モデル・レプリカの平均の勾配で更新されます。
###変数と操作のデバイスへの配置
操作と変数をデバイスへ配置するには、いくつかの特別な抽象化が必要です。
必要とする最初の抽象化は、単一のモデル・レプリカの推論と勾配を計算するための関数です。コードでは、この抽象化を"tower"と呼びます。各 tower に2つの属性を設定する必要があります。
-
tower 内のすべての操作のための一意の名前。tf.name_scope() は、スコープを付加することで、この一意の名前を提供します。例えば、最初の tower 内のすべての操作の前に tower_0 が付加されます、例えば tower_0/conv1/Conv2D
-
好ましいハードウェア・デバイスが、tower 内の操作を実行します。tf.device() は、これを明示します。例えば、最初の tower 内の device('/gpu:0') スコープに存在するすべての操作は、最初の GPU 上で実行する必要があることを示します
すべての変数は CPU に固定され、複数 GPU バージョンでそれらを共有するためには tf.get_variable() を介してアクセスします。共有変数のハウツーを参照してください。
###複数 GPU カードでのモデルの起動と訓練
複数の GPU カードを持っていて、マシンにインストールされている場合、モデルをより高速に訓練するために cifar10_multi_gpu_train.py スクリプトにより、それらを使用することができます。訓練スクリプトのこのバージョンでは、複数の GPU カード全体でモデルを並列化します。
python cifar10_multi_gpu_train.py --num_gpus=2
訓練スクリプトの出力は次のようになるはずです:
Filling queue with 20000 CIFAR images before starting to train. This will take a few minutes.
2015-11-04 11:45:45.927302: step 0, loss = 4.68 (2.0 examples/sec; 64.221 sec/batch)
2015-11-04 11:45:49.133065: step 10, loss = 4.66 (533.8 examples/sec; 0.240 sec/batch)
2015-11-04 11:45:51.397710: step 20, loss = 4.64 (597.4 examples/sec; 0.214 sec/batch)
2015-11-04 11:45:54.446850: step 30, loss = 4.62 (391.0 examples/sec; 0.327 sec/batch)
2015-11-04 11:45:57.152676: step 40, loss = 4.61 (430.2 examples/sec; 0.298 sec/batch)
2015-11-04 11:46:00.437717: step 50, loss = 4.59 (406.4 examples/sec; 0.315 sec/batch)
...
GPU カードの数はデフォルトで1が使用されることに注意してください。さらに、マシンで利用可能な GPU がただ一つの場合、より多く要請した場合であっても、すべての計算はその唯一の GPU に配置されます。
演習:cifar10_train.py のデフォルト設定では、バッチ・サイズ128で実行されます。cifar10_multi_gpu_train.py を2つの GPU でバッチ・サイズ64で実行し、訓練速度を比較してください。
##次のステップ
おめでとう!あなたは CIFAR-10 チュートリアルをコンプリートしました。
独自の画像分類システムを開発し、訓練することに興味があるならば、このチュートリアルをフォークして、その画像分類問題に対処するためにコンポーネントを交換することをお勧めします。
演習:ストリート・ビュー・ハウス番号(SVHN)データ・セットをダウンロードしてください。CIFAR-10 チュートリアルをフォークして、入力データを SVHN に交換してください。予測性能を改善するために、ネットワーク・アーキテクチャを改造してみてください。