1. 概要
このチュートリアルは、TensorFlow 2.2 用に更新されました。
この Codelab では、手書きの数字を認識するニューラル ネットワークを構築してトレーニングする方法を学びます。その過程で、ニューラル ネットワークを強化して 99% の精度を達成する過程で、ディープ ラーニングの専門家がモデルを効率的にトレーニングするために使用しているツールも見えてきます。
この Codelab では MNIST データセットを使用します。MNIST は 60,000 桁のラベル付き数字のコレクションで、何世代にもわたる博士号取得者が約 20 年間利用されてきたものです。100 行未満の Python / TensorFlow コードで問題を解決できます。
学習内容
- ニューラル ネットワークの概要とトレーニング方法
- tf.keras を使用して基本的な 1 層ニューラル ネットワークを構築する方法
- レイヤを追加する方法
- 学習率のスケジュールを設定する方法
- 畳み込みニューラル ネットワークの構築方法
- 正則化手法の使用方法: ドロップアウト、バッチ正規化
- 過学習とは
必要なもの
ブラウザだけで、このワークショップは、すべて Google Colaboratory で実施できます。
フィードバック
このラボで問題が見つかった場合や、改善が必要だと思われる点がありましたら、お知らせください。フィードバックは、GitHub の問題 [フィードバック リンク] から対応いたします。
2. Google Colaboratory クイック スタート
このラボでは Google Colaboratory が使用されます。ユーザー側での設定は必要ありません。Chromebook から実行できます。以下のファイルを開き、セルを実行して、Colab ノートブックについての理解を深めてください。
その他の手順は次のとおりです。
GPU バックエンドを選択する
Colab のメニューで [ランタイム] >ランタイム タイプを変更してから、GPU を選択します。ランタイムへの接続は初回実行時に自動的に行われますが、[接続] オプションを使用してボタンをタップします。
ノートブックの実行
セルを 1 つずつ実行するには、セルをクリックして Shift+Enter キーを押します。[ランタイム >すべて実行
目次
すべてのノートブックには目次があります。左側にある黒い矢印をクリックすると開きます。
非表示のセル
一部のセルにはタイトルのみが表示されます。これは Colab 固有のノートブック機能です。それらをダブルクリックすると内部のコードを確認できますが、通常はあまり興味を引かないものです。通常はサポート関数または可視化関数です。内部の関数を定義するには、これらのセルを実行する必要があります。
3. ニューラル ネットワークをトレーニングする
まず、ニューラル ネットワークのトレーニングを確認します。以下のノートブックを開き、すべてのセルを実行してください。コードはまだ注意しないでください。後で説明します。
ノートブックを実行するときは、可視化に集中してください。説明については以下をご覧ください。
トレーニング データ
手書きの数字のデータセットがあり、それぞれの絵が何を表しているかがわかるようにラベル付けされています。つまり、0 から 9 までの数字です。ノートブックに抜粋が表示されます。
これから構築するニューラル ネットワークは、手書きの数字を 10 クラス(0、..、9)に分類します。これは、分類が適切に機能するために正しい値を持つ必要がある内部パラメータに基づいて行われます。この「正しい値」は「ラベル付きデータセット」を必要とするトレーニング プロセスで学習する画像とそれに関連する正解が表示されます
トレーニング済みのニューラル ネットワークが適切に機能するかどうかを判断するには、どうすればよいでしょうか。トレーニング データセットを使用してネットワークをテストするのは不正行為です。このデータセットはすでにトレーニング中に複数回確認されており、非常に高いパフォーマンスを発揮していることは間違いありません。「実世界」を評価するには、トレーニング中に使用されたことのない別のラベル付きデータセットが必要パフォーマンスが向上します。これは「検証データセット」と呼ばれます。
トレーニング
トレーニングが進むにつれて、一度に 1 つのバッチ トレーニング データで内部モデル パラメータが更新され、モデルは手書きの数字を認識しやすくなります。これはトレーニング グラフで確認できます。
右側の「精度」は正しく認識された数字の割合です。トレーニングが進むにつれて値が上がっていきます。これは良いことです。
左側に "loss" が表示されます。トレーニングを進めるために、損失と関数です。これは、システムがどの程度数字を認識し、最小化するかを表します。ご覧のとおり、トレーニングが進むにつれて、トレーニング データと検証データの両方で損失が減少しています。これは良い状態です。これはニューラル ネットワークが学習中であることを意味します。
X 軸は「エポック」の数を表すデータセット全体に対する反復処理です。
予測
モデルをトレーニングしたら、それを使用して手書きの数字を認識できます。次の可視化は、ローカル フォントからレンダリングされた数桁(最初の行)でレンダリングされた数桁、次に検証データセットの 10,000 桁の数字でのパフォーマンスを示しています。予測されたクラスが各数字の下に表示されます。正しくない場合は赤色で表示されます。
ご覧のとおり、この初期モデルはあまり良い状態ではありませんが、一部の数字を正しく認識しています。最終的な検証精度は約 90% で、最初のシンプルなモデルとしてはそれほど悪くありませんが、それでも 10, 000 桁のうち 1,000 桁の検証が欠けています。表示可能な量ははるかに多いため、すべての答えが間違っているように見えます(赤)。
テンソル
データは行列形式で保存されます。28×28 ピクセルのグレースケール画像は、28×28 の 2 次元マトリクスに収まります。一方、カラー画像の場合は、より多くのサイズが必要です。ピクセルごとに 3 つの色値(Red、Green、Blue)があるため、次元 [28, 28, 3] の 3 次元テーブルが必要になります。また、128 個のカラー画像のバッチを保存するには、次元 [128, 28, 28, 3] の 4 次元テーブルが必要です。
こうした多次元テーブルは「テンソル」と呼ばれ、そのディメンションのリストは「形状」です。
4. [情報]: ニューラル ネットワーク入門編
概要
次の段落で太字で示されている用語をすべて理解している場合は、次の演習に進みましょう。ディープ ラーニングを始めたばかりの方は、ぜひ本をお読みください。
一連のレイヤとして構築されたモデルの場合、Keras では Sequential API を使用できます。たとえば、3 つの Dense レイヤを使用する画像分類器は、Keras で次のように記述できます。
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=[28, 28, 1]),
tf.keras.layers.Dense(200, activation="relu"),
tf.keras.layers.Dense(60, activation="relu"),
tf.keras.layers.Dense(10, activation='softmax') # classifying into 10 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, ... )
単一の Dense レイヤ
MNIST データセットにおける手書きの数字は、28x28 ピクセルのグレースケール画像です。それらを分類するための最も単純なアプローチは、1 層ニューラルネットワークの入力として 28x28=784 ピクセルを使用することです。
ニューラル ネットワーク内の各「ニューロン」は、そのすべての入力の加重合計を行い、「バイアス」と呼ばれる定数を追加します。その後、結果を非線形の「活性化関数」に通します。「重み」と「バイアス」は、トレーニングによって決定されるパラメータです。最初はランダムな値で初期化されます。
上の図は、数字を 10 クラス(0 ~ 9)に分類したいので、10 個の出力ニューロンを持つ 1 層ニューラル ネットワークを表しています。
行列の乗算あり
次に、画像のコレクションを処理するニューラル ネットワーク レイヤを行列乗算で表現する方法を示します。
重み行列 W の最初の列の重みを使用して、最初の画像のすべてのピクセルの重み付き合計を計算します。この合計が最初のニューロンに対応します。2 番目の重み列を使用して、2 番目のニューロンについても同様に、10 番目のニューロンまで同様の処理を行います。残りの 99 枚の画像についても、この操作を繰り返すことができます。100 枚の画像を含む行列を X と呼ぶと、100 枚の画像に対して計算された 10 個のニューロンの加重合計はすべて、行列の乗算である X.W になります。
各ニューロンはバイアス(定数)を加算する必要があります。ニューロンが 10 個あるため、バイアス定数は 10 個です。この 10 個の値のベクトルを b と呼びます。以前に計算した行列の各行に追加する必要があります。「ブロードキャスト」という魔法を使って単純なプラス記号を使用して記述します。
最後に活性化関数(たとえば「softmax」(後述)して、1 層のニューラル ネットワークを表す式を 100 個の画像に適用します。
Keras の場合
Keras のような高水準のニューラル ネットワーク ライブラリでは、この式を実装する必要はありません。しかし、ニューラル ネットワーク レイヤは単なる乗算と足し算で成り立っていることを理解しておくことが重要です。Keras では、密レイヤは次のように記述します。
tf.keras.layers.Dense(10, activation='softmax')
さらに詳しく
ニューラル ネットワーク層を連結するのは簡単です。第 1 レイヤでは、ピクセルの加重合計を計算します。後続のレイヤは、前のレイヤの出力の加重合計を計算します。
ニューロンの数以外の違いは、活性化関数の選択だけです。
活性化関数: RELU、ソフトマックス、シグモイド
通常は "relu" を最後の層を除くすべての層で活性化関数を分類器の最後のレイヤでは、「softmax」を使用します。支援します
ここでも「ニューロン」はすべての入力の加重合計を計算し、「bias」と呼ばれる値を加算します。活性化関数で結果をフィードします
最も一般的な活性化関数は、正規化線形ユニットの「RELU」と呼ばれます。上のグラフからわかるように、これは非常にシンプルな関数です。
ニューラル ネットワークの従来の活性化関数は「シグモイド」ですが、「relu」はほぼすべての場所で収束特性が向上することがわかっており、現在は採用されています。
分類のためのソフトマックスの有効化
ニューラル ネットワークの最後のレイヤには、手書きの数字を 10 クラス(0、..9)に分類するため、10 個のニューロンがあります。これは、この数字が 0、a 1、a 2 などになる確率を表す 0 から 1 までの 10 個の数字を出力するはずです。最後のレイヤでは、softmax と呼ばれる活性化関数を使用します。
ベクトルにソフトマックスを適用するには、各要素の指数を受け取り、そのベクトルを正規化(通常は L1 で除算)します。正規化された値の合計が 1 になり、確率として解釈できるようにします。
アクティベーション前の最後のレイヤの出力は、「ロジット」と呼ばれることもあります。このベクトルが L = [L0, L1, L2, L3, L4, L5, L6, L7, L8, L9] の場合:
交差エントロピー損失
ニューラル ネットワークが入力画像から予測を生成するようになったので、予測の精度、すなわちネットワークが教える情報と正解(しばしば「ラベル」と呼ばれる)の間の距離を測定する必要があります。データセット内のすべての画像に正しいラベルが付けられていることを思い出してください。
どのような距離でもかまいませんが、分類問題では、いわゆる「交差エントロピー距離」を使用して最も効果的な方法です。これをエラーまたは「損失」と呼ぶことにします。関数:
勾配降下法
トレーニングニューラル ネットワークとは、交差エントロピー損失関数が最小になるように、トレーニング用の画像とラベルを使って重みとバイアスを調整することを意味します。仕組みは次のとおりです。
交差エントロピーは、トレーニング画像の重み、バイアス、ピクセル、および既知のクラスの関数です。
すべての重みとすべてのバイアスに対して交差エントロピーの偏導関数を計算すると、与えられた画像、ラベル、重みとバイアスの現在値に対して計算される「勾配」が得られます。何百万もの重みとバイアスが存在する可能性があるため、勾配の計算は大変な作業に思えます。幸いなことに、TensorFlow がそれを自動でやってくれます。勾配の数学的特性は、「上」を指すことです。交差エントロピーが低いところに目的のため、逆方向に移動します。重みとバイアスは、勾配のごく一部で更新されます。その後、トレーニング ループ内で、次のトレーニング画像とラベルのバッチを使用して、同じことを何度も繰り返します。うまくいけば、交差エントロピーが最小になるところに収束することが期待されます。ただし、この最小値が一意であるという保証はありません。
ミニバッチ処理とモメンタム
1 つのサンプル画像についてグラデーションを計算し、重みとバイアスをすぐに更新することもできますが、たとえば 128 枚の画像でバッチにすることで、異なるサンプル画像による制約をより適切に表現するグラデーションが得られるため、より速く解に収束する可能性が高くなります。ミニバッチのサイズは調整可能なパラメータです。
この手法は「確率的勾配降下法」とも呼ばれます。には、より現実的な利点がもう一つあります。バッチで作業することは、より大きな行列で作業することも意味し、通常は GPU や TPU での最適化が容易になります。
ただし、収束はやや混沌としたものになり、勾配ベクトルがすべてゼロになると収束が停止することさえあります。最小値を見つけたということでしょうか?必ずしも違反警告を受けるとは限りません。勾配コンポーネントは、最小値または最大値で 0 にできます。何百万もの要素を含む勾配ベクトルで、それらがすべてゼロの場合、すべてのゼロが最小値に対応し、どのゼロでも最大ポイントに対応する確率はかなり小さくなります。さまざまな次元の空間では、サドルポイントは非常に一般的なものなので、それだけでは不十分です。
イラスト: サドルポイント。勾配は 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 次元以上にすることができます。
5. コードを詳しく見ていきましょう。
学習用ノートブックに戻り、今度はコードを読みましょう。
このノートブックのすべてのセルを確認してみましょう。
セル「Parameters」
バッチサイズ、トレーニング エポックの数、データファイルの場所はここで定義されます。データファイルは Google Cloud Storage(GCS)バケットでホストされるため、アドレスは gs://
で始まります。
セル「Imports」
TensorFlow や可視化用の matplotlib など、必要なすべての Python ライブラリがここにインポートされています。
セル「visualization utilities [RUN ME]****」
このセルには、あまり興味のない可視化コードが含まれています。デフォルトでは閉じていますが、時間があるときにダブルクリックしてコードを開いて確認することができます。
セル「tf.data.Dataset: parse files and prepare training and validation datasets」
このセルでは、tf.data.Dataset API を使用して、データファイルから MNIST データセットを読み込みました。このセルに多くの時間を費やす必要はありません。tf.data.Dataset API について興味がある方は、TPU スピードのデータ パイプラインのチュートリアルをご覧ください。現時点の基本は次のとおりです。
MNIST データセットの画像とラベル(正解)は、固定長のレコードとして 4 つのファイルに保存されます。ファイルは、専用の固定レコード関数を使用して読み込むことができます。
imagedataset = tf.data.FixedLengthRecordDataset(image_filename, 28*28, header_bytes=16)
これで、画像バイトのデータセットが作成されました。これらは画像にデコードする必要があります。そのための関数を定義します。画像は圧縮されていないため、関数で何もデコードする必要はありません(decode_raw
は基本的に何もしません)。その後、画像は 0 ~ 1 の浮動小数点値に変換されます。ここではこれを 2D 画像として再形成することもできますが、実際にはサイズ 28*28 のフラットなピクセル配列として維持します。これは最初の Dense レイヤで期待されるものです。
def read_image(tf_bytestring):
image = tf.io.decode_raw(tf_bytestring, tf.uint8)
image = tf.cast(image, tf.float32)/256.0
image = tf.reshape(image, [28*28])
return image
.map
を使用してこの関数をデータセットに適用し、画像のデータセットを取得します。
imagedataset = imagedataset.map(read_image, num_parallel_calls=16)
ラベルに対しても同じ種類の読み取りとデコードを行い、画像とラベルを一緒に .zip
します。
dataset = tf.data.Dataset.zip((imagedataset, labelsdataset))
これで、ペア(画像、ラベル)のデータセットができました。モデルが期待していることです。トレーニング関数で使用する準備がまだできていません。
dataset = dataset.cache()
dataset = dataset.shuffle(5000, reshuffle_each_iteration=True)
dataset = dataset.repeat()
dataset = dataset.batch(batch_size)
dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)
tf.data.Dataset API には、データセットを準備するために必要なユーティリティ関数がすべて用意されています。
.cache
は、データセットを RAM にキャッシュします。これは小さなデータセットなので、問題なく機能します。.shuffle
は、5,000 要素からなるバッファでこれをシャッフルします。トレーニング データを適切にシャッフルすることが重要です。.repeat
は、データセットをループします。トレーニングは複数回行われます(複数のエポック)。.batch
は、複数の画像とラベルをミニバッチに pull します。最後に、.prefetch
は現在のバッチを GPU でトレーニングしている間に、CPU を使用して次のバッチを準備できます。
検証データセットも同様の方法で準備されます。これで、モデルを定義し、このデータセットを使用してトレーニングする準備が整いました。
セル「Keras Model」
モデルはすべてまっすぐなレイヤのシーケンスであるため、tf.keras.Sequential
スタイルを使用してモデルを作成できます。最初は 1 つの Dense レイヤです。手書きの数字を 10 クラスに分類しているため、10 個のニューロンがあります。「ソフトマックス」を使用します。このレイヤが分類器の最後のレイヤであるため、
Keras モデルでは入力の形状も把握する必要があります。tf.keras.layers.Input
を使用して定義できます。ここで、入力ベクトルは、長さ 28×28 のピクセル値のフラット ベクトルです。
model = tf.keras.Sequential(
[
tf.keras.layers.Input(shape=(28*28,)),
tf.keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='sgd',
loss='categorical_crossentropy',
metrics=['accuracy'])
# print model layers
model.summary()
# utility callback that displays training curves
plot_training = PlotTraining(sample_rate=10, zoom=1)
モデルの構成は、Keras で model.compile
関数を使用して行います。ここでは、基本的なオプティマイザーの 'sgd'
(確率的勾配降下法)を使用します。分類モデルには、Keras で 'categorical_crossentropy'
と呼ばれる交差エントロピー損失関数が必要です。最後に、正しく分類された画像の割合である 'accuracy'
指標を計算するようモデルに指示します。
Keras には、作成したモデルの詳細を出力する非常に便利な model.summary()
ユーティリティが備わっています。親切な講師が PlotTraining
ユーティリティ(「ビジュアリゼーション ユーティリティ」セルで定義)を追加しました。このユーティリティは、トレーニング中にさまざまなトレーニング曲線を表示します。
セル「モデルのトレーニングと検証」
ここで model.fit
を呼び出して、トレーニング データセットと検証データセットの両方を渡すことでトレーニングを行います。デフォルトでは、Keras は各エポックの終了時に検証を 1 回実行します。
model.fit(training_dataset, steps_per_epoch=steps_per_epoch, epochs=EPOCHS,
validation_data=validation_dataset, validation_steps=1,
callbacks=[plot_training])
Keras では、トレーニング中にコールバックを使用してカスタムの動作を追加できます。このようにして、このワークショップでは動的に更新されるトレーニング プロットを実装しました。
セル「予測を可視化する」
モデルのトレーニングが完了したら、model.predict()
を呼び出して予測を取得できます。
probabilities = model.predict(font_digits, steps=1)
predicted_labels = np.argmax(probabilities, axis=1)
ここでは、テストとして、ローカル フォントからレンダリングされた印刷された数字のセットを用意しました。ニューラル ネットワークは最後の「ソフトマックス」から 10 個の確率のベクトルを返すことを思い出してください。ラベルを取得するには、確率が最も高いものを見つける必要があります。numpy ライブラリの np.argmax
がそれを行います。
axis=1
パラメータが必要な理由を理解するため、ここでは 128 個の画像をバッチで処理したため、このモデルは 128 個の確率ベクトルを返すことになります。出力テンソルの形状は [128, 10] です。各画像に対して返される 10 個の確率で argmax を計算するため、axis=1
(最初の軸は 0)になります。
この単純なモデルはすでに数字の 90% を認識しています。悪くはありませんが、これを大幅に改善します。
6. レイヤの追加
認識精度を高めるために、ニューラル ネットワークにレイヤを追加します。
最後のレイヤの活性化関数はソフトマックスが分類に最適であるためです。ただし、中間層では、最も古典的な活性化関数であるシグモイドを使用します。
たとえば、モデルは次のようになります(カンマを忘れないでください。tf.keras.Sequential
はレイヤのカンマ区切りのリストを受け取ります)。
model = tf.keras.Sequential(
[
tf.keras.layers.Input(shape=(28*28,)),
tf.keras.layers.Dense(200, activation='sigmoid'),
tf.keras.layers.Dense(60, activation='sigmoid'),
tf.keras.layers.Dense(10, activation='softmax')
])
[summary] を確認します。モデルのことです。少なくとも 10 倍のパラメータが追加されました。10 倍向上しているはずです。でも、どうやって違うの ...
死者も屋根を飛び越えたようだ。不正解です。
7. ディープ ネットワークに対する特別な注意
80 年代から 90 年代にニューラル ネットワークを設計していたので、今度はニューラル ネットワークを経験したばかりです。当然のことながら、彼らがアイデアをあきらめ、いわゆる「AI の冬」が到来しました。実際、レイヤを追加するにつれ、ニューラル ネットワークの収束はますます困難になります。
実際、多数のレイヤ(現在は 20、50、100 のレイヤ)を持つディープ ニューラル ネットワークは、それらを収束させるための数学的なダーティなトリックがいくつか与えられれば、非常にうまく機能することがわかりました。こうした単純な手法が発見されたことが、2010 年代にディープ ラーニングが再発した理由の一つです。
RELU の有効化
シグモイド活性化関数は、実はディープ ネットワークで大きな問題となります。0 から 1 までのすべての値が押しつぶされ、これを繰り返すと、ニューロンの出力とその勾配が完全に消えてしまう可能性があります。これは歴史的な理由から言及されていますが、最新のネットワークでは次のような RELU(正規化線形ユニット)を使用しています。
一方、レルーは少なくとも右側に 1 の導関数を持ちます。RELU 活性化により、一部のニューロンからの勾配がゼロにできる場合でも、他のニューロンから明確なゼロ以外の勾配を与えるが常に存在するようになり、トレーニングを適切なペースで継続できます。
優れたオプティマイザー
ここに示すような非常に高次元な空間(1 万単位の重みとバイアス)では、「サドルポイント」と呼ばれる頻繁に発生します局所的な最小値ではないものの、勾配が 0 であるにもかかわらず、勾配降下オプティマイザーがそこで停止したままになるポイントです。TensorFlow には使用可能なオプティマイザーが豊富に用意されています。オプティマイザーには、ある程度の慣性によって機能し、サドルポイントを安全に通過できるものもあります。
ランダム初期化
トレーニングの前に重みバイアスを初期化する技術自体は研究分野であり、このトピックに関する論文は多数発表されています。Keras で使用できるすべてのイニシャライザは、こちらで確認できます。幸いなことに、Keras はデフォルトで適切な処理を行い、ほぼすべてのケースで最適な 'glorot_uniform'
イニシャライザを使用します。
Keras はすでに適切な処理を行っているため、ユーザーが行う必要はありません。
ナニ ???
交差エントロピー式には対数が含まれ、log(0) は数値ではありません(NaN、必要に応じて数値クラッシュです)。交差エントロピーの入力を 0 にできますか?入力はソフトマックスから取得されます。ソフトマックスは基本的に指数であり、指数はゼロではありません。だから安全だ!
本当にそうでしょうか美しい数学の世界では安全ですが、コンピュータの世界では、float32 形式で表される exp(-150) はゼロになり、交差エントロピーがクラッシュします。
幸い、Keras がこれに対処し、ソフトマックスとそれに続く交差エントロピーを特に慎重に計算して数値の安定性を確保し、厄介な NaN を回避するために、ユーザーが行う操作はありません。
成功?
今度は 97% の精度が得られるはずです。このワークショップの目標は、99% を大幅に上回ることです。この調子で続けましょう。
行き詰まっている場合は、この時点での解決策は次のとおりです。
8. 学習率の減衰
もっと速くトレーニングできるでしょうか?Adam オプティマイザーのデフォルトの学習率は 0.001 です。増やしてみましょう。
速度を速くしてもあまり役に立たないと思います。ノイズは何でしょう?
トレーニング曲線はかなりノイズが多く、2 つの検証曲線が上下に跳ね上がっています。処理が速すぎるということです。前の速度に戻すこともできますが、もっと良い方法があります。
この解決策は、迅速に起動し、学習率を指数関数的に減衰させることです。Keras では、この処理に tf.keras.callbacks.LearningRateScheduler
コールバックを使用します。
コピーと貼り付けに便利なコード:
# lr decay function
def lr_decay(epoch):
return 0.01 * math.pow(0.6, epoch)
# lr schedule callback
lr_decay_callback = tf.keras.callbacks.LearningRateScheduler(lr_decay, verbose=True)
# important to see what you are doing
plot_learning_rate(lr_decay, EPOCHS)
作成した lr_decay_callback
を必ず使用してください。これを model.fit
のコールバックのリストに追加します。
model.fit(..., callbacks=[plot_training, lr_decay_callback])
この小さな変化がもたらす影響は計り知れません。ノイズの大部分がなくなり、テストの精度が持続的に 98% を超えていることがわかります。
9. ドロップアウト、過学習
モデルはうまく収束しているようです。さらに掘り下げてみましょう。
お役に立ちましたか?
実際には精度は 98% のままです。検証の損失を見てみましょう。どんどん上がってる!学習アルゴリズムはトレーニング データのみに基づいて動作し、それに応じてトレーニングの損失を最適化します。検証データはまったく表示されないため、しばらくすると検証の損失に影響が及ばず、その低下が止まり、場合によっては元に戻ることもあります。
これはモデルの現実世界の認識機能にすぐには影響しませんが、何度も反復処理を行えなくなるため、一般的にはトレーニングがもはやプラスの効果を得ていないことを示しています。
この差異は通常「過学習」と呼ばれます。見つかったら、「dropout」と呼ばれる正則化手法を適用してみます。ドロップアウト手法では、トレーニングの反復処理ごとにランダムなニューロンが打ち出されます。
成果
ノイズが再表示されます(ドロップアウトの仕組みを考えると当然ですが)。検証の損失は今は増えていないようですが、全体的にはドロップアウトなしの場合より高くなります。検証の精度も若干低下しました。期待外れの結果です。
ドロップアウトが適切な解決策ではなかったか、「過学習」が発生しているようですより複雑な概念であり、その原因の一部は「ドロップアウト」には当てはまらない修正しますか?
「過学習」とは過学習は、ニューラル ネットワークがトレーニング サンプルでは機能するものの、実世界のデータではうまく機能しないような方法で「正しく」学習した場合に発生します。ドロップアウトのような正則化手法を使うとより適切な方法で学習させることができますが、過学習はより深い根底を持ちます。
基本的な過学習は、目の前の問題に対してニューラル ネットワークに自由度が多すぎる場合に発生します。非常に多くのニューロンがあり、ネットワークがトレーニング画像をすべて保存し、パターン マッチングによって認識できると想像してみてください。実世界のデータでは完全に失敗します。ニューラル ネットワークは、トレーニング中に学習することを無理に一般化しなければならないように、ある程度制約する必要があります。
トレーニング データがほとんどない場合、小規模なネットワークでも心から学習することができ、「過学習」が発生します。一般的に、ニューラル ネットワークをトレーニングするには、常に大量のデータが必要です。
最後に、本書ですべてを完了し、さまざまなサイズのネットワークをテストして、自由度の制約、ドロップアウトの適用、多くのデータでのトレーニングを行っても、改善できないようなパフォーマンス レベルで停滞している可能性があります。つまり、現在のニューラル ネットワークでは、この例のようにデータからより多くの情報を抽出できません。
単一のベクトルにフラット化された画像の使い方を覚えていますか?不正解です。手書きの数字は図形でできています。ピクセルをフラット化するときに図形情報は破棄しました。しかし、形状情報を利用できるタイプのニューラル ネットワークがあります。それは畳み込みネットワークです。ぜひお試しください。
行き詰まっている場合は、この時点での解決策は次のとおりです。
10. [情報] 畳み込みネットワーク
概要
次の段落で太字で示されている用語をすべて理解している場合は、次の演習に進みましょう。畳み込みニューラル ネットワークを使い始めたばかりの方は、この後をお読みください。
イラスト: 4x4x3=48 の学習可能な重みから成る 2 つの連続したフィルタで画像をフィルタしています。
Keras での単純な畳み込みニューラル ネットワークは次のようになります。
model = tf.keras.Sequential([
tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1)),
tf.keras.layers.Conv2D(kernel_size=3, filters=12, activation='relu'),
tf.keras.layers.Conv2D(kernel_size=6, filters=24, strides=2, activation='relu'),
tf.keras.layers.Conv2D(kernel_size=6, filters=32, strides=2, activation='relu'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(10, activation='softmax')
])
畳み込みネットワークの層には、1 つの「ニューロン」がは、画像の小さな領域に対してのみ、そのすぐ上にあるピクセルの加重合計を行います。通常の密レイヤのニューロンと同様に、バイアスを加え、活性化関数を通じて合計をフィードします。この処理は、同じ重みを使用して画像全体で繰り返されます。密な層では、各ニューロンに独自の重みがあったことを思い出してください。ここでは、単一の「パッチ」がの重みが画像上を両方向にスライドする(「畳み込み」)。出力には、画像内のピクセルと同じ数の値が含まれます(ただし、端にはパディングが必要です)。フィルタリング オペレーションです。上の図では、4x4x3=48 の重みのフィルタを使用しています。
ただし、48 個の重みでは不十分です。自由度をさらに広げるには、新しい重みのセットで同じ操作を繰り返します。これにより、新しいフィルタ出力のセットが生成されます。ここでは「チャンネル」とします。出力の 1 対 1 です。
2 組(または複数)の重みのセットは、新しい次元を追加することで 1 つのテンソルとして合計できます。これにより、畳み込みレイヤの重みテンソルの一般的な形状がわかります。入力チャンネルと出力チャンネルの数はパラメータであるため、畳み込みレイヤのスタックとチェーンを開始できます。
イラスト: 畳み込みニューラル ネットワークが「立方体」を変換する他の「キューブ」に分割できます。可視化できます
ストライド畳み込み、最大プーリング
2 または 3 のストライドで畳み込みを実行すると、結果として得られるデータキューブを水平方向に縮小することもできます。一般的な方法は次の 2 つです。
- ストライド畳み込み: 上記のスライディング フィルタで、ストライドが 1 より大きい
- 最大プーリング: MAX 演算を適用するスライディング ウィンドウ(通常は 2x2 パッチで、2 ピクセルごとに繰り返される)
イラスト: 計算ウィンドウを 3 ピクセル分スライドすると、出力値が少なくなります。ストライド畳み込みまたは最大プーリング(2 ストライドでスライドする 2x2 ウィンドウの最大)は、データキューブを水平方向に縮小する方法です。
最後のレイヤ
最後の畳み込み層の後、データは「立方体」の形式になります。最後の Dense レイヤに通す方法は 2 つあります。
1 つ目は、データのキューブをフラット化してベクトルにしてから、ソフトマックス レイヤに渡すことです。場合によっては、ソフトマックス レイヤの前に Dense レイヤを追加することもできます。これは、重みの数という点でコストが大きくなる傾向があります。畳み込みネットワークの最後にある密レイヤには、ニューラル ネットワーク全体の重みの半分以上が含まれる場合があります。
高価な高密度レイヤを使用する代わりに、受信データの「キューブ」を分割することもできます。それらの値を平均し、ソフトマックス活性化関数にフィードします。この方法で分類ヘッドを作成する場合、重みは 0 です。Keras には、そのためのレイヤ tf.keras.layers.GlobalAveragePooling2D()
があります。
次のセクションに進み、当面の問題に対する畳み込みネットワークを構築しましょう。
11. 畳み込みネットワーク
手書き数字認識用の畳み込みネットワークを構築しましょう。上部に 3 つの畳み込み層、下部に従来のソフトマックス読み出し層を使用し、これらを 1 つの全結合層で接続します。
2 番目と 3 番目の畳み込みレイヤのストライドが 2 であることから、出力値の数が 28x28 から 14x14、そして 7x7 に減少する理由がわかります。
Keras コードを記述しましょう。
最初の畳み込みレイヤの前に特別な注意が必要です。確かに、それは 3D の「キューブ」データセットはここまでで密レイヤ用に設定されていますが、画像のすべてのピクセルはベクトルに平坦化されています。それらを 28x28x1 の画像(グレースケール画像の場合は 1 チャンネル)に再形成する必要があります。
tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1))
先ほどの tf.keras.layers.Input
レイヤの代わりに、この行を使用できます。
Keras における「relu」で活性化される畳み込みレイヤの構文は次のとおりです。
tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu')
ストライド畳み込みの場合、次のようになります。
tf.keras.layers.Conv2D(kernel_size=6, filters=24, padding='same', activation='relu', strides=2)
データの立方体をベクトルにフラット化して Dense レイヤで使用できるようにするには:
tf.keras.layers.Flatten()
Dense レイヤでは、構文は変わっていません。
tf.keras.layers.Dense(200, activation='relu')
モデルは 99% の精度の壁を破ったか?もう少しです...検証の損失曲線を見てくださいご興味はおありでしょうか。
予測も確認します。初めて、10,000 のテスト桁のほとんどが正しく認識されるはずです。誤検出は約 4 と 1/2 行しか残っていない(1 万行中約 110 桁)
行き詰まっている場合は、この時点での解決策は次のとおりです。
12. 再ドロップアウト
以前のトレーニングでは明らかに過学習の兆候が見られました(精度は 99% に達していません)。もう一度ドロップアウトしてみますか?
今回はいかがでしたか?
今回はドロップアウトがうまくいったようです。検証の損失はもう増大しておらず、最終的な精度は 99% をはるかに超えるはずです。お疲れさまでした
初めてドロップアウトを適用しようとしたときは、過学習の問題があると思ったのですが、実際はニューラル ネットワークのアーキテクチャに問題があったのです。畳み込みレイヤなしではこれ以上のことは進められません。また、ドロップアウトでは何もできません。
今回は、過学習が問題の原因であり、ドロップアウトが実際に役に立ったようです。トレーニングの損失曲線と検証の損失曲線の間にずれが生じる原因は数多くあり、検証の損失が徐々に広がっていきます。過学習(自由度が多すぎる、ネットワークでの不適切な使用)は、その一つにすぎません。データセットが小さすぎる場合やニューラル ネットワークのアーキテクチャが十分でない場合、損失曲線でも同様の動作が見られることがありますが、ドロップアウトは役に立ちません。
13. バッチ正規化
最後に、バッチ正規化を追加してみましょう。
これが理論上、いくつかのルールを覚えておいてください。
とりあえず、本をざっと見て、各ニューラル ネットワーク レイヤにバッチノルムレイヤを追加しましょう(最後のレイヤは除く)。最後の「ソフトマックス」には追加しないレイヤです。それは役に立たないでしょう。
# Modify each layer: remove the activation from the layer itself.
# Set use_bias=False since batch norm will play the role of biases.
tf.keras.layers.Conv2D(..., use_bias=False),
# Batch norm goes between the layer and its activation.
# The scale factor can be turned off for Relu activation.
tf.keras.layers.BatchNormalization(scale=False, center=True),
# Finish with the activation.
tf.keras.layers.Activation('relu'),
現在の精度はいかがですか?
少し微調整(BATCH_SIZE=64、学習率の減衰パラメータ 0.666、Dense レイヤでのドロップアウト率 0.3)と少し運を加えると、99.5% になります。学習率とドロップアウトの調整を「ベスト プラクティス」に従って行ったバッチノルムを使用する場合:
- バッチノルムはニューラル ネットワークの収束に役立ち、通常はトレーニングを高速化できます。
- バッチノルムは正則化器です。通常は、使用するドロップアウトの数を減らすことも、まったく使用しないことも可能です。
ソリューション ノートブックでは 99.5% のトレーニングが実行されています。
14. パワフルなハードウェアを使ったクラウドでのトレーニング: AI Platform
GitHub の mlengine フォルダには、クラウド対応バージョンのコードと Google Cloud AI Platform での実行手順が記載されています。この部分を実行する前に、Google Cloud アカウントを作成し、課金を有効にする必要があります。このラボを完了するために必要なリソースは数ドル未満です(1 つの GPU で 1 時間のトレーニング時間を想定)。アカウントを準備するには:
- Google Cloud Platform プロジェクトを作成します(http://cloud.google.com/console)。
- 課金の有効化
- GCP コマンドライン ツールをインストールします(GCP SDK はこちら)。
- Google Cloud Storage バケットを作成します(リージョン
us-central1
に配置します)。これは、トレーニング コードをステージングし、トレーニング済みモデルを保存するために使用されます。 - 必要な API を有効にし、必要な割り当てをリクエストします(トレーニング コマンドを 1 回実行すると、何を有効にするかを示すエラー メッセージが表示されます)。
15. 完了
初めてのニューラル ネットワークを構築して、99% の精度までトレーニングしました。その過程で学習した手法は MNIST データセットに固有のものではなく、実際にはニューラル ネットワークを扱う際に広く使用されています。別れの贈り物の「崖のメモ」アニメ風のカードです。これを使用して、学習した内容を復習できます。
次のステップ
- 完全接続された畳み込みネットワークの後に、再帰型ニューラル ネットワークを学習するはずです。
- トレーニングや推論を分散インフラストラクチャ上のクラウドで実行できるように、Google Cloud には AI Platform が用意されています。
- ぜひフィードバックをお寄せください。このラボで問題が見つかった場合や、改善が必要だと思われる点がありましたら、お知らせください。フィードバックは、GitHub の問題 [フィードバック リンク] から対応いたします。
著者: Martin GörnerTwitter: @martin_gorner |
このラボ内のすべての漫画画像の著作権: alexpokusay / 123RF ストックフォト