博士号のない TensorFlow、Keras、ディープ ラーニング

この Codelab では、手書きの数字を認識するニューラル ネットワークを構築してトレーニングする方法を学びます。同時に、99% の精度を得るためにニューラル ネットワークを強化しながら、ディープ ラーニングの専門家がモデルのトレーニングを効率的に行うためのツールも紹介します。

この Codelab では MNIST データセットを使用します。このデータセットは 60,000 個のラベル付き数字のコレクションで、何十年にもわたって PhD をほぼ 20 年間にわたって維持してきました。100 行未満の Python / TensorFlow コードで問題を解決できます。

学習内容

  • ニューラル ネットワークの概要とトレーニング方法
  • tf.keras で基本的な 1 層ニューラル ネットワークを構築する方法
  • レイヤを追加する方法
  • 学習率のスケジュールを設定する方法
  • 畳み込みニューラル ネットワークを構築する方法
  • 正規化手法の使用方法: ドロップアウト、バッチ正規化
  • 過学習とは

必要なもの

ブラウザだけ。このワークショップは、すべて Google Colaboratory アカウントで実施できます。

Feedback

このラボで何か問題が見つかった場合や、改善が必要と思われる点がありましたらお知らせください。Google では、GitHub の問題を通じてフィードバックに対処します [feedback link]。

このラボでは Google Colaboratory を使用します。お客様側での設定は必要ありません。Chromebook から実行できます。下のファイルを開いてセルを実行し、Colab ノートブックに慣れてください。

Welcome to Colab.ipynb

追加の手順は以下のとおりです。

GPU バックエンドの選択

Colab メニューで、[Runtime > Change runtime type] を選択してから [GPU] を選択します。ランタイムの接続は初回実行時に自動的に行われますが、右上の [接続] ボタンを使用することもできます。

ノートブックの実行

セルを 1 つずつクリックして Shift+Enter キーを押して、セルを 1 つずつ実行します。[Runtime > Run all] を実行することで、ノートブック全体を実行することもできます。

目次

すべてのノートブックには目次があります。左側の黒い矢印を使用して開くことができます。

非表示のセル

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

まず、ニューラル ネットワークのトレーニングを確認します。以下のノートブックを開いて、すべてのセルを実行してください。コードにはまだ注意を払わないでください。後ほど説明します。

keras_01_mnist.ipynb

ノートブックを実行するときは、可視化に注目してください。以下の説明を参照してください。

トレーニング データ

手書きの数字のデータセットには、各画像の表現、つまり 0 ~ 9 の数字が記載されています。ノートブックに抜粋が表示されます。

構築するニューラル ネットワークは、手書きの数字を 10 のクラス(0、..、9)に分類します。これは、分類が適切に機能するように、正しい値を必要とする内部パラメータに基づいて行われます。この「正しい値」は、トレーニング プロセスを通じて学習されます。このトレーニング プロセスでは、「ラベル付けされたデータセット」と画像および関連する正解が必要になります。

トレーニング済みのニューラル ネットワークのパフォーマンスが高いかどうかを確認する方法トレーニング データセットを使用してネットワークをテストする場合は、注意が必要です。すでにデータセットをトレーニング中に複数回行っていたため、きわめてパフォーマンスが高いことがわかっています。ネットワークの実際のパフォーマンスを評価するには、トレーニング中に見たことのない別のラベル付けされたデータセットが必要です。これは検証データセットと呼ばれます

トレーニング

トレーニングが進むにつれ、一度に 1 つのトレーニング データがバッチ処理されると、内部モデル パラメータが更新され、手書きの数字を認識するモデルが改善されていきます。これはトレーニング グラフで確認できます。

右側の「正確性」は、認識された数字のパーセンテージです。トレーニングが進むにつれて向上していきます。これは良いことです。

左側に「損失」があります。トレーニングを進めるために、システムがどれだけ低い数値を認識するかを表す「損失」関数を定義し、それを最小化しようとします。トレーニングが進むにつれて、トレーニング データと検証データの両方で損失が減少することがわかります。これは、ニューラル ネットワークが学習中であることを意味します。

