TPU での Keras と最新の convnets

1. 概要

このラボでは、Keras と TensorFlow 2 を使用して、独自の畳み込みニューラル ネットワークをゼロから構築、トレーニング、チューニングする方法を学びます。これは、TPU のパワーを利用して数分で行うことができます。また、非常にシンプルな転移学習から Squeezenet などの最新の畳み込みアーキテクチャまで、さまざまなアプローチについて学びます。このラボにはニューラル ネットワークに関する理論的な説明が含まれており、ディープ ラーニングについて学ぶための出発点として最適です。

ディープ ラーニングの論文を読むのは難しく、わかりにくいことがあります。最新の畳み込みニューラル ネットワーク アーキテクチャを実際に見てみましょう。

ca8cc21f6838eccc.png

学習内容

  • Keras と Tensor Processing Unit(TPU)を使用して、カスタムモデルをより迅速に構築する。
  • tf.data.Dataset API と TFRecord 形式を使用して、トレーニング データを効率的に読み込む。
  • だましを立てるために、独自のモデルを構築する代わりに転移学習を使用する。
  • Keras Sequential および Functional モデルスタイルを使用する。
  • ソフトマックス レイヤと交差エントロピー損失を使用して独自の Keras 分類器を構築する。
  • 適切な畳み込みレイヤを選択してモデルをファインチューニングする場合。
  • モジュール、グローバル平均プーリングなど、最新の Convnet アーキテクチャのアイデアを確認すること。
  • Squeezenet アーキテクチャを使用して、シンプルでモダンな Convnet を構築する方法。

フィードバック

この Codelab で何か問題がありましたらお知らせください。フィードバックは、GitHub の問題 [フィードバック リンク] からお送りいただけます。

2. Google Colaboratory クイック スタート

このラボでは Google Collaboratory を使用します。ユーザー側での設定は必要ありません。Chromebook から実行できます。以下のファイルを開き、セルを実行して、Colab ノートブックについての理解を深めてください。

c3df49e90e5a654f.png Welcome to Colab.ipynb

TPU バックエンドを選択する

8832c6208c99687d.png

Colab のメニューで [ランタイム] >ランタイム タイプを変更し、[TPU] を選択します。この Codelab では、ハードウェア アクセラレーションによるトレーニングをサポートする強力な TPU(Tensor Processing Unit)を使用します。ランタイムへの接続は初回実行時に自動的に行われますが、[接続] オプションを使用してボタンをタップします。

ノートブックの実行

76d05caa8b4db6da.png

