機械学習の世界に足を踏み入れると、まず目にするのが線形回帰です。線形回帰はそのシンプルさと解析的に解ける性質から、多くの入門者にとって最初の学習対象となります。しかし、現代の機械学習の主流であるニューラルネットワーク(NN)は、はるかに複雑な非線形問題を解くために進化してきました。この記事では、線形回帰とニューラルネットワークの基本概念、そしてそれぞれの最適化手法の違いについて詳しく解説します。
線形回帰:基本から大域的最適解まで
線形回帰の概要
線形回帰は、入力データと出力データの間の線形な関係を学習するモデルです。モデルは以下のように表されます。 $$y = w_1 x_1 + w_2 x_2 + \dots + w_n x_n + b$$
ここで
- $y$ は予測値
- $x_i$ は各特徴量
- $w_i$ は学習すべき重み
- $b$ はバイアス(切片)
誤差関数と最適解
線形回帰では、主に平均二乗誤差(Mean Squared Error: MSE)が用いられます。 $$J(w, b) = \frac{1}{N} \sum_{i=1}^{N} \big(y_i – (w x_i + b)\big)^2$$
この誤差関数は二次関数(凸関数)となるため、どの初期値から始めても最終的には大域的最小解(グローバルミニマム)に収束します。実際、解析的な方法(正規方程式)やシンプルな勾配降下法で確実に最適解が求まります。
実装例(Python)
以下は、Scikit-learn を用いた線形回帰の実装例です。
import numpy as np
from sklearn.linear_model import LinearRegression
# ダミーデータ生成(y = 2x の関係)
X = np.array([[1], [2], [3], [4], [5]])
y = np.array([2, 4, 6, 8, 10])
# 線形回帰モデルの学習
model = LinearRegression()
model.fit(X, y)
# 予測結果の表示
y_pred = model.predict(X)
print("予測結果:", y_pred)
ニューラルネットワーク:非凸問題と局所最小解の克服
NNの誤差関数の特性
ニューラルネットワークでは、入力と出力の間に複数の非線形変換を導入しています。ネットワーク全体の出力は以下のように表現されます。 $$J(W) = \sum_{i=1}^{N} L\big(y_i, f(W, x_i)\big)$$
ここで、
- $f(W, x)$ はネットワークの出力(隠れ層を経由して計算される)
- $W$は全パラメータ、
- $L$は損失関数(例えば交差エントロピーやMSE)です。
この誤差関数は非凸関数であり、多数の局所的最小解や鞍点(サドルポイント)を持つため、最適解の探索が非常に難しくなります。
最適化手法の違い
線形回帰の場合
- 解析的解法
解析的に解を求める方法として正規方程式があり、計算量は特徴数に依存するものの、理論上は大域的最小解が保証されます。 - 勾配降下法
シンプルな勾配降下法を用いれば、凸関数の性質からどの初期値からでも最適解に収束します。
ニューラルネットワークの場合
ニューラルネットワークの最適化は、誤差関数の複雑さからより洗練された手法が必要です。
- SGD(確率的勾配降下法)
ランダムに選んだミニバッチに対して勾配計算を行うため、大量のデータセットに対して効率的ですが、局所最小解に落ちやすいという欠点もあります。 - モメンタム付きSGD
前回の勾配を考慮し、鞍点を乗り越える効果を狙います。以下の式で更新されます:$$v_t = \beta v_{t-1} + (1-\beta) \nabla J(W)$$
$$W \leftarrow W – \eta v_t$$モメンタムの導入により、よりスムーズな更新が可能になります。 - Adam(Adaptive Moment Estimation)
勾配の1次モーメント(平均)と2次モーメント(分散)を利用して、学習率を自動調整する手法です。更新式は以下の通りです:$$m_t = \beta_1 m_{t-1} + (1 – \beta_1) \nabla J(W)$$
$$v_t = \beta_2 v_{t-1} + (1 – \beta_2) (\nabla J(W))^2$$
$$\hat{m_t} = \frac{m_t}{1 – \beta_1^t}, \quad \hat{v_t} = \frac{v_t}{1 – \beta_2^t}$$
$$W \leftarrow W – \eta \frac{\hat{m_t}}{\sqrt{\hat{v_t}} + \epsilon}$$
多くの実践的な問題において、Adamは高速で安定した収束を示すため広く利用されています。
まとめ:モデルと最適化手法の違いを理解する
- 線形回帰
- 損失関数: 平均二乗誤差(MSE)
- 特性: 誤差関数が二次関数で凸であるため、大域的最小解が保証される
- 最適化: 解析的解法(正規方程式)やシンプルな勾配降下法で十分
- ニューラルネットワーク
- 損失関数: 非線形かつ非凸(例:交差エントロピーや MSE)
- 特性: 複数の局所最小解や鞍点を持つため、最適解が初期値に依存しやすい
- 最適化: SGD、モメンタム付きSGD、Adamなど高度な手法が必要
このように、線形回帰では単純な最適化手法で大域的最小解に辿り着けるのに対し、ニューラルネットワークでは非凸性という難題を克服するために、工夫を凝らした最適化手法が求められます。各手法の利点と欠点を理解することは、実際のモデル設計やチューニングにおいて非常に重要です。
実装例:ニューラルネットワークの学習(PyTorch)
以下は、PyTorch を用いて XOR 問題を解くシンプルなニューラルネットワークの例です。
import torch
import torch.nn as nn
import torch.optim as optim
# XORデータの作成
X = torch.tensor([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=torch.float32)
y = torch.tensor([0, 1, 1, 0], dtype=torch.float32).reshape(-1, 1)
# シンプルなニューラルネットワークの定義
class NeuralNetwork(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(2, 5) # 入力2次元、隠れ層5ユニット
self.fc2 = nn.Linear(5, 1) # 出力1次元
self.sigmoid = nn.Sigmoid()
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.sigmoid(self.fc2(x))
return x
# モデル、損失関数、最適化手法の設定
model = NeuralNetwork()
criterion = nn.BCELoss() # バイナリ分類用の損失関数
optimizer = optim.Adam(model.parameters(), lr=0.01)
# 学習ループ
for epoch in range(1000):
optimizer.zero_grad() # 勾配初期化
output = model(X) # 順伝播
loss = criterion(output, y) # 損失計算
loss.backward() # 逆伝播
optimizer.step() # パラメータ更新
# 学習結果の予測
y_pred = model(X).detach().numpy()
print("予測結果:", y_pred)
最後に
線形回帰とニューラルネットワークは、どちらも回帰という枠組みでありながら、扱う誤差関数や最適化の難易度に大きな違いがあります。線形回帰はその解析的な解法が可能なシンプルなモデルであり、基礎を学ぶには最適です。一方、ニューラルネットワークは多層の非線形変換を通じて複雑なパターンを学習しますが、最適化の面で様々な工夫が必要となります。各手法の違いとその背景を理解することで、実際の問題に対して最も適したアプローチを選択できるようになるでしょう。
コメント