X 軸は「エポック」またはデータセット全体の反復回数を表します。

予測

モデルがトレーニングされると、そのモデルを使用して手書きの数字を認識できるようになります。次のグラフでは、ローカル フォント(1 行目)から数桁の数字、次に検証データセットの 10,000 桁でレンダリングされた際のパフォーマンスを示しています。予測クラスは、各桁の下に正しくない場合は、赤色で表示されます。

ご覧のように、この初期モデルはあまり良いとは言えませんが、正しく認識できる数字があります。最終的な検証精度は約 90% であり、このモデルは単純化して開始したモデルではさほど悪くありませんでしたが、10, 000 件中 1,000 件も検証で不合格でした。これはかなり多く表示できるため、すべて間違っている(赤)ように見えます。

テンソル

データはマトリックスに格納されます。28 x 28 ピクセルのグレースケール画像は、28 x 28 の 2 次元マトリックスに収まります。カラー画像の場合は、さらにサイズが必要です。ピクセルごとに 3 つの色値(赤、緑、青)があるため、ディメンション [28, 28, 3] を持つ 3 次元テーブルが必要になります。また、128 個のカラー画像のバッチを保存するには、ディメンション [128, 28, 28, 3] の 4 次元テーブルが必要です。

これらの多次元テーブルは「テンソル」と呼ばれ、そのディメンションのリストは「シェイプ」です。

概要

次の段落の太字の用語がすべてわかっている場合は、次の演習に進めます。ディープ ラーニングを始めたばかりの方は、ぜひお読みください。

魔女.png

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

単一の高密度レイヤ

MNIST データセットの手書き数字は 28 x 28 ピクセルのグレースケール画像です。それらを分類するための最も簡単な方法は、1 層のニューラルネットワークの入力として 28x28=784 ピクセルを使用することです。

スクリーンショット 2016-07-26、12.32.24.png

各「ニューロン」は、ニューラル ネットワークのすべての入力の加重合計を行い、「バイアス」と呼ばれる定数を加算してから、非線形の「活性化関数」を通じて結果をフィードします。重みとバイアスは、トレーニングによって決定されるパラメータです。これらは最初はランダムな値で初期化されます。

上の図は、数字を 10 クラス(0 ~ 9)に分類するため、10 個の出力ニューロンがある 1 層のニューラル ネットワークを表しています。

行列乗算を使用する場合

画像のコレクションを処理するニューラル ネットワーク レイヤを行列乗算で表すと、次のようになります。

matmul.gif

重みマトリックス W の最初の重み列を使用して、最初の画像のすべてのピクセルの重み合計を計算します。この合計は最初のニューロンに対応します。2 番目の重み列を使用して、2 番目のニューロンについても同じことを行い、同様に 10 番目のニューロンまで繰り返します。残りの 99 枚の画像については、同じ操作を繰り返します。X を 100 個の画像を含む行列と呼ぶ場合、100 個の画像に対して計算された 10 個のニューロンのすべての加重合計は単に X.W という行列乗算となります。

それぞれのニューロンにバイアス(定数)を付加する必要があります。10 個のニューロンがあるので、バイアス定数は 10 個です。この 10 個の値のベクトルを b と呼びます。これは、以前に計算されたマトリックスの各行に追加する必要があります。「ブロードキャスト」というマジックを使って、シンプルなプラス記号でこれを記述します。

最後に、「softmax」などのアクティベーション関数(以下で説明)を適用して、1 層ニューラル ネットワークを記述する式を取得し、100 個の画像に適用します。

スクリーンショット 2016-07-26、16.02.36.png

Keras

Keras などのハイレベル ニューラル ネットワーク ライブラリでは、この数式を実装する必要はありません。ただし、ニューラル ネットワーク レイヤは単に積算と加算の集まりであることを理解しておくことが重要です。Keras では、高密度レイヤは次のように記述されます。

tf.keras.layers.Dense(10, activation='softmax')

深く掘り下げる