セルを 1 つずつ実行するには、セルをクリックして Shift+Enter キーを押します。[ランタイム >すべて実行

目次

429f106990037ec4.png

すべてのノートブックには目次があります。左側にある黒い矢印をクリックすると開きます。

非表示のセル

edc3dba45d26f12a.png

一部のセルにはタイトルのみが表示されます。これは Colab 固有のノートブック機能です。それらをダブルクリックすると内部のコードを確認できますが、通常はあまり興味を引かないものです。通常はサポート関数または可視化関数です。内部の関数を定義するには、これらのセルを実行する必要があります。

認証

cdd4b41413100543.png

承認済みアカウントで認証すれば、Colab が限定公開の Google Cloud Storage バケットにアクセスできます。上記のコード スニペットによって認証プロセスがトリガーされます。

3. [情報] Tensor Processing Unit(TPU)とは

概要

f88cf6facfc70166.png

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 を使用して、インタラクティブな速度(トレーニング実行あたり数分)で花の分類器を構築し、最適化します。

688858c21e3beff2.png

TPU を選ぶ理由

最新の GPU は、プログラム可能な「コア」を中心に構成されています。コアは、3D レンダリング、ディープ ラーニング、物理シミュレーションなどのさまざまなタスクを処理できる非常に柔軟なアーキテクチャです。一方、TPU は従来のベクトル プロセッサと専用の行列乗算ユニットをペアにして、ニューラル ネットワークなど、大規模な行列乗算が主体となるあらゆるタスクに優れています。

8eb3e718b8e2ed08.png

イラスト: 行列乗算としての高密度ニューラル ネットワーク レイヤ。ニューラル ネットワークによって一度に処理された 8 つの画像のバッチを示しています。1 行 x 列の乗算を実行して、画像のすべてのピクセル値の加重合計が実際に行われていることを確認してください。畳み込みレイヤも行列の乗算として表すことができますが、やや複雑です( 説明はセクション 1)。

ハードウェア

MXU と VPU

TPU v2 コアは、行列の乗算を実行する行列乗算ユニット(MXU)と、活性化やソフトマックスなどの他のタスクのためのベクトル処理ユニット(VPU)で構成されます。VPU は、float32 と int32 の計算を処理します。一方、MXU は、混合精度の 16 ~ 32 ビット浮動小数点形式で動作します。

7d68944718f76b18.png

混合精度の浮動小数点数と bfloat16

MXU は、bfloat16 入力と float32 出力を使用して行列乗算を計算します。中間の累積は float32 精度で行われます。

19c5fc432840c714.png

通常、ニューラル ネットワーク トレーニングでは浮動小数点精度の低下によるノイズに耐えられます。ノイズがオプティマイザーの収束に役立つ場合もあります。従来、計算を高速化するために 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=16K「コア」が必要になります。通常は実現できません最大の GPU は約 4,000 コアです。一方、TPU は、MXU のコンピューティング単位に最小限のハードウェア(bfloat16 x bfloat16 => float32 乗アキュムレータ、それ以外は何も使用しません)を使用します。これらは非常に小さいため、TPU は 128x128 MXU で 16K を実装し、この行列の乗算を一度に処理できます。

f1b283fc45966717.gif

イラスト: MXU シストリック アレイ。コンピューティング要素は乗算アキュムレータです。1 つの行列の値が配列に読み込まれます(赤いドット)。もう一方の行列の値は、配列(グレーのドット)を通過します。縦線により値が上に移動します。水平線は部分合計を表します。配列の中をデータが流れるときに、行列乗算の結果が右側から出力されることを確認するのは、ユーザーの演習として行います。

それに加えて、ドット積が MXU で計算されている間、中間合計は単に隣接する計算ユニット間で流れます。メモリやレジスタ ファイルへの格納や取得も不要です。最終的に、TPU シストリック アレイ アーキテクチャは、行列の乗算の計算において、密度と電力の大幅な利点があるだけでなく、GPU よりも無視できないほどの速度の点で優れています。

Cloud TPU

Cloud TPU v2 インチGoogle Cloud Platform では、PCI 接続 TPU ボードを備えた仮想マシン(VM)を利用できます。TPU ボードには、デュアルコア TPU チップが 4 つあります。各 TPU コアは、VPU(ベクトル プロセッシング ユニット)と 128x128 MXU(MatriX 乗算ユニット)を備えています。この「Cloud TPU」は通常はネットワーク経由で要求元の VM に接続されます。全体像は次のようになります。

dfce5522ed644ece.png

図: ネットワークに接続された「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 コアを含めることができます。

2ec1e0d341e7fc34.jpeg

イラスト: TPU v3 Pod。HPC Interconnect で接続された TPU ボードとラック。

トレーニング中に、TPU コア間で勾配が all-reduce アルゴリズムを使用して交換されます(all-reduce についてはこちらの説明をご覧ください)。トレーニング対象のモデルは、大規模なバッチサイズでトレーニングすることでハードウェアを利用できます。

d97b9cc5d40fdb1d.gif

イラスト: Google TPU の 2D トロイダル メッシュ HPC ネットワークで all-reduce アルゴリズムを使用したトレーニング中の勾配の同期。

ソフトウェア

大規模なバッチサイズのトレーニング

TPU の理想的なバッチサイズは TPU コアあたり 128 個のデータアイテムですが、ハードウェアではすでに TPU コアあたり 8 個のデータアイテムから良好な利用率を示しています。1 つの Cloud TPU には 8 つのコアがあることを思い出してください。

この Codelab では、Keras API を使用します。Keras で指定するバッチは、TPU 全体のグローバル バッチサイズです。バッチは自動的に 8 に分割され、TPU の 8 コアで実行されます。

da534407825f01e3.png

パフォーマンスに関するその他のヒントについては、TPU パフォーマンス ガイドをご覧ください。非常に大きなバッチサイズの場合、一部のモデルでは特別な注意が必要です。詳細については、LARSOptimizer をご覧ください。

仕組み: XLA

TensorFlow プログラムは計算グラフを定義します。TPU は Python コードを直接実行するのではなく、TensorFlow プログラムで定義された計算グラフを実行します。内部では、XLA(アクセラレータ線形代数コンパイラ)と呼ばれるコンパイラが、計算ノードの Tensorflow グラフを TPU マシンコードに変換します。このコンパイラは、コードとメモリ レイアウトに対して多くの高度な最適化も行います。処理が TPU に送信されると、コンパイルが自動的に行われます。ビルドチェーンに XLA を明示的に含める必要はありません。

edce61112cd57972.png

図: 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」ユーティリティで作成されたディープ ラーニング VM)で、パラメータなしで機能します。これらのシステムは、TPU_NAME 環境変数のおかげで TPU の場所を把握しています。TPU を手動で作成する場合は、TPU_NAME 環境を設定します。変数使用している VM に対して実行するか、明示的なパラメータで TPUClusterResolver を呼び出します(TPUClusterResolver(tp_uname, zone, project))。
  • 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 オペレーションはサポートされていません。こちらのリストをご覧ください。幸いなことに、この制限はトレーニング コード、つまりモデルのフォワード パスとバックワード パスにのみ適用されます。Tensorflow のオペレーションはすべて CPU で実行されるため、データ入力パイプラインでも使用できます。
  • tf.py_func は TPU でサポートされていません。

4. データを読み込んでいます

c0ecb860e4cad0a9.jpeg cc4781a7739c49ae.jpeg 81236b00f8bbf39e.jpeg 961e2228974076bb.jpeg 7517dc163bdffcd5.jpeg 96392df4767f566d.png

今回は花の画像のデータセットを使用します。目標は、花を 5 種類に分類する方法を学ぶことです。データの読み込みは tf.data.Dataset API を使用して実行されます。まず、API について説明します。

ハンズオン

次のノートブックを開いてセルを実行(Shift+Enter キー)し、「必要な作業」が表示されている箇所をすべて手順に沿って操作してくださいラベルです。

c3df49e90e5a654f.png Fun with tf.data.Dataset (playground).ipynb

その他の情報

「花」についてデータセット

データセットは 5 つのフォルダで構成されています。各フォルダには 1 種類の花が入っています。フォルダの名前は、ヒマワリ、デイジー、タンポポ、チューリップ、バラです。データは Google Cloud Storage の公開バケットでホストされています。抜粋:

gs://flowers-public/sunflowers/5139971615_434ff8ed8b_n.jpg
gs://flowers-public/daisy/8094774544_35465c1c64.jpg
gs://flowers-public/sunflowers/9309473873_9d62b9082e.jpg
gs://flowers-public/dandelion/19551343954_83bb52f310_m.jpg
gs://flowers-public/dandelion/14199664556_188b37e51e.jpg
gs://flowers-public/tulips/4290566894_c7f061583d_m.jpg
gs://flowers-public/roses/3065719996_c16ecd5551.jpg
gs://flowers-public/dandelion/8168031302_6e36f39d87.jpg
gs://flowers-public/sunflowers/9564240106_0577e919da_n.jpg
gs://flowers-public/daisy/14167543177_cd36b54ac6_n.jpg

tf.data.Dataset を選ぶ理由

Keras と Tensorflow は、すべてのトレーニング関数と評価関数でデータセットを受け入れます。データセットにデータを読み込むと、ニューラル ネットワークのトレーニング データに役立つ共通機能が API によってすべて提供されます。

