SGD(確率的勾配降下法)とは?機械学習の最適化を徹底解説

「機械学習モデルが賢くなる仕組みって何?」

「勾配降下法とSGDの違いが分からない…」

ニューラルネットワークの学習に欠かせないのが、SGD(確率的勾配降下法)です。

この記事では、機械学習における最も基本的な最適化アルゴリズムであるSGDについて、基礎から実践まで分かりやすく解説していきますね。


スポンサーリンク

SGDの基本概念

SGDって何?

SGD(Stochastic Gradient Descent:確率的勾配降下法)は、機械学習モデルのパラメータ(重みやバイアス)を最適化するアルゴリズムです。

「確率的」という言葉が付いているのは、データをランダムに選んで更新するからなんですね。

損失関数(誤差)を最小化することで、モデルの予測精度を向上させます。

山登りの逆バージョン

SGDは、「山を下る」過程に例えられます。

目的:

谷底(損失が最小の地点)にたどり着くこと

方法:

現在地で最も急な下り坂の方向を見つけて、少しずつ降りていく

勾配:

その「下り坂の方向」を示すのが勾配(gradient)です

霧の中で下山するように、一歩ずつ慎重に進んでいくイメージですね。

なぜSGDが必要なの?

機械学習では、最適な重みを直接計算できない場合がほとんどです。

理由:

  • 問題が複雑すぎる
  • データ量が膨大
  • 解析的な解が存在しない

そこで、試行錯誤しながら少しずつ改善していく方法が必要になるんです。


通常の勾配降下法との違い

バッチ勾配降下法(BGD)

すべてのデータを使って1回更新します。

特徴:

  • 全データで誤差を計算
  • 更新が正確
  • 計算に時間がかかる
  • メモリを大量に消費

100万件のデータがあったら、100万件すべてを見てから1回更新しますね。

確率的勾配降下法(SGD)

1つのデータだけで更新します。

特徴:

  • 1データごとに誤差を計算して即座に更新
  • 更新が頻繁
  • 計算が高速
  • 経路がジグザグになる

100万件のデータなら、100万回の更新が行われます。

ミニバッチSGD

現代で最も一般的な方法です。

特徴:

  • 小さなグループ(例:32件)ごとに更新
  • BGDとSGDの良いとこ取り
  • 実質的な「SGD」として使われる

100万件のデータを32件ずつ処理して、約31,250回更新しますよ。


SGDのアルゴリズム

基本的な手順

SGDは、シンプルなステップの繰り返しです。

ステップ1:初期化

重みとバイアスをランダムな小さな値で初期化します。

ステップ2:データを選択

訓練データからランダムに1つ(またはミニバッチ)選びます。

ステップ3:順伝播

選んだデータで予測値を計算しますね。

ステップ4:損失計算

予測値と正解の差(損失)を計算します。

ステップ5:勾配計算

逆伝播で各パラメータの勾配を計算します。

ステップ6:パラメータ更新

勾配を使って重みとバイアスを更新するんです。

ステップ7:繰り返し

収束するまで、またはエポック数に達するまで繰り返します。

更新式

パラメータ更新の数式は、とてもシンプルです。

w_new = w_old - η × ∂L/∂w

記号の意味:

  • w:重み(パラメータ)
  • η(イータ):学習率
  • ∂L/∂w:損失関数Lの重みwに関する勾配

「現在の重みから、勾配に学習率を掛けた値を引く」という操作です。


学習率の重要性

学習率って何?

1回の更新でどれだけパラメータを変更するかを決める値です。

山下りの例えでは、「1歩の大きさ」に相当しますね。

学習率が大きすぎる場合

大股で進みすぎて、谷底を飛び越してしまいます。

問題点:

  • 振動して収束しない
  • 損失が発散する
  • 最適解に到達できない

症状:

学習曲線がジグザグに上下します。

学習率が小さすぎる場合

小股で少しずつ進むため、時間がかかります。

問題点:

  • 収束が遅い
  • 局所最適解に陥りやすい
  • 訓練時間が長くなる

症状:

学習曲線がほとんど変化しません。

適切な学習率の選び方

初期値として、以下が一般的です。

推奨値:

  • 0.1、0.01、0.001のいずれか
  • Adam使用時:0.001(デフォルト)
  • SGD使用時:0.01〜0.1

実験的に調整することが多いですね。


ミニバッチサイズの影響

バッチサイズって何?

1回の更新に使用するデータの数です。

一般的な値:

  • 32、64、128、256
  • 2の累乗が推奨される

GPUのメモリ効率の観点からも重要ですよ。

小さいバッチサイズ(8〜32)

メリット:

  • 更新頻度が高い
  • 正則化効果がある
  • メモリ使用量が少ない
  • 汎化性能が良い場合がある