ニューラル ネットワーク チェーンを連鎖させることは簡単です。最初のレイヤは、ピクセルの加重合計を計算します。後続のレイヤは、前のレイヤの出力の加重合計を計算します。

ニューロンの数を除いて、唯一の違いは活性化関数の選択です。

アクティベーション関数: relu、softmax、sigmoid

通常は、最後の層を除くすべてのレイヤに「relut」のアクティベーション関数を使用します。分類器の最後のレイヤは「softmax」アクティベーションを使用します。

ここでも、「ニューロン」がすべての入力の加重合計を計算し、「バイアス」という値を追加して、アクティベーション関数を通じて結果にフィードします。

最もよく使用されるアクティベーション関数は、Rectified Linear Unit の場合「RELU」です。上のグラフが示すように、これは非常にシンプルな関数です。

ニューラル ネットワークの従来の活性化関数は「シグモイド」でしたが、「relu」はほぼすべての収束特性を有することが示されており、現在はこれが好まれます。

分類のための Softmax アクティベーション

手書きの数字を 10 のクラス(0 ~ 9)に分類するため、ニューラル ネットワークの最後のレイヤには 10 個のニューロンがあります。この数字が 0、1、2 などになる確率を示す 0 ~ 1 の数字を 10 個出力します。そのために、最後のレイヤで "softmax" というアクティベーション関数を使用します。

ベクトルにソフトマックスをかけるには、各要素の指数を求め、ベクトルを正規化します。通常は、「L1」ノルム(絶対値の合計)で除算します。正規化された値の合計は 1 になり、確率として解釈できます。

有効化前の最後のレイヤの出力は「&logt」と呼ばれることもあります。このベクトルが L = [L0, L1, L2, L3, L4, L5, L6, L7, L8, L9] の場合:

クロス エントロピー損失

ニューラル ネットワークが入力画像から予測を生成するので、それらがどの程度良好か、つまり、ネットワークが伝える内容と正しい答え(多くの場合「ラベル」と呼ばれる)の間の距離を測定する必要があります。データセット内のすべての画像に正しいラベルがあることに留意してください。

どの距離でも問題なく機能しますが、分類問題では、いわゆる「クロス エントロピー距離」が最も効果的です。これを「エラー」または「損失」関数と呼びます。

勾配降下

&トレーニング仕組みは次のとおりです。

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

すべての重みおよびすべてのバイアスに対して相対的にクロス エントロピーの導関数を計算した場合、特定の画像、ラベル、および重みとバイアスの現在の値に対して計算された「勾配」が得られます。重みとバイアスは数百万に上るので、勾配の計算は大変な作業のように思えます。TensorFlow はそのために行います。勾配の数学的特性は、勾配が「上」を指すことです。交差エントロピーが低い場合は、逆方向に向かいます。重みとバイアスは勾配の何分の一かだけ更新されます。次に、トレーニング ループの次のバッチを使用して、同じことを何度も繰り返します。これが、この最低限の一意性を保証するものはないものの、クロス エントロピーが最小となる場所に収束されることを願っています。

グラデーション descent2.png

ミニバッチ処理と勢い

1 つのサンプル画像のみの勾配を計算して、重みとバイアスを即座に更新することもできますが、たとえば 128 の画像のバッチで行うと、異なるサンプル画像による制約をより適切に表し、ソリューションにすばやく収束する勾配が得られます。ミニバッチのサイズは調整可能なパラメータです。

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

ただし、収束は多少混雑していても、勾配ベクトルがすべてゼロであれば停止することもあります。最低額を見つけたということですか?必ずしも違反警告を受けるとは限りません。勾配コンポーネントは、最小値または最大値に対してゼロにできます。何百万もの要素を持つ勾配ベクトルでは、それらがすべてゼロである場合、すべてのゼロが最小値に対応し、それらが最大値になる確率はごくわずかです。ディメンションが 1 つしかない空間では、サドル ポイントが非常に一般的になり、それらだけを停止したくありません。