dataset = ... # load something (see below)
dataset = dataset.shuffle(1000) # shuffle the dataset with a buffer of 1000
dataset = dataset.cache() # cache the dataset in RAM or on disk
dataset = dataset.repeat() # repeat the dataset indefinitely
dataset = dataset.batch(128) # batch data elements together in batches of 128
AUTOTUNE = tf.data.AUTOTUNE
dataset = dataset.prefetch(AUTOTUNE) # prefetch next batch(es) while training

パフォーマンスのヒントとデータセットのベスト プラクティスについては、こちらの記事をご覧ください。リファレンス ドキュメントはこちらです。

tf.data.Dataset の基本

データは通常、複数のファイル(ここでは画像)に含まれます。以下を呼び出して、ファイル名のデータセットを作成できます。

filenames_dataset = tf.data.Dataset.list_files('gs://flowers-public/*/*.jpg')
# The parameter is a "glob" pattern that supports the * and ? wildcards.

次に「map」を使用して各ファイル名に関数を追加します。この関数は通常、ファイルをメモリ内の実際のデータに読み込んでデコードします。

def decode_jpeg(filename):
  bits = tf.io.read_file(filename)
  image = tf.io.decode_jpeg(bits)
  return image

image_dataset = filenames_dataset.map(decode_jpeg)
# this is now a dataset of decoded images (uint8 RGB format)

データセットを反復処理するには:

for data in my_dataset:
  print(data)

タプルのデータセット

教師あり学習では通常、トレーニング データセットはトレーニング データと正解のペアで構成されます。これを可能にするために、デコード関数はタプルを返すことができます。タプルのデータセットが得られます。これに対して反復処理を行うと、タプルが返されます。返される値は、モデルで使用できる Tensorflow のテンソルです。これらに対して .numpy() を呼び出すと、未加工の値を表示できます。

def decode_jpeg_and_label(filename):
  bits = tf.read_file(filename)
  image = tf.io.decode_jpeg(bits)
  label = ... # extract flower name from folder name
  return image, label

image_dataset = filenames_dataset.map(decode_jpeg_and_label)
# this is now a dataset of (image, label) pairs 

for image, label in dataset:
  print(image.numpy().shape, label.numpy())

まとめ:画像を 1 つずつ読み込むのは時間がかかります。

このデータセットを反復処理すると、1 秒あたり 1 ~ 2 個の画像を読み込むことができることがわかります。動作が遅すぎます。トレーニングに使用するハードウェア アクセラレータは、この速度を何倍も持続できます。次のセクションに進み、この目標を達成する方法をご確認ください。

ソリューション

こちらがソリューション ノートブックです。行き詰まった場合に使用できます。

c3df49e90e5a654f.png Fun with tf.data.Dataset (solution).ipynb

学習した内容

  • 🤔? tf.data.Dataset.list_files
  • 🤔? tf.data.Dataset.map
  • 🤔? タプルのデータセット
  • 😃? データセットの反復処理

このチェックリストをぜひチェックしてみてください。

5. データの高速読み込み

このラボで使用する Tensor Processing Unit(TPU)ハードウェア アクセラレータは非常に高速です。多くの場合、仕事をこなすのに十分な速さでデータを供給することが課題です。Google Cloud Storage(GCS)は非常に高いスループットを維持できますが、他のクラウド ストレージ システムと同様に、接続の開始にはネットワーク間のコストがかかります。そのため、データを数千の個別ファイルとして保存することは理想的ではありません。少数のファイルにまとめて処理し、tf.data.Dataset を活用して複数のファイルを並行して読み取ります。

リードスルー

画像ファイルを読み込んで一般的なサイズに変更し、16 個の TFRecord ファイルに保存するコードは、次のノートブックにあります。ざっと目を通してください。この Codelab の残りの部分では、適切な TFRecord 形式のデータが提供されるため、これを実行する必要はありません。

c3df49e90e5a654f.png Flower pictures to TFRecords.ipynb

最適な GCS スループットを実現する理想的なデータ レイアウト

TFRecord ファイル形式

Tensorflow でデータの保存に推奨されるファイル形式は、protobuf ベースの TFRecord 形式です。他のシリアル化形式も機能しますが、次のように記述して、TFRecord ファイルからデータセットを直接読み込むことができます。

filenames = tf.io.gfile.glob(FILENAME_PATTERN)
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...) # do the TFRecord decoding here - see below

最適なパフォーマンスを得るには、以下の複雑なコードを使用して複数の TFRecord ファイルから一度に読み取ることをおすすめします。このコードは、N 個のファイルを並行して読み取り、読み取り速度を優先してデータの順序を無視します。

AUTOTUNE = tf.data.AUTOTUNE
ignore_order = tf.data.Options()
ignore_order.experimental_deterministic = False

filenames = tf.io.gfile.glob(FILENAME_PATTERN)
dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE)
dataset = dataset.with_options(ignore_order)
dataset = dataset.map(...) # do the TFRecord decoding here - see below

TFRecord クイック リファレンス

TFRecord には、バイト文字列(バイトのリスト)、64 ビット整数、32 ビット浮動小数点の 3 種類のデータを格納できます。それらは常にリストとして格納され、1 つのデータ要素はサイズ 1 のリストになります。次のヘルパー関数を使用して、データを TFRecord に保存できます。

バイト文字列の書き込み

# warning, the input is a list of byte strings, which are themselves lists of bytes
def _bytestring_feature(list_of_bytestrings):
  return tf.train.Feature(bytes_list=tf.train.BytesList(value=list_of_bytestrings))

整数の書き込み

def _int_feature(list_of_ints): # int64
  return tf.train.Feature(int64_list=tf.train.Int64List(value=list_of_ints))

浮動小数点数の書き込み

def _float_feature(list_of_floats): # float32
  return tf.train.Feature(float_list=tf.train.FloatList(value=list_of_floats))

TFRecord の書き込み(上記のヘルパーを使用)

