1. 概要
このラボでは、最新の畳み込みアーキテクチャについて学び、その知識を使用して、シンプルながら効果的な「squeezenet」という畳み込みニューラル ネットワークを実装します。
このラボでは、畳み込みニューラル ネットワークに関する必要な理論的な説明が含まれており、ディープ ラーニングを学習するデベロッパーにとって良い出発点となります。
このラボは「TPU での Keras」シリーズのパート 4 です。これらの操作は、次の順序で行うことも、個別に行うこともできます。
- TPU スピードのデータ パイプライン: tf.data.Dataset と TFRecords
- 転移学習を使用した最初の Keras モデル
- Keras と TPU を使用した畳み込みニューラル ネットワーク
- [このラボ] Keras と TPU での最新の convnets、squeezenet、Xception
学習内容
- Keras の関数スタイルをマスターする
- squeezenet アーキテクチャを使用してモデルを構築する
- アーキテクチャの迅速なトレーニングとイテレーションのために TPU を使用する
- tf.data.dataset を使用してデータ拡張を実装する
- TPU で事前トレーニング済みの大規模モデル(Xception)をファインチューニングする
フィードバック
この Codelab で問題が見つかった場合は、お知らせください。フィードバックは、GitHub の問題 [フィードバック リンク] からお送りいただけます。
2. Google Colaboratory のクイック スタート
このラボでは Google Collaboratory を使用します。ユーザー側での設定は必要ありません。Colaboratory は、教育目的のオンライン ノートブック プラットフォームです。CPU、GPU、TPU を無料でトレーニングできます。
このサンプル ノートブックを開いていくつかのセルを実行し、Colaboratory についてよく理解してください。
TPU バックエンドを選択する
Colab メニューで、[ランタイム] > [ランタイムのタイプを変更] の順に選択し、TPU を選択します。この Codelab では、ハードウェア アクセラレーションによるトレーニングをサポートする強力な TPU(Tensor Processing Unit)を使用します。ランタイムへの接続は初回実行時に自動的に行われますが、右上の [接続] ボタンを使用して接続することもできます。
ノートブックの実行
セルをクリックして Shift+Enter キーを押すことで、セルを 1 つずつ実行します。[ランタイム] > [すべて実行] でノートブック全体を実行することもできます。
目次
すべてのノートブックには目次があります。左側にある黒い矢印をクリックすると開きます。
非表示のセル
一部のセルにはタイトルのみが表示されます。これは Colab 固有のノートブック機能です。ダブルクリックすると内部のコードを確認できますが、通常はあまり興味深いものではありません。通常は、サポート機能または可視化機能です。内部の関数を定義するには、これらのセルを実行する必要があります。
認証
承認済みアカウントで認証すれば、Colab が限定公開の Google Cloud Storage バケットにアクセスできます。上記のコード スニペットによって認証プロセスがトリガーされます。
3. [INFO] Tensor Processing Unit(TPU)とは
概要
Keras で TPU でモデルをトレーニングするコード(TPU が使用できない場合は GPU または CPU にフォールバックします)。
try: # detect TPUs
tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines
# use TPUStrategy scope to define model
with strategy.scope():
model = tf.keras.Sequential( ... )
model.compile( ... )
# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)
本日は TPU を使用して、インタラクティブな速度(トレーニング実行あたり数分)で花の分類器を構築し、最適化します。
TPU を使用する理由
最新の GPU は、プログラム可能な「コア」を中心に構成されています。コアは、3D レンダリング、ディープ ラーニング、物理シミュレーションなどのさまざまなタスクを処理できる非常に柔軟なアーキテクチャです。一方、TPU は従来のベクトル プロセッサと専用の行列乗算ユニットを組み合わせており、ニューラル ネットワークなど、大規模な行列乗算が支配的なタスクに優れています。
図: 行列乗算としての密なニューラル ネットワーク レイヤ。8 つの画像のバッチがニューラル ネットワークで一度に処理されます。1 行 x 列の乗算を実行して、画像のすべてのピクセル値の重み付け合計が実際に実行されていることを確認してください。畳み込みレイヤは、少し複雑になりますが、行列乗算として表すこともできます(説明はこちらのセクション 1)。
ハードウェア
MXU と VPU
TPU v2 コアは、行列乗算を実行する Matrix Multiply Unit(MXU)と、活性化、ソフトマックスなどの他のすべてのタスク用の Vector Processing Unit(VPU)で構成されています。VPU は、float32 と int32 の計算を処理します。一方、MXU は、混合精度の 16 ~ 32 ビット浮動小数点形式で動作します。
混合精度の浮動小数点数と bfloat16
MXU は、bfloat16 入力と float32 出力を使用して行列乗算を計算します。中間の累積は float32 精度で行われます。
通常、ニューラル ネットワーク トレーニングでは浮動小数点精度の低下によるノイズに耐えられます。ノイズがオプティマイザーの収束に役立つ場合もあります。従来、計算の高速化には 16 ビットの浮動小数点精度が使用されていましたが、float16 形式と float32 形式の範囲は大きく異なります。精度を float32 から float16 に下げると、通常はオーバーフローやアンダーフローが生じます。ソリューションは存在しますが、通常、float16 を機能させるには追加の作業が必要です。
そのため、Google は TPU に bfloat16 形式を導入しました。bfloat16 は、float32 とまったく同じ指数ビットと範囲を持つ切り捨て型 float32 です。これは、TPU が bfloat16 入力と float32 出力の混合精度で行列乗算を計算するという事実に加え、通常はコードを変更しなくても精度が下がるというパフォーマンス向上のメリットを享受できることを意味します。
シストリック配列
MXU は、データ要素がハードウェア計算ユニットのアレイを流れる、いわゆる「シストリック アレイ」アーキテクチャを使用して、ハードウェアで行列乗算を実装します。(医学用語では、「最高血圧」は心臓の収縮と血流を意味し、ここではデータの流れを指します)。
行列の乗算の基本要素は、一方の行列の行ともう一方の行列の列との内積です(このセクションの上部にある図を参照)。行列の乗算 Y=X*W の場合、結果の 1 つの要素は次のようになります。
Y[2,0] = X[2,0]*W[0,0] + X[2,1]*W[1,0] + X[2,2]*W[2,0] + ... + X[2,n]*W[n,0]
GPU では、この内積を GPU の「コア」にプログラムし、使用可能なすべての「コア」で並列に実行して、結果のマトリックスのすべての値を一度に計算します。結果のマトリックスが 128x128 の場合、128x128=16, 000 個のコアが必要になりますが、通常は不可能です。最大の GPU には約 4,000 個のコアがあります。一方、TPU は MXU の計算ユニットに最小限のハードウェアを使用します。bfloat16 x bfloat16 => float32
乗算累算器のみです。これらは非常に小さいため、TPU は 128x128 MXU で 16K を実装し、この行列の乗算を一度に処理できます。
図: MXU シストリック アレイ。コンピューティング要素は乗算アキュムレータです。1 つの行列の値が配列に読み込まれます(赤いドット)。もう一方の行列の値は、配列(グレーのドット)を通過します。縦線により値が上に移動します。水平線は部分和を伝播します。配列の中をデータが流れるときに、行列乗算の結果が右側から出力されることを確認するのは、ユーザーの演習として行います。
さらに、ドット積が MXU で計算されている間、中間和は隣接するコンピューティング単位間で単純に転送されます。メモリやレジスタ ファイルとの間で保存や取得を行う必要はありません。最終的に、TPU シストリック アレイ アーキテクチャは、行列の乗算の計算において、密度と電力の大幅な利点があるだけでなく、GPU よりも無視できないほどの速度で優れています。
Cloud TPU
Google Cloud Platform で「Cloud TPU v2」を 1 つリクエストすると、PCI 接続 TPU ボードを備えた仮想マシン(VM)が手に入ります。TPU ボードには、デュアルコア TPU チップが 4 つあります。各 TPU コアには、VPU(ベクトル処理ユニット)と 128x128 MXU(MatriX 乗算ユニット)が搭載されています。この「Cloud TPU」は通常、リクエストした VM にネットワーク経由で接続されます。全体像は次のようになります。
図: ネットワークに接続された「Cloud TPU」アクセラレータを使用した VM。「Cloud TPU」自体は、4 つのデュアルコア TPU チップを備えた PCI アタッチの TPU ボードを備えた VM で構成されています。
TPU Pod
Google のデータセンターでは、TPU がハイ パフォーマンス コンピューティング(HPC)相互接続に接続されているため、1 つの非常に大規模なアクセラレータのように見えます。Google では Pod と呼び、最大 512 個の TPU v2 コアまたは 2,048 個の TPU v3 コアを包含できます。
イラスト: TPU v3 Pod。HPC Interconnect で接続された TPU ボードとラック。
トレーニング中、all-reduce アルゴリズムを使用して TPU コア間で勾配が交換されます(all-reduce の詳細な説明はこちら)。トレーニング対象のモデルは、大規模なバッチサイズでトレーニングすることでハードウェアを利用できます。
図: Google TPU の 2 次元トーラス状メッシュ HPC ネットワークで all-reduce アルゴリズムを使用してトレーニング中に勾配を同期する。
ソフトウェア
大規模なバッチサイズのトレーニング
TPU に最適なバッチサイズは TPU コアあたり 128 個のデータ項目ですが、ハードウェアは TPU コアあたり 8 個のデータ項目からすでに優れた使用率を示します。1 つの Cloud TPU には 8 個のコアがあることに注意してください。
この Codelab では、Keras API を使用します。Keras で指定するバッチは、TPU 全体のグローバル バッチサイズです。バッチは自動的に 8 つに分割され、TPU の 8 コアで実行されます。
パフォーマンスに関するその他のヒントについては、TPU パフォーマンス ガイドをご覧ください。非常に大きなバッチサイズの場合、一部のモデルでは特別な注意が必要です。詳細については、LARSOptimizer をご覧ください。
仕組み: XLA
TensorFlow プログラムは計算グラフを定義します。TPU は Python コードを直接実行するのではなく、Tensorflow プログラムで定義された計算グラフを実行します。内部的には、XLA(Accelerated Linear Algebra コンパイラ)というコンパイラが、コンピューティング ノードの TensorFlow グラフを TPU マシンコードに変換します。このコンパイラは、コードとメモリ レイアウトに対して多くの高度な最適化も行います。処理が TPU に送信されると、コンパイルが自動的に行われます。XLA をビルドチェーンに明示的に含める必要はありません。
例: TPU で実行するには、TensorFlow プログラムで定義された計算グラフがまず XLA(高速線形代数コンパイラ)表現に変換され、次に XLA によって TPU マシンコードにコンパイルされます。
Keras での TPU の使用
TPU は、Tensorflow 2.1 以降、Keras API でサポートされています。Keras のサポートは、TPU と TPU Pod で機能します。TPU、GPU、CPU で動作する例を次に示します。
try: # detect TPUs
tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines
# use TPUStrategy scope to define model
with strategy.scope():
model = tf.keras.Sequential( ... )
model.compile( ... )
# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)
このコード スニペットは次のようになります。
TPUClusterResolver().connect()
は、ネットワーク上の TPU を検索します。ほとんどの Google Cloud システム(AI Platform ジョブ、Colaboratory、Kubeflow、ctpu up ユーティリティで作成された Deep Learning VM)でパラメータなしで動作します。これらのシステムは、TPU_NAME 環境変数により TPU の場所を認識します。TPU を手動で作成する場合は、使用している VM で TPU_NAME 環境変数を設定するか、明示的なパラメータTPUClusterResolver(tp_uname, zone, project)
を指定してTPUClusterResolver
を呼び出します。TPUStrategy
は、分布と「all-reduce」勾配同期アルゴリズムを実装する部分です。- 戦略はスコープを通じて適用されます。モデルは、戦略 scope() 内で定義する必要があります。
tpu_model.fit
関数は、TPU トレーニングの入力として tf.data.Dataset オブジェクトを想定しています。
TPU の一般的な移植タスク
- Tensorflow モデルにデータを読み込む方法は多数ありますが、TPU の場合は
tf.data.Dataset
API を使用する必要があります。 - TPU は非常に高速であり、実行時にデータの取り込みがボトルネックになることがよくあります。データのボトルネックを検出するために使用できるツールとその他のパフォーマンスに関するヒントについては、TPU パフォーマンス ガイドをご覧ください。
- int8 または int16 の数値は int32 として扱われます。TPU には、32 ビット未満で動作する整数ハードウェアはありません。
- 一部の TensorFlow オペレーションはサポートされていません。こちらのリストをご覧ください。ただし、この制限はトレーニング コード(モデルのフォワードパスとバックワード パス)にのみ適用されます。データ入力パイプラインでは、CPU で実行されるため、すべての TensorFlow オペレーションを使用できます。
tf.py_func
は TPU でサポートされていません。
4. [情報] ニューラル ネットワーク分類器 101
概要
次の段落の太字の用語をすべてご存じの場合は、次の演習に進んでください。ディープ ラーニングを始めたばかりの方は、ぜひ本をお読みください。
レイヤのシーケンスとして構築されたモデルの場合、Keras には Sequential API があります。たとえば、3 つの密なレイヤを使用する画像分類システムは、Keras で次のように記述できます。
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=[192, 192, 3]),
tf.keras.layers.Dense(500, activation="relu"),
tf.keras.layers.Dense(50, activation="relu"),
tf.keras.layers.Dense(5, activation='softmax') # classifying into 5 classes
])
# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy']) # % of correct answers
# train the model
model.fit(dataset, ... )
高密度ニューラル ネットワーク
これは、画像を分類するための最もシンプルなニューラル ネットワークです。ニューラル ネットワークは、層状に配置された「ニューロン」で構成されています。最初のレイヤは入力データを処理し、出力を他のレイヤにフィードします。各ニューロンが前の層のすべてのニューロンに接続されているため、「密結合」と呼ばれます。
このようなネットワークに画像を入力するには、すべてのピクセルの RGB 値を長いベクトルにフラット化して入力として使用します。これは画像認識には最適な手法ではありませんが、今後改善していく予定です。
ニューロン、活性化、RELU
「ニューロン」は、すべての入力の加重合計を計算し、「バイアス」と呼ばれる値を加算して、その結果をいわゆる「活性化関数」に通します。重みとバイアスは最初は不明です。これらはランダムに初期化され、多くの既知のデータでニューラル ネットワークをトレーニングすることで「学習」されます。
最も一般的な活性化関数は、正規化線形ユニット(ReLU)と呼ばれます。上のグラフからわかるように、これは非常にシンプルな関数です。
Softmax 活性化
上記のネットワークは、花を 5 つのカテゴリ(バラ、チューリップ、タンポポ、デイジー、ヒマワリ)に分類するため、5 ニューロンのレイヤで終了します。中間層のニューロンは、従来の RELU 活性化関数を使用して活性化されます。最後のレイヤでは、この花がバラやチューリップである確率を表す 0~1 の数値を計算します。そのために、「softmax」という活性化関数を使用します。
ベクトルにソフトマックスを適用するには、各要素の指数を受け取り、通常は L1 ノルム(絶対値の合計)を使用してベクトルを正規化します。これにより、値の和が 1 になり、確率として解釈できるようになります。
交差エントロピー損失
ニューラル ネットワークが入力画像から予測を生成するようになったので、予測の精度、すなわち、ネットワークが教える情報と正解(しばしば「ラベル」と呼ばれる)の間の距離を測定する必要があります。データセット内のすべての画像に正しいラベルが付いていることを覚えておいてください。
任意の距離を使用できますが、分類問題では、いわゆる「交差エントロピー距離」が最も効果的です。これはエラー関数または損失関数と呼ばれます。
勾配降下法
ニューラル ネットワークの「トレーニング」とは、トレーニング画像とラベルを使用して重みとバイアスを調整し、交差エントロピー損失関数を最小化することを意味します。仕組みは次のとおりです。
クロスエントロピーは、トレーニング画像の重み、バイアス、ピクセル、既知のクラスの関数です。
すべての重みとすべてのバイアスに対して交差エントロピーの偏導関数を計算すると、与えられた画像、ラベル、重みとバイアスの現在値に対して計算される「勾配」が得られます。数百万もの重みとバイアスがある可能性があるため、勾配の計算は大変な作業のように思えます。幸い、Tensorflow がその作業を行います。勾配の数学的特性は、「上」を指すことです。交差エントロピーが低い方向に移動したいので、反対の方向に移動します。重みとバイアスは勾配の一部で更新されます。その後、トレーニング ループ内で、次のトレーニング画像とラベルのバッチを使用して、同じことを何度も繰り返します。うまくいけば、交差エントロピーが最小になるところに収束することが期待されます。ただし、この最小値が一意であるとは限りません。
ミニバッチ処理とモメンタム
1 つのサンプル画像でのみ勾配を計算し、重みとバイアスをすぐに更新できますが、128 個の画像のバッチで計算すると、さまざまなサンプル画像によって課せられる制約をより適切に表す勾配が得られるため、解決策への収束が早まる可能性があります。ミニバッチのサイズは調整可能なパラメータです。
この手法(「確率的勾配降下法」と呼ばれることもあります)には、より実用的な別の利点もあります。バッチを使用すると、より大きな行列を扱うことになり、通常、GPU と TPU で最適化しやすくなります。
ただし、収束はやや混沌とすることがあり、勾配ベクトルがすべてゼロになると停止することさえあります。最小値を見つけたということでしょうか?必ずしも違反警告を受けるとは限りません。勾配コンポーネントは、最小値または最大値で 0 にできます。数百万個の要素を持つ勾配ベクトルがすべてゼロの場合、すべてのゼロが最小値に対応し、最大値に対応するゼロがないことはかなり低い確率です。多くの次元の空間では鞍点は非常に一般的であり、鞍点に留まることは望ましくありません。
イラスト: サドルポイント。勾配は 0 ですが、すべての方向で最小値ではありません。(画像の帰属 Wikimedia: Nicoguaro による - 自身の作品、CC BY 3.0)
解決策は、最適化アルゴリズムにモメンタムを追加して、鞍点に止まることなく通過できるようにすることです。
用語集
バッチまたはミニバッチ: トレーニングは常に、トレーニング データとラベルのバッチに対して実行されます。これにより、アルゴリズムの収束が促進されます。通常、「バッチ」次元はデータ テンソルの最初の次元です。たとえば、シェイプが [100, 192, 192, 3] のテンサーには、ピクセルあたり 3 つの値(RGB)を持つ 192 x 192 ピクセルの画像が 100 個含まれています。
交差エントロピー損失: 分類システムでよく使用される特別な損失関数。
dense レイヤ: ニューロンの層。各ニューロンが前のレイヤのすべてのニューロンに接続されています。
特徴量: ニューラル ネットワークの入力は「特徴量」と呼ばれることもあります。ニューラル ネットワークにフィードして適切な予測を取得するデータセットの部分(または部分の組み合わせ)を特定する手法は「特徴量エンジニアリング」と呼ばれます。
labels: 教師あり分類問題の「クラス」または正解の別名
学習率: トレーニング ループの反復処理で重みとバイアスが更新される勾配の割合。
ロジット: 活性化関数が適用される前のニューロン層の出力は「ロジット」と呼ばれます。この用語は、かつて最も一般的な活性化関数であった「ロジスティック関数」(別名「シグモイド関数」)に由来しています。「ロジスティック関数前のニューロン出力」は「ロジット」に短縮されました。
loss: ニューラル ネットワークの出力と正解を比較する誤差関数
ニューロン: 入力の加重合計を計算し、バイアスを追加して、結果を活性化関数に渡します。
ワンホット エンコーディング: クラス 5 のうち、クラス 3 が 5 つの要素のベクトルとしてエンコードされます。3 番目の要素である 1 を除き、すべてゼロになります。
relu: 正規化線形ユニット。ニューロンでよく使用される活性化関数。
シグモイド: 以前は一般的になっていたもう一つの活性化関数で、特殊なケースでも現在でも有用です。
ソフトマックス: ベクトルに対して作用する特別な活性化関数。最大の成分と他のすべての成分の差を増加させ、確率のベクトルとして解釈できるようにベクトルを合計 1 に正規化します。分類器の最後のステップとして使用されます。
テンソル: 「テンソル」は行列に似ていますが、任意の次元数を持ちます。1 次元のテンソルはベクトルです。2 次元テンソルは行列です。テンソルは 3 次元、4 次元、5 次元以上にすることができます。
5. [INFO] 畳み込みニューラル ネットワーク
概要
次の段落で太字になっている用語をすべて理解している場合は、次の演習に進みましょう。畳み込みニューラル ネットワークを初めて使用する場合は、以下をお読みください。
図: 4x4x3=48 の学習可能な重みで構成された 2 つの連続するフィルタで画像をフィルタ。
Keras での単純な畳み込みニューラル ネットワークは次のようになります。
model = tf.keras.Sequential([
# input: images of size 192x192x3 pixels (the three stands for RGB channels)
tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu', input_shape=[192, 192, 3]),
tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=6, padding='same', activation='relu'),
tf.keras.layers.Flatten(),
# classifying into 5 categories
tf.keras.layers.Dense(5, activation='softmax')
])
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy'])
畳み込みニューラル ネットワーク 101
畳み込みネットワークのレイヤでは、1 つの「ニューロン」が、画像の小さな領域にわたって、そのすぐ上のピクセルの重み付き合計を計算します。通常の密集層のニューロンと同様に、バイアスを追加して、その和を活性化関数に渡します。この操作は、同じ重みを使用して画像全体で繰り返されます。密なレイヤでは、各ニューロンに独自の重みがありました。ここでは、重みの単一の「パッチ」が画像の両方向にスライドします(「畳み込み」)。出力には、画像内のピクセル数と同じ数の値が含まれます(ただし、エッジには若干のパディングが必要です)。これは、4×4×3=48 の重みのフィルタを使用するフィルタリング演算です。
ただし、48 個の重みでは不十分です。自由度を増やすには、新しい重みセットで同じ操作を繰り返します。これにより、新しい一連のフィルタ出力が生成されます。入力画像の R、G、B チャネルにたとえて、出力の「チャネル」と呼ぶことにしましょう。
2 つ(またはそれ以上)の重みセットは、新しいディメンションを追加することで 1 つのテンソルとして合計できます。これにより、畳み込みレイヤの重みテンソルの一般的な形状が得られます。入力チャンネルと出力チャンネルの数はパラメータであるため、畳み込みレイヤのスタックとチェーンを開始できます。
図: 畳み込みニューラル ネットワークは、データの「キューブ」を他の「キューブ」に変換します。
ストライド畳み込み、最大プーリング
2 または 3 のストライドで畳み込みを実行すると、結果として得られるデータキューブを水平方向に縮小することもできます。一般に、これを実現するには次の 2 つの方法があります。
- ストライド コンボリューション: 上記のスライディング フィルタですが、ストライドが 1 より大きい
- 最大プーリング: MAX 演算を適用するスライディング ウィンドウ(通常は 2x2 パッチで、2 ピクセルごとに繰り返される)
例: 計算ウィンドウを 3 ピクセルずらすと、出力値が少なくなります。ストライド畳み込みまたは最大プーリング(2 ストライドでスライドする 2x2 ウィンドウの最大)は、データキューブを水平次元で縮小する方法です。
畳み込み分類器
最後に、最後のデータキューブをフラット化し、ソフトマックス活性化の密なレイヤに入力して、分類ヘッドを接続します。一般的な畳み込み分類器は次のようになります。
図: 畳み込みレイヤとソフトマックス レイヤを使用する画像分類器。3x3 フィルタと 1x1 フィルタを使用します。maxpool レイヤは、2x2 データポイントのグループの最大値を取ります。分類ヘッドは、ソフトマックス活性化関数を持つ密集層で実装されます。
Keras で
上記の畳み込みスタックは、Keras で次のように記述できます。
model = tf.keras.Sequential([
# input: images of size 192x192x3 pixels (the three stands for RGB channels)
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu', input_shape=[192, 192, 3]),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=16, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=8, padding='same', activation='relu'),
tf.keras.layers.Flatten(),
# classifying into 5 categories
tf.keras.layers.Dense(5, activation='softmax')
])
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy'])
6. [最新情報] 最新の畳み込みアーキテクチャ
概要
図: 畳み込み「モジュール」。この時点で最善の方法はmax-pool レイヤの後に 1x1 の畳み込みレイヤが続くのか、それともレイヤの別の組み合わせなのか?すべて試して結果を連結し、ネットワークに決定を任せます。右は、このようなモジュールを使用した「 inception」畳み込みアーキテクチャです。
Keras では、データフローを分岐させるモデルを作成するには、「関数型」モデルスタイルを使用する必要があります。以下に例を示します。
l = tf.keras.layers # syntax shortcut
y = l.Conv2D(filters=32, kernel_size=3, padding='same',
activation='relu', input_shape=[192, 192, 3])(x) # x=input image
# module start: branch out
y1 = l.Conv2D(filters=32, kernel_size=1, padding='same', activation='relu')(y)
y3 = l.Conv2D(filters=32, kernel_size=3, padding='same', activation='relu')(y)
y = l.concatenate([y1, y3]) # output now has 64 channels
# module end: concatenation
# many more layers ...
# Create the model by specifying the input and output tensors.
# Keras layers track their connections automatically so that's all that's needed.
z = l.Dense(5, activation='softmax')(y)
model = tf.keras.Model(x, z)
その他の手軽な方法
小さな 3x3 フィルタ
この図は、2 つの連続した 3x3 フィルタの結果を示しています。結果に寄与したデータポイントをトレースしてみましょう。これら 2 つの連続する 3x3 フィルタは、5x5 領域のなんらかの組み合わせを計算します。これは 5x5 フィルタで計算される組み合わせと完全には同じではありませんが、2 つの連続した 3x3 フィルタは 1 つの 5x5 フィルタよりも低コストであるため、試してみる価値があります。
1 行 1 の畳み込み?
数学的には、「1x1」畳み込みは定数による乗算であり、あまり有用な概念ではありません。ただし、畳み込みニューラル ネットワークでは、フィルタは 2D 画像だけでなくデータキューブにも適用されることを覚えておいてください。したがって、「1x1」フィルタは 1x1 のデータ列の加重合計を計算します(図を参照)。このフィルタをデータにスライドさせると、入力チャネルの線形結合が得られます。これは実に便利です。チャネルを個々のフィルタリング操作の結果として考える場合、たとえば「ひげ」のフィルタ、「ひげ」のフィルタ、3 つ目の「ひげ」のフィルタ、そして「1x1」の畳み込み層は、これらの特徴の複数の可能な線形組み合わせを計算することになり、「猫」を探すのに便利です。さらに、1x1 レイヤでは使用する重みが少なくなります。
7. Squeezenet
これらのアイデアを組み合わせる簡単な方法は、「Squeezenet」の論文で紹介されています。著者らは、1x1 と 3x3 の畳み込みレイヤのみを使用する非常にシンプルな畳み込みモジュールの設計を提案しています。
図: 「Fire モジュール」に基づく SqueezeNet アーキテクチャ。入力データを垂直方向に「圧縮」する 1x1 レイヤと、データの深さを再度「拡張」する 2 つの並列 1x1 畳み込みレイヤと 3x3 畳み込みレイヤを交互に使用します。
ハンズオン
前のノートブックで引き続き、SqueezeNet にインスパイアされた畳み込みニューラル ネットワークを構築します。モデルのコードを Keras の「関数型スタイル」に変更する必要があります。
Keras_Flowers_TPU (playground).ipynb
その他の情報
この演習では、squeezenet モジュールのヘルパー関数を定義すると便利です。
def fire(x, squeeze, expand):
y = l.Conv2D(filters=squeeze, kernel_size=1, padding='same', activation='relu')(x)
y1 = l.Conv2D(filters=expand//2, kernel_size=1, padding='same', activation='relu')(y)
y3 = l.Conv2D(filters=expand//2, kernel_size=3, padding='same', activation='relu')(y)
return tf.keras.layers.concatenate([y1, y3])
# this is to make it behave similarly to other Keras layers
def fire_module(squeeze, expand):
return lambda x: fire(x, squeeze, expand)
# usage:
x = l.Input(shape=[192, 192, 3])
y = fire_module(squeeze=24, expand=48)(x) # typically, squeeze is less than expand
y = fire_module(squeeze=32, expand=64)(y)
...
model = tf.keras.Model(x, y)
今回の目標は、精度を 80% にすること。
次の方法をお試しください
最初は 1 つの畳み込みレイヤから始め、次に「fire_modules
」を追加し、MaxPooling2D(pool_size=2)
レイヤと交互に追加します。ネットワーク内の最大プーリング レイヤを 2 ~ 4 層、最大プーリング レイヤ間に連続する 1、2、または 3 つのファイア モジュールを接続してテストできます。
Fire モジュールでは、通常、「squeeze」パラメータは「expand」パラメータよりも小さくする必要があります。これらのパラメータは、実際にはフィルタの数です。通常は 8 ~ 196 の範囲になります。ネットワーク内でフィルタ数が徐々に増加するアーキテクチャや、すべての発射モジュールに同じ数のフィルタがある単純なアーキテクチャをテストできます。
以下に例を示します。
x = tf.keras.layers.Input(shape=[*IMAGE_SIZE, 3]) # input is 192x192 pixels RGB
y = tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu')(x)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.GlobalAveragePooling2D()(y)
y = tf.keras.layers.Dense(5, activation='softmax')(y)
model = tf.keras.Model(x, y)
この時点で、テストの進捗状況があまり良くなく、80% の精度目標が達成できそうにないと感じるかもしれません。次は、もう 2 つの簡単なトリックです。
バッチ正規化
バッチ正規化は、発生している収束の問題に役立ちます。この手法については、次のワークショップで詳しく解説します。ひとまず、fire_module 関数内のレイヤを含め、ネットワーク内のすべての畳み込みレイヤの後に以下の行を追加して、これをブラック ボックスの「マジック」ヘルパーとして使用してください。
y = tf.keras.layers.BatchNormalization(momentum=0.9)(y)
# please adapt the input and output "y"s to whatever is appropriate in your context
今回はデータセットが小さいため、モメンタム パラメータをデフォルト値の 0.99 から 0.9 に小さくする必要があります。現時点では、この詳細は気にしないでください。
データの拡張
彩度を左右に反転させるなどの簡単な変換でデータを拡張することで、さらにパーセンテージ ポイントが得られます。
tf.data.Dataset API を使用して Tensorflow でこれを行うのはとても簡単です。データに新しい変換関数を定義します。
def data_augment(image, label):
image = tf.image.random_flip_left_right(image)
image = tf.image.random_saturation(image, lower=0, upper=2)
return image, label
次に、そのデータセットを最終的なデータ変換に使用します(セル「トレーニングと検証のデータセット」、関数「get_batched_dataset」)。
dataset = dataset.repeat() # existing line
# insert this
if augment_data:
dataset = dataset.map(data_augment, num_parallel_calls=AUTO)
dataset = dataset.shuffle(2048) # existing line
データの拡張をオプションにすることと、トレーニング データセットのみが拡張されるように必要なコードを追加することを忘れないでください。検証データセットを拡張しても意味がありません。
35 エポックで 80% の精度は達成できるレベルに達しているはずです。
ソリューション
ソリューション ノートブックは次のとおりです。行き詰まった場合に使用できます。
Keras_Flowers_TPU_squeezenet.ipynb
学習した内容
- 🤔? Keras の「関数型」モデル
- 🤓 Squeezenet アーキテクチャ
- 🤓? tf.data.datset によるデータの拡張
以下のチェックリストを頭の中で確認してください。
8. Xception ファインチューニング
分離可能な畳み込み
最近、畳み込みレイヤを実装する別の方法として、深度分離畳み込みが人気を集めています。やや複雑な概念ですが、Tensorflow と Keras で tf.keras.layers.SeparableConv2D
として実装されます。
可分畳み込みも画像にフィルタを適用しますが、入力画像の各チャネルに異なる重みセットを使用します。その後に「1x1 畳み込み」があります。これは一連のドット積で、フィルタされたチャネルの加重合計となります。毎回、新しい重みを使用して、チャネルの重み付き再結合が必要な回数だけ計算されます。
イラスト: 分離可能な畳み込み。フェーズ 1: チャンネルごとに個別のフィルタを使用した畳み込み。フェーズ 2: チャネルの線形再結合。目的の出力チャネル数に達するまで、新しい重みセットで繰り返します。フェーズ 1 は、毎回新しい重みを使用して繰り返すこともできます。ただし、実際にはほとんど行われません。
分離可能な畳み込みは、最近の畳み込みネットワーク アーキテクチャ(MobileNetV2、Xception、EfficientNet)で使用されています。ちなみに、MobileNetV2 は、以前に転移学習に使用したものです。
この方法は通常の畳み込み積みよりも低コストで、実際には同じように効果的であることがわかっています。上記の例の重み数は次のとおりです。
畳み込み層: 4 x 4 x 3 x 5 = 240
分離可能な畳み込み層: 4 x 4 x 3 + 3 x 5 = 48 + 15 = 63
各スタイルの畳み込みレイヤ スケールを同様の方法で適用するために必要な乗算回数を計算することは、演習として残しておきます。分離畳み込みはサイズが小さく、計算効率が大幅に向上します。
ハンズオン
「転移学習」の Playground ノートブックから再開します。ただし、今回は事前トレーニング済みモデルとして Xception を選択します。Xception は可分畳み込みのみを使用します。すべての重みをトレーニング可能な状態にしておきます。トレーニング済みのレイヤをそのまま使うのではなく、データに対してトレーニング済みの重みを微調整します。
Keras Flowers transfer learning (playground).ipynb
目標: 精度 95% 超(これは本当に可能です)
これが最後の演習であるため、もう少しコードとデータ サイエンスの作業が必要です。
ファインチューニングの詳細
Xception は、tf.keras.application の標準の事前トレーニング済みモデルで利用できます*。今回は、すべての重みをトレーニング可能のままにしてください。
pretrained_model = tf.keras.applications.Xception(input_shape=[*IMAGE_SIZE, 3],
include_top=False)
pretrained_model.trainable = True
モデルのファインチューニングで良い結果を得るには、学習率に注意し、ランプアップ期間のある学習率スケジュールを使用する必要があります。この場合、次のように指定します。
標準の学習率から始めると、モデルの事前トレーニング済み重みが中断されます。段階的に開始すると、モデルがデータに固定され、データを適切に変更できるようになるまで、それらの値が保持されます。開始後は、学習率を一定または指数関数的に減衰させながら学習を継続できます。
Keras では、学習率はコールバックによって指定され、エポックごとに適切な学習率を計算できます。Keras はエポックごとに正しい学習率をオプティマイザーに渡します。
def lr_fn(epoch):
lr = ...
return lr
lr_callback = tf.keras.callbacks.LearningRateScheduler(lr_fn, verbose=True)
model.fit(..., callbacks=[lr_callback])
ソリューション
ソリューション ノートブックは次のとおりです。行き詰まった場合に使用できます。
07_Keras_Flowers_TPU_xception_fine_tuned_best.ipynb
学習した内容
- 🤔? 深度分離可能な畳み込み
- 🤓 学習率のスケジュール
- 😈 事前トレーニング済みモデルのファインチューニング。
このチェックリストの内容を頭に入れておいてください。
9. 完了
最新の畳み込みニューラル ネットワークを初めて構築し、90% 以上の精度までトレーニングしました。TPU のおかげで、わずか数分で連続的なトレーニングのイテレーションを実行できました。これで、4 つの「TPU での Keras Codelab」は終了です。
- TPU スピードのデータ パイプライン: tf.data.Dataset と TFRecords
- 転移学習を使用した最初の Keras モデル
- Keras と TPU を使用した畳み込みニューラル ネットワーク
- [このラボ] Keras と TPU による最新の convnets、squeezenet、Xception
TPU の実践
TPU と GPU は Cloud AI Platform で使用できます。
最後に、皆様のフィードバックをお待ちしています。このラボで問題が見つかった場合や、改善が必要だと思われる場合は、お知らせください。フィードバックは、GitHub の問題 [フィードバック リンク] からお送りいただけます。
|