青の統計学-DS Playground-

損失関数と最適化

機械学習の核心は、モデルの予測と実際の値との「ずれ」を最小化することです。この「ずれ」を定量化するのが損失関数(Loss Function)であり、それを最小化する手法が最適化(Optimization)です。本章では、これらの基本概念と手法について学習します。

損失関数とは何か

関連教材(青の統計学)


[図1] 損失関数の概念 損失関数の概念


基本的な考え方

損失関数は、モデルの予測がどれだけ「悪い」かを数値で表す関数です。

数式表現: $\text{損失} = \mathcal{L}(\text{実際の値}, \text{予測値})$

全体の損失(平均損失): $J = \frac{1}{n}\sum_{i=1}^{n} \mathcal{L}(y_i, \hat{y}_i)$

ここで:

  • $y_i$:実際の値
  • $\hat{y}_i$:モデルの予測値
  • $n$:データの数

なぜ損失関数が必要なのか

機械学習の目的は「良い」モデルを作ることですが、「良い」の定義は問題によって異なります。損失関数は、この「良さ」を数値化し、コンピュータが最適化できる形にします。

回帰の損失関数


[図4] 損失関数 損失関数


平均二乗誤差(MSE)

最も一般的な回帰の損失関数です。

定義: $MSE = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2$

特徴:

  • 大きな誤差に重いペナルティ(2乗のため)
  • 微分可能で最適化しやすい
  • 外れ値の影響を受けやすい

例:住宅価格予測

  • 実際:3000万円、予測:2800万円 → 誤差:200万円 → MSE寄与:40,000
  • 実際:3000万円、予測:3200万円 → 誤差:200万円 → MSE寄与:40,000

平均絶対誤差(MAE)

絶対値を使った損失関数です。

定義: $MAE = \frac{1}{n}\sum_{i=1}^{n}|y_i - \hat{y}_i|$

特徴:

  • 外れ値の影響を受けにくい(線形のため)
  • 解釈しやすい(誤差の平均)
  • 微分が難しい場合がある

MSEとMAEの比較:

  • MSE:大きな誤差により敏感
  • MAE:すべての誤差を同等に扱う

分類の損失関数

交差エントロピー損失

分類問題で最も一般的な損失関数です。

2クラス分類の場合: $BCE = -\frac{1}{n}\sum_{i=1}^{n}[y_i \log(\hat{y}_i) + (1-y_i)\log(1-\hat{y}_i)]$

ここで:

  • $y_i \in \{0, 1\}$:真のラベル
  • $\hat{y}_i \in [0, 1]$:クラス1の予測確率

多クラス分類の場合: $CE = -\frac{1}{n}\sum_{i=1}^{n}\sum_{k=1}^{K} y_{ik} \log(\hat{y}_{ik})$

特徴:

  • 確率的な予測に適している
  • 確信のない予測(0.5付近)により大きなペナルティ
  • 微分可能で最適化しやすい

交差エントロピーの直感的理解

スパムメール判定の例:

  • 実際:スパム(1)、予測確率:0.9 → 損失:小(良い予測)
  • 実際:スパム(1)、予測確率:0.1 → 損失:大(悪い予測)
  • 実際:正常(0)、予測確率:0.9 → 損失:大(悪い予測)

最適化の基本概念

関連教材(青の統計学)

勾配降下法の原理


[図2] 勾配降下法の可視化 勾配降下法の可視化


目的: 損失関数を最小化するパラメータを見つける

$\boldsymbol{\theta}^* = \arg\min_{\boldsymbol{\theta}} J(\boldsymbol{\theta})$

基本的な更新式: $\boldsymbol{\theta}_{新} = \boldsymbol{\theta}_{古} - \eta \times \nabla J(\boldsymbol{\theta})$

ここで:

  • $\eta$:学習率(どれだけ大きく更新するか)
  • $\nabla J$:損失関数の勾配(どの方向に更新するか)

山登りのアナロジー

勾配降下法は「山を下る」ことに例えられます:

  • 現在地:現在のパラメータ値
  • 傾き:勾配(どの方向が下り坂か)
  • 歩幅:学習率(どれだけ大きく歩くか)
  • 目標:谷底(損失関数の最小値)

学習率の重要性


[図5] 最適化過程 最適化過程


学習率による影響

学習率が大きすぎる場合:

  • メリット:高速に学習が進む
  • デメリット:最適解を飛び越えて発散する可能性

学習率が小さすぎる場合:

  • メリット:安定した学習
  • デメリット:学習が遅い、局所解に陥りやすい

適切な学習率:

  • 安定して最適解に向かう
  • 実用的な時間で収束

学習率の調整方法

固定学習率:

learning_rate = 0.01  # 一定値を使用
          

学習率減衰:

# エポックが進むにつれて学習率を減少
          learning_rate = initial_lr / (1 + decay_rate * epoch)
          

