Keras でモデルを構築したら、次は訓練と評価のステップに進む。TensorFlow では compile でモデルの学習方法を定義し、fit で訓練を実行し、evaluate でテストデータに対する性能を測定する。この 3 つのメソッドがモデル訓練の中核を成している。
compile - 学習の設定を決める
訓練を始める前に、モデルに対して「何を最適化するか」「どう最適化するか」「何を計測するか」を伝える必要がある。それが compile の役割だ。
model.compile(
optimizer="adam",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
optimizer は重みの更新方法、loss は予測と正解のズレを測る関数、metrics は訓練中にモニタリングする指標を指定する。compile はモデルの構造を変えるわけではなく、訓練の「ルール」を設定するだけの処理である点に注意したい。
主要なオプティマイザ
オプティマイザの選択は訓練の収束速度や最終的な精度に影響する。よく使われるものを整理しておく。
| オプティマイザ | 特徴 | 典型的な用途 |
|---|---|---|
| SGD | シンプルで安定的 | 画像分類の微調整 |
| Adam | 学習率を自動調整 | 多くのタスクの第一選択 |
| RMSprop | 勾配の大きさを平滑化 | RNN 系のタスク |
迷ったらまず Adam を選んでおけばよい。学習率のデフォルトは 0.001 で、多くの場合そのまま動作する。細かく制御したい場合はオブジェクトとして渡す。
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.0005),
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
文字列で渡す場合はデフォルト設定が適用され、オブジェクトで渡す場合は学習率などのハイパーパラメータを個別に指定できる。
損失関数の選び方
タスクの種類によって適切な損失関数は異なる。選択を間違えると訓練がまったく進まないこともあるため、ここは正確に把握しておきたい。
ラベルが整数(0, 1, 2, ...)で与えられる多クラス分類に使う。MNIST のようにラベルが 0〜9 の整数ならこれを選ぶ。
ラベルが one-hot ベクトル([0, 0, 1, 0, ...])で与えられる多クラス分類に使う。sparse 版と数学的には同じだが、ラベルの形式が異なる。
2 クラス分類(陽性 / 陰性、スパム / 非スパムなど)に使う。出力層の活性化関数は sigmoid を指定する。
回帰タスクに使う。住宅価格や気温の予測など、連続値を出力するモデル向け。
fit - 訓練を実行する
compile で設定した内容に基づいて、実際にデータを流してモデルを訓練するのが fit メソッドだ。
history = model.fit(
x_train,
y_train,
epochs=10,
batch_size=32,
validation_split=0.2,
)
epochs はデータ全体を何周するか、batch_size は 1 回の重み更新に使うサンプル数、validation_split は訓練データのうち検証用に分割する割合を指定する。戻り値の history には各エポックの損失や精度の履歴が記録されている。
エポックとバッチの関係
訓練データが 10,000 件、バッチサイズが 32 の場合を考える。
10,000 件の訓練データ
32 件ずつのミニバッチに分割(313 バッチ)
313 回の重み更新で 1 エポック完了
これを epochs 回繰り返す
バッチサイズを大きくすると 1 エポックの計算は速くなるが、メモリ消費が増え、学習が不安定になることもある。32 や 64 が一般的な出発点とされている。
validation_split と validation_data
検証データの指定方法は 2 通りある。
訓練データの末尾から指定割合を自動で切り出す。手軽だが、データの順序に依存する。
あらかじめ用意した検証データをタプルで渡す。データの分割を自分で制御したい場合に使う。
validation_data を使う場合は次のように書く。
history = model.fit(
x_train,
y_train,
epochs=10,
batch_size=32,
validation_data=(x_val, y_val),
)
検証データを指定しておくと、各エポックの終了時に val_loss と val_accuracy が表示される。訓練データの精度だけが上がって検証データの精度が停滞し始めたら、過学習の兆候である。
コールバックで訓練を制御する
fit にはコールバックを渡すことで、訓練中の挙動を細かく制御できる。特に実用上よく使われるのが EarlyStopping と ModelCheckpoint の 2 つだ。
callbacks = [
keras.callbacks.EarlyStopping(
monitor="val_loss",
patience=3,
restore_best_weights=True,
),
keras.callbacks.ModelCheckpoint(
filepath="best_model.keras",
monitor="val_loss",
save_best_only=True,
),
]
history = model.fit(
x_train, y_train,
epochs=50,
batch_size=32,
validation_split=0.2,
callbacks=callbacks,
)
EarlyStopping は val_loss が 3 エポック連続で改善しなかった場合に訓練を自動停止する。restore_best_weights=True を指定しておけば、最良のエポック時点の重みに巻き戻してくれる。ModelCheckpoint は val_loss が最も低いモデルをファイルに自動保存する。epochs を大きめに設定しておいても、この 2 つを組み合わせれば無駄な訓練を防げる。
evaluate - テストデータで性能を測定する
訓練が完了したら、モデルが未知のデータに対してどの程度の性能を発揮するかを evaluate で確認する。
test_loss, test_accuracy = model.evaluate(x_test, y_test)
print(f"テスト損失: {test_loss:.4f}")
print(f"テスト精度: {test_accuracy:.4f}")
evaluate は訓練には一切関与せず、純粋にモデルの推論結果と正解を比較して損失と指標を返すだけの処理だ。ここで得られる数値が、そのモデルの実力を示す最終的なスコアとなる。
predict - 推論を実行する
評価ではなく、新しいデータに対する予測結果そのものが欲しい場合は predict を使う。
predictions = model.predict(x_test[:5])
print(predictions.shape) # (5, 10)
print(predictions[0]) # 各クラスの確率
出力層が softmax の場合、predict は各クラスの確率を返す。もっとも確率の高いクラスのインデックスを取り出すには、NumPy の argmax を使う。
import numpy as np
predicted_labels = np.argmax(predictions, axis=1)
print(predicted_labels) # [7, 2, 1, 0, 4] のような整数配列
history オブジェクトを使った学習曲線の確認
fit が返す history オブジェクトには、エポックごとの損失と指標が辞書形式で格納されている。matplotlib と組み合わせれば学習曲線を描画できる。
import matplotlib.pyplot as plt
plt.plot(history.history["loss"], label="訓練損失")
plt.plot(history.history["val_loss"], label="検証損失")
plt.xlabel("エポック")
plt.ylabel("損失")
plt.legend()
plt.show()
訓練損失が下がり続けているのに検証損失が上昇に転じていれば、過学習が起きている。この曲線を見てエポック数やモデルの複雑さを調整するのが、モデル改善の基本的な進め方になる。
訓練の全体像
ここまでの流れを改めて整理する。
compile で学習ルールを設定
fit で訓練データを使って重みを更新
evaluate でテストデータに対する最終性能を測定
compile → fit → evaluate の 3 ステップが TensorFlow におけるモデル訓練の基本パターンだ。コールバックや学習曲線の確認を挟むことで、より効率的に精度の高いモデルへ近づけていくことができる。
validation_split=0.2 を指定して fit を実行した場合、訓練データの何パーセントが検証用に使われるか?
- 2%
- 20%
- 80%
- 検証データは使われない