イラスト: サドルポイント。勾配は 0 ですが、すべての方向で最小値ではありません。(画像の帰属ウィキメディア: Nicoguaro - 自作の作品、CC BY 3.0

この解決策は、最適化アルゴリズムに勢いを加えることで、止まることなくサドルを越えていくことができます。

用語集

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

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

高密度レイヤ: 各ニューロンが、前のレイヤのすべてのニューロンに接続されたニューロン レイヤ。

特徴: ニューラル ネットワークの入力は「特徴」と呼ばれることもあります。データセット内のどの部分(または部分の組み合わせ)をニューラル ネットワークにフィードして適切な予測を得るかを理解することを、「特徴量エンジニアリング」と呼びます。

labels: 「クラス」の別名、または管理対象の分類問題での正解。

学習率: トレーニング ループのイテレーションで重みとバイアスが更新される勾配の割合。

ロジット: 活性化関数が適用される前のニューロンの層の出力を「ロジット」と呼びます。この用語は、「ロジスティック関数」(別名「シグモイド関数」)に由来し、最も活発な活性化関数でした。「ロジスティック関数以前の中性子出力」は「ロジット」と短縮されていました。

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

neuron: 入力の加重合計を計算し、バイアスを追加し、活性化関数によって結果をフィードします。

ワンホット エンコード: 5 段階のうちクラス 3 は 5 要素のベクトルとしてエンコードされます。ただし、3 番目は 1 のゼロ以外はゼロになります。

relu: 正規化された線形単位。ニューロンの人気の活性化関数です。

sigmoid: 以前から使用されている人気のアクティベーション関数で、特殊なケースでもまだ有効です。

softmax: ベクトルに対して動作する特殊なアクティベーション関数。最大のコンポーネントと他のすべての要素の差分が大きくなり、ベクトルの合計が 1 になるように正規化されます。これにより、確率のベクトルとして解釈できます。分類器の最後のステップとして使用されます。

テンソル: 「テンソル」はマトリックスに似ていますが、任意の数のディメンションを持ちます。1 次元のテンソルはベクトルです。2 次元のテンソルが行列になります。3 次元、4 次元、5 次元以上の次元を持つテンソルを作成できます。

スタディのノートブックに戻って、コードを見てみましょう。

keras_01_mnist.ipynb

このノートブックのすべてのセルを見ていきましょう。

セルの「パラメータ」

バッチサイズ、トレーニング エポックの数、データファイルの場所はここで定義します。データファイルは Google Cloud Storage(GCS)バケットでホストされるため、アドレスは gs:// で始まります。

セル「インポート」

TensorFlow や可視化用の matplotlib など、必要な Python ライブラリがすべてインポートされます。

セル可視化ユーティリティ [RUN ME]

このセルには不適切な可視化コードが含まれています。デフォルトでは折りたたまれていますが、時間を設けてダブルクリックすることでコードを見ることができます。

セル tf.data.Dataset: ファイルを解析し、トレーニング データセットと検証データセットを準備する"

このセルでは、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 のピクセルのフラット配列として維持します。

def read_image(tf_bytestring):
    image = tf.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)

Google はラベルの読み取りとデコードを同じく行い、画像とラベルを .zip します。

dataset = tf.data.Dataset.zip((imagedataset, labelsdataset))

ペアのデータセット(image、label)があります。これは Google のモデルが想定しているものです。まだトレーニング関数で使用する準備ができていません。

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 は、複数の画像とラベルをミニノッチにまとめます。最後に、.prefetch は CPU を使用して、現在のバッチを GPU でトレーニングしている間に次のバッチを準備します。

検証用データセットも同様の方法で準備します。モデルを定義し、このデータセットを使用してモデルをトレーニングする準備が整いました。

セル&Keras モデル

モデルはすべて、連続したレイヤのシーケンスであるため、tf.keras.Sequential スタイルを使用して作成できます。最初は、こちらは 1 つの高密度レイヤです。手書きの数字を 10 個のクラスに分類するため、10 個のニューロンがあります。分類器の最後のレイヤであるため、「softmax」アクティベーションを使用します。

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'(Stochastic Gradient Descent)を使用します。分類モデルには、Keras では 'categorical_crossentropy' というクロス エントロピー損失関数が必要です。最後に、'accuracy' 指標(正しく分類された画像の割合)を計算するようモデルに指示します。

