異なる学習率の影響の可視化
同じ関数に対して、小さすぎる・適切・大きすぎる学習率でそれぞれ勾配降下法を実行し、3つの典型的な曲線の比較を直感的に確認できます。
このケースを学び終えると、次のことが理解できます:学習率は勾配降下法において最も重要なハイパーパラメータです——小さすぎると収束が遅く、大きすぎると発散します。
生活からの導入
車で家に帰る——アクセルはどれくらい踏む?
家まで10km。アクセルを踏む力が弱すぎる(学習率が小さすぎる)——1時間運転してもまだ着かない。アクセルを踏む力が強すぎる(学習率が大きすぎる)——行き過ぎて、引き返して再び進むが、また行き過ぎて、行ったり来たり振動する。ちょうど良い力加減(適切な学習率)——安定して到着する。
勾配降下法の学習率の選択と車のアクセル制御は、まったく同じ問題に直面しています。
直感的な理解
目的関数 \(f(x) = x^2\) は、最小値を \(x=0\) でとります。勾配は \(f'(x) = 2x\) です。
更新規則:\(x_{t+1} = x_t - \eta \cdot 2x_t = x_t \cdot (1 - 2\eta)\)。\(\eta < 0.5\) なら収束し、\(\eta > 1.0\) なら発散します。
小さすぎる lr=0.01
収束が非常に遅く、30ステップでも少ししか進まない
適切 lr=0.2
高速かつ安定して0に収束する
大きすぎる lr=1.05
振動しながら発散し、どんどん遠ざかる
Python ハンズオン
実例
import numpy as np
def f(x): return x ** 2
def grad(x): return 2 * x
def run_gd(lr, steps=30, x0=8.0):
x = x0
history = [x]
for _ in range(steps):
x = x - lr * grad(x)
history.append(x)
if abs(x) > 1e6: break
return history
settings = {
"小さすぎる (lr=0.01)": 0.01,
"適切 (lr=0.2)": 0.2,
"大きすぎる (lr=1.05)": 1.05,
}
results = {name: run_gd(lr) for name, lr in settings.items()}
print("サンプル 3種類の学習率を30ステップ後に比較:\n")
print(f"{'学習率設定':<20} {'最終 x':<12} {'f(x)':<14} {'判定'}")
print("-" * 56)
for name, hist in results.items():
x_final = hist[-1]
judgement = ("収束済み" if abs(x_final) < 0.01
else "収束中" if abs(x_final) < 3
else "発散")
print(f"{name:<20} {x_final:<12.4f} {f(x_final):<14.6f} {judgement}")
# 分析:|x| < 0.01 にするには何ステップ必要か?
print("\nサンプル は |x| < 0.01 に収束するまでに何ステップ必要か?")
for name, lr in settings.items():
x = 8.0
for step in range(1, 1001):
x = x - lr * grad(x)
if abs(x) < 0.01:
print(f" {name}: {step} ステップ必要")
break
else:
print(f" {name}: 1000 ステップ以内では収束しません")
def f(x): return x ** 2
def grad(x): return 2 * x
def run_gd(lr, steps=30, x0=8.0):
x = x0
history = [x]
for _ in range(steps):
x = x - lr * grad(x)
history.append(x)
if abs(x) > 1e6: break
return history
settings = {
"小さすぎる (lr=0.01)": 0.01,
"適切 (lr=0.2)": 0.2,
"大きすぎる (lr=1.05)": 1.05,
}
results = {name: run_gd(lr) for name, lr in settings.items()}
print("サンプル 3種類の学習率を30ステップ後に比較:\n")
print(f"{'学習率設定':<20} {'最終 x':<12} {'f(x)':<14} {'判定'}")
print("-" * 56)
for name, hist in results.items():
x_final = hist[-1]
judgement = ("収束済み" if abs(x_final) < 0.01
else "収束中" if abs(x_final) < 3
else "発散")
print(f"{name:<20} {x_final:<12.4f} {f(x_final):<14.6f} {judgement}")
# 分析:|x| < 0.01 にするには何ステップ必要か?
print("\nサンプル は |x| < 0.01 に収束するまでに何ステップ必要か?")
for name, lr in settings.items():
x = 8.0
for step in range(1, 1001):
x = x - lr * grad(x)
if abs(x) < 0.01:
print(f" {name}: {step} ステップ必要")
break
else:
print(f" {name}: 1000 ステップ以内では収束しません")
サンプル 三种学习率 30 步后对比: 学习率设置 最终 x f(x) 判断 -------------------------------------------------------- 过小 (lr=0.01) 4.3219 18.678861 收敛中 合适 (lr=0.2) 0.0000 0.000000 已收敛 过大 (lr=1.05) -34.5929 1196.668701 发散 サンプル 需要多少步收敛到 |x| < 0.01? 过小 (lr=0.01): 需要 377 步 合适 (lr=0.2): 需要 20 步 过大 (lr=1.05): 1000 步内无法收敛
AIにおける応用シーン
| シーン | 説明 |
|---|---|
| 学習率スケジューリング | 実際のトレーニングでは「学習率減衰」がよく使われます——最初は大きく、後で小さくして、前段階では素早く収束させ、後段階では細かく調整します |
| Warmup | Transformer を学習する際は、最初に非常に小さな学習率でウォームアップし、その後徐々に大きくします——初期の振動を防ぐためです |
| Adam 適応 | パラメータごとに学習率を自動調整します——「オプティマイザ比較」のケースで詳しく説明します |