# input data in my_img_bytes, my_class, my_height, my_width, my_floats
with tf.python_io.TFRecordWriter(filename) as out_file:
  feature = {
    "image": _bytestring_feature([my_img_bytes]), # one image in the list
    "class": _int_feature([my_class]),            # one class in the list
    "size": _int_feature([my_height, my_width]),  # fixed length (2) list of ints
    "float_data": _float_feature(my_floats)       # variable length  list of floats
  }
  tf_record = tf.train.Example(features=tf.train.Features(feature=feature))
  out_file.write(tf_record.SerializeToString())

TFRecord からデータを読み取るには、まず、保存したレコードのレイアウトを宣言する必要があります。宣言では、固定長のリストまたは可変長のリストとして名前付きフィールドにアクセスできます。

TFRecords からの読み取り

def read_tfrecord(data):
  features = {
    # tf.string = byte string (not text string)
    "image": tf.io.FixedLenFeature([], tf.string), # shape [] means scalar, here, a single byte string
    "class": tf.io.FixedLenFeature([], tf.int64),  # shape [] means scalar, i.e. a single item
    "size": tf.io.FixedLenFeature([2], tf.int64),  # two integers
    "float_data": tf.io.VarLenFeature(tf.float32)  # a variable number of floats
  }

  # decode the TFRecord
  tf_record = tf.io.parse_single_example(data, features)

  # FixedLenFeature fields are now ready to use
  sz = tf_record['size']

  # Typical code for decoding compressed images
  image = tf.io.decode_jpeg(tf_record['image'], channels=3)

  # VarLenFeature fields require additional sparse.to_dense decoding
  float_data = tf.sparse.to_dense(tf_record['float_data'])

  return image, sz, float_data

# decoding a tf.data.TFRecordDataset
dataset = dataset.map(read_tfrecord)
# now a dataset of triplets (image, sz, float_data)

有用なコード スニペット:

単一のデータ要素の読み取り

tf.io.FixedLenFeature([], tf.string)   # for one byte string
tf.io.FixedLenFeature([], tf.int64)    # for one int
tf.io.FixedLenFeature([], tf.float32)  # for one float

要素の固定サイズリストの読み取り

tf.io.FixedLenFeature([N], tf.string)   # list of N byte strings
tf.io.FixedLenFeature([N], tf.int64)    # list of N ints
tf.io.FixedLenFeature([N], tf.float32)  # list of N floats

可変長のデータアイテムの読み取り

tf.io.VarLenFeature(tf.string)   # list of byte strings
tf.io.VarLenFeature(tf.int64)    # list of ints
tf.io.VarLenFeature(tf.float32)  # list of floats

VarLenFeature はスパース ベクトルを返します。TFRecord のデコード後に追加のステップが必要です。

dense_data = tf.sparse.to_dense(tf_record['my_var_len_feature'])

TFRecords にオプション フィールドを含めることもできます。フィールドの読み取り時にデフォルト値を指定した場合、そのフィールドが欠落していても、エラーの代わりにデフォルト値が返されます。

tf.io.FixedLenFeature([], tf.int64, default_value=0) # this field is optional

学習した内容

  • 🤔? データファイルをシャーディングして GCS からすばやくアクセス
  • 😝? TFRecord の書き方について確認しました。(構文を忘れた場合は、このページをクイック リファレンスとしてブックマークしておいてください)
  • 🤔? TFRecordDataset を使用して TFRecord からデータセットを読み込む

このチェックリストをぜひチェックしてみてください。

6. [情報] ニューラル ネットワーク分類器 101

概要

次の段落で太字で示されている用語をすべて理解している場合は、次の演習に進みましょう。ディープ ラーニングを始めたばかりの方は、ぜひ本をお読みください。

一連のレイヤとして構築されたモデルの場合、Keras では Sequential API を使用できます。たとえば、3 つの Dense レイヤを使用する画像分類器は、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, ... )

688858c21e3beff2.png

高密度ニューラル ネットワーク

これは、画像を分類するための最も単純なニューラル ネットワークです。「ニューロン」でできています。レイヤに配置されます。第 1 レイヤは入力データを処理し、その出力を他のレイヤにフィードします。「高密度」と呼ばれる各ニューロンが前のレイヤのすべてのニューロンと接続されているからです。

c21bae6dade487bc.png

すべてのピクセルの RGB 値を長いベクトルにフラット化し、入力として使用することで、このようなネットワークに画像をフィードできます。これは画像認識に最適な手法ではありませんが、後で改善します。

ニューロン、アクティベーション、RELU

「ニューロン」すべての入力の加重合計を計算し、「bias」と呼ばれる値を加算します。いわゆる「活性化関数」を通じて結果をフィードします重みとバイアスは、最初は未知です。これらはランダムに初期化され、「学習」されるニューラル ネットワークをトレーニングします。

644f4213a4ee70e5.png

最も一般的な活性化関数は、正規化線形ユニットの RELU です。上のグラフからわかるように、これは非常にシンプルな関数です。

ソフトマックスの有効化

花を 5 つのカテゴリ(バラ、チューリップ、タンポポ、ヒマワリ、ヒマワリ)に分類しているため、上記のネットワークは 5 個のニューロン層で終わります。中間層のニューロンは、従来の RELU 活性化関数を使用して活性化されます。最後のレイヤでは、この花がバラやチューリップなどになる確率を表す 0 ~ 1 の数値を計算します。そのために、「softmax」という活性化関数を使用します。

ベクトルにソフトマックスを適用するには、各要素の指数を受け取り、通常は L1 ノルム(絶対値の合計)を使用してベクトルを正規化します。これにより、値の和が 1 になり、確率として解釈できるようになります。

ef0d98c0952c262d.png d51252f75894479e.gif

交差エントロピー損失

ニューラル ネットワークが入力画像から予測を生成するようになったので、予測の精度、すなわちネットワークが教える情報と正解(しばしば「ラベル」と呼ばれる)の間の距離を測定する必要があります。データセット内のすべての画像に正しいラベルが付けられていることを思い出してください。