Keras には、作成したモデルの詳細を出力する model.summary() ユーティリティが用意されています。種類別のインストラクターが PlotTraining ユーティリティ(「可視化ユーティリティ」セルで定義)を追加しました。このユーティリティは、トレーニング中にさまざまなトレーニング カーブを表示します。

セルをトレーニングし、検証する

そこでトレーニングが発生します。model.fit を呼び出して、トレーニング データセットと検証データセットの両方を渡します。デフォルトでは、Keras は各エポックの終了時に検証を行います。

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)

ここでは、テストとしてローカル フォントでレンダリングされた数字の印刷セットを用意しました。ニューラル ネットワークは、最終的な「softmax」から 10 個の確率のベクトルを返します。ラベルを取得するには、どの確率が最も高いかを見極める必要があります。これは numpy ライブラリの np.argmax が行います。

axis=1 パラメータが必要な理由を理解するために、このモデルは 128 個の画像のバッチを処理したため、モデルは 128 個の確率ベクトルを返します。出力テンソルの形状は [128, 10] です。各画像に対して返される 10 個の確率に対して引数の最大値を計算します。したがって、最初の軸は 0 です。axis=1

この単純なモデルでは、すでに 90% の桁数を認識しています。それは悪いことではありませんが、今は大幅に改善するでしょう。

godeep.png

認識精度を向上させるため、ニューラル ネットワークにレイヤを追加します。

スクリーンショット 2016-07-27 15.36.55.png

ソフトマックスは最後のレイヤのアクティベーション関数として保持します。これが分類に最適なためです。ただし、中間レイヤでは、最も古典的なアクティベーション関数 sigmoid を使用します。

たとえば、モデルは次のようになります(カンマを忘れないでください。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')
  ])

モデルの概要を確認します。パラメータは 10 倍以上になりました。10 倍になっているはずです。どういうわけか ...

損失も屋根を突破したようです。エラーが発生しました。

人々は 80 年代と 90 年代でネットワークの設計に携わっていたようなニューラル ネットワークを経験したばかりです。彼らがこのアイデアをあきらめて、いわゆる「冬の AI」を祝い、実際、レイヤを追加すると、ニューラル ネットワークの収束がますます困難になります。

多くのレイヤ(現在は 20、50、さらには 100)を使用したディープ ニューラル ネットワークは、いくつかの数学的なダーティなトリックによって収束させることができ、非常にうまく機能することが明らかになりました。このような単純なトリックの発見は、2010 年代にディープ ラーニングが再開発された理由の 1 つです。

RELU の有効化

relu.png

シグモイド活性化関数は、実際にはディープ ネットワークでは非常に問題となります。0 ~ 1 の値をすべて押しつぶすと、ニューロンの出力とその勾配が完全に消える可能性があります。これは以前の理由から説明されていますが、最新のネットワークでは RELU(Rectified Linear Unit)が使用されており、たとえば次のようになります。

一方、relu は、少なくとも右側に 1 の導関数があります。RELU 活性化では、一部のニューロンからの勾配がゼロになっても、他の勾配がゼロ以外の明確なものであると常にあり、トレーニングは適切なペースで続行できます。

優れたオプティマイザー

このような高次元の空間(重みとバイアスは 10,000 個程度)では「サドル ポイント」が頻繁に発生します。これらはローカル最小値ではありませんが、勾配がゼロで、勾配降下オプティマイザーがそこで停止しているポイントです。TensorFlow には、利用可能なオプティマイザーが豊富に揃っています。たとえば、ある程度の慣性を活かし、サドルの終点を安全に移動できるオプティマイザーもあります。

ランダム初期化

トレーニングの前に重みのバイアスを初期化する技術は、それ自体が研究の分野であり、このトピックに関する多くの論文が発表されています。Keras で使用できるすべてのイニシャライザについては、こちらをご覧ください。幸いなことに、Keras はデフォルトで適切な動作をし、'glorot_uniform' イニシャライザを使用します。これはほとんどの場合に最も適しています。

