TensorFlow (Hub) : Tutorials : Images : 画像分類器を新しいカテゴリーのためにどのように再訓練するか (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 07/16/2018 (v1.9)
再作成日時 : 04/04/2018
* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。
* TensorFlow Hub の画像再訓練についてのチュートリアルの翻訳です。これは TensorFlow 全体のチュートリアルの位置づけにもなっていて、転移学習による画像分類器の再訓練を Hub を利用して遂行しています。
* 本ページは、TensorFlow の本家サイトの Tutorials – Images – How to Retrain an Image Classifier for New Categories を
翻訳した上で適宜、補足説明したものです:
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
現代的な物体認識モデルは数百万のパラメータを持ちます。スクラッチからの訓練はラベル付けされた多くの訓練データと多くの計算パワーを必要とします (数百の GPU-hours あるいはそれ以上)。転移学習は関連タスク上で既に訓練されたモデルのピースを取り新しいモデルでそれを再利用することによりこれらの多くをショートカットするテクニックです。このチュートリアルでは、ImageNet 上で訓練されたパワフルな画像分類器から特徴抽出機能を再利用して単純にその上で新しい分類層を訓練します。このアプローチの更なる情報については Decaf のこのペーパー を参照できます。
完全なモデルを訓練するほどには良くありませんが、これは多くのアプリケーションに対して驚くほど効果的で、中くらいの総量の訓練データ (ラベル付けされたデータの数千です、数百万ではありません) でも動作し、そして GPU がないラップトップ上で 30 分程度と少ない時間で実行できます。このチュートリアルは貴方自身の画像でサンプルスクリプトをどのように実行するかを示し、そして訓練プロセスを制御するためのオプションの幾つかを説明します。
このチュートリアルは事前訓練されたモデルのピース、あるいはいわゆるモジュールを摂取するために TensorFlow Hub を利用します。手始めに、ImageNet 上で訓練された Inception V3 アーキテクチャを持つ画像特徴抽出モジュールを使用し、後で NASNet /PNASNet そして MobileNet V1 と V2 を含む、更なるオプションへと戻ります。
花の(画像の)上で訓練する
どのような訓練を開始する前でも、ネットワークに認識させたい新しいクラスについて教えるための画像のセットが必要です。どのように貴方自身の画像を準備するかを説明する後のセクションがありますが、最初の利用を簡単にするために creative-commons license の花の画像のアーカイブを作成しました。花の画像のセットを得るためには、これらのコマンドを実行してください :
cd ~ curl -O http://download.tensorflow.org/example_images/flower_photos.tgz tar xzf flower_photos.tgz
画像をひとたび得たのであれば、サンプルコードを GitHub からダウンロードできます (それはライブラリ・インストールの一部ではありません) :
mkdir ~/example_code cd ~/example_code curl -LO https://github.com/tensorflow/hub/raw/r0.1/examples/image_retraining/retrain.py
最も単純なケースでは retrainer はそれからこのように実行できます (およそ 30 分ほどかかります) :
python retrain.py --image_dir ~/flower_photos
このスクリプトは多くの他のオプションを持ちます。以下で完全なリストを得ることができます :
python retrain.py -h
このスクリプトは事前訓練されたモジュールをロードしてその上で新しい分類器を貴方がダウンドードした花の画像のために訓練します。完全なネットワークがその上で訓練された元の ImageNet クラスの中にどの花の種もありません。転移学習の魔法は、幾つかのオブジェクト間を識別するために訓練されたより低い層は多くの認識タスクのために変更なしに再利用可能なことです。
ボトルネック
貴方のマシンの速度に依存して、スクリプトは完了するのに 30 分かそれ以上かかるでしょう。最初の段階はディスクの全ての画像を解析してそれらの各々についてボトルネック値を計算してキャッシュします。「ボトルネック」 は、実際に分類を行なう最終出力層のすぐ前の層のためにしばしば使う非公式な用語です。(TensorFlow Hub はこれを「画像特徴ベクトル」と呼びます。) この最後から2番目 (= penultimate) の層は、認識するために問われる全てのクラス間を識別するために使用する分類器のために十分に良い値のセットを出力するために訓練されています。それは画像の意味あるコンパクトな要約でなければならないことを意味しています、何故ならばそれは分類器が値の非常に小さなセットにおいて良い選択を行なうために十分な情報を含まなければならないからです。最終層の再訓練が新しいクラスで動作できる理由は ImageNet の全ての 1,000 クラス間を識別するために必要な情報の類が新しい種類の物体間の識別のためにもしばしば有用であることが判明していることです。
訓練の間全ての画像は複数回再利用されて各ボトルネックの計算はかなりの時間がかかるので、繰り返し再計算する必要がないようにディスク上にこれらのボトルネック値をキャッシュすることで高速化します。デフォルトではこれらは /tmp/bottleneck ディレクトリにストアされ、そしてスクリプトを再実行するならばそれらが再利用されるのでこのパートについては再び待つ必要がありません。
訓練する
ボトルネック (の計算) が完了したら、ネットワークのトップ層の実際の訓練が始まります。貴方はステップの系列の出力を見るでしょう、各々の一つは訓練精度、検証精度、そして交差エントロピーを示します。訓練精度は現在の訓練バッチで使用されている画像の何パーセントが正しいクラスでラベル付けされたかを示します。検証精度は異なるセットからのランダムに選択された画像のグループ上の精度です。主な違いは訓練精度はネットワークがそれから学習可能な画像を基にしていることでそのためネットワークは訓練データのノイズに過剰適合 (過学習) する可能性があることです。ネットワークのパフォーマンスの真の尺度は訓練データに含まれないデータセット上のパフォーマンスを計測することです — これは検証精度によって測られます。 訓練精度が高いがしかし検証精度が低いままである場合には、これはネットワークが過剰適合してより一般的には役に立たない、訓練画像の特定の特徴を記憶していることを意味します。交差エントロピーは損失関数で学習プロセスが上手く進んでいるかの一見 (= glimpse) を与えてくれます。訓練の目的は損失をできるだけ小さくすることですから、短期のノイズは無視して、損失が下落傾向を保持しているかどうかを注視することにより学習が動作しているかを識別することができます。
デフォルトではこのスクリプトは 4,000 訓練ステップを実行します。各ステップは訓練セットから無作為に 10 画像を選択し、キャッシュからそれらのボトルネックを見つけて、そして予測を得るための最終層へとそれらを供給します。それからそれらの予測が実際のラベルと比較されて back-propagation プロセスを通して最終層の重みを更新します。プロセスが続くにつれてレポートされる精度が改善されることを見るはずです、そして全てのステップが終わった後で訓練と検証写真から分離されていた画像のセット上で最終的なテスト精度評価が実行されます。このテスト評価は訓練されたモデルが分類タスクでどのように遂行されるかの最良推定値です。訓練プロセスにランダムネスがあるので正確な値は実行毎に変わりますが 90% と 95 % の間の精度値を見るはずです。この数字は、モデルが完全に訓練された後で正しいラベルが与えられたテストセットの画像のパーセントを基にしています。
再訓練を TensorBoard で可視化する
スクリプトは TensorBoard summaries を含みます、これは再訓練を理解し、デバッグし、そして最適化することをより容易にします。例えば、重みあるいは精度が訓練の間にどのように変わるか、グラフと統計を可視化できます。
TensorBoard を launch するためには、再訓練の間またはその後でこのコマンドを実行します :
tensorboard --logdir /tmp/retrain_logs
TensorBoard が実行されたら、TensorBoard を見るために web ブラウザを localhost:6006 に navigate してください。
retrain.py スクリプトは TensorBoard summaries をデフォルトでは /tmp/retrain_logs にログ出力します。ディレクトリは –summaries_dir フラグで変更可能です。
TensorBoard の GitHub レポジトリ は TensorBoard 使用法について、チップ (tip) & トリック、そしてデバッギング情報を含むより多くの情報を持ちます。
再訓練されたモデルを使用する
スクリプトは貴方のカテゴリー上で訓練された新しいモデルを /tmp/output_graph.pb に、そしてラベルを含むテキストファイルを /tmp/output_labels.txt に書き出します。新しいモデルはそれにインライン化された TF-Hub モジュールと新しい分類層の両者を含んでいます。2つのファイルは両者とも C++ と Python 画像分類サンプル が読み込めるフォーマットにあるので、貴方の新しいモデルを直ちに使用開始することができます。トップ層を置き換えたので、label_image を使用している場合には例えばフラグ –output_layer=final_result によってスクリプトで新しい名前を指定する必要があります。
ここに label_image サンプルを貴方の再訓練したグラフでどのように実行するかのサンプルがあります。慣習的に、総ての TensorFlow Hub モジュールは固定範囲 [0, 1] のカラー値を持つ画像入力を受け取りますので、–input_mean や –input_std フラグを設定する必要はありません。
curl -LO https://github.com/tensorflow/tensorflow/raw/master/tensorflow/examples/label_image/label_image.py python label_image.py \ --graph=/tmp/output_graph.pb --labels=/tmp/output_labels.txt \ --input_layer=Placeholder \ --output_layer=final_result \ --image=$HOME/flower_photos/daisy/21652746_cc379e0eea_m.jpg
花のラベルのリストを見るはずです、多くの場合トップはひなげし (daisy) です (各再訓練されたモデルは少し異なるかもしれませんが)。–image パラメータを貴方自身の画像でそれらを試すために置き変えることができます。
貴方自身の Python プログラムで再訓練したモデルを使用することを望むのであれば、上の label_image スクリプトは合理的な開始ポイントです。label_image ディレクトリはまた C++ コードを含み、tensorflow を貴方自身のアプリケーションと統合するためにこれをテンプレートとして使用できます。
もしデフォルトの Inception V3 モジュールが貴方のアプリケーションにとって巨大過ぎたり遅過ぎたりする場合には、貴方のネットワークをスピードアップしてスリムにするためのオプションのために後述の「他のモデル・アーキテクチャ」セクションを見てください。
貴方自身のカテゴリ上で訓練する
花のサンプル画像上でスクリプトを何とか動作させたならば、代わりに貴方がケアするカテゴリをそれに認識することを教えることに目を向け始めることができます。理論的には必要なことの全てはそれにサブフォルダのセットをポイントさせることで、各々はカテゴリの一つから名前付けられていてそのカテゴリからの画像のみを含みます。それを行なってサブディレクトリ (群) のルート・フォルダを –image_dir への引数として渡せば、スクリプトは花のために行なったように訓練するはずです。
以下が花のアーカイブのフォルダ構造がどのように見えるか、スクリプトが求めるレイアウトの種類の例を与えるものです :
実際には望むような精度を得るためにはある程度の作業がかかるでしょう。貴方が出会うかもしれない一般的な問題の幾つかを通して下でガイドしてみます。
訓練画像のセットを作成する
始める最初の場所は集めた画像に目を向けることです、何故ならば訓練で見る最も一般的な問題は供給されるデータに由来するからです。訓練が上手くいくためには、認識したい物体の各種の 100 の写真は少なくとも集めるべきです。集めれば集めるほど、訓練モデルの精度はより良くなりがちです。写真はアプリケーションが実際に遭遇するものの良い表現であることも確実にすることも必要です。例えば、全ての写真をインドアで開口部のない壁 (= blank wall) に対して撮りそしてユーザが物体をアウトドアで認識しようとするならば、デプロイした時に良い結果を見ることはおそらくないでしょう。回避すべきもう一つの落とし穴は、学習プロセスはラベル付けされた画像が互いに共通に持つ任意のものを拾ってしまうことで、それに注意を払わないのであれば有用ではないものになるでしょう。例えば物体の一つの種類を青い部屋で、他のものを緑色の部屋で撮れば、モデルはその予測を実際に大事にしたい物体の特徴ではなく、背景に基礎を置く結果になります。これを回避するためには、できる限り状況の多様性を広くして、違う時間、そして異なるデバイスで写真を撮ってみてください。
使用するカテゴリについて考えることもまた望むかもしれません。多くの異なる物理的な形をカバーする大きなカテゴリをより視覚的に明確な小さなものに分割する価値はあるかもしれません。例えば、貴方は「乗り物 (vehicle)」の代わりに「自動車 (car)」、「motorbike (バイク)」、そして「トラック (truck)」を使用するかもしれません。あなたは「閉じた世界 (closed world)」を持つのか「開いた世界 (open world)」を持つのかという問題についてもまた考える価値があります。閉じた世界では、カテゴリ分けを要求されるもの全ては既知の物体のクラスです。これは植物認識アプリケーションにも適用されるでしょう、そこではユーザは花の写真を多分撮るであろうことを知っており、従って行なわなければならないことの全てはどの種かを決定することです。対照的に歩き回るロボットは世界をさまよう時にそのカメラを通してあらゆる種類の異なるものを見るでしょう。そのケースでは見たものが不確かであるかを分類器がレポートすることを望むでしょう。これは上手くやるのは難しいですが、しかししばしばもし関連する物体のない数多くの典型的な「背景」写真を集めれば、画像フォルダの特別な「未知の (unknown)」クラスにそれらを追加できるでしょう。
画像全てが正しくラベル付けされていることを確かにするために確認することも価値があります。しばしばユーザ生成タグは私達の目的のためには当てになりません。例えば : #daisy とタグ付けされた写真は Daisy という名前の人々やキャラクターを含むかもしれません。画像を通してどのようなミスも取り除けば全体的な精度に貴方は驚くかもしれません。
訓練ステップ
画像に満足するならば、学習プロセスの細部を変更することで結果を改善することに目を向けられるでしょう。試すべきもっとも単純なものは –how_many_training_steps です。このデフォルトは 4,000 ですが、それを 8,000 に増やせば2倍の長さの間訓練します。精度の改善のレートはより長い間訓練すればゆっくりになりますが、あるポイントで全体として止まるでしょう (あるいは overfitting により下がりさえします)、しかし貴方のモデルについてベストで動作するものを見ることを経験できるでしょう。
歪める (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 を追加することで成されます。これは、画像のどのタイプがモデルにとって最も紛らわしいのか、どのカテゴリが最も識別するのが難しいのかについての感覚を得る手助けとなるでしょう。例えば、特定のカテゴリのあるサブタイプ、またはある通常でない写真角度が特に識別が難しいことをを発見するかもしれません、これはそのサブタイプの更なる訓練画像を追加をすることを奨励するかもしれません。しばしば、誤分類された画像の検証は誤ったラベル付け、低い質、あるいはあいまいな画像のような入力データセットにおけるエラーを指し示すこともあります。けれども、一般的にはテストセットの個々のエラーのピンポイントな修正は回避すべきです、何故ならばそれらは多分 (遥かに大きな) 訓練セットのより一般的な問題を単に反映しているだけだからです。
他のモデル・アーキテクチャ
デフォルトではスクリプトは Inception V3 アーキテクチャの事前訓練されたインスタンスを持つ画像特徴抽出モジュールを使用します。これは始めるには良い場所です、何故ならばそれは再訓練スクリプトに対して中くらいの実行時間で高い精度の結果を提供するからです。しかし今 TensorFlow Hub モジュールの更なるオプションを見てみましょう。
一方では、そのリストは NASNet (特に nasnet_large と pnasnet_large) のようなより最近の、パワフルなアーキテクチャを示します。これはある程度特別な精度を与えるでしょう。
その一方で、モバイルデバイスや他のリソースが制限される環境上で貴方のモデルを配備することを意図する場合、遥かにより小さなファイルサイズやより高速なスピード (訓練でもまた) のために少しの精度を交換することを望むかもしれません。そのためには、MobileNet V1 か V2 アーキテクチャを実装する異なるモジュール、あるいはまた nasnet_mobile を試してください。
異なるモジュールでの訓練は簡単です : 単に –tfhub_module フラグをモジュール URL と一緒に渡します、例えば :
python retrain.py \ --image_dir ~/flower_photos \ --tfhub_module https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/feature_vector/1
これは MobileNet V2 のベースライン・バージョンを使用するモデルを持つ 9 MB モデルファイルを /tmp/output_graph.pb に作成するでしょう。ブラウザでモジュール URL を開けばモジュール・ドキュメントに案内するでしょう。
もしそれを少しだけ早くすることを望むのであれば、入力画像のサイズを ‘224’ から ‘192’, ‘160’, あるいは ‘128’ ピクセル四方に、あるいは ’96’ (V2 のみ) にまでも削減することができます。より積極的な節約のためには、”特徴 depth” か位置毎のニューロン数を制御するためにパーセント ‘100’, ‘075’, ‘050’, or ‘035’ (それは V1 のためには ‘025’) を選択することができます。重みの数 (そしてそれ故にファイルサイズとスピード) はその分数の自乗で縮小されます。MobileNet V1 ブログ投稿 と GitHub 上の MobileNet V2 ページ は Imagenet 分類に対するそれぞれのトレードオフについてレポートしています。
Mobilenet V2 は特徴 depth パーセンテージをボトルネック層には適用しません。Mobilenet V1 は行ないました、それは分類層のジョブを小さな depth についてはより厳しいものにしました。タイトなボトルネックの代わりに誤魔化して元の 1001 ImageNet クラスのためのスコアを使用することは役立つでしょうか。モジュール名において mobilenet_v1…/feature_vector を mobilenet_v1…/classification で置き換えることにより単純に試すことができます。
前のように、label_image.py で再訓練されたモデルの総てを利用できます。貴方のモデルが期待する画像サイズを指定する必要があります、例えば :
python label_image.py \ --graph=/tmp/output_graph.pb --labels=/tmp/output_labels.txt \ --input_layer=Placeholder \ --output_layer=final_result \ --input_height=224 --input_width=224 \ --image=$HOME/flower_photos/daisy/21652746_cc379e0eea_m.jpg
再訓練されたモデルのモバイルデバイスへの配備についての更なる情報については、このチュートリアルの codelab バージョン を、特に パート 2 を見てください。これは TensorFlow Lite と (モデル重みの量子化を含む) それが与える追加の最適化について記述しています。
以上