どのような距離でもかまいませんが、分類問題では、いわゆる「交差エントロピー距離」を使用して最も効果的な方法です。これをエラーまたは「損失」と呼ぶことにします。関数:

7bdf8753d20617fb.png

勾配降下法

トレーニングニューラル ネットワークとは、交差エントロピー損失関数が最小になるように、トレーニング用の画像とラベルを使って重みとバイアスを調整することを意味します。仕組みは次のとおりです。

交差エントロピーは、トレーニング画像の重み、バイアス、ピクセル、および既知のクラスの関数です。

すべての重みとすべてのバイアスに対して交差エントロピーの偏導関数を計算すると、与えられた画像、ラベル、重みとバイアスの現在値に対して計算される「勾配」が得られます。何百万もの重みとバイアスが存在する可能性があるため、勾配の計算は大変な作業に思えます。幸いなことに、TensorFlow がそれを自動でやってくれます。勾配の数学的特性は、「上」を指すことです。交差エントロピーが低いところに目的のため、逆方向に移動します。重みとバイアスは、勾配のごく一部で更新されます。その後、トレーニング ループ内で、次のトレーニング画像とラベルのバッチを使用して、同じことを何度も繰り返します。うまくいけば、交差エントロピーが最小になるところに収束することが期待されます。ただし、この最小値が一意であるという保証はありません。

グラデーション descent2.png

ミニバッチ処理とモメンタム

1 つのサンプル画像についてグラデーションを計算し、重みとバイアスをすぐに更新することもできますが、たとえば 128 枚の画像でバッチにすることで、異なるサンプル画像による制約をより適切に表現するグラデーションが得られるため、より速く解に収束する可能性が高くなります。ミニバッチのサイズは調整可能なパラメータです。

この手法は「確率的勾配降下法」とも呼ばれます。には、より現実的な利点がもう一つあります。バッチで作業することは、より大きな行列で作業することも意味し、通常は GPU や TPU での最適化が容易になります。

ただし、収束はやや混沌としたものになり、勾配ベクトルがすべてゼロになると収束が停止することさえあります。最小値を見つけたということでしょうか?必ずしも違反警告を受けるとは限りません。勾配コンポーネントは、最小値または最大値で 0 にできます。何百万もの要素を含む勾配ベクトルで、それらがすべてゼロの場合、すべてのゼロが最小値に対応し、どのゼロでも最大ポイントに対応する確率はかなり小さくなります。さまざまな次元の空間では、サドルポイントは非常に一般的なものなので、それだけでは不十分です。

52e824fe4716c4a0.png