Keras はすでに正しいので、何もする必要はありません。

NaN ???

クロス エントロピーの公式には対数が含まれ、log(0) は数値ではありません (NaN、必要に応じて数値のクラッシュ)。クロス エントロピーへの入力を 0 にすることはできますか。ソフトマックスからの入力は本質的に指数であり、指数はゼロではありません。ユーザーに安心してお使いいただけます。

本当にそうでしょうか数学が美しい世界では、問題はありませんが、コンピュータの世界では、float32 形式で表された exp(-150) はゼロとなり、クロス エントロピーはクラッシュします。

幸いなことに、Keras はこの処理を行ってソフトマックスを計算し、次にクロスエントロピーを計算して、数値の安定性を確保し、心配な NaN を回避します。そのため、ここで対処する必要はありません。

正常に完了したかどうか

97% の精度が得られます。このワークショップの目標は、99% を大幅に超えることなので、このまま続けましょう。

行き詰まった場合は、次の解決策をお試しください。

keras_02_mnist_dense.ipynb

もっと早くトレーニングしてみませんか?Adam オプティマイザーのデフォルトの学習率は 0.001 です。この値を増やしてみましょう。

もっと速く走ってもあまり役に立たないようですが、全部このノイズは何でしょうか?

トレーニング曲線は非常にノイズが多く、両方の検証曲線に注目します。つまり、これらの曲線は上下に上昇しています。これは、当社の動きが速すぎることを意味します。以前の速度に戻すこともできますが、よりよい方法があります。

ゆっくり - png

適切なソリューションは、学習速度を速めに開始し、指数関数的に低下させることです。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% を超えていることを確認できます。

このモデルはうまく収束しつつあるようです。さらに詳しく学んでいきましょう。

メリット

実際には、精度は 98% から進まず、検証で損失を確認しています。上がっています!学習アルゴリズムは、トレーニング データのみに基づいて機能し、それに応じてトレーニングの損失を最適化します。検証データは表示されないため、しばらくしても検証の損失に影響がなくなり、ドロップが止まったり、元に戻ったりすることさえあります。

これは、モデルの実際の認識機能にはすぐに影響しませんが、多くの反復実行が可能ではなく、一般的には、トレーニングでプラスの効果を得られなくなったことを示しています。

ドロップアウト.png

この切断は通常、「過学習」と呼ばれ、見つけたら「ドロップアウト」という正則化手法を適用できます。ドロップアウト手法では、各トレーニング イテレーションでランダムにニューロンが発射されます。

成果

ノイズが再発生します(当然ながら、ドロップアウトの動作に違いがあります)。検証の損失は減少しているようには見えませんが、ドロップアウトがない場合よりも全体的に高くなります。検証精度も若干低下しました。これはかなり残念な結果です。

ドロップアウトは正しいソリューションでなかったように見えます。あるいは、過学習であると考えられ、その原因の一部は「ドロップアウト」による解決に適していません。

「過学習」とは過学習は、ニューラル ネットワークがトレーニング サンプルではうまく機能するものの、実際のデータではうまくいかない方法で、ニューラル ネットワークが「悪い」学習をする場合に発生します。ドロップアウトなどの正則化手法がありますが、これは学習の改善を強制する一方で、過剰適合には深い根本があります。

overffit.png

基本的な過剰適合は、ニューラル ネットワークの目の前の自由度が過度に高い場合に発生します。ネットワークにはトレーニング画像がすべて格納されているニューロンが多数あり、それらをパターン マッチングによって認識できるとします。実際のデータでは完全に失敗します。トレーニング中に学習する内容を一般化できるよう、ニューラル ネットワークはある程度制約を受ける必要があります。

トレーニング データが非常に少ない場合でも、小規模なネットワークでも、心臓で学習できるため、「過剰適合」する場合があります。一般的にニューラル ネットワークをトレーニングするには、常に大量のデータが必要です。

