本文へスキップ
ひもとくAI

正則化:重みを大きくしすぎない

レッスン 2/7

正則化:重みが大きすぎることにも、罰を与える

前のレッスンの14次式は、訓練データの15点をほぼすべて通りましたが、点のない場所で大きく暴れました。このとき、式の係数を調べると、大きさが約7億4000万という、とても大きな数になっていました。大きな係数どうしが打ち消し合って点を通り、少しでも点から離れると、打ち消し合いが崩れて暴れる のです。

そこで、係数(ニューラルネットワークなら重み)が大きくなりすぎないように、損失に「係数の大きさの罰」を足します。これを 正則化(せいそくか)と呼びます。

L2正則化:係数の2乗の和を、罰として足す

損失=誤差の2乗の平均+α×(w12+w22+⋯+wd2)\text{損失} = \text{誤差の2乗の平均} + \alpha \times (w_1^2 + w_2^2 + \cdots + w_d^2)

w1,w2,…w_1, w_2, \ldots は係数、α\alpha(アルファ)は罰の強さを決める数です。学習(損失を小さくすること)では、誤差を小さくしたい一方で、係数を大きくすると罰が増えるので、必要以上に大きな係数を使わなくなります。係数の2乗を使うので L2正則化 と呼び、線形回帰にL2正則化を加えたものを Ridge回帰(リッジ回帰)と呼びます。

実験:14次式にL2正則化を加える

前のレッスンと同じ15点に、14次式を当てはめます(テストは、当てはめに使っていない別の200点)。

当てはめ方訓練誤差テスト誤差係数の最大の大きさ
正則化なし0.001約2.9万約7億4000万
L2正則化、α=0.01\alpha = 0.010.0380.0973.22
L2正則化、α=1\alpha = 10.1170.2780.29
L2正則化、α=10\alpha = 100.1680.3650.13
xy0123456-2-1012
  • 14次式+L2(α=0.01)
  • 14次式+L2(α=10)
15個の点(○)に、L2正則化を加えた14次式を当てはめた曲線。α=0.01では、点の間をなめらかに通る波の形になり、暴れなくなった。α=10では、罰が強すぎて曲がれず、山のない、ゆるやかに右下がりの曲線になり、x=1.6の点(1.31)から大きく離れている
  • α=0.01\alpha = 0.01 では、係数が約7億4000万から3.22まで小さくなり、曲線が暴れなくなりました。テスト誤差0.097は、前のレッスンの3次式(ちょうどよい式)に近い値です。次数が高い式でも、正則化を加えれば過学習を抑えられます
  • α\alpha を大きくするほど、係数は小さくなりますが、強すぎると曲線が曲がれなくなり、訓練誤差もテスト誤差も大きくなります(過少適合)

α\alpha はちょうどよい強さを選ぶ必要がある ということです。どう選ぶかは、レッスン7で学びます。

L1正則化と、ニューラルネットワークの正則化

L1正則化:係数の絶対値の和を、罰として足す

L2正則化の「2乗」の代わりに、「絶対値」(符号を外した大きさ、例えば ∣−3∣=3|-3| = 3)を使うこともできます。

損失=誤差の2乗の平均+α×(∣w1∣+∣w2∣+⋯+∣wd∣)\text{損失} = \text{誤差の2乗の平均} + \alpha \times (|w_1| + |w_2| + \cdots + |w_d|)

これを L1正則化 と呼び、線形回帰にL1正則化を加えたものを Lasso回帰(ラッソ回帰)と呼びます。

L1正則化には、いくつかの係数を、ちょうど0にする という特徴があります。同じ14次式に、L1正則化(α=0.001\alpha = 0.001)を加えると、次のようになりました。

当てはめ方訓練誤差テスト誤差0でない係数の数
L2正則化、α=0.01\alpha = 0.010.0380.09714個中14個
L1正則化、α=0.001\alpha = 0.0010.0320.09914個中 4個

L1正則化では、14個の係数のうち10個がちょうど0になり、xx・x2x^2・x5x^5(と、もう1つ、ごく小さい値)の項だけが残りました。テスト誤差は、L2正則化とほぼ同じです。多くの係数が0になっている状態を スパース(まばら)と呼びます。