イラスト: サドルポイント。勾配は 0 ですが、すべての方向で最小値ではありません。(画像の帰属: Wikimedia: 執筆者: Nicoguaro - 自著、CC BY 3.0

解決策は、最適化アルゴリズムに勢いをつけて、止まることなく踏切を乗り越えることです。

用語集

バッチまたはミニバッチ: トレーニングは常に、トレーニング データとラベルのバッチに対して実行されます。これにより、アルゴリズムが収束しやすくなります。「バッチ」ディメンションは通常、データテンソルの 1 次元目です。たとえば、形状 [100, 192, 192, 3] のテンソルには、1 ピクセルあたり 3 つの値(RGB)を持つ 192x192 ピクセルの画像が 100 個含まれています。

交差エントロピー損失: 分類器でよく使用される特別な損失関数。

dense レイヤ: ニューロンの層。各ニューロンが前のレイヤのすべてのニューロンに接続されています。

特徴: ニューラル ネットワークの入力は「特徴」と呼ばれることもあります。適切な予測を得るために、データセットのどの部分(または複数の部分の組み合わせ)をニューラル ネットワークに入力すべきかを考える技術は「特徴量エンジニアリング」と呼ばれます。

labels: 「classes」の別の名前教師あり分類問題での正解や正解の

学習率: トレーニング ループの反復処理ごとに重みとバイアスが更新される勾配の割合。

ロジット: 活性化関数が適用される前のニューロン層の出力は「ロジット」と呼ばれます。この用語は「ロジスティック関数」に由来します。別名「シグモイド関数」活性化関数として最も広く利用されていました。「ロジスティック関数の前のニューロン出力」「Logits」に短縮されます。

loss: ニューラル ネットワークの出力と正解を比較する誤差関数

neuron: 入力の加重合計を計算し、バイアスを加えて、その結果を活性化関数に渡します。

ワンホット エンコーディング: クラス 5 のうち、クラス 3 が 5 つの要素のベクトルとしてエンコードされます。3 番目の要素である 1 を除き、すべてゼロになります。

relu: 正規化線形ユニット。ニューロンによく使用される活性化関数です。

シグモイド: 以前は一般的になっていたもう一つの活性化関数で、特殊なケースでも有用です。

ソフトマックス: ベクトルに対して作用する特別な活性化関数。最大の成分と他のすべての成分の差を増加させ、確率のベクトルとして解釈できるようにベクトルを合計 1 に正規化します。分類器の最後のステップとして使用されます。

tensor: 「テンソル」行列に似ていますが、次元数は任意です。1 次元テンソルはベクトルです。2 次元のテンソルは行列です。テンソルは 3 次元、4 次元、5 次元以上にすることができます。

7. 転移学習

画像分類の問題の場合、密レイヤでは不十分でしょう。畳み込みレイヤと、それらを配置できる多くの方法を学ぶ必要があります。

近道することもできます。トレーニング済みの畳み込みニューラル ネットワークがダウンロード可能です。最後のレイヤであるソフトマックス分類ヘッドを取り除き、独自のレイヤに置き換えることができます。トレーニング済みの重みとバイアスはそのまま維持され、追加したソフトマックス レイヤのみを再トレーニングします。この手法は転移学習と呼ばれ、驚くべきことに、ニューラル ネットが事前トレーニングされるデータセットが「十分に近似」されている限り機能します。あります。

ハンズオン

次のノートブックを開いてセルを実行(Shift+Enter キー)し、「必要な作業」が表示されている箇所をすべて手順に沿って操作してくださいラベルです。

c3df49e90e5a654f.png Keras Flowers transfer learning (playground).ipynb

その他の情報

転移学習では、トップクラスの研究者によって開発された高度な畳み込みニューラル ネットワーク アーキテクチャと、膨大な画像データセットでの事前トレーニングの両方を利用できます。この例では、ImageNet でトレーニングされたネットワークから転移学習を行います。ImageNet は、花に近い多くの植物や屋外のシーンを含む画像のデータベースです。

b8fc1efd2001f072.png

イラスト: トレーニング済みの複雑な畳み込みニューラル ネットワークをブラック ボックスとして使用し、分類ヘッドのみを再トレーニングします。これは転移学習です。このような畳み込みレイヤの複雑な配置がどのように機能するかは、後で説明します。今のところは、それは他の人の問題です。

Keras での転移学習

Keras では、tf.keras.applications.* コレクションから事前トレーニング済みモデルをインスタンス化できます。たとえば MobileNet V2 は、きわめて優れた畳み込みアーキテクチャであり、サイズも妥当です。include_top=False を選択すると、最終的なソフトマックス レイヤのない事前トレーニング済みモデルを取得し、独自のモデルを追加できます。

pretrained_model = tf.keras.applications.MobileNetV2(input_shape=[*IMAGE_SIZE, 3], include_top=False)
pretrained_model.trainable = False

model = tf.keras.Sequential([
    pretrained_model,
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(5, activation='softmax')
])

pretrained_model.trainable = False 設定にも注意してください。事前トレーニング済みモデルの重みとバイアスを固定して、ソフトマックス レイヤのみをトレーニングします。通常、この作業は比較的少ない重みで迅速に実行でき、大規模なデータセットは必要ありません。ただし、データが大量にある場合は、pretrained_model.trainable = True を使用して転移学習をさらに効果的に行うことができます。トレーニング済みの重みは優れた初期値を提供しますが、問題に適合するようにトレーニングによって調整することもできます。

最後に、密なソフトマックス レイヤの前に Flatten() レイヤが挿入されています。高密度レイヤはフラットなデータベクトルに対して機能しますが、それが事前トレーニング済みモデルから得られるものであるかどうかはわかりません。だからこそ、フラット化する必要があります。次の章では、畳み込みアーキテクチャについて詳しく説明しながら、畳み込みレイヤから返されるデータ形式について説明します。

この方法では 75% 近くの精度が得られるはずです。

ソリューション

こちらがソリューション ノートブックです。行き詰まった場合に使用できます。

c3df49e90e5a654f.png Keras Flowers transfer learning (solution).ipynb

学習した内容

  • 🤔? Keras で分類器を記述する方法
  • 🤓? 最後のソフトマックス層と交差エントロピー損失で構成され
  • 🎃? 転移学習
  • 🤔? 最初のモデルのトレーニング
  • 🧐? トレーニング中の損失と精度を追跡する

このチェックリストをぜひチェックしてみてください。

8. [情報] 畳み込みニューラル ネットワーク

概要

次の段落で太字で示されている用語をすべて理解している場合は、次の演習に進みましょう。畳み込みニューラル ネットワークをこれから始める場合は、この後をお読みください。

convolutional.gif

イラスト: 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'])

688858c21e3beff2.png

畳み込みニューラル ネットワーク 101

畳み込みネットワークの層には、1 つの「ニューロン」がは、画像の小さな領域に対してのみ、そのすぐ上にあるピクセルの加重合計を行います。通常の密レイヤのニューロンと同様に、バイアスを加え、活性化関数を通じて合計をフィードします。この処理は、同じ重みを使用して画像全体で繰り返されます。密な層では、各ニューロンに独自の重みがあったことを思い出してください。ここでは、単一の「パッチ」がの重みが画像上を両方向にスライドする(「畳み込み」)。出力には、画像内のピクセルと同じ数の値が含まれます(ただし、端にはパディングが必要です)。これは、4×4×3=48 の重みのフィルタを使用するフィルタリング演算です。

ただし、48 個の重みでは不十分です。自由度をさらに広げるには、新しい重みのセットで同じ操作を繰り返します。これにより、新しいフィルタ出力のセットが生成されます。ここでは「チャンネル」とします。出力の 1 対 1 です。

Screen Shot 2016-07-29 at 16.02.37.png

2 組(または複数)の重みのセットは、新しい次元を追加することで 1 つのテンソルとして合計できます。これにより、畳み込みレイヤの重みテンソルの一般的な形状がわかります。入力チャンネルと出力チャンネルの数はパラメータであるため、畳み込みレイヤのスタックとチェーンを開始できます。

d1b557707bcd1cb9.png

イラスト: 畳み込みニューラル ネットワークが「立方体」を変換する他の「キューブ」に分割できます。可視化できます

ストライド畳み込み、最大プーリング

2 または 3 のストライドで畳み込みを実行すると、結果として得られるデータキューブを水平方向に縮小することもできます。一般的な方法は次の 2 つです。

  • ストライド畳み込み: 上記のスライディング フィルタで、ストライドが 1 より大きい
  • 最大プーリング: MAX 演算を適用するスライディング ウィンドウ(通常は 2x2 パッチで、2 ピクセルごとに繰り返される)

2b2d4263bb8470b.gif

イラスト: 計算ウィンドウを 3 ピクセル分スライドすると、出力値が少なくなります。ストライド畳み込みまたは最大プーリング(2 ストライドでスライドする 2x2 ウィンドウの最大)は、データキューブを水平方向に縮小する方法です。

畳み込み分類器

最後に、最後のデータキューブをフラット化し、密集したソフトマックス活性化層に通すことで分類ヘッドを接続します。一般的な畳み込み分類器は次のようになります。

4a61aaffb6cba3d1.png

イラスト: 畳み込みレイヤとソフトマックス レイヤを使用した画像分類器。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'])

9. お客様のカスタム コンバージョン

ハンズオン

畳み込みニューラル ネットワークをゼロから構築してトレーニングしましょう。TPU を使用すると、非常に高速に反復処理を行うことができます。次のノートブックを開いてセルを実行(Shift+Enter キー)し、「必要な作業」が表示されている箇所をすべて手順に沿って操作してくださいラベルです。

c3df49e90e5a654f.png Keras_Flowers_TPU (playground).ipynb

目標は、転移学習モデルの精度 75% を超えることです。このモデルには利点があり、今は 3, 670 枚の画像しかないにもかかわらず、何百万枚もの画像のデータセットで事前トレーニングされています。少なくとも一致させられるでしょうか?

その他の情報

レイヤはいくつ、どのくらいの大きさですか?

レイヤサイズの選択は、単なる科学ではなく芸術です。パラメータ(重みとバイアス)が少なすぎる場合も多すぎる場合も、適切なバランスを見出す必要があります。重みが少なすぎると、ニューラル ネットワークは花の形状の複雑さを表現できません。多すぎると「過学習(過学習)が発生しやすくなります。つまり、トレーニング画像に特化していて、一般化できない」可能性があります。パラメータが多いと、モデルのトレーニングも遅くなります。Keras では、model.summary() 関数を使用してモデルの構造とパラメータ数を表示します。

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 192, 192, 16)      448       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 192, 192, 30)      4350      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 96, 96, 30)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 96, 96, 60)        16260     
_________________________________________________________________
 ... 
