「バッチサイズを上げると精度が落ちる」——これは以前の記事 → バッチサイズを変えると精度はどう変わる?(16 vs 64 vs 256)【Keras×CIFAR-10実験】 で確認した通りです。しかし機械学習の世界には「バッチサイズを上げるときは学習率も比例して上げれば精度が戻る」という経験則(Linear Scaling Rule)があります。今回はこれが本当に成立するのか、CIFAR-10で検証します。
📘 この記事でわかること
- バッチサイズを上げても学習率を比例して上げれば精度が維持できるのか
- Linear Scaling RuleはAdamのような適応的最適化手法でも成立するのか
- 「30epoch固定」という条件が、バッチサイズごとの重み更新回数にどう影響するか
Linear Scaling Ruleとは
Linear Scaling Ruleは、Goyalら(2017, "Accurate, Large Minibatch SGD")が提唱した経験則で、バッチサイズをk倍にしたら学習率もk倍にする、というものです。
$$ \eta_{new} = \eta_{base} \times \frac{B_{new}}{B_{base}} $$
直感的には、バッチサイズを大きくすると1回の重み更新に使う勾配の平均が安定する(ノイズが減る)ため、その分学習率を上げてより大きく重みを動かしても学習が不安定になりにくい、という理屈です。ただしこのルールはもともとSGD+Momentumを前提に提唱されたものです。Adamのような適応的最適化手法は、パラメータごとに勾配のスケールを内部で正規化する仕組みを持つため、一部の研究では平方根スケーリングの方が適しているという指摘もあります。
$$ \eta_{new} = \eta_{base} \times \sqrt{\frac{B_{new}}{B_{base}}} $$
今回はKerasで最も使われるAdamを使いつつ、まずは素直にLinear Scaling Rule(線形スケーリング)が成立するかを検証します。
| パターン | batch_size | learning_rate | スケーリング倍率 |
|---|---|---|---|
| A:baseline | 64 | 0.001 | 基準(1倍) |
| B:2倍スケーリング | 128 | 0.002 | batch 2倍 → lr 2倍 |
| C:4倍スケーリング | 256 | 0.004 | batch 4倍 → lr 4倍 |
| D:スケーリングなし(対照群) | 256 | 0.001 | batchのみ4倍、lrは据え置き |
パターンDは「バッチサイズだけ上げてlrはそのまま」という、Linear Scaling Ruleを使わなかった場合の対照群です。もしLinear Scaling Ruleが本当に効くなら、C(4倍スケーリング)はA(baseline)に近い精度まで戻り、D(スケーリングなし)はAより明確に劣るという結果になるはずです。
実験コード
環境準備(最初に一度だけ実行)
# ── 環境準備(最初に一度だけ実行)──────────────────────
!apt-get -y install fonts-ipafont-gothic
!rm -rf /root/.cache/matplotlib
!pip install -q japanize_matplotlib
print("環境準備完了")
実行結果をクリックして内容を開く
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
fonts-ipafont-mincho
The following NEW packages will be installed:
fonts-ipafont-gothic fonts-ipafont-mincho
0 upgraded, 2 newly installed, 0 to remove and 53 not upgraded.
Need to get 8,237 kB of archives.
After this operation, 28.7 MB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu jammy/universe amd64 fonts-ipafont-gothic all 00303-21ubuntu1 [3,513 kB]
Get:2 http://archive.ubuntu.com/ubuntu jammy/universe amd64 fonts-ipafont-mincho all 00303-21ubuntu1 [4,724 kB]
Fetched 8,237 kB in 2s (4,639 kB/s)
Selecting previously unselected package fonts-ipafont-gothic.
(Reading database ... 122403 files and directories currently installed.)
Preparing to unpack .../fonts-ipafont-gothic_00303-21ubuntu1_all.deb ...
Unpacking fonts-ipafont-gothic (00303-21ubuntu1) ...
Selecting previously unselected package fonts-ipafont-mincho.
Preparing to unpack .../fonts-ipafont-mincho_00303-21ubuntu1_all.deb ...
Unpacking fonts-ipafont-mincho (00303-21ubuntu1) ...
Setting up fonts-ipafont-mincho (00303-21ubuntu1) ...
update-alternatives: using /usr/share/fonts/opentype/ipafont-mincho/ipam.ttf to provide /usr/share/fonts/truetype/fonts-japanese-mincho.ttf (fonts-japanese-mincho.ttf) in auto mode
Setting up fonts-ipafont-gothic (00303-21ubuntu1) ...
update-alternatives: using /usr/share/fonts/opentype/ipafont-gothic/ipag.ttf to provide /usr/share/fonts/truetype/fonts-japanese-gothic.ttf (fonts-japanese-gothic.ttf) in auto mode
Processing triggers for fontconfig (2.13.1-4.2ubuntu5) ...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.1/4.1 MB 65.2 MB/s eta 0:00:00
Preparing metadata (setup.py) ... done
Building wheel for japanize_matplotlib (setup.py) ... done
環境準備完了
import・データ準備・モデル構築関数
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
import japanize_matplotlib
import time
(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
def build_model():
model = keras.Sequential([
keras.layers.Input(shape=(32, 32, 3)),
keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
keras.layers.MaxPooling2D((2, 2)),
keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
keras.layers.MaxPooling2D((2, 2)),
keras.layers.GlobalAveragePooling2D(),
keras.layers.Dense(128, activation='relu'),
keras.layers.Dropout(0.2),
keras.layers.Dense(10, activation='softmax'),
])
return model
def compile_and_fit(batch_size, lr):
keras.backend.clear_session()
model = build_model()
model.compile(optimizer=keras.optimizers.Adam(learning_rate=lr),
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
start = time.time()
history = model.fit(x_train, y_train, epochs=30, batch_size=batch_size,
validation_split=0.2, verbose=1)
return model, history, time.time() - start
実行結果をクリックして内容を開く
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz 170498071/170498071 ━━━━━━━━━━━━━━━━━━━━ 1773s 10us/step
4パターンの学習実行
# (batch_size, learning_rate, 表示ラベル)
patterns = [
(64, 0.001, 'A_baseline'),
(128, 0.002, 'B_scale2x'),
(256, 0.004, 'C_scale4x'),
(256, 0.001, 'D_noscale'),
]
histories, scores, times, steps_per_epoch = {}, {}, {}, {}
for batch_size, lr, label in patterns:
print(f"\n=== {label}(batch_size={batch_size}, lr={lr}) ===")
model, h, t = compile_and_fit(batch_size, lr)
s = model.evaluate(x_test, y_test, verbose=0)
histories[label] = h
scores[label] = s
times[label] = t
steps_per_epoch[label] = len(h.history['loss']) and (40000 // batch_size)
print(f"test_accuracy: {s[1]:.4f} 学習時間: {t:.1f}秒 "
f"1epochあたりstep数: {40000 // batch_size}")
実行結果をクリックして内容を開く
=== A_baseline(batch_size=64, lr=0.001) === Epoch 1/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 16s 11ms/step - accuracy: 0.2634 - loss: 1.9214 - val_accuracy: 0.2950 - val_loss: 1.8349 Epoch 2/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.3648 - loss: 1.6868 - val_accuracy: 0.4108 - val_loss: 1.5902 Epoch 3/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4256 - loss: 1.5565 - val_accuracy: 0.4585 - val_loss: 1.4735 Epoch 4/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.4595 - loss: 1.4732 - val_accuracy: 0.4865 - val_loss: 1.4130 Epoch 5/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.4861 - loss: 1.4062 - val_accuracy: 0.4924 - val_loss: 1.4114 Epoch 6/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5045 - loss: 1.3624 - val_accuracy: 0.4962 - val_loss: 1.3915 Epoch 7/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5191 - loss: 1.3227 - val_accuracy: 0.5047 - val_loss: 1.3879 Epoch 8/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5329 - loss: 1.2845 - val_accuracy: 0.5398 - val_loss: 1.2515 Epoch 9/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5464 - loss: 1.2517 - val_accuracy: 0.5464 - val_loss: 1.2446 Epoch 10/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.5563 - loss: 1.2196 - val_accuracy: 0.5685 - val_loss: 1.1825 Epoch 11/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5670 - loss: 1.1986 - val_accuracy: 0.5811 - val_loss: 1.1625 Epoch 12/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5753 - loss: 1.1692 - val_accuracy: 0.5779 - val_loss: 1.1574 Epoch 13/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.5879 - loss: 1.1442 - val_accuracy: 0.5837 - val_loss: 1.1374 Epoch 14/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.5933 - loss: 1.1229 - val_accuracy: 0.6059 - val_loss: 1.0880 Epoch 15/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6011 - loss: 1.1076 - val_accuracy: 0.6071 - val_loss: 1.0874 Epoch 16/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6097 - loss: 1.0811 - val_accuracy: 0.6145 - val_loss: 1.0546 Epoch 17/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6150 - loss: 1.0707 - val_accuracy: 0.6273 - val_loss: 1.0289 Epoch 18/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6237 - loss: 1.0511 - val_accuracy: 0.6253 - val_loss: 1.0458 Epoch 19/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6289 - loss: 1.0362 - val_accuracy: 0.6148 - val_loss: 1.0666 Epoch 20/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6370 - loss: 1.0163 - val_accuracy: 0.6174 - val_loss: 1.0437 Epoch 21/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6421 - loss: 0.9968 - val_accuracy: 0.6356 - val_loss: 1.0004 Epoch 22/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6416 - loss: 0.9924 - val_accuracy: 0.6495 - val_loss: 0.9742 Epoch 23/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step - accuracy: 0.6478 - loss: 0.9750 - val_accuracy: 0.6459 - val_loss: 1.0009 Epoch 24/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6553 - loss: 0.9634 - val_accuracy: 0.6539 - val_loss: 0.9702 Epoch 25/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6597 - loss: 0.9500 - val_accuracy: 0.6458 - val_loss: 0.9742 Epoch 26/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6656 - loss: 0.9384 - val_accuracy: 0.6654 - val_loss: 0.9384 Epoch 27/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6705 - loss: 0.9248 - val_accuracy: 0.6647 - val_loss: 0.9514 Epoch 28/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6718 - loss: 0.9205 - val_accuracy: 0.6696 - val_loss: 0.9211 Epoch 29/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6766 - loss: 0.9026 - val_accuracy: 0.6702 - val_loss: 0.9460 Epoch 30/30 625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.6788 - loss: 0.8983 - val_accuracy: 0.6523 - val_loss: 0.9640 test_accuracy: 0.6495 学習時間: 135.4秒 1epochあたりstep数: 625 === B_scale2x(batch_size=128, lr=0.002) === Epoch 1/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 11s 22ms/step - accuracy: 0.2528 - loss: 1.9465 - val_accuracy: 0.3160 - val_loss: 1.7953 Epoch 2/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.3596 - loss: 1.7006 - val_accuracy: 0.4132 - val_loss: 1.5985 Epoch 3/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.4277 - loss: 1.5466 - val_accuracy: 0.4666 - val_loss: 1.4617 Epoch 4/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.4657 - loss: 1.4520 - val_accuracy: 0.4758 - val_loss: 1.4143 Epoch 5/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - accuracy: 0.4967 - loss: 1.3779 - val_accuracy: 0.5120 - val_loss: 1.3351 Epoch 6/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.5139 - loss: 1.3317 - val_accuracy: 0.5400 - val_loss: 1.2713 Epoch 7/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.5316 - loss: 1.2829 - val_accuracy: 0.5351 - val_loss: 1.2792 Epoch 8/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.5484 - loss: 1.2445 - val_accuracy: 0.5396 - val_loss: 1.2723 Epoch 9/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.5579 - loss: 1.2141 - val_accuracy: 0.5660 - val_loss: 1.1998 Epoch 10/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.5719 - loss: 1.1767 - val_accuracy: 0.5780 - val_loss: 1.1611 Epoch 11/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.5813 - loss: 1.1565 - val_accuracy: 0.5907 - val_loss: 1.1426 Epoch 12/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.5900 - loss: 1.1328 - val_accuracy: 0.6038 - val_loss: 1.1071 Epoch 13/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6007 - loss: 1.1059 - val_accuracy: 0.5993 - val_loss: 1.0958 Epoch 14/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6084 - loss: 1.0859 - val_accuracy: 0.6218 - val_loss: 1.0554 Epoch 15/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.6166 - loss: 1.0616 - val_accuracy: 0.5993 - val_loss: 1.1049 Epoch 16/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6246 - loss: 1.0419 - val_accuracy: 0.6279 - val_loss: 1.0254 Epoch 17/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6288 - loss: 1.0319 - val_accuracy: 0.6250 - val_loss: 1.0413 Epoch 18/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6346 - loss: 1.0100 - val_accuracy: 0.6404 - val_loss: 1.0049 Epoch 19/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 11ms/step - accuracy: 0.6454 - loss: 0.9886 - val_accuracy: 0.6350 - val_loss: 0.9977 Epoch 20/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 5s 10ms/step - accuracy: 0.6495 - loss: 0.9776 - val_accuracy: 0.6403 - val_loss: 0.9957 Epoch 21/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6567 - loss: 0.9636 - val_accuracy: 0.6529 - val_loss: 0.9547 Epoch 22/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6639 - loss: 0.9423 - val_accuracy: 0.6542 - val_loss: 0.9613 Epoch 23/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6688 - loss: 0.9319 - val_accuracy: 0.6626 - val_loss: 0.9476 Epoch 24/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6703 - loss: 0.9193 - val_accuracy: 0.6678 - val_loss: 0.9267 Epoch 25/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6774 - loss: 0.9105 - val_accuracy: 0.6667 - val_loss: 0.9314 Epoch 26/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6829 - loss: 0.8966 - val_accuracy: 0.6767 - val_loss: 0.9078 Epoch 27/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6837 - loss: 0.8847 - val_accuracy: 0.6770 - val_loss: 0.9126 Epoch 28/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6929 - loss: 0.8656 - val_accuracy: 0.6784 - val_loss: 0.9172 Epoch 29/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6929 - loss: 0.8624 - val_accuracy: 0.6835 - val_loss: 0.8987 Epoch 30/30 313/313 ━━━━━━━━━━━━━━━━━━━━ 3s 10ms/step - accuracy: 0.6975 - loss: 0.8494 - val_accuracy: 0.6670 - val_loss: 0.9502 test_accuracy: 0.6654 学習時間: 104.8秒 1epochあたりstep数: 312 === C_scale4x(batch_size=256, lr=0.004) === Epoch 1/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 10s 39ms/step - accuracy: 0.2392 - loss: 1.9946 - val_accuracy: 0.3178 - val_loss: 1.7917 Epoch 2/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.3448 - loss: 1.7298 - val_accuracy: 0.3661 - val_loss: 1.7086 Epoch 3/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.3951 - loss: 1.6254 - val_accuracy: 0.4046 - val_loss: 1.5791 Epoch 4/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.4452 - loss: 1.5098 - val_accuracy: 0.4674 - val_loss: 1.4605 Epoch 5/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.4776 - loss: 1.4290 - val_accuracy: 0.4867 - val_loss: 1.3765 Epoch 6/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.4962 - loss: 1.3808 - val_accuracy: 0.4954 - val_loss: 1.3550 Epoch 7/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5119 - loss: 1.3350 - val_accuracy: 0.5130 - val_loss: 1.3311 Epoch 8/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5265 - loss: 1.3059 - val_accuracy: 0.5186 - val_loss: 1.3423 Epoch 9/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5304 - loss: 1.2881 - val_accuracy: 0.5535 - val_loss: 1.2335 Epoch 10/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 19ms/step - accuracy: 0.5433 - loss: 1.2555 - val_accuracy: 0.5279 - val_loss: 1.2630 Epoch 11/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.5558 - loss: 1.2218 - val_accuracy: 0.5552 - val_loss: 1.2046 Epoch 12/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5570 - loss: 1.2157 - val_accuracy: 0.5528 - val_loss: 1.2707 Epoch 13/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5712 - loss: 1.1843 - val_accuracy: 0.5732 - val_loss: 1.1649 Epoch 14/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.5788 - loss: 1.1621 - val_accuracy: 0.5884 - val_loss: 1.1308 Epoch 15/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 5s 18ms/step - accuracy: 0.5849 - loss: 1.1464 - val_accuracy: 0.6035 - val_loss: 1.0948 Epoch 16/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5959 - loss: 1.1183 - val_accuracy: 0.5999 - val_loss: 1.0972 Epoch 17/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.6028 - loss: 1.0975 - val_accuracy: 0.5987 - val_loss: 1.1071 Epoch 18/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 19ms/step - accuracy: 0.6065 - loss: 1.0907 - val_accuracy: 0.6034 - val_loss: 1.0834 Epoch 19/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.6130 - loss: 1.0692 - val_accuracy: 0.6190 - val_loss: 1.0554 Epoch 20/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.6200 - loss: 1.0535 - val_accuracy: 0.6231 - val_loss: 1.0388 Epoch 21/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.6254 - loss: 1.0392 - val_accuracy: 0.6091 - val_loss: 1.0617 Epoch 22/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.6305 - loss: 1.0298 - val_accuracy: 0.6189 - val_loss: 1.0602 Epoch 23/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 19ms/step - accuracy: 0.6308 - loss: 1.0215 - val_accuracy: 0.5959 - val_loss: 1.1311 Epoch 24/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.6338 - loss: 1.0149 - val_accuracy: 0.6362 - val_loss: 1.0086 Epoch 25/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.6420 - loss: 0.9895 - val_accuracy: 0.6318 - val_loss: 1.0138 Epoch 26/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.6421 - loss: 0.9993 - val_accuracy: 0.6333 - val_loss: 1.0046 Epoch 27/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 19ms/step - accuracy: 0.6523 - loss: 0.9680 - val_accuracy: 0.6159 - val_loss: 1.0667 Epoch 28/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.6564 - loss: 0.9633 - val_accuracy: 0.6540 - val_loss: 0.9690 Epoch 29/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.6637 - loss: 0.9442 - val_accuracy: 0.6367 - val_loss: 1.0021 Epoch 30/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.6608 - loss: 0.9436 - val_accuracy: 0.6452 - val_loss: 0.9904 test_accuracy: 0.6434 学習時間: 94.6秒 1epochあたりstep数: 156 === D_noscale(batch_size=256, lr=0.001) === Epoch 1/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 9s 36ms/step - accuracy: 0.2247 - loss: 2.0396 - val_accuracy: 0.2836 - val_loss: 1.8822 Epoch 2/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.3029 - loss: 1.8264 - val_accuracy: 0.3358 - val_loss: 1.7668 Epoch 3/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 19ms/step - accuracy: 0.3426 - loss: 1.7280 - val_accuracy: 0.3571 - val_loss: 1.7483 Epoch 4/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.3784 - loss: 1.6674 - val_accuracy: 0.3925 - val_loss: 1.6485 Epoch 5/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.4069 - loss: 1.6071 - val_accuracy: 0.4225 - val_loss: 1.5712 Epoch 6/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.4273 - loss: 1.5642 - val_accuracy: 0.4495 - val_loss: 1.5252 Epoch 7/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 19ms/step - accuracy: 0.4410 - loss: 1.5293 - val_accuracy: 0.4513 - val_loss: 1.4966 Epoch 8/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.4570 - loss: 1.4876 - val_accuracy: 0.4807 - val_loss: 1.4385 Epoch 9/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.4703 - loss: 1.4544 - val_accuracy: 0.4696 - val_loss: 1.4510 Epoch 10/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.4828 - loss: 1.4203 - val_accuracy: 0.4967 - val_loss: 1.3861 Epoch 11/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.4942 - loss: 1.3967 - val_accuracy: 0.4981 - val_loss: 1.4003 Epoch 12/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 19ms/step - accuracy: 0.5028 - loss: 1.3668 - val_accuracy: 0.4796 - val_loss: 1.4141 Epoch 13/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.5097 - loss: 1.3524 - val_accuracy: 0.5162 - val_loss: 1.3254 Epoch 14/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.5148 - loss: 1.3356 - val_accuracy: 0.5193 - val_loss: 1.3115 Epoch 15/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.5219 - loss: 1.3138 - val_accuracy: 0.5334 - val_loss: 1.2826 Epoch 16/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 20ms/step - accuracy: 0.5285 - loss: 1.2949 - val_accuracy: 0.5229 - val_loss: 1.3003 Epoch 17/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.5326 - loss: 1.2817 - val_accuracy: 0.5458 - val_loss: 1.2557 Epoch 18/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5437 - loss: 1.2562 - val_accuracy: 0.5411 - val_loss: 1.2457 Epoch 19/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5483 - loss: 1.2427 - val_accuracy: 0.5359 - val_loss: 1.2731 Epoch 20/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.5504 - loss: 1.2386 - val_accuracy: 0.5394 - val_loss: 1.2625 Epoch 21/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 5s 18ms/step - accuracy: 0.5572 - loss: 1.2232 - val_accuracy: 0.5606 - val_loss: 1.2019 Epoch 22/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5626 - loss: 1.2072 - val_accuracy: 0.5578 - val_loss: 1.2294 Epoch 23/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5654 - loss: 1.1938 - val_accuracy: 0.5699 - val_loss: 1.1942 Epoch 24/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 19ms/step - accuracy: 0.5699 - loss: 1.1878 - val_accuracy: 0.5740 - val_loss: 1.1618 Epoch 25/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5767 - loss: 1.1723 - val_accuracy: 0.5643 - val_loss: 1.1875 Epoch 26/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.5778 - loss: 1.1638 - val_accuracy: 0.5738 - val_loss: 1.1686 Epoch 27/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 17ms/step - accuracy: 0.5820 - loss: 1.1520 - val_accuracy: 0.5759 - val_loss: 1.1817 Epoch 28/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 19ms/step - accuracy: 0.5847 - loss: 1.1458 - val_accuracy: 0.5750 - val_loss: 1.1627 Epoch 29/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.5875 - loss: 1.1351 - val_accuracy: 0.5940 - val_loss: 1.1208 Epoch 30/30 157/157 ━━━━━━━━━━━━━━━━━━━━ 3s 18ms/step - accuracy: 0.5932 - loss: 1.1242 - val_accuracy: 0.5967 - val_loss: 1.1091 test_accuracy: 0.5937 学習時間: 95.0秒 1epochあたりstep数: 156
グラフ+サマリー
# ── val_accuracy / val_loss 比較グラフ ───────────────
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
for label, h in histories.items():
axes[0].plot(h.history['val_accuracy'], label=label)
axes[1].plot(h.history['val_loss'], label=label)
axes[0].set_title('val_accuracy の比較(全30エポック)')
axes[1].set_title('val_loss の比較(全30エポック)')
for ax in axes:
ax.set_xlabel('Epoch'); ax.legend(); ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('batch_lr_scaling_comparison.png', dpi=150)
plt.show()
print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>11} | {'Batch':>5} | {'LR':>6} | {'Test Acc':>9} | {'Time(s)':>8} | {'Steps/epoch':>11}")
print("-" * 66)
for batch_size, lr, label in patterns:
test_acc = scores[label][1]
t = times[label]
sp = 40000 // batch_size
print(f"{label:>11} | {batch_size:>5} | {lr:>6} | {test_acc:>9.4f} | {t:>8.1f} | {sp:>11}")
print("-" * 66)
実行結果をクリックして内容を開く
===== 最終結果サマリー =====
Pattern | Batch | LR | Test Acc | Time(s) | Steps/epoch
------------------------------------------------------------------
A_baseline | 64 | 0.001 | 0.6495 | 135.4 | 625
B_scale2x | 128 | 0.002 | 0.6654 | 104.8 | 312
C_scale4x | 256 | 0.004 | 0.6434 | 94.6 | 156
D_noscale | 256 | 0.001 | 0.5937 | 95.0 | 156
------------------------------------------------------------------
実験結果
精度・損失グラフ
精度グラフ
損失グラフ
結果サマリー
| パターン | batch_size | learning_rate | test_accuracy | 学習時間 | 1epochあたりstep数 |
|---|---|---|---|---|---|
| A:baseline | 64 | 0.001 | 64.95% | 135.4秒 | 625 |
| B:2倍スケーリング | 128 | 0.002 | 66.54% | 104.8秒 | 312 |
| C:4倍スケーリング | 256 | 0.004 | 64.34% | 94.6秒 | 156 |
| D:スケーリングなし | 256 | 0.001 | 59.37% | 95.0秒 | 156 |
同じbatch=256・同じstep数(156/epoch)でlrだけが違うC・Dを比較すると、lrスケーリングの有無だけでtest_accuracyに+4.97pt(59.37%→64.34%)の差が生まれました。2倍スケーリング(B)は66.54%とbaseline(64.95%)を+1.59pt上回り、4倍スケーリング(C)はbaselineに近い水準(-0.61pt)まで戻りました。
ハマりポイント
- 「30epoch固定」は「重み更新回数(step数)固定」ではない。batch_size=64なら1epochあたり625step、batch_size=256なら156stepと、4倍近い差がある。30epoch通しで見ると、batch=64は約18,750回、batch=256は約4,680回しか重みを更新していない。この差はLinear Scaling Ruleの効果とは別に精度差を生みうる要因なので、結果を見るときは「lrスケーリングの効果」と「総step数の差」を混同しないよう注意する。
- Linear Scaling RuleはもともとSGD+Momentum・大規模分散学習を前提に提唱されたルール。Adamは内部で勾配を正規化する性質を持つため、同じ倍率でスケーリングしても理論的な裏付けが弱く、平方根スケーリングなど別の経験則の方が適している可能性がある。
- 大規模バッチ学習の文献では、Linear Scaling Ruleは通常ウォームアップとセットで使われる。lrを急に大きくすると学習初期が不安定になりやすいため、本来は学習率ウォームアップと組み合わせるのが定石。今回はウォームアップなしで「スケーリング単体」の効果を見る設計にしている。
- Adamのoptimizerインスタンスは
compile_and_fit内で毎回新規作成する必要がある。使い回すと前のパターンの内部状態(モーメント推定値など)が残ってしまう。
考察
① Linear Scaling Ruleは精度を回復させたか(C vs A・D)
Cvs Dの比較が最も明確な答えを示しています。同じbatch=256・同じstep数でありながら、lrをスケーリングしたC(64.34%)はスケーリングしなかったD(59.37%)を+4.97pt上回りました。さらにCはbaseline(64.95%)とほぼ同水準(-0.61pt)まで戻っており、Linear Scaling RuleはAdamでも「バッチサイズを上げたことによる精度低下」の大部分を打ち消す効果を持つことが確認できました。完全にbaselineへ戻りきらなかった0.61pt分については、②③で詳しく見ていきます。
② 2倍スケーリング(B)と4倍スケーリング(C)で傾向は一貫しているか
興味深いことに、2倍スケーリング(B:66.54%)はbaselineを+1.59pt上回った一方、4倍スケーリング(C:64.34%)はbaselineをわずかに下回り(-0.61pt)ました。つまりLinear Scaling Ruleは倍率が上がるにつれて効果が目減りしていく傾向が見えます。これは記事冒頭で触れた「AdamはSGDと違い、Linear Scaling Ruleではなく平方根スケーリングの方が適しているかもしれない」という指摘と整合的です。倍率が小さいうち(2倍)は線形スケーリングでも十分機能しますが、倍率が大きくなる(4倍)と学習率が本来の適正値より上振れし始め、baselineに完全には届かなくなる、という解釈ができます。
③ 総step数の差はどこまで結果に影響したか
CとDはstep数が同じ(156/epoch)なので、この2つの差(+4.97pt)は総step数の影響を含まない、純粋なlrスケーリングの効果と解釈できます。一方でA(baseline、625step/epoch)とC(156step/epoch)を比べると、Cはstep数がAの約1/4しかないにもかかわらず-0.61ptしか下回っていません。これは「step数が少ない不利」を、大きな学習率による1回あたりの更新量の大きさがかなりの部分打ち消していることを示唆しています。total step数の差そのものよりも、lrが適切にスケーリングされているかどうかの方が、今回の結果への影響が大きかったと言えそうです。
④ 想定との差分と確認ポイント
今回の結果は、事前に立てた仮説(線形スケーリングは倍率が大きくなるほど効果が目減りする可能性)と概ね一致しており、大きな異常値は見られませんでした。ただし、もし手元の実験でCがDと大差ない、またはCがAより明確に劣るような結果になった場合は、以下を優先して確認してください。
- 学習曲線(val_loss)に、学習率が大きすぎることによる序盤の乱高下や発散の兆候がないか
- 各パターンのAdamのlearning_rateが意図通り設定されているか(
model.optimizer.learning_rateで確認) - ウォームアップなしでlrを4倍にしたことが、Adamの初期の不安定さを助長していないか(ウォームアップを追加した再実験で改善するか確認する価値がある)
| ケース | 実務での推奨 |
|---|---|
| Adamでバッチサイズを大きくする | Linear Scaling Ruleは有効な出発点になる。今回の実験では、スケーリングなしに比べて+4.97pt(4倍バッチ時)の改善効果があった。 |
| lrスケーリングを試す際の倍率 | 2倍程度まではLinear Scaling Ruleがよく機能し、baselineを上回ることもある。4倍まで上げると効果が目減りし始めるため、倍率が大きい場合は平方根スケーリングなど控えめな倍率も試す価値がある。 |
| SGD+Momentumで大規模バッチ学習を行う | Linear Scaling Ruleの本来の適用対象。ウォームアップとセットで使うのが定石。 |
| 学習が不安定になった場合 | ウォームアップを組み合わせる、またはLinear Scaling Ruleではなく平方根スケーリングを試す。 |
まとめ
AdamでもLinear Scaling Ruleは有効に機能し、batch=256でlrをスケーリングしなかった場合(59.37%)に比べ、スケーリングした場合(64.34%)はtest_accuracyが+4.97pt改善し、baselineとほぼ同水準まで戻りました。ただし2倍スケーリング(66.54%、baseline比+1.59pt)の方が4倍スケーリング(64.34%、baseline比-0.61pt)より良い結果だった点から、倍率が大きくなるほど線形スケーリングの効果は目減りする傾向も見えました。総step数の差(Aは625、C/Dは156)よりも、lrが適切にスケーリングされているかどうかの方が精度への影響が大きいという結果です。
関連記事もあわせてどうぞ:
- バッチサイズの影響 → バッチサイズを変えると精度はどう変わる?(16 vs 64 vs 256)【Keras×CIFAR-10実験】
- Adam学習率の最適値 → Adam学習率の最適値は?3パターンの比較実験【Keras】
- 学習率ウォームアップの長さ → 学習率ウォームアップの長さ(0 / 3 / 5 / 10エポック)で精度はどう変わる?【Keras×CIFAR-10実験】
- Optimizer比較 → optimizerを変えると精度はどう変わる?(Adam vs SGD vs RMSprop)【Keras×CIFAR-10実験】



0 件のコメント:
コメントを投稿