最後に、本書ですべてのことを行い、さまざまなサイズのネットワークを試して、自由度が制約され、ドロップアウトが持続され、大量のデータでトレーニングされていることを確認することで、改善が見込めないパフォーマンス レベルに行き詰まることがあります。つまり、今回の例のように、現在のネットワークでは、ニューラル ネットワークがデータからより多くの情報を抽出することができません。

では、画像を使って 1 つのベクトルにフラット化しました。非常に悪い考えでした。手書きの数字はシェイプで構成され、ピクセルをフラット化したときはシェイプの情報を破棄しました。ただし、畳み込みネットワークという形状情報を活用できるタイプのニューラル ネットワークがあります。ぜひお試しください。

行き詰まった場合は、次の解決策をお試しください。

keras_03_mnist_dense_lrdecay_dropout.ipynb

概要

次の段落の太字の用語がすべてわかっている場合は、次の演習に進めます。畳み込みニューラル ネットワークを始めたばかりの場合は、以下をお読みください。

convolutional.gif

図: 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 ウェイトでは不十分です。さらに自由度を高めるために、新しい重みセットで同じ操作を繰り返します。これにより、新しいフィルタ出力セットが生成されます。入力画像の R、G、B チャネルに例えて、出力の「チャネル」と呼びます。

スクリーンショット 2016-07-29、16.02.37.png

2 つ以上の重みセットを合計すると、1 つのテンソルとして新しい次元を追加できます。これにより、畳み込み層の汎用テンソルの形状が得られます。入力チャンネルと出力チャンネルの数はパラメータであるため、畳み込み層のスタックとチェーンを開始できます。

図: 畳み込みニューラル ネットワークがデータの「キューブ」を他の「キューブ」に変換する。

無理な畳み込み、最大プーリング

2 または 3 のストライドで畳み込みを行うことで、得られたデータキューブを水平方向に縮小できます。これには次の 2 つの方法があります。

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

図: コンピューティング ウィンドウを 3 ピクセルスライドすると、出力値が小さくなる。ストリングされた畳み込みまたは最大プール(2x2 ウィンドウが最大 2 スライド、最大回転)は、データ次元のキューを水平方向に縮小する方法です。

最後のレイヤ

最後の畳み込み層の後、データは「キューブ」の形式をとります。最終的な高密度レイヤにフィードする方法は 2 つあります。

1 つ目は、立方体のデータをベクトルに変換し、softmax レイヤにフィードすることです。場合によっては、softmax レイヤの前に高密度レイヤを追加することもできます。重み付けの観点から見ると、費用がかかる傾向にあります。畳み込みネットワークの末端の高密度レイヤには、ニューラル ネットワーク全体の半分の重みを含めることができます。

高コストな高密度レイヤを使用する代わりに、受信データを「キューブ」に分割し、クラスと同じ数だけパーツに分割し、その値を平均化して、softmax アクティベーション関数によってフィードできます。この方法で分類ヘッドを構築するには 0 の重みが必要です。Keras には、そのためのレイヤ tf.keras.layers.GlobalAveragePooling2D() があります。

次のセクションに進んで、現在の問題のための畳み込みネットワークを構築します。

手書きの数字認識の畳み込みネットワークを構築しましょう。上部に 3 つの畳み込みレイヤ、下部に従来の softmax 読み出しレイヤを使用し、完全に接続された 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)

立方体のデータをベクトルに変換し、高密度レイヤで使用できるようにするには:

tf.keras.layers.Flatten()

高密度レイヤでは、構文は変更されていません。

tf.keras.layers.Dense(200, activation='relu')

モデルは 99% の精度のハードルを破りましたか?おしいですね。検証損失曲線を確認します。これはベルを鳴らしますか?

予測も確認します。最初に、10,000 個のテスト数字の大部分が正しく認識されるようになりました。約 41⁄2 行の誤検出が残っている(10,000 桁のうち約 110 桁)

行き詰まった場合は、次の解決策をお試しください。

keras_04_mnist_convolutional.ipynb

