1. 概要
このラボでは、Keras と TensorFlow 2 を使用して、独自の畳み込みニューラル ネットワークをゼロから構築、トレーニング、調整する方法を学びます。TPU のパワーを活用することで、この処理は数分で完了します。また、非常にシンプルな転移学習から、Squeezenet などの最新の畳み込みアーキテクチャまで、複数のアプローチについても説明します。このラボでは、ニューラル ネットワークに関する理論的な説明を扱います。ディープ ラーニングを学ぶデベロッパーにとって、このラボは最初のステップとして適しています。
ディープ ラーニングの論文を読むのは難しく、わかりにくいことがあります。最新の畳み込みニューラル ネットワーク アーキテクチャを実際に見てみましょう。
学習内容
- Keras と Tensor Processing Unit(TPU)を使用して、カスタムモデルをより迅速に構築する。
- tf.data.Dataset API と TFRecord 形式を使用して、トレーニング データを効率的に読み込む。
- だましを立てるために、独自のモデルを構築する代わりに転移学習を使用する。
- Keras Sequential および Functional モデルスタイルを使用する。
- ソフトマックス レイヤと交差エントロピー損失を使用して、独自の Keras 分類器を構築する。
- 適切な畳み込みレイヤを選択してモデルをファインチューニングする。
- モジュール、グローバル平均プーリングなどの最新のコンボネット アーキテクチャのアイデアを探る。
- Squeezenet アーキテクチャを使用して、シンプルな最新の ConvNet を構築する。
フィードバック
この Codelab で何か問題がありましたらお知らせください。フィードバックは、GitHub の問題 [フィードバック リンク] からお送りいただけます。
2. Google Colaboratory クイック スタート
このラボでは Google Collaboratory を使用します。ユーザー側での設定は必要ありません。Chromebook から実行できます。以下のファイルを開き、セルを実行して Colab ノートブックに慣れてください。
TPU バックエンドを選択する
Colab のメニューで [ランタイム] > [ランタイムのタイプを変更] を選択し、[TPU] を選択します。このコードラボでは、ハードウェア アクセラレーションによるトレーニングをサポートする強力な TPU(Tensor Processing Unit)を使用します。ランタイムへの接続は、初回実行時に自動的に行われます。右上の [接続] ボタンを使用することもできます。
ノートブックの実行
セルを 1 つずつ実行するには、セルをクリックして Shift+Enter キーを押します。[ランタイム] > [すべて実行] でノートブック全体を実行することもできます。
目次
すべてのノートブックには目次があります。左側の黒い矢印を使用して開くことができます。
非表示のセル
一部のセルにはタイトルのみが表示されます。これは 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 コアは、行列乗算を実行する行列乗算ユニット(MXU)と、活性化やソフトマックスなどの他のタスクのためのベクトル処理ユニット(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 に 16,000 個を実装し、この行列乗算を一度に処理できます。
イラスト: MXU シストリック アレイ。コンピューティング要素は乗算累積器です。1 つの行列の値が配列に読み込まれます(赤いドット)。他の行列の値は配列(グレーのドット)を介して流れます。縦線により値が上に移動します。水平線は部分和を伝播します。配列の中をデータが流れるときに、行列乗算の結果が右側から出力されることを確認するのは、ユーザーの演習として行います。
さらに、ドット積が MXU で計算されている間、中間和は隣接するコンピューティング単位間で単純に転送されます。メモリやレジスタ ファイルへの格納や取得も不要です。最終的に、TPU シストリック アレイ アーキテクチャは、行列の乗算の計算において、密度と電力の大幅な利点があるだけでなく、GPU よりも無視できないほどの速度で優れています。
Cloud TPU
Google Cloud Platform で 1 つの「Cloud TPU v2」をリクエストすると、PCI に接続された TPU ボードを備えた仮想マシン(VM)が提供されます。TPU ボードには、4 つのデュアルコア TPU チップが搭載されています。各 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 相互接続を介して接続された TPU ボードとラック。
トレーニング中には、all-reduce アルゴリズムを使用して TPU コア間で勾配が交換されます(all-reduce についてはこちらの説明をご覧ください)。トレーニング対象のモデルは、大規模なバッチサイズでトレーニングすることでハードウェアを利用できます。
イラスト: Google TPU の 2D トロイダル メッシュ 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(アクセラレータ線形代数コンパイラ)と呼ばれるコンパイラが、計算ノードの 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」ユーティリティで作成されたディープ ラーニング 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 オペレーションはサポートされていません。リストはこちらです。ただし、この制限はトレーニング コード(モデルのフォワードパスとバックワード パス)にのみ適用されます。Tensorflow のオペレーションはすべて CPU で実行されるため、データ入力パイプラインでも使用できます。
tf.py_func
は TPU でサポートされていません。
4. データを読み込んでいます
花の写真のデータセットを使用します。目標は、花を 5 種類に分類する方法を学ぶことです。データの読み込みは tf.data.Dataset
API を使用して実行されます。まず、API について説明します。
ハンズオン
次のノートブックを開き、セルを実行(Shift+Enter)し、[作業が必要] ラベルが表示されている箇所の手順に沿って操作してください。
Fun with tf.data.Dataset (playground).ipynb
その他の情報
「花」データセットについて
データセットは 5 つのフォルダで構成されています。各フォルダには 1 種類の花が含まれています。フォルダ名は、sunflowers、daisy、dandelion、tulips、roses です。データは 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 は、すべてのトレーニング関数と評価関数で Dataset を受け入れます。データセットにデータを読み込むと、ニューラル ネットワークのトレーニング データに役立つ共通機能が 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.
次に、各ファイル名に関数を「マッピング」します。通常、この関数はファイルを読み込んでメモリ内の実際のデータにデコードします。
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 枚の画像を読み込むことができます。読み込みが遅すぎます。トレーニングに使用するハードウェア アクセラレータは、このレートの何倍も処理できます。次のセクションに進み、この目標を達成する方法をご確認ください。
ソリューション
こちらがソリューション ノートブックです。行き詰まった場合に使用できます。
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 形式のデータが提供されるため、実行する必要はありません。
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 のリストになります。次のヘルパー関数を使用して、データを 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())
TFRecords からデータを読み取るには、まず保存したレコードのレイアウトを宣言する必要があります。宣言では、固定長のリストまたは可変長のリストとして名前付きフィールドにアクセスできます。
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 からすばやくアクセス
- 😓 TFRecords の書き方。(構文を忘れた場合は、問題ありません。このページをブックマークしておいてください)
- 🤔? TFRecordDataset を使用して TFRecord からデータセットを読み込む
以下のチェックリストを頭の中で確認してください。
6. [情報] ニューラル ネットワーク分類器 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, ... )
高密度ニューラル ネットワーク
これは、画像を分類する最も単純なニューラル ネットワークです。層状に並んだ「ニューロン」で構成されています。第 1 レイヤは入力データを処理し、その出力を他のレイヤにフィードします。各ニューロンが前の層のすべてのニューロンに接続されているため、「密結合」と呼ばれます。
このようなネットワークに画像を入力するには、すべてのピクセルの 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 で、残りはすべて 0 です。
relu: 正規化線形ユニット。ニューロンでよく使用される活性化関数。
シグモイド: 以前は一般的になっていたもう一つの活性化関数で、特殊なケースでも現在でも有用です。
ソフトマックス: ベクトルに対して作用する特別な活性化関数。最大の成分と他のすべての成分の差を増加させ、確率のベクトルとして解釈できるようにベクトルを合計 1 に正規化します。分類システムの最後のステップとして使用されます。
テンソル: 「テンソル」は行列に似ていますが、任意の次元数を持ちます。1 次元テンソルはベクトルです。2 次元テンソルは行列です。テンソルは 3 次元、4 次元、5 次元以上にすることができます。
7. 転移学習
画像分類の問題の場合、密レイヤでは不十分でしょう。畳み込みレイヤと、それらを配置できる多くの方法を学ぶ必要があります。
ショートカットを使うこともできます。完全にトレーニング済みの畳み込みニューラル ネットワークをダウンロードできます。最後のレイヤであるソフトマックス分類ヘッドを切り取り、独自のものに置き換えることができます。トレーニング済みの重みとバイアスはそのまま維持され、追加したソフトマックス レイヤのみを再トレーニングします。この手法は転移学習と呼ばれ、驚くべきことに、ニューラル ネットが事前トレーニングされるデータセットが自分のデータセットに「十分」である限り機能します。
ハンズオン
次のノートブックを開いてセルを実行(Shift+Enter キー)し、「WORK REQUIRED」というラベルが付いている場所をすべて手順に沿って操作してください。
Keras Flowers transfer learning (playground).ipynb
その他の情報
転移学習では、トップクラスの研究者によって開発された高度な畳み込みニューラル ネットワーク アーキテクチャと、膨大な画像データセットでの事前トレーニングの両方を利用できます。この例では、ImageNet でトレーニングされたネットワークから転移学習を行います。ImageNet は、花に近い多くの植物や屋外のシーンを含む画像のデータベースです。
イラスト: トレーニング済みの複雑な畳み込みニューラル ネットワークをブラック ボックスとして使用し、分類ヘッドのみを再トレーニングします。これは転移学習です。このような畳み込みレイヤの複雑な配置がどのように機能するかは、後で説明します。現時点では、他の担当者の問題です。
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% 近くになるはずです。
ソリューション
こちらがソリューション ノートブックです。行き詰まった場合に使用できます。
Keras Flowers transfer learning (solution).ipynb
学習した内容
- 🤔? Keras で分類器を記述する方法
- 🤓 最後のレイヤに Softmax を配置し、交差エントロピー損失を構成
- 🎃? 転移学習
- 🤔? 最初のモデルのトレーニング
- 🧐? トレーニング中の損失と精度を追跡する
以下のチェックリストを頭の中で確認してください。
8. [情報] 畳み込みニューラル ネットワーク
概要
次の段落で太字になっている用語をすべて理解している場合は、次の演習に進みましょう。畳み込みニューラル ネットワークを初めて使用する場合は、以下をお読みください。
図: それぞれ 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 つの「ニューロン」が、画像の小さな領域のみで、そのすぐ上にあるピクセルの加重合計を行います。通常の密集層のニューロンと同様に、バイアスを追加して、その和を活性化関数に渡します。この処理は、同じ重みを使用して画像全体で繰り返されます。密なレイヤでは、各ニューロンに独自の重みがありました。ここでは、重みの単一の「パッチ」が画像の両方向にスライドします(「畳み込み」)。出力には、画像内のピクセル数と同じ数の値が含まれます(ただし、エッジには若干のパディングが必要です)。これは、4x4x3=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'])
9. お客様のカスタム コンバージョン
ハンズオン
畳み込みニューラル ネットワークをゼロから構築してトレーニングします。TPU を使用すると、反復処理を非常に高速に行うことができます。次のノートブックを開いてセルを実行(Shift+Enter キー)し、「WORK REQUIRED」というラベルが付いている場所をすべて手順に沿って操作してください。
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().
です。
ソリューション
こちらがソリューション ノートブックです。行き詰まった場合に使用できます。
Keras_Flowers_TPU (solution).ipynb
学習した内容
- 🤔? 畳み込みレイヤで遊んでいた
- 🤓 マックスプーリング、ストライド、グローバル平均プーリングなどを試しました。
- 😀 TPU で実世界のモデルを高速に反復処理
以下のチェックリストを頭の中で確認してください。
10. [INFO] 最新のコンボリューション アーキテクチャ
概要
イラスト: 畳み込み「モジュール」。この時点で最善の方法は最大プーリング レイヤの後に 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 の畳み込み?
数学的には、「1 x 1」畳み込みは定数の乗算であり、あまり有用な概念ではありません。ただし、畳み込みニューラル ネットワークでは、フィルタは 2D 画像だけでなくデータキューブにも適用されます。したがって、「1x1」フィルタは 1x1 列のデータの加重合計を計算し(図を参照)、データを横にスライドさせると、入力の各チャネルの線形結合が得られます。これは実に便利です。チャネルを個々のフィルタリング操作の結果として考える場合、たとえば「ひげ」のフィルタ、「ひげ」のフィルタ、3 つ目の「ひげ」のフィルタ、そして「1x1」の畳み込み層は、これらの特徴の複数の可能な線形組み合わせを計算することになり、「猫」を探すのに便利です。さらに、1x1 レイヤでは使用する重みが少なくなります。
11. 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 の範囲で指定できます。ネットワークを通じてフィルタの数が徐々に増えるアーキテクチャや、すべての 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% の精度目標が達成できそうにないと感じるかもしれません。次は、もう 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 に減らす必要があります。現時点では、この詳細は気にしないでください。
データの拡張
彩度を左右に反転させるなどの簡単な変換でデータを拡張することで、さらにパーセンテージ ポイントが得られます。
これは、Tensorflow で tf.data.Dataset API を使用して非常に簡単に行えます。データの新しい変換関数を定義します。
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 を使用したデータ拡張
このチェックリストの内容を頭に入れておいてください。
12. 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
学習した内容
- 🤔? 深度分離可能な畳み込み
- 🤓 学習率のスケジュール
- 😈 事前トレーニング済みモデルのファインチューニング。
以下のチェックリストを頭の中で確認してください。
13. 完了
最新の畳み込みニューラル ネットワークを初めて構築し、TPU のおかげで、わずか数分でトレーニングを繰り返して精度を 90% 以上に高めました。
TPU の実践
TPU と GPU は、Google Cloud の Vertex AI で使用できます。
最後に、皆様のフィードバックをお待ちしています。このラボで問題が見つかった場合や、改善が必要だと思われる場合は、お知らせください。フィードバックは GitHub Issues [フィードバック リンク] から送信できます。
|