_________________________________________________________________
global_average_pooling2d (Gl (None, 130)               0         
_________________________________________________________________
dense (Dense)                (None, 90)                11790     
_________________________________________________________________
dense_1 (Dense)              (None, 5)                 455       
=================================================================
Total params: 300,033
Trainable params: 300,033
Non-trainable params: 0
_________________________________________________________________

ヒント:

  • 複数のレイヤがあることで、「ディープ」な必要があります。この単純な花の認識問題では、5 ~ 10 個のレイヤが理にかなっています。
  • 小さいフィルタを使用します。一般的に、3×3 のフィルタはどの場所にも適しています。
  • 1×1 フィルタも使用でき、低コストです。実際には「フィルタ」は行いませんチャネルの線形結合以外の処理です実際のフィルタで切り替えます。(「1x1 畳み込み」の詳細については、次のセクションで説明します)。
  • このような分類問題では、最大プーリング レイヤ(またはストライドが 1 を超える畳み込み)で頻繁にダウンサンプリングします。花の位置は気にせず、花かタンポポかは気にしないため、x と y の情報が失われることは重要ではなく、より小さな領域をフィルタするほうがコストはかさみます。
  • フィルタの数は通常、ネットワークの最後にあるクラスの数とほぼ同じになります(なぜなら、以下の「グローバル平均プーリング」を参照してください)。何百ものクラスに分類する場合は、連続するレイヤでフィルタ数を段階的に増やしていきます。5 つのクラスがある花のデータセットの場合、5 つのフィルタでフィルタリングするだけでは不十分です。ほとんどのレイヤで同じフィルタ数(例: 32)を使用して、終端に向かって減らすことができます。
  • 最終的な高密度レイヤは高価です。すべての畳み込みレイヤの合計よりも多くの重みを持つことができます。たとえば、最後のデータ キューブである 24 x 24 x 10 のデータポイントから得られる出力がきわめて合理的であったとしても、100 個のニューロン密レイヤには 24 x 24 x 10 x 100=576,000 の重みがかかります。慎重に検討するか、グローバル平均プール(以下を参照)を試してください。

グローバル平均プーリング

畳み込みニューラル ネットワークの最後に高価な高密度レイヤを使用する代わりに、入力データ「キューブ」を分割できます。それらの値を平均し、ソフトマックス活性化関数にフィードします。この方法で分類ヘッドを作成する場合、重みは 0 です。Keras の構文は tf.keras.layers.GlobalAveragePooling2D(). です。

93240029f59df7c2.png

ソリューション

こちらがソリューション ノートブックです。行き詰まった場合に使用できます。

c3df49e90e5a654f.png Keras_Flowers_TPU (solution).ipynb

学習した内容

  • 🤔? 畳み込みレイヤで遊んでいた
  • 🤓? 最大プーリング、ストライド、グローバル平均プーリングなどで実験しました...
  • 😃? TPU で実際のモデルを迅速に反復処理

このチェックリストをぜひチェックしてみてください。

10. [情報] 最新の畳み込みアーキテクチャ

概要

7968830b57b708c0.png

イラスト: 畳み込み「モジュール」。現時点で最適なことは?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)

688858c21e3beff2.png

その他の格安コツ

小さな 3x3 フィルタ

40a7b15fb7dbe75c.png

この図では、2 つの連続する 3x3 フィルタの結果が表示されています。結果に寄与したデータポイントをトレースしてみましょう。これら 2 つの連続する 3x3 フィルタは、5x5 領域のなんらかの組み合わせを計算します。5×5 フィルターとまったく同じ組み合わせではありませんが、2 つの連続した 3×3 フィルターは単一の 5×5 フィルターよりも安価であるため、試してみる価値があります。

1 行 1 の畳み込み?

fd7cac16f8ecb423.png

数学的に「1×1」は畳み込みは定数の乗算なので、あまり有用な概念ではありません。ただし、畳み込みニューラル ネットワークでは、フィルタは 2D 画像だけでなくデータキューブにも適用されることを覚えておいてください。したがって、「1x1」はfilter は 1 行 1 列のデータの加重合計を計算し(図を参照)、データにわたってスライドしていくと、入力の各チャネルの線形結合が得られます。これは実に便利です。チャネルを個々のフィルタリング処理の結果として考える場合、たとえば「尖った耳」に対する別のフィルタや「ひげ」のフィルタなどが考えられます。3 つ目は「目がスリット」です次に「1x1」畳み込みレイヤは、これらの特徴の可能な線形組み合わせを複数計算します。これは「猫」を探すのに便利です。さらに、1x1 レイヤでは使用する重みが少なくなります。