適応的学習率:

  • Adam、RMSprop等のアルゴリズム
  • パラメータごとに学習率を自動調整

コラム:バッチサイズの違い

最適化では、データの使い方によって異なる手法があります。

3つの手法

バッチ勾配降下法:

  • 全データを一度に使用
  • 安定した学習、計算コスト高

確率的勾配降下法(SGD):

  • 1つのデータずつ使用
  • 高速、ノイズが多い

ミニバッチ勾配降下法:

  • 小さなグループ(例:32個)ずつ使用
  • バランスが良い、最も一般的

実際の使い分け

# ミニバッチサイズの例
          batch_size = 32  # 32個のデータを一度に処理

          for epoch in range(num_epochs):
              for batch in data_loader(batch_size):
                  # バッチごとに勾配を計算・更新
                  loss = compute_loss(batch)
                  gradients = compute_gradients(loss)
                  update_parameters(gradients)
          

コラム:過学習と学習曲線

最適化の過程で重要なのが過学習の監視です。

学習曲線の見方


[図3] 学習曲線の例 学習曲線の例


訓練損失:訓練データでの損失 検証損失:検証データでの損失

理想的なケース:

  • 両方の損失が同じように減少
  • 最終的に似たような値に収束

過学習のケース:

  • 訓練損失は減少し続ける
  • 検証損失は途中から増加に転じる

対処法

早期停止(Early Stopping):

best_val_loss = float('inf')
          patience = 5  # 5回連続で改善しなかったら停止

          for epoch in range(max_epochs):
              train_model()
              val_loss = validate_model()
              
              if val_loss < best_val_loss:
                  best_val_loss = val_loss
                  patience_counter = 0
              else:
                  patience_counter += 1
                  
              if patience_counter >= patience:
                  print(f"早期停止: エポック {epoch}")
                  break
          

実装例

基本的な勾配降下法

import numpy as np
          import matplotlib.pyplot as plt

          # 簡単な線形回帰での勾配降下法
          def gradient_descent_example():
              # データ生成(y = 2x + 1 + ノイズ)
              np.random.seed(42)
              X = np.random.randn(100, 1)
              y = 2 * X.flatten() + 1 + 0.1 * np.random.randn(100)
              
              # パラメータ初期化
              w = 0.0  # 重み
              b = 0.0  # バイアス
              learning_rate = 0.01
              epochs = 1000
              
              losses = []
              
              for epoch in range(epochs):
                  # 予測
                  y_pred = w * X.flatten() + b
                  
                  # 損失計算(MSE)
                  loss = np.mean((y - y_pred) ** 2)
                  losses.append(loss)
                  
                  # 勾配計算
                  dw = -2 * np.mean((y - y_pred) * X.flatten())
                  db = -2 * np.mean(y - y_pred)
                  
                  # パラメータ更新
                  w -= learning_rate * dw
                  b -= learning_rate * db
                  
                  if epoch % 100 == 0:
                      print(f"エポック {epoch}: 損失 = {loss:.4f}")
              
              print(f"最終パラメータ: w = {w:.3f}, b = {b:.3f}")
              print(f"真の値: w = 2.0, b = 1.0")
              
              return losses

          # 実行例
          losses = gradient_descent_example()
          

scikit-learnでの実装

from sklearn.linear_model import SGDRegressor, SGDClassifier
          from sklearn.model_selection import train_test_split
          from sklearn.preprocessing import StandardScaler

          # 回帰の例
          def sgd_regression_example():
              # データ準備
              X = np.random.randn(1000, 3)
              y = 2*X[:,0] + 3*X[:,1] - X[:,2] + np.random.randn(1000) * 0.1
              
              X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
              
              # 正規化(SGDには重要)
              scaler = StandardScaler()
              X_train_scaled = scaler.fit_transform(X_train)
              X_test_scaled = scaler.transform(X_test)
              
              # SGD回帰
              model = SGDRegressor(learning_rate='constant', eta0=0.01, max_iter=1000)
              model.fit(X_train_scaled, y_train)
              
              # 評価
              train_score = model.score(X_train_scaled, y_train)
              test_score = model.score(X_test_scaled, y_test)
              
              print(f"訓練R²: {train_score:.3f}")
              print(f"テストR²: {test_score:.3f}")

          sgd_regression_example()
          

まとめ

損失関数と最適化は機械学習の核心的な概念です。重要なポイント:

  1. 損失関数の選択:問題に応じてMSE、MAE、交差エントロピーなどを使い分け
  2. 勾配降下法の理解:「山を下る」ように最適解を探索
  3. 学習率の重要性:大きすぎず小さすぎない適切な値設定
  4. 過学習の監視:訓練・検証損失の両方を確認
  5. 実装のポイント:正規化、バッチサイズ、早期停止などの技術

これらの概念を理解することで、機械学習モデルの学習過程を適切に制御できるようになります。

次章では、モデルの評価と汎化性能向上のための「訓練・検証・テストデータの分割」について学習します。