前回のトレーニングでは、過剰適合の明確な兆候が見られました(まだ 99% の精度に収まりません)。もう一度ドロップアウトしてみてくださいか?

今回はどうしましたか?

今回はドロップアウトが機能していたようです。検証の損失は解消されていません。最終精度は 99% を上回っているはずです。お疲れさまでした

はじめてドロップアウトを適用しようとしたところ、過学習の問題があると思ったのですが、実際にはニューラル ネットワークのアーキテクチャに問題があったのです。畳み込み層がなければ、これだけ先に進めることはできず、抜け落ちは何にもなり得ません。

今回は過剰適合が問題の原因のように見えますが、実際に離脱したのは役に立ちました。トレーニング損失と検証損失の曲線がずれる原因は数多くあり、検証の損失が徐々に上昇していることを覚えておいてください。過学習(自由度が高すぎ、ネットワークが不正に使用する)はその 1 つだけです。データセットが小さすぎる場合、またはニューラル ネットワークのアーキテクチャが十分でない場合は、損失曲線で似たような動作をすることがありますが、ドロップアウトは役に立ちません。

最後に、バッチ正規化を追加してみましょう。

理論的には、以下のルールに注意してください。

ここでは、書籍を使って、ニューラル ネットワーク レイヤごとにバッチ ノルム レイヤを追加します。最後の「softmax」レイヤには追加しないでください。そこでは役に立ちません。

# 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、高密度レイヤでのドロップアウト率 0.3)と運が少しあれば、99.5% を達成できます。学習率と降下調整は、バッチノルムの使用に関する「ベスト プラクティス」に従って行われました。

  • バッチ ノルムは、ニューラル ネットワークの収束を助け、通常は短時間でトレーニングを行うことができます。
  • バッチノルムは正則化です。通常は、使用するドロップアウトの量を減らすことができます。また、ドロップアウトをまったく使用しないようにすることもできます。

ソリューション ノートブックには 99.5% のトレーニングが実施されます。

keras_05_mnist_batch_norm.ipynb

クラウド対応バージョンのコードは、GitHub の mlengine フォルダに、Google Cloud AI Platform 上で実行する手順が記載されています。このパートを実行するには、Google Cloud アカウントを作成し、課金を有効にする必要があります。ラボを完了するために必要なリソースは数ドル未満である必要があります(1 つの GPU で 1 時間のトレーニング時間を想定)。アカウントを準備するには:

  1. Google Cloud Platform プロジェクトを作成します(http://cloud.google.com/console)。
  2. 課金を有効にします。
  3. GCP コマンドライン ツール(GCP SDK)をインストールします。
  4. Google Cloud Storage バケットを作成します(リージョン us-central1 に配置)。トレーニング コードのステージングとトレーニング済みモデルの保存に使用されます。
  5. 必要な API を有効にして、必要な割り当てをリクエストします(トレーニング コマンドを 1 回実行すると、何を有効にするかを示すエラー メッセージが表示されます)。

最初のニューラル ネットワークを構築し、それを 99% の精度でトレーニングすることができました。その過程で学習した手法は、MNIST データセットに限らず、ニューラル ネットワークを扱うときに広く使用されています。別れのギフトとして、こちらは「崖のメモ」のラボ用のカード(漫画版)です。ここで学習した内容を思い出すことができます。

cliffs notes tensorflow lab.png

次のステップ

  • 完全に接続され、畳み込みネットワークの後は、繰り返しニューラル ネットワークを確認する必要があります。
  • 分散インフラストラクチャ上でクラウド上でトレーニングまたは推論を実行するために、Google Cloud には AI Platform が用意されています。
  • 最後に、皆様からのフィードバックをお待ちしています。このラボで何か問題が見つかった場合や、改善が必要と思われる点がありましたらお知らせください。Google では、GitHub の問題を通じてフィードバックに対処します [feedback link]。

人事.png

Martin Görner ID Small.jpg

著者: Martin Görner

Twitter: @martin_gorner

このラボで使用するすべての漫画の画像: alexpokusay / 123RF stock photos