はじめに
こんにちは、SHOU です!
ディープラーニングでは、モデルが訓練データに過剰に適応してしまい、未知のデータに対して性能が落ちる「過学習」が起こることがあります。その対策としてよく使われる手法のひとつが Dropout です。
本記事では、Dropoutの有無によってどれほど性能に差が出るかを、Kerasを使って実験してみます。
Dropoutとは?
Dropoutは、訓練中にランダムに一部のニューロンを無効化(0に)する正則化手法です。
- ニューロンの co-adaptation(共同適応)を防ぐ
- 汎化性能を向上させる(特定の重みだけに依存しない)
- 「訓練時のみ」有効、推論時には全ノードを使用
データの準備
今回はMNISTの手書き数字画像を使います。
from tensorflow import keras
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train / 255.0
x_test = x_test / 255.0
x_train = x_train[..., None] # チャンネル次元を追加
x_test = x_test[..., None]
Dropoutなしのモデル
28×28のグレースケール画像(例:手書き数字画像) を入力として、10クラス分類(例:0~9の数字) を行うためのシンプルなCNN(畳み込みニューラルネットワーク)を定義します。
- 入力層:入力データの形を指定(高さ28 × 幅28、1チャンネルの白黒画像)
- 畳み込み層:3×3のフィルターを32個適用し、特徴マップを抽出
- プーリング層:2×2で、空間サイズを半分に縮小。位置情報の粗視化と計算量削減
- 平坦化層:3次元の特徴マップ(13×13×32)を1次元に変換して全結合層に渡す
- 全結合層(隠れ層):5408個の入力を128次元に圧縮(情報抽出)
- 出力層:10個のクラスに分類(0~9)
また、訓練では訓練データのうち 20% を検証データ(バリデーション)として使用し、全データを20回学習させて、Dropoutあり・なしで比較します。
以下が実際のコードです。
from tensorflow.keras import layers
model_no_dropout = keras.Sequential([
layers.Input(shape=(28, 28, 1)),
layers.Conv2D(32, 3, activation='relu'),
layers.MaxPooling2D(),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(10, activation='softmax')
])
model_no_dropout.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
history_no_dropout = model_no_dropout.fit(
x_train, y_train, validation_split=0.2, epochs=20
)
Dropoutありのモデル
以下のDropout層を追加します。
layers.Dropout(0.5)
- Dropout:訓練時にランダムにニューロンの出力を0にして、一部のユニットを「無効化」する層。
- 0.5:50%の確率でユニットを無効化する、という意味です。
実際のコードは以下です:
from tensorflow.keras import layers
model_with_dropout = keras.Sequential([
layers.Input(shape=(28, 28, 1)),
layers.Conv2D(32, 3, activation='relu'),
layers.MaxPooling2D(),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5), # ★ Dropout 層を追加
layers.Dense(10, activation='softmax')
])
model_with_dropout.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
history_with_dropout = model_with_dropout.fit(
x_train, y_train, validation_split=0.2, epochs=20
)
学習の様子を比較
Dropoutあり・なしの精度・損失を比較してみましょう。
import matplotlib.pyplot as plt
# 精度プロット
plt.plot(history_no_dropout.history['accuracy'], label='No Dropout Train Accuracy')
plt.plot(history_no_dropout.history['val_accuracy'], label='No Dropout Val Accuracy')
plt.plot(history_with_dropout.history['accuracy'], label='Dropout Train Accuracy')
plt.plot(history_with_dropout.history['val_accuracy'], label='Dropout Val Accuracy')
plt.title('Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
# 損失をプロット
plt.plot(history_no_dropout.history['loss'], label='No Dropout Train Loss')
plt.plot(history_no_dropout.history['val_loss'], label='No Dropout Val Loss')
plt.plot(history_with_dropout.history['loss'], label='Dropout Train Loss')
plt.plot(history_with_dropout.history['val_loss'], label='Dropout Val Loss')
plt.title('Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
以下が精度プロットしたものです。
以下が損失プロットしたものです。
結果からわかること
1. Dropoutなし:精度は高いが過学習が発生
Dropoutなしのモデルでは、学習が進むにつれて 訓練精度が1.0(100%)に達し、一見すると完璧なモデルに見えます。 しかし、検証損失(val_loss)が徐々に悪化していく様子が確認されました。
このパターンは典型的な過学習であり、モデルが訓練データに「暗記」的に適応してしまったことを示しています。
2. Dropoutあり:やや精度が下がるが安定性あり
Dropoutを使ったモデルでは、訓練精度は完全には達しませんが(最大でも約99.5%)、検証損失の推移が安定し、汎化性能が高いことが確認されました。
Dropoutによって、学習中にランダムにノードが無効化されることで、特定のノードへの依存が減り、 よりロバストで汎化能力のある学習が可能になります。
結論:Dropoutは汎化性能の向上に効果的
Dropoutを用いることで、バランスの取れたモデルが得られることがわかりました。
実務や未知のデータに強いモデルを作るためには、少しの精度を犠牲にしてでもDropoutを使う価値があると言えるでしょう。
まとめ
- Dropoutは過学習の抑制に有効
- 特に全結合層に追加するのが一般的
- パラメータ(rate)は0.3〜0.5程度が多い
少ないデータや深いネットワークを使う場合は、Dropoutの導入を検討すると良いでしょう。
0 件のコメント:
コメントを投稿