損失関数と最適化 — モデルはどうやって「間違いから学ぶ」のか
MSEと交差エントロピーはなぜあの形なのか。勾配とは何を指す矢印なのか。勾配降下法の1ステップを、式・動く図・10行のコードで分解します。学習率を上げすぎたときの発散も手元で体感できます。
比喩: 濃霧の山を、足元の傾きだけで下りる
霧の濃い山の中腹に立っています。目標は谷底。しかし視界は1メートル、地図もありません。使える情報は足元の傾きだけです。
やれることは1つ。地面を探って最も下っている向きを見つけ、その方向へ一歩進む。着いた場所でまた傾きを調べ、また一歩。単純ですが、繰り返せば谷底に近づきます。
機械学習の最適化はこれそのものです。標高にあたるのが損失(loss)=いまのモデルの間違いの大きさ、足元の傾きが勾配(gradient)、一歩の大きさが学習率(learning rate)。前の記事(機械学習とは何か)で「つまみを回す」と呼んだ作業を、ここで具体的な手続きに落とします。
損失関数: 間違いを1つの数にまとめる
学習を始めるには、「どれくらい間違っているか」をたった1つの数で表す必要があります。数が1つでなければ「改善した/悪化した」を判定できないからです。この数を返す関数が損失関数(loss function)です。
回帰の定番: 平均二乗誤差(MSE)
家賃や気温のような連続した数値を当てるときの標準形です。
記号を1つずつ。 はデータの件数、 は 番目のデータの正解、(ワイ・ハット)は同じデータに対する予測、 は「全データについて足し合わせる」記号です。要するに「予測と正解のズレを2乗して、全件の平均を取る」だけの式です。
なぜ2乗するのか。符号が消えること(+3のズレと−3のズレを同じ大きさに扱える)、大きな外れを重く罰すること(ズレが2倍なら損失は4倍)、そして微分がきれいで傾きの計算が素直なこと。この3つです。絶対値を使うMAEは外れ値に引きずられにくい代わりに、大きな失敗を優先して潰す動きが弱くなります。外れ値が測定ミスならMAE、外れ値こそ重要ならMSEが目安です。
分類の定番: 交差エントロピー
「猫か犬か」のようなカテゴリを当てるとき、モデルは各クラスの確率を出力します。その良し悪しを測るのが交差エントロピー(cross-entropy)です。
は、 番目のデータに対してモデルが正解クラスに与えた確率(0から1の値)です。 は自然対数、先頭のマイナスは値を正に揃えるための符号です。
記号を外して言い換えると、これは「正解に与えた確率が低いほど罰が重くなる。しかも確率がゼロに近づくほど、罰の増え方そのものが加速する」という約束事です。
数値を入れると式の気持ちが分かります。正解に0.9を与えていれば とほぼ無罰。0.5なら約0.69。0.01しか与えていなければ約4.6と罰が跳ね上がり、0に近づけば無限大へ発散します。交差エントロピーは、自信満々で間違えることを極端に嫌う損失です。
なぜ対数なのか。統計の言葉では、手元のデータが得られる確率(尤度)の積を最大にしたいのが元の目的です。積は扱いにくいので対数で和に変え、最大化を最小化に直すためマイナスを付ける。すると上の式になります。交差エントロピーの最小化=最尤推定という対応は、覚えておくと後で効きます。
勾配: どちらに動けば損失が下がるか
つまみ(パラメータ)を1つだけ、ほんの少し増やしてみます。損失が増えたなら回す向きは逆、減ったならその向きで正解。この「少し動かしたとき損失がどれだけ変わるか」の比率が微分であり、つまみが複数あるときに各つまみの微分を並べたベクトルが勾配(gradient)、記号では (ナブラL)です。
押さえるべき性質は1つだけ。勾配は、損失が最も急に増える向きを指す。だから下りたければ勾配の逆向きに進みます。次の式の頭にマイナスが付くのはこのためです。
勾配降下法: 学習の1ステップ
(シータ)は回したいつまみ、(イータ)は学習率で一歩の大きさを決める小さな正の数(0.1や0.001)。 は損失 をそのつまみで偏微分したもの、つまり「そのつまみ方向の傾き」です。先頭のマイナスが「傾きと逆向きに進む」を表します。
学習とは、この1行を何万回・何百万回と繰り返すことです。深層学習でも構造は変わりません。違うのは、つまみが数十億本あることと、傾きの計算に誤差逆伝播という工夫が要ることだけです。
学習率が大きすぎると何が起きるか
初学者が最初にぶつかる壁が、この の設定です。
小さすぎると、方向は正しいのに一歩が細かすぎて、いつまでも谷底に着きません。学習曲線はほぼ平らに見え、「学習しないモデルだ」と誤診しがちです。
大きすぎるともっと劇的です。谷底を通り越して反対側の斜面に着地し、そこの傾きはさらに急なので次はもっと大きく飛ぶ。往復のたびに振れ幅が増え、損失は上がり続け、やがて数値があふれて NaN(非数)になります。これが発散です。
言葉より動かすほうが早い。下のスライダーで学習率を上げてみてください。
損失が NaN になったら、まず学習率を10分の1にして再実行する。これが実務での定石です。
谷は1次元とは限らない
ここまではつまみが1本の世界の話でした。現実のモデルはつまみが多数あり、損失は曲面になります。等高線の地図の上を降りていくイメージです。
細長い谷では、勾配が谷の軸ではなく壁のほうを向くため、経路がジグザグに震えます。これを抑えるのがモーメンタム(慣性)で、過去の進行方向をいくらか引き継ぐ工夫です。ボールに重さを持たせるとジグザグが打ち消し合い、谷に沿う方向だけが加速されます。Adamなど現代の最適化手法は、これに「つまみごとの学習率の自動調整」を足したものです。
なお高次元では、あらゆる方向に対して底になっている局所最小より、ある方向には下がる鞍点や、傾きがほぼ消えた平坦領域のほうが圧倒的に多いと指摘されています。学習が止まったら、局所解より平坦領域や学習率を疑うほうが当たります。
10行で書く勾配降下法
線形回帰()をMSEで学習させる、正真正銘の実装です。
import numpy as np
def train(X, y, lr=0.1, steps=200):
w = np.zeros(X.shape[1]) # つまみを0から始める
for _ in range(steps):
pred = X @ w # いまの予測
grad = 2 * X.T @ (pred - y) / len(y) # MSEの勾配(式そのまま)
w -= lr * grad # 傾きと逆向きに一歩
return w
grad の行がMSEを微分した結果、w -= lr * grad が先ほどの更新式そのものです。フレームワークの自動微分が肩代わりするのは grad の計算であって、骨格はこの5行から出ません。lr=10 にすると数十ステップで w が inf になり、発散を手元で再現できます。
SGD: 全部見てから動くのは遅すぎる
上のコードは、1ステップ進むのに全データで勾配を計算しています(バッチ勾配降下法)。100万件あれば一歩のために100万件の計算が必要で、現実的ではありません。
そこで、データを1件だけ、あるいは32〜1024件程度のミニバッチを取り出して勾配を計算し、すぐ一歩進みます。これが確率的勾配降下法(SGD, Stochastic Gradient Descent)です。現代の学習ループは、ほぼ例外なくこの方式です。
直感的には、正確な地図を作ってから一歩進むのをやめ、ざっくり足元を触ってすぐ動くのを何百回も繰り返すやり方です。1歩ごとの向きはぶれますが、同じ計算量で桁違いに多くのステップを踏めるため、結果としてはるかに速く谷底へ近づきます。このぶれが浅いくぼみから抜ける助けになることもあります。
実務で効く4つのポイント
1. まず損失曲線を見る 学習が進んでいるかは、精度より先に損失の推移で判断します。跳ねる・発散するならまず学習率、まったく動かないなら入力のスケールやラベルの与え方を疑います。
2. 学習率は10倍刻みで探す 0.1 → 0.01 → 0.001 と桁で振るのが効率的です。1つの値だけ試して見限るのは早すぎます。
3. 入力のスケールを揃える 特徴量Aが0〜1、Bが0〜100000だと損失曲面が極端に細長くなり、収束が遅れます。平均0・分散1に標準化するだけで谷が丸くなり、同じ学習率で通ることも珍しくありません。
4. 交差エントロピーは自前で書かない は無限大に飛ぶため、確率を出してから対数を取る実装は数値的に不安定です。softmaxと対数をまとめた安定版(logitsを直接受け取る関数)を使ってください。
まとめ
- 損失関数は「間違いの大きさ」を1つの数にまとめる装置。回帰はMSE、分類は交差エントロピーが基本形
- 交差エントロピーは自信を持って外した予測を極端に重く罰する(最尤推定と同じもの)
- 勾配は損失が最も急に増える向き。だから逆向きに、学習率の分だけ進む
- 学習率が大きすぎれば発散、小さすぎれば進まない。
NaNを見たらまず学習率を下げる - 実務はミニバッチのSGD系。ぶれた勾配でも、回数で押し切るほうが速い
次回は、この最適化がうますぎたときに起きる問題——訓練データにだけ完璧に合ってしまう過学習と、それを検出する評価設計を扱います(過学習と評価設計)。
コメント
コメントにはログインが必要です