L1とL2の違いを、直感で理解する

  • L2 は、大きな係数ほど強く罰します(2乗なので、係数が2倍になると罰は4倍)。そのため、全部の係数を、少しずつ小さくします。0にはなりにくい
  • L1 は、係数の大きさに比例して罰します。係数を少し減らすことで得られる罰の減り方が、小さな係数でも大きな係数でも同じなので、「あまり役に立っていない係数」は、ちょうど0まで減らされます

L1正則化は、たくさんの特徴量の中から、本当に必要なものだけを選びたいとき(どの特徴量が効いているかを知りたいとき)に便利です。

ニューラルネットワークの正則化(weight decay)

ニューラルネットワークでも、損失に重みの2乗の和を足すL2正則化がよく使われます。勾配降下法で学習すると、罰の分だけ、毎回すべての重みが0に向かって少しずつ縮むので、weight decay(重みの減衰)とも呼ばれます。

第6章の MLPClassifier では、alpha という設定がL2正則化の強さです。ノイズ(ばらつき)の多い2クラスの分類のデータ(訓練60点・テスト500点)で、大きなネットワーク(隠れ層100個を2層)を学習させると、次のようになりました。

alpha訓練データの正解率テストデータの正解率
0(正則化なし)0.9500.828
0.10.9000.862
1.00.8670.846
3.00.8670.828

正則化を加えると、訓練データの正解率は下がりますが(丸暗記しにくくなるため)、テストデータの正解率は上がりました。ここでも、強すぎると下がります。

Pythonで正則化を使う、よくある誤解と振り返り

scikit-learnでは、L2正則化の線形回帰は Ridge、L1正則化は Lasso です。alpha で罰の強さを決めます。

import numpy as np
from sklearn.linear_model import Ridge, Lasso

x = np.array([0.02, 0.10, 0.20, 0.25, 1.62, 3.26, 3.64, 3.82,
              4.38, 4.38, 4.88, 4.90, 5.14, 5.48, 5.61])
y = np.array([-0.20, -0.06, 0.11, 0.37, 1.31, -0.16, -0.07, -0.83,
              -0.84, -0.67, -0.96, -1.21, -1.18, -0.86, -0.56])

# 特徴量: x, x², x³, ..., x¹⁴ を横に並べた表(15行×14列)
X = np.column_stack([x ** k for k in range(1, 15)])
# 標準化: 列ごとに、平均を引いて標準偏差で割る(大きさをそろえる)
X = (X - X.mean(axis=0)) / X.std(axis=0)

ridge = Ridge(alpha=0.01).fit(X, y)
print(np.abs(ridge.coef_).max())     # → 約3.22(係数の最大の大きさ)

lasso = Lasso(alpha=0.001, max_iter=200000).fit(X, y)
print(np.sum(lasso.coef_ != 0))      # → 4(0でない係数の数)
  • np.column_stack は、配列を列として横に並べて表にします
  • 標準化: x14x^{14} は x=5x = 5 で約61億にもなり、xx とは桁がまったく違います。このまま正則化すると、係数の罰が列ごとに不公平になるので、列ごとに平均を引いて標準偏差で割り、どの列も「平均0、ばらつき1」にそろえてから当てはめます。第3章の前処理で学んだ「値の大きさをそろえる」の一つの方法です
  • max_iter は、Lassoの計算(少しずつ係数を動かす)を最大何回まで繰り返すかです

よくある誤解

  • 「正則化をすると、訓練データの正解率が下がるので、悪い変化」: 正則化は、訓練データへの当てはまりを少しあきらめる代わりに、新しいデータでの当てはまりをよくする方法です。見るべきはテストデータでの結果です
  • 「alphaは大きいほど安全」: 大きすぎると、係数を小さくしすぎて過少適合になります(α=10\alpha = 10 の曲線)
  • 「L1とL2は、どちらも係数を0にする」: ちょうど0にしやすいのはL1です。L2は全体を少しずつ小さくします

振り返り

  • 正則化は、損失に「係数(重み)の大きさの罰」を足して、係数が大きくなりすぎるのを防ぐ。過学習を抑えられる
  • L2(Ridge、weight decay)は係数の2乗の和、L1(Lasso)は絶対値の和。L1は多くの係数をちょうど0にする(スパース)
  • 罰の強さ α\alpha は、強すぎても弱すぎてもよくない

演習

演習を読み込んでいます…