はじめに|Kerasで学習率の違いを比較してみよう
ディープラーニングにおいて学習率(Learning Rate)は、モデルの精度や訓練の安定性に大きく影響する重要なハイパーパラメータです。学習率が適切でないと、学習が遅すぎたり、逆に発散してモデルが不安定になることもあります。
本記事では、KerasとMNIST(手書き数字データセット)を使い、異なる学習率(0.0001
, 0.001
, 0.01
, 0.1
)でモデルを訓練し、精度や損失の違いを比較しました。
特に初心者の方にもわかりやすいように、学習率がどのように影響するのかを表やグラフを交えて丁寧に解説しています。「Keras 学習率の調整方法」や「最適な学習率の見つけ方」に興味がある方は、ぜひ最後までご覧ください。
学習率とは?
学習率とは、ニューラルネットワークを訓練する際に、パラメータ(重み)をどの程度更新するかを決める係数です。
学習率が小さい場合 | 学習率が大きい場合 |
---|---|
学習が遅くなる | 学習は速いが不安定になりやすい |
収束は安定しやすい | 発散して精度が上がらないことがある |
精度の向上がゆっくり | 最適解に到達できないことがある |
このように、学習率の設定はモデルの性能に直接関わる重要なポイントです。特にSGD(確率的勾配降下法)のような単純なオプティマイザでは、学習率の影響が顕著に現れるため、今回はあえてSGDを使用して比較実験を行いました。
実験の概要|Kerasで学習率ごとのモデル精度を比較
ここでは、学習率の違いがディープラーニングモデルの性能に与える影響を検証するため、KerasとMNISTデータセットを使って実験を行いました。
Kerasは使いやすさと柔軟性を兼ね備えた深層学習フレームワークであり、学習率の設定や変更が非常に簡単です。今回は、画像分類タスクを通じて、以下の条件でモデルを訓練しました。
- 使用データセット:MNIST(0〜9の手書き数字画像、28×28ピクセル)
- モデル構成:シンプルなCNN(Conv2D + MaxPooling + Dense)
- オプティマイザ:SGD(確率的勾配降下法)
- 比較する学習率:
0.0001
,0.001
,0.01
,0.1
学習率はモデルの学習の「スピード」と「安定性」を左右するため、最適な値を見つけることが重要です。今回は、以下のような理由でSGDオプティマイザを採用しました。
- SGDはもっとも基本的な最適化手法で、学習率の影響が明確に現れやすい
- AdamやRMSpropなどの最適化手法は内部で学習率を自動調整するが、それでも初期学習率の設定が結果に影響する
この実験では、「学習率がモデルの性能(精度と損失)にどう影響するのか?」を初心者でも理解しやすく可視化していきます。
準備:MNISTデータセットの読み込みと前処理
実験では、手書き数字画像の分類タスクとして有名な「MNISTデータセット」を使用します。 MNISTには、0〜9の手書き数字画像(28×28ピクセル、モノクロ)が含まれており、ディープラーニングの学習実験に最適なデータセットです。
まず、Kerasからデータを読み込み、モデルが学習しやすいように前処理(正規化とチャンネル次元の追加)を行います。
from tensorflow import keras
from tensorflow.keras import layers
# MNISTデータの読み込み
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# 正規化とチャンネル次元追加
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0
x_train = x_train[..., None]
x_test = x_test[..., None]
モデル定義|シンプルなCNN構造を採用
今回の分類タスクには、畳み込みニューラルネットワーク(CNN)を使用します。
構造はシンプルで、Conv2D → MaxPooling → Flatten → Dense
の流れになっています。
このような基本的な構造でも、学習率の設定次第で性能が大きく変わることが分かる実験になっています。
model = keras.Sequential([
layers.Input(shape=(28, 28, 1)),
layers.Conv2D(32, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Flatten(),
layers.Dense(64, activation='relu'),
layers.Dense(10, activation='softmax')
])
学習率を変えてモデルを訓練|0.0001〜0.1の4種類で比較
いよいよ本実験の中心である、学習率(Learning Rate)ごとの比較訓練を行います。 使用する最適化手法(Optimizer)は、先述のとおり「SGD(確率的勾配降下法)」です。
学習率は以下の4種類で比較します:
0.0001
:非常に小さい0.001
:一般的な値0.01
:やや大きめ0.1
:かなり大きい
それぞれの学習率に対して、同じモデルを10エポック学習させ、検証精度(val_accuracy)とテスト精度(test_accuracy)を記録します。
results = {}
histories = {}
for lr in [0.0001, 0.001, 0.01, 0.1]:
print(f"Learning rate: {lr}")
optimizer = keras.optimizers.SGD(learning_rate=lr)
model.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
histories[lr] = model.fit(x_train, y_train, epochs=10, validation_split=0.2)
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
results[lr] = {
"val_acc": histories[lr].history["val_accuracy"][-1],
"test_acc": test_acc
}
このようにして、学習率によるパフォーマンスの違いを数値として記録し、後ほどグラフで可視化していきます。
実際の訓練状況
Learning rate: 0.0001 Epoch 1/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.9987 - loss: 0.0055 - val_accuracy: 0.9847 - val_loss: 0.0610 Epoch 2/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 9s 3ms/step - accuracy: 0.9989 - loss: 0.0049 - val_accuracy: 0.9851 - val_loss: 0.0605 Epoch 3/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9993 - loss: 0.0045 - val_accuracy: 0.9851 - val_loss: 0.0601 Epoch 4/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9993 - loss: 0.0044 - val_accuracy: 0.9851 - val_loss: 0.0597 Epoch 5/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9994 - loss: 0.0041 - val_accuracy: 0.9852 - val_loss: 0.0594 Epoch 6/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9992 - loss: 0.0042 - val_accuracy: 0.9852 - val_loss: 0.0591 Epoch 7/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9993 - loss: 0.0041 - val_accuracy: 0.9852 - val_loss: 0.0589 Epoch 8/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9996 - loss: 0.0038 - val_accuracy: 0.9854 - val_loss: 0.0587 Epoch 9/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9995 - loss: 0.0041 - val_accuracy: 0.9852 - val_loss: 0.0585 Epoch 10/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9996 - loss: 0.0037 - val_accuracy: 0.9852 - val_loss: 0.0583 Learning rate: 0.001 Epoch 1/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9993 - loss: 0.0037 - val_accuracy: 0.9855 - val_loss: 0.0572 Epoch 2/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9998 - loss: 0.0031 - val_accuracy: 0.9856 - val_loss: 0.0566 Epoch 3/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 10s 3ms/step - accuracy: 0.9998 - loss: 0.0029 - val_accuracy: 0.9855 - val_loss: 0.0562 Epoch 4/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9997 - loss: 0.0028 - val_accuracy: 0.9852 - val_loss: 0.0558 Epoch 5/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9998 - loss: 0.0027 - val_accuracy: 0.9852 - val_loss: 0.0556 Epoch 6/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 4ms/step - accuracy: 0.9998 - loss: 0.0024 - val_accuracy: 0.9852 - val_loss: 0.0554 Epoch 7/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 12s 5ms/step - accuracy: 0.9998 - loss: 0.0024 - val_accuracy: 0.9852 - val_loss: 0.0553 Epoch 8/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9998 - loss: 0.0024 - val_accuracy: 0.9853 - val_loss: 0.0552 Epoch 9/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.9997 - loss: 0.0025 - val_accuracy: 0.9853 - val_loss: 0.0551 Epoch 10/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9998 - loss: 0.0025 - val_accuracy: 0.9856 - val_loss: 0.0550 Learning rate: 0.01 Epoch 1/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.9999 - loss: 0.0022 - val_accuracy: 0.9862 - val_loss: 0.0549 Epoch 2/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9999 - loss: 0.0019 - val_accuracy: 0.9861 - val_loss: 0.0547 Epoch 3/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9998 - loss: 0.0018 - val_accuracy: 0.9866 - val_loss: 0.0549 Epoch 4/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9999 - loss: 0.0016 - val_accuracy: 0.9868 - val_loss: 0.0555 Epoch 5/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.9999 - loss: 0.0016 - val_accuracy: 0.9867 - val_loss: 0.0559 Epoch 6/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 8s 5ms/step - accuracy: 0.9999 - loss: 0.0015 - val_accuracy: 0.9866 - val_loss: 0.0557 Epoch 7/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.9999 - loss: 0.0015 - val_accuracy: 0.9862 - val_loss: 0.0559 Epoch 8/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 4ms/step - accuracy: 1.0000 - loss: 0.0014 - val_accuracy: 0.9862 - val_loss: 0.0562 Epoch 9/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 1.0000 - loss: 0.0014 - val_accuracy: 0.9864 - val_loss: 0.0566 Epoch 10/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 1.0000 - loss: 0.0013 - val_accuracy: 0.9863 - val_loss: 0.0567 Learning rate: 0.1 Epoch 1/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 8s 4ms/step - accuracy: 0.9998 - loss: 0.0018 - val_accuracy: 0.9849 - val_loss: 0.0633 Epoch 2/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9992 - loss: 0.0033 - val_accuracy: 0.9865 - val_loss: 0.0598 Epoch 3/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 0.9999 - loss: 0.0013 - val_accuracy: 0.9843 - val_loss: 0.0708 Epoch 4/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 7s 4ms/step - accuracy: 0.9999 - loss: 0.0015 - val_accuracy: 0.9862 - val_loss: 0.0624 Epoch 5/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 12s 5ms/step - accuracy: 1.0000 - loss: 6.9728e-04 - val_accuracy: 0.9866 - val_loss: 0.0631 Epoch 6/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 1.0000 - loss: 5.1740e-04 - val_accuracy: 0.9861 - val_loss: 0.0636 Epoch 7/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 1.0000 - loss: 4.5181e-04 - val_accuracy: 0.9868 - val_loss: 0.0644 Epoch 8/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 9s 3ms/step - accuracy: 1.0000 - loss: 4.4254e-04 - val_accuracy: 0.9865 - val_loss: 0.0654 Epoch 9/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 6s 3ms/step - accuracy: 1.0000 - loss: 3.8648e-04 - val_accuracy: 0.9864 - val_loss: 0.0656 Epoch 10/10 1500/1500 ━━━━━━━━━━━━━━━━━━━━ 4s 3ms/step - accuracy: 1.0000 - loss: 3.3382e-04 - val_accuracy: 0.9865 - val_loss: 0.0668
学習結果の可視化:精度と損失
検証精度と損失の推移を可視化することで、学習率ごとの安定性や過学習傾向を確認します。以下に各グラフを表示します。
import matplotlib.pyplot as plt
for lr in [0.0001, 0.001, 0.01, 0.1]:
print(f"{lr}: {results[lr]}")
for lr in [0.0001, 0.001, 0.01, 0.1]:
plt.plot(histories[lr].history['val_accuracy'], label=f'{lr} val_accuracy')
plt.title('Validation Accuracy per Epoch')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.grid(True)
plt.show()
for lr in [0.0001, 0.001, 0.01, 0.1]:
plt.plot(histories[lr].history['val_loss'], label=f'{lr} val_loss')
plt.title('Validation Loss per Epoch')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.show()
最終評価結果
各学習率での最終的な検証精度(val_accuracy)とテスト精度(test_accuracy)は以下の通りです。
0.0001: {'val_acc': 0.9852499961853027, 'test_acc': 0.9868000149726868} 0.001: {'val_acc': 0.9855833053588867, 'test_acc': 0.9872999787330627} 0.01: {'val_acc': 0.9863333106040955, 'test_acc': 0.9872999787330627} 0.1: {'val_acc': 0.9865000247955322, 'test_acc': 0.9868999719619751}
精度グラフ
損失グラフ
結果を表で整理
学習率 | Val Accuracy | Val Loss | 特徴と考察 |
---|---|---|---|
0.0001 | 0.9852 | 0.0583 | 学習が非常に安定だが、改善の速度が遅い |
0.001 | 0.9856 | 0.0550 | 最もバランスが良く、実用的な学習率 |
0.01 | 0.9863 | 0.0567 | 高精度だが損失に不安定な傾向あり |
0.1 | 0.9865 | 0.0668 | 精度は出るが過学習の可能性が高い |
学習率の違いによる影響の考察
- 0.001:バランスが非常に良く、推奨される学習率
- 0.01:精度は良いがval_lossの変動がやや大きい
- 0.1:学習が速い反面、過学習しやすく安定性に欠ける
- 0.0001:学習は安定するが、収束に時間がかかる
まとめ:適切な学習率の選定が鍵
学習率は、モデルの性能に直接影響を与える重要なハイパーパラメータです。本記事では、0.001が最も安定かつ高性能であることが確認できました。タスクやデータセットによって最適な学習率は異なるため、必ず複数の学習率で検証することが望まれます。
今後の応用
学習率は「固定値」ではなく、「学習率スケジューラ(Learning Rate Scheduler)」や「EarlyStopping」などと組み合わせることで、より洗練された学習が可能になります。
最後に
学習率を意識するだけで、モデルの性能を大きく改善することができます。ぜひ、あなたのプロジェクトでも試行錯誤しながら最適な学習率を見つけてみてください。
付録:Kerasのデフォルト学習率(learning rate)
Optimizer名 | クラス名 | デフォルトのlearning_rate | 特徴 |
---|---|---|---|
SGD | tf.keras.optimizers.SGD |
0.01 | 最も基本的な手法。学習率調整が重要。 |
Adam | tf.keras.optimizers.Adam |
0.001 | 自動調整あり。ほとんどのケースで高性能。 |
RMSprop | tf.keras.optimizers.RMSprop |
0.001 | 勾配の分散を利用して調整。RNN系でよく使われる。 |
Adagrad | tf.keras.optimizers.Adagrad |
0.001 | 学習率が段々小さくなる。疎なデータに強い。 |
Adadelta | tf.keras.optimizers.Adadelta |
1.0 | 学習率指定不要の工夫あり。 |
Nadam | tf.keras.optimizers.Nadam |
0.001 | Adam + Nesterov Momentum。収束が速い。 |
0 件のコメント:
コメントを投稿