TensorFlow : Tutorials : Inception の最終層を新しいカテゴリーのためにどのように再訓練するか (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/10/2017
* 本ページは、TensorFlow の本家サイトの Tutorials – How to Retrain Inception’s Final Layer for New Categories を
翻訳した上で適宜、補足説明したものです:
https://www.tensorflow.org/tutorials/image_retraining
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
現代的な物体認識モデルは数百万のパラメータを持ち完全に訓練するためには数週間かかります。転移学習は完全に訓練済みのモデルを ImageNet のようなカテゴリのセットに利用してこれらの作業の多くをショートカットするテクニックで、既存の重みから新しいクラスのために再訓練します。本稿の例では、最終層をスクラッチから再訓練しますが、全ての他のものはさわらずにそのままにしておきます。このアプローチの更なる情報については Decaf のこのペーパー を参照できます。
完全な訓練を実行するほどには良くありませんが、多くのアプリケーションに対して驚くほど効果的で、そして GPU を必要とすることなく、ラップトップ上で早ければ 30 分のうちに実行可能です。このチュートリアルは貴方自身の画像でサンプルスクリプトをどのように実行するかを示し、そして訓練プロセスを制御するためのオプションの幾つかを説明します。
花の(画像の)上で訓練する
どのような訓練を開始する前でも、ネットワークに認識させたい新しいクラスについて教えるための画像のセットが必要です。どのように貴方自身の画像を準備するかを説明する後のセクションがありますが、最初の利用を簡単にするために creative-commons license の花の画像のアーカイブを作成しました。花の画像のセットを得るためには、これらのコマンドを実行してください :
cd ~ curl -O http://download.tensorflow.org/example_images/flower_photos.tgz tar xzf flower_photos.tgz
画像をひとたび得たのであれば、retrainer を次のようにビルドできます、TensorFlow ソース・ディレクトリのルートからです :
bazel build tensorflow/examples/image_retraining:retrain
AVX 命令セット (common in x86 CPUs produced in the last few years) をサポートするマシンを持つならば、次のように (configure で適切なオプション選択後)、そのアーキテクチャのためにビルドすることによって再訓練の実行速度を改善できます :
bazel build --config opt tensorflow/examples/image_retraining:retrain
そして retrainer は次のように実行できます :
bazel-bin/tensorflow/examples/image_retraining/retrain --image_dir ~/flower_photos
このスクリプトは事前訓練された Inception v3 モデルをロードし、古いトップ層を取り除き、そしてダウンロードした花の写真で新しいものを訓練します。
どの花の種 (species) も、完全なネットワークが訓練された元の ImageNet クラスにはありません。転移学習のマジックは幾つかの物体間を識別するために訓練されたより低い層(群) が変更なしで多くの認識タスクに再利用できることです。
ボトルネック
貴方のマシンの速度に依存して、スクリプトは完了するのに 30 分かそれ以上かかるでしょう。最初の段階はディスクの全ての画像を解析してそれらの各々についてボトルネック値を計算します。「ボトルネック」 は、実際に分類を行なう最終出力層のすぐ前の層のためにしばしば使う非公式な用語です。この最後から2番目 (= penultimate) の層は、認識するために問われる全てのクラス間を識別するために使用される分類器のために十分に良い値のセットを出力するために訓練されています。それは画像の意味あるコンパクトな要約でなければならないことを意味しています、何故ならばそれは分類器が値の非常に小さなセットにおいて良い選択を行なうために十分な情報を含まなければならないからです。最終層の訓練が新しいクラスで動作できる理由は ImageNet の全ての 1,000 クラス間を識別するために必要な情報の種類が新しい種類の物体間の識別のためにもしばしば有用であることが判明していることです。
訓練の間全ての画像は複数回再利用されて各ボトルネックの計算はかなりの時間がかかるので、繰り返し再計算する必要がないようにディスク上にこれらのボトルネック値をキャッシュすることで高速化します。デフォルトではこれらは /tmp/bottleneck ディレクトリにストアされ、そしてスクリプトを再実行するならばそれらが再利用されるのでこのパートについては再び待つ必要がありません。
訓練する
ボトルネック (の計算) が完了したら、ネットワークのトップ層の実際の訓練が始まります。貴方はステップの系列の出力を見るでしょう、各々の一つは訓練精度、検証精度、そして交差エントロピーを示します。訓練精度は現在の訓練バッチで使用されている画像の何パーセントが正しいクラスでラベル付けされたかを示します。検証精度は異なるセットからのランダムに選択された画像のグループ上の精度です。主な違いは訓練精度はネットワークがそれから学習可能な画像を基にしていることでそのためネットワークは訓練データのノイズに過剰適合 (過学習) する可能性があることです。ネットワークのパフォーマンスの真の尺度は訓練データに含まれないデータセット上のパフォーマンスを計測することです — これは検証精度によって測られます。 訓練精度が高いがしかし検証精度が低いままである場合には、これはネットワークが過剰適合してより一般的には役に立たない、訓練画像の特定の特徴を記憶していることを意味します。交差エントロピーは損失関数で学習プロセスが上手く進んでいるかの一見を与えてくれます。訓練の目的は損失をできるだけ小さくすることですから、短期のノイズは無視して、損失が下落傾向を保持しているかどうかを注視することにより学習が動作しているかを識別することができます。
デフォルトではこのスクリプトは 4,000 訓練ステップを実行します。各ステップは訓練セットから無作為に 10 画像を選択し、キャッシュからボトルネックを見つけて、そしてそれらを予測を得るための最終層へと供給します。それからそれらの予測が実際のラベルと比較されて back-propagation プロセスを通して最終層の重みを更新します。プロセスが続くにつれてレポートされる精度が改善されることを見るはずです、そして全てのステップが終わった後で訓練と検証写真から分離されていた画像のセット上で最終的なテスト精度評価が実行されます。このテスト評価は訓練モデルが分類タスクでどのように遂行されるかの最良推定値です。訓練プロセスにランダムネスがあるので正確な値は実行毎に変わりますが 90% と 95 % の間の精度値を見るべきです。この数字は、モデルが完全に訓練された後で正しいラベルが与えられたテストセットの画像のペーセントを基にしています。
再訓練を TensorBoard で可視化する
スクリプトは TensorBoard summaries を含みます、これは再訓練を理解し、デバッグし、そして最適化することをより容易にします。例えば、重みあるいは精度が訓練の間にどのように変わるか、グラフと統計を可視化できます。
TensorBoard を launch するためには、再訓練の間またはその後でこのコマンドを実行します :
tensorboard --logdir /tmp/retrain_logs
TensorBoard が実行されたら、TensorBoard を見るために web ブラウザを localhost:6006 に navigate してください。
スクリプトは TensorBoard summaries とデフォルトでは /tmp/retrain_logs にログ出力します。ディレクトリは –summaries_dir フラグで変更可能です。
TensorBoard README は TensorBoard 使用法について、チップ (tip) & トリック、そしてデバッギング情報を含むより多くの情報を持ちます。
再訓練されたモデルを使用する
スクリプトは貴方のカテゴリーに再訓練された最終層とともに Inception v3 ネットワークのバージョンを /tmp/output_graph.pb に、そしてラベルを含むテキストファイルを /tmp/output_labels.txt に書き出します。これらは両者とも C++ と Python 画像分類サンプル が読み込めるフォーマットなので、新しいモデルを直ちに使用開始できます。トップ層を置き換えたので、label_image を使用しているのであれば例えばフラグ –output_layer=final_result で スクリプトでは新しい名前を指定する必要があります、
これは再訓練したグラフでどのように label_image サンプルをビルドして実行するかのサンプルです :
bazel build tensorflow/examples/label_image:label_image && \ bazel-bin/tensorflow/examples/label_image/label_image \ --graph=/tmp/output_graph.pb --labels=/tmp/output_labels.txt \ --output_layer=final_result \ --image=$HOME/flower_photos/daisy/21652746_cc379e0eea_m.jpg
花のラベルのリストを見るでしょう、多くの場合トップはひなげし (daisy) です (各再訓練されたモデルは少し異なるかもしれませんが)。–image パラメータを貴方自身の画像でそれらを試すために置き変えることができ、C++ コードを貴方自身のアプリケーションと統合するためにテンプレートとして使用できます。
もし Python プログラムで再訓練したモデルを使用したのであれば @eldor4do からのこのサンプルが行なう必要があることを示します。
貴方自身のカテゴリ上で訓練する
花のサンプル画像上でスクリプトを何とか動作させたならば、代わりに貴方がケアするカテゴリをそれに認識することを教えることに目を向け始めることができます。理論的には必要なことの全てはそれにサブフォルダのセットをポイントさせることで、各々はカテゴリの一つから名前付けられてそのカテゴリからの画像のみを含みます。それを行なってサブディレクトリ(群)のルート・フォルダを –image_dir への引数として渡せば、スクリプトは花のために行なったように訓練するでしょう。
以下が花のアーカイブのフォルダ構造がどのように見えるか、スクリプトが求めるレイアウトの種類の例を与えるものです :
実際には望むような精度を得るためにはある程度の作業がかかるでしょう。下に出会うかもしれない一般的な問題の幾つかを通してガイドしてみます。
訓練画像のセットを作成する
始める最初の場所は集める画像に目を向けることです、何故なら訓練と一緒に見る最も一般的な問題は供給されるデータに由来するからです。
訓練が上手くいくためには、認識したい物体の各種の 100 の写真は少なくとも集めるべきです。集めるほど、訓練モデルの精度は良くなりがちです。写真はアプリケーションが実際に遭遇するものの良い表現であることも確実にすることが必要です。例えば、全ての写真をブランク・ウォールに対してインドアで撮りそしてユーザが物体を外で認識しようとするならば、デプロイした時に良い結果を見ることはおそらくないでしょう。
回避すべき他の落とし穴は、学習プロセスはラベル付けされた画像が互いに共通に持つ任意のものを拾ってしまうことで、それに注意を払わないならば有用ではないものになるでしょう。例えば物体の一つの種類を青い部屋で、他のものを緑色の部屋で撮れば、モデルはその予測を実際に大事にしたい物体の特徴ではなく、背景に基礎を置く結果になります。これを回避するためには、できる限り状況の多様性を広くして、違う時間、そして異なるデバイスで写真を撮ってみてください。この問題について更に知りたいのであれば、古典的な (そして出典の怪しい) タンク認識問題 について読むことができます。
使用するカテゴリについて考えることもまた望むかもしれません。多くの異なる物理的な形をカバーする大きなカテゴリをより視覚的に明確な小さなものに分ける価値はあるかもしれません。例えば、「乗り物 (vehicle)」の代わりに「自動車 (car)」、「motorbike (バイク)」、そして「トラック (truck)」を使用するかもしれません。あなたは「閉じた世界 (closed world)」を持つのか「開いた世界 (open world)」を持つのかという問題についても考える価値があります。
閉じた世界では、カテゴリ分けを要求されるもの全ては既知の物体のクラスです。これは植物認識アプリケーションにも適用されるでしょう、そこではユーザは花の写真を撮りがちであることを知っており、行なわなければならないことの全てはどの種かを決定することです。対照的に歩き回るロボットは世界をさまよう時にそのカメラを通してあらゆる種類の異なるものを見るでしょう。その場合には見たものが不確かであるかを分類器がレポートすることを望むでしょう。これは上手くやるのは難しいですが、しかししばしばもし関連する物体のない数多くの典型的な「背景」写真を集めれば、画像フォルダの特別な「未知の (unknown)」クラスにそれらを追加できるでしょう。
画像全てが正しくラベル付けされていることを確かにするために確認することも価値があります。しばしばユーザ生成タグは目的のためには不確実で、例えば Daisy という人の写真のために #daisy を使用したりします。画像を通してミスを取り除けば全体的な精度に驚くかもしれません。
訓練ステップ
画像に満足するならば、学習プロセスの詳細を変更することで結果を改善することに目を向けられるでしょう。挑戦すべきもっとも単純なものは –how_many_training_steps です。このデフォルトは 4,000 ですが、それを 8,000 に増やせば2倍の長さの間訓練します。精度の改善のレートは長い間訓練すればゆっくりになりますが、あるポイントで完全に止まるでしょう、しかしモデルの限界に当たる時を見ることを経験できるでしょう。
歪める (Distortions)
画像訓練の結果を改善する一般的な方法は訓練入力をランダムな方法で変形し、クロップし、あるいは明るくすることによります。これは同じ画像の全ての可能なバリエーションのおかげで訓練データの効果的なサイズ (= 画像数) を拡張する優位点を持ち、分類器の現実的な利用で起きる歪み (distortions) を処理することをネットワークが学習することを助ける傾向にあります。スクリプトでこれらの歪みを有効にする最大の不利益はボトルネック・キャッシングがもはや利用できないことです、入力画像はもはや正確には再利用できないからです。これは訓練プロセスが非常に長くなることを意味しますので、合理的に満足できるモデルを一度得てからモデルの再調整の方法として歪みを試すことを推奨します。
これらの歪みはスクリプトに –random_crop, –random_scale そして –random_brightness を渡すことで有効になります。これらは全てパーセント値で各画像に歪みの各々をどのくらい適用するかを制御します。それらの各々について 5 あるいは 10 の値から始めるのが合理的でそれからどれがアプリケーションに有用であるかを見る実験をします。–flip_left_right はランダムに画像の半分を水平に反転します、これはアプリケーションでそれらの反転が起こりがちであるならば意味があります。例えば文字を認識しようとしているならばそれは良い考えではありません、それらを反転することは意味を破壊しますので。
ハイパー・パラメータ
結果に有用であるかを見るために調整を試すことができる幾つかの他のパラメータがあります。-learning_rate は訓練の間に最終層への更新の重要さを制御します。直感的にはこれが小さければ学習は長くかかりますが、全体的な精度を助ける結果になります。けれどもいつも事実であるとは限りませんので、貴方のケースで何が動作するかを見るには注意深く実験する必要があります。
–train_batch_size は一つの訓練ステップの間にどれだけの画像の画像が検査されるかを制御します、そして学習率はバッチ単位で適用されるのでより大きなバッチで同じ全体的な効果を得るためにはそれを減じる必要があります。
訓練、検証、そしてテストセット
スクリプトが内部で行なうことの一つは画像のフォルダをポイントさせる時に3つの異なるセットに分割します。最大のものは通常は訓練セットで、これは訓練の間にネットワークに供給される全ての画像で、その結果はモデルの重みを更新するために使用されます。訓練のために全ての画像を何故使わないのか不思議に思うかもしれませんね?機械学習を行なう時の大きな潜在的な問題はモデルは正しい答えを出すために訓練画像の不適切な詳細を記憶してしまうかもしれないことです。例えば、見せられた各写真の背景のパターンを覚えてしまい物体とラベルをマッチさせるためにそれを使用してしまうネットワークを想像できるでしょう。それは訓練の間に前に見た全ての画像上では良い結果を生成できるでしょう、しかし新しい画像では失敗します、何故ならばそれは物体の一般的な特徴を学習しておらず、単に訓練画像の重要でない詳細を記憶しているからです。
この問題は過剰適合 (過学習) として知られ、これを回避するにはモデルがそれらを記憶できないように、データの一部を訓練プロセスから外へ保持しておきます。そして過剰適合が起きていないことを確認するためにそれらの画像をチェック用に使用します、何故ならばそれらの上で良い精度を見るならばネットワークが過剰適合してないことの良い兆候ですから。
通常の分割は画像の 80% を主要な訓練セットに置いて、10 % を訓練の間に頻繁に検証として実行するために取り分け、そしてそれから最後の 10% を保持しておきます。これは分類器の現実的なパフォーマンスを予測するために頻度は低いですがテストセットとして使用されます。これらの比率は –testing_percentage と –validation_percentage フラグを使用して制御可能です。一般的にはこれらの値はそのデフォルトのままにしておくことが可能です、何故ならばそれらを調整することは訓練へのどのような優位点も通常は見出せないからです。
スクリプトは訓練、検証、そしてテストセットの中で画像を分割するために (完全にランダムな関数よりも) 画像ファイル名を使用することに注意してください。異なる実行において画像が訓練とテストセット間で移動しないことを保証するために行なわれます、何故ならばもしモデルを訓練するために使用される画像が続いて検証セットでも使用されるならば問題となるからです。
検証精度は反復の間に変動することに気がつくかもしれません。この変動の多くは検証セットのランダムなサブセットは各々の検証精度計測のために選択されるという事実から発生します。変動は–validation_batch_size=-1 を選択することで訓練時間のある程度の増加を犠牲にして大きく減じることが可能です、これは各精度計算のために検証セット全体を使用します。
訓練が完了したら、テストセットの誤分類された画像を検証することは洞察に満ちたこととして見出すかもしれません。これはフラグ –print_misclassified_test_images を追加することで成されます。これは、画像のどのようなタイプがモデルにとって最も紛らわしいのか、どのカテゴリが最も識別するのが難しいのかに対する感覚を得る手助けとなるでしょう。例えば、特定のカテゴリのあるサブタイプ、またはある通常でない写真角度が特に識別が難しいことをを発見するかもしれません、これはそのサブタイプの更なる訓練画像の追加をすることを奨励するかもしれません。しばしば、誤分類された画像の検証は誤ったラベル付け、低い質、あるいはあいまいな画像のような入力データセットのエラーを指し示すこともあります。けれども、一般的にはテストセットの個々のエラーのピンポイントな修正は回避すべきです、何故ならばそれらは (更に大きな) 訓練セットのより一般的な問題を単に反映しがちであるからです。
以上