LESSON

最適化テクニック

田中VPoE:「正則化で過学習を防ぐ方法を学んだ。次は、学習プロセス自体を最適化するテクニックだ。学習率の動的調整や Mixed Precision など、実務で効果が大きい手法を押さえよう。」

あなた:「学習率を固定するのではなく、学習中に変化させるんですね。」

田中VPoE:「そうだ。最初は大きな学習率で広く探索し、徐々に小さくして精密に収束させる。このテクニック1つで性能が大きく変わることもある。」

学習率スケジューリング

学習率を学習中に動的に変化させることで、収束の速度と精度を改善します。

主要なスケジューラー

スケジューラー挙動用途
StepLR一定エポックごとに減衰シンプルな学習
CosineAnnealingLRコサインカーブで減衰CNN の学習
OneCycleLR1サイクルで上昇→減衰高速収束
ReduceLROnPlateau検証損失が停滞したら減衰汎用的
Linear with Warmup線形 Warmup + 線形減衰Transformer Fine-tuning
import torch.optim as optim
from torch.optim.lr_scheduler import (
    StepLR, CosineAnnealingLR, OneCycleLR, ReduceLROnPlateau
)

optimizer = optim.Adam(model.parameters(), lr=1e-3)

# StepLR: 30エポックごとに学習率を0.1倍
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)

# CosineAnnealing: 50エポックで最小学習率まで減衰
scheduler = CosineAnnealingLR(optimizer, T_max=50, eta_min=1e-6)

# OneCycleLR: 最大学習率まで上昇→減衰
scheduler = OneCycleLR(
    optimizer, max_lr=1e-2,
    steps_per_epoch=len(train_loader),
    epochs=num_epochs
)

# ReduceLROnPlateau: 検証損失が10エポック改善しなければ0.5倍
scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.5, patience=10)

学習ループでの使用

for epoch in range(num_epochs):
    model.train()
    for batch_X, batch_y in train_loader:
        optimizer.zero_grad()
        outputs = model(batch_X)
        loss = criterion(outputs, batch_y)
        loss.backward()
        optimizer.step()

        # OneCycleLR はバッチごとに更新
        # scheduler.step()

    # StepLR, CosineAnnealing はエポックごとに更新
    scheduler.step()

    # ReduceLROnPlateau は検証損失を渡す
    # scheduler.step(val_loss)

    print(f"Epoch {epoch+1}: lr={optimizer.param_groups[0]['lr']:.6f}")

Warmup

学習の最初は小さい学習率から始め、徐々に本来の学習率まで上げる手法です。

from transformers import get_linear_schedule_with_warmup

total_steps = len(train_loader) * num_epochs
warmup_steps = int(total_steps * 0.1)  # 最初の10%

scheduler = get_linear_schedule_with_warmup(
    optimizer,
    num_warmup_steps=warmup_steps,
    num_training_steps=total_steps
)

なぜ Warmup が必要か

Warmup なし:
  初期の大きな学習率 → ランダムな重みが大きく更新 → 不安定

Warmup あり:
  小さい学習率で開始 → 重みが安定してから本来の学習率に → 安定した学習

Warmup は特に以下の場面で効果的です:

  • Transformer の Fine-tuning
  • 大きなバッチサイズでの学習
  • 複雑なモデルの学習初期

AdamW

Adam の改良版で、Weight Decay を勾配更新から分離して正しく適用します。

optimizer = torch.optim.AdamW(
    model.parameters(),
    lr=1e-3,
    betas=(0.9, 0.999),    # モメンタム係数
    eps=1e-8,               # ゼロ除算防止
    weight_decay=0.01       # 重み減衰
)
Optimizer推奨場面
SGD + MomentumCNN の長時間学習(最終精度が高い)
Adam汎用的、素早い収束
AdamWTransformer、Fine-tuning

Mixed Precision Training

FP32(32ビット浮動小数点)と FP16(16ビット)を混合して計算することで、メモリ使用量を削減し、学習を高速化します。

from torch.cuda.amp import autocast, GradScaler

scaler = GradScaler()

for batch_X, batch_y in train_loader:
    batch_X, batch_y = batch_X.to(device), batch_y.to(device)
    optimizer.zero_grad()

    # autocast で FP16 計算を自動適用
    with autocast():
        outputs = model(batch_X)
        loss = criterion(outputs, batch_y)

    # GradScaler で勾配のスケーリング
    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()

Mixed Precision の効果

項目FP32 のみMixed Precision
メモリ使用量基準約50%削減
学習速度基準1.5~3倍高速
精度基準ほぼ同等
GPU 要件任意Volta 以降(Tensor Core)

Gradient Clipping

勾配が大きくなりすぎるのを防ぎ、学習を安定化します。

# 勾配のノルムを最大1.0に制限
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

# 学習ループ内での使用
optimizer.zero_grad()
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()

最適化テクニックの組み合わせ

# 実務的な最適化設定の例
model = MyModel().to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=0.01)

total_steps = len(train_loader) * num_epochs
scheduler = get_linear_schedule_with_warmup(
    optimizer,
    num_warmup_steps=int(total_steps * 0.1),
    num_training_steps=total_steps
)
scaler = GradScaler()

for epoch in range(num_epochs):
    model.train()
    for batch in train_loader:
        optimizer.zero_grad()
        with autocast():
            loss = compute_loss(model, batch)
        scaler.scale(loss).backward()
        scaler.unscale_(optimizer)
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        scaler.step(optimizer)
        scaler.update()
        scheduler.step()

まとめ

  • 学習率スケジューリングで収束速度と精度を改善する
  • Warmup は学習初期の不安定さを解消する
  • AdamW は Transformer の Fine-tuning に推奨される Optimizer
  • Mixed Precision はメモリ削減と高速化を両立する
  • Gradient Clipping は勾配爆発を防ぎ、学習を安定化する

チェックリスト

  • 主要な学習率スケジューラーの特徴と使い分けを理解した
  • Warmup の必要性と設定方法を理解した
  • AdamW と通常の Adam の違いを説明できる
  • Mixed Precision Training の概念と効果を理解した

推定読了時間: 30分