デメリット:

  • 勾配の推定が不安定
  • GPUを活用しきれない

大きいバッチサイズ(256〜512)

メリット:

  • 勾配の推定が安定
  • 計算効率が良い
  • GPUを最大限活用

デメリット:

  • メモリを大量消費
  • 汎化性能が低下する場合がある
  • シャープな最適解に陥りやすい

バランスが重要ですね。


実装例

NumPyでのシンプル実装

基本的なSGDを実装してみましょう。

import numpy as np

def sgd_update(params, grads, learning_rate=0.01):
    """
    SGDによるパラメータ更新

    params: パラメータの辞書 {'W1': w1, 'b1': b1, ...}
    grads: 勾配の辞書 {'W1': dw1, 'b1': db1, ...}
    learning_rate: 学習率
    """
    for key in params.keys():
        params[key] -= learning_rate * grads[key]

    return params

# 使用例
weights = {'W1': np.random.randn(10, 5)}
gradients = {'W1': np.random.randn(10, 5)}

# パラメータ更新
weights = sgd_update(weights, gradients, learning_rate=0.01)

たった3行の核心部分で実装できますね。

PyTorchでの実装

実際のディープラーニングフレームワークでの使い方です。

import torch
import torch.nn as nn
import torch.optim as optim

# モデル定義
model = nn.Sequential(
    nn.Linear(784, 128),
    nn.ReLU(),
    nn.Linear(128, 10)
)

# SGDオプティマイザの作成
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 訓練ループ
for epoch in range(num_epochs):
    for batch_x, batch_y in dataloader:
        # 勾配をゼロにリセット
        optimizer.zero_grad()

        # 順伝播
        outputs = model(batch_x)
        loss = criterion(outputs, batch_y)

        # 逆伝播
        loss.backward()

        # パラメータ更新(SGD)
        optimizer.step()

optimizer.step()が、SGD更新を実行しています。

TensorFlow/Kerasでの実装

import tensorflow as tf
from tensorflow import keras

# モデル構築
model = keras.Sequential([
    keras.layers.Dense(128, activation='relu', input_shape=(784,)),
    keras.layers.Dense(10, activation='softmax')
])

# SGDオプティマイザを指定してコンパイル
model.compile(
    optimizer=keras.optimizers.SGD(learning_rate=0.01),
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy']
)

# 訓練
model.fit(x_train, y_train, epochs=10, batch_size=32)

フレームワークが内部でSGDを実行してくれますよ。


SGDの改良版

モメンタム(Momentum)

過去の勾配の情報を利用します。

アイデア:

ボールが転がるように、勢いを持って進みます。

式:

v = γ × v - η × ∂L/∂w
w = w + v
  • v:速度(velocity)
  • γ(ガンマ):モメンタム係数(通常0.9)

局所最適解を抜け出しやすくなりますね。

Nesterov Accelerated Gradient(NAG)

先読みするモメンタムです。

特徴:

  • 進む方向を事前に予測
  • より効率的な収束

勾配を計算する位置が、通常のモメンタムと異なります。

Adagrad

学習率を自動調整します。

特徴:

  • パラメータごとに異なる学習率
  • よく更新されるパラメータは学習率を小さく
  • あまり更新されないパラメータは学習率を大きく

欠点:

学習率が徐々に小さくなりすぎる問題があります。

RMSprop

Adagradの改良版です。

特徴:

  • 学習率の減衰を緩和
  • 最近の勾配を重視

ディープラーニングでよく使われますよ。

Adam

最も人気のある最適化アルゴリズムです。

特徴:

  • モメンタムとRMSpropの良いとこ取り
  • デフォルト設定でも高性能
  • ほとんどの問題で良好な結果

デフォルト設定:

  • 学習率:0.001
  • β1(モメンタム):0.9
  • β2(RMSprop):0.999

迷ったらAdamを使うのが無難ですね。


学習率スケジューリング

固定学習率の問題

ずっと同じ学習率だと、最適化が難しい場合があります。

問題:

  • 初期:学習率が小さすぎて遅い
  • 後期:学習率が大きすぎて振動

ステップ減衰

一定エポックごとに学習率を減らします。

# 10エポックごとに学習率を半分に
if epoch % 10 == 0:
    learning_rate *= 0.5

シンプルで効果的な方法です。

指数減衰

エポックに応じて指数関数的に減衰します。

learning_rate = initial_lr * (decay_rate ** epoch)

Cosine Annealing

コサイン関数で滑らかに減衰します。

特徴:

  • 滑らかな減衰
  • 周期的に学習率を上げることも可能

最近のディープラーニングでよく使われますね。

Learning Rate Warmup

最初に学習率を徐々に上げます。

アイデア:

初期の不安定な時期に、小さな学習率で慎重に進みます。