11. スクイーズネット

これらのアイデアをまとめる簡単な方法は、「Squeezenet」ホワイトペーパーをご覧ください。著者は、1x1 と 3x3 の畳み込み層のみを使用する、非常にシンプルな畳み込みモジュール設計を提案しています。

1730ac375379269b.png

イラスト: 「ファイア モジュール」に基づくスクイーズネット アーキテクチャ。1×1 の層を「押しつぶす」ように配置されています。垂直次元の受信データの後に「拡張」する 2 つの並列 1x1 および 3x3 畳み込み層必要があります。

ハンズオン

前のノートブックで、スクイーズネットにヒントを得た畳み込みニューラル ネットワークを構築します。モデルのコードを Keras の「関数型スタイル」に変更する必要があります。

c3df49e90e5a654f.png 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 つのファイア モジュールを接続してテストできます。

ファイア モジュールでは、「スクイーズ」というパラメータは通常、「expand」パラメータパラメータを指定します。これらのパラメータは実際にはフィルタの数です。通常は 8 ~ 196 の範囲になります。ネットワークを通じてフィルタの数が徐々に増えるアーキテクチャや、すべての Fire モジュールに同じ数のフィルタを使用する単純なアーキテクチャを試すことができます。

以下に例を示します。

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% という目標では難しいようだということに気付くかもしれません。もっと手ごろな価格のコツを試してみましょう。

バッチ正規化

バッチノルムは、発生している収束の問題に対処するのに役立ちます。この手法については、次のワークショップで詳しく説明します。今のところは、これをブラック ボックスの「魔法」として使ってください。以下のように、ネットワーク内のすべての畳み込みレイヤ(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 に小さくする必要があります。今のところ、この詳細は気にしないでください。

データの拡張

彩度を左右に反転させるなどの簡単な変換でデータを拡張することで、さらにパーセンテージ ポイントが得られます。

4ed2958e09b487ca.png

ad795b70334e0d6b.png

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% の精度は達成できるレベルに達しているはずです。

ソリューション

こちらがソリューション ノートブックです。行き詰まった場合に使用できます。

c3df49e90e5a654f.png Keras_Flowers_TPU_squeezenet.ipynb

学習した内容

  • 🤔? Keras の「関数型」モデル
  • 🤓? Squeezenet アーキテクチャ
  • 🤓? tf.data.datset によるデータの拡張

このチェックリストをぜひチェックしてみてください。

12. Xception ファインチューニング

分離可能な畳み込み

最近では、深度分離可能な畳み込み積み重ねという、畳み込みレイヤを実装する別の方法の人気が高まっています。一口で言いますが、コンセプトはとてもシンプルです。Tensorflow と Keras で tf.keras.layers.SeparableConv2D として実装されます。

分離可能な畳み込みも画像に対してフィルタを実行しますが、入力画像の各チャネルに対して異なる重みのセットを使用します。その後に「1x1 畳み込み」があります。これは一連のドット積で、フィルタされたチャネルの加重合計となります。毎回、新しい重みを使用して、チャネルの重み付き再結合が必要な回数だけ計算されます。

615720b803bf8dda.gif

イラスト: 分離可能な畳み込み。フェーズ 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

各スタイルの畳み込みレイヤ スケールを同様の方法で適用するために必要な乗算回数を計算することは、演習として残しておきます。分離可能な畳み込みは、より小規模で計算効率が大幅に向上します。

ハンズオン

「転移学習」からやり直す今回は Xception を事前トレーニング済みモデルとして選択します。Xception では分離可能な畳み込みのみを使用します。すべての重みをトレーニング可能な状態にしておきます。トレーニング済みのレイヤをそのまま使うのではなく、データに対してトレーニング済みの重みを微調整します。

c3df49e90e5a654f.png 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

モデルをファインチューニングするときに適切な結果を得るには、学習率に注意を払い、立ち上げ期間を設定した学習率のスケジュールを使用する必要があります。この場合、次のように指定します。

9b1af213b2b36d47.png

標準の学習率から始めると、事前にトレーニングされたモデルの重みが中断されます。開始すると、モデルが適切な方法でデータを変更できるようになるまで、データが徐々に保持されます。立ち上げ後は、学習率を一定または指数関数的に減衰させながら学習を継続できます。

Keras では、学習率はコールバックによって指定され、エポックごとに適切な学習率を計算できます。Keras は、エポックごとに正しい学習率をオプティマイザーに渡します。

def lr_fn(epoch):
  lr = ...
  return lr

lr_callback = tf.keras.callbacks.LearningRateScheduler(lr_fn, verbose=True)

model.fit(..., callbacks=[lr_callback])

ソリューション

こちらがソリューション ノートブックです。行き詰まった場合に使用できます。

c3df49e90e5a654f.png 07_Keras_Flowers_TPU_xception_fine_tuned_best.ipynb

学習した内容

  • 🤔? 深度分離可能な畳み込み
  • 🤓? 学習率のスケジュール
  • Roku 事前トレーニング済みモデルのファインチューニング。

このチェックリストをぜひチェックしてみてください。

13. 完了

初めて最新の畳み込みニューラル ネットワークを構築し、それを 90% 以上の精度までトレーニングしました。TPU のおかげで、連続するトレーニングの実行をわずか数分でイテレーションできました。

TPU の実践

TPU と GPU は、Google Cloud の Vertex AI で使用できます。

ぜひフィードバックをお寄せください。このラボで問題が見つかった場合や、改善が必要だと思われる点がありましたら、お知らせください。フィードバックは、GitHub の問題 [フィードバック リンク] からお送りいただけます。

HR.png

マーティン・ゲルナー(Martin Görner)氏、small.jpg
著者: Martin Görner
Twitter: @martin_gorner