大きなバッチサイズを使う場合に特に有効ですよ。


収束の判定

いつ学習を止めるべき?

適切なタイミングで訓練を終了することが重要です。

エポック数の固定:

事前に決めた回数だけ訓練します。

簡単ですが、必ずしも最適ではありません。

早期終了(Early Stopping):

検証データでの性能が改善しなくなったら停止します。

過学習を防ぐ効果もありますね。

損失の変化を監視:

連続して一定以下しか改善しなければ停止します。

学習曲線の確認

訓練データと検証データの損失をプロットします。

正常な学習:

  • 訓練損失:順調に減少
  • 検証損失:訓練損失とほぼ同じ傾向

過学習:

  • 訓練損失:減少し続ける
  • 検証損失:途中から増加

検証損失が増加し始めたら、学習を止めるタイミングですよ。


ハイパーパラメータの調整

主要なハイパーパラメータ

SGDで調整すべきパラメータです。

学習率(最重要):

  • 0.001、0.01、0.1を試す
  • ログスケールで探索

バッチサイズ:

  • 32、64、128を試す
  • GPUメモリと相談

エポック数:

  • 早期終了を使えば、多めに設定してOK

モメンタム(使用する場合):

  • 0.9が標準的

グリッドサーチ

すべての組み合わせを試します。

learning_rates = [0.001, 0.01, 0.1]
batch_sizes = [32, 64, 128]

for lr in learning_rates:
    for bs in batch_sizes:
        # 学習と評価
        train_and_evaluate(lr, bs)

確実ですが、時間がかかりますね。

ランダムサーチ

ランダムに組み合わせを選んで試します。

グリッドサーチより効率的な場合が多いです。

Bayesian Optimization

過去の結果を元に、次に試すべきパラメータを賢く選択します。

効率的ですが、実装がやや複雑ですよ。


よくある問題と対処法

損失が減少しない

学習が進まない場合の対処です。

原因と対策:

学習率が小さすぎる:

学習率を10倍にしてみます。

初期化が悪い:

重みの初期化方法を変更します。

勾配消失:

活性化関数をReLUに変更、Batch Normalizationを導入しますね。

損失が発散する

数値が無限大になってしまう問題です。

原因と対策:

学習率が大きすぎる:

学習率を1/10にします。

勾配爆発:

勾配クリッピングを導入します。

torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

過学習

訓練データでは良いが、新しいデータで性能が低い問題です。

対策:

  • データ拡張
  • ドロップアウト
  • 正則化(L1、L2)
  • 早期終了
  • より多くの訓練データ

実践的なヒント

学習開始時のチェックリスト

訓練を始める前に確認すべき事項です。

□ データの正規化

入力データを平均0、標準偏差1に正規化します。

□ 適切な損失関数

問題に応じた損失関数を選択しましょう。

□ 適切な評価指標

精度、F1スコア、AUCなど、目的に合った指標を使います。

□ ベースラインの設定

最低限超えるべき性能を決めておきますね。

デバッグのコツ

問題の切り分け方です。

小さなデータセットで過学習させる:

数十件のデータで訓練損失がゼロに近づくか確認します。

これができなければ、モデルに問題があります。

勾配をチェック:

勾配が正常に計算されているか確認しましょう。

学習率を変えてみる:

1桁上下させて、挙動の変化を観察します。

ログと可視化

訓練の進捗を記録・可視化します。

import matplotlib.pyplot as plt

# 損失の記録
train_losses = []
val_losses = []

for epoch in range(num_epochs):
    train_loss = train_one_epoch()
    val_loss = validate()

    train_losses.append(train_loss)
    val_losses.append(val_loss)

# プロット
plt.plot(train_losses, label='Train')
plt.plot(val_losses, label='Validation')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()

視覚的に状況を把握できますよ。


まとめ:SGDで機械学習モデルを最適化しよう

SGD(確率的勾配降下法)は、機械学習における最も基本的で重要な最適化アルゴリズムです。

シンプルながら強力で、現代のディープラーニングの基礎となっています。

この記事の重要ポイント:

  • SGDはパラメータを少しずつ更新して最適化する
  • 学習率は最も重要なハイパーパラメータ
  • ミニバッチSGDが実用的で最も一般的
  • モメンタムやAdamなどの改良版が効果的
  • 学習率スケジューリングで性能向上
  • 適切なバッチサイズはGPUと相談
  • 学習曲線の可視化で状況把握
  • 過学習には早期終了が有効
  • ハイパーパラメータ調整が成功の鍵
  • デバッグには小さなデータで確認

まずはシンプルなデータセットで、PyTorchやTensorFlowのSGDを試してみましょう。

実際に動かしながら学習率を変えてみることで、SGDの挙動が体感的に理解できますよ。

コメント

タイトルとURLをコピーしました