前回の記事『【機械学習入門⑥】過学習とは?回避・対策する3つの方法も解説』では、過学習について学びました。
過学習は訓練データに過剰に適合してしまうことで、その対策方法は以下の3つでした。
- テストデータを準備する
- モデルを簡略化する
- 正則化する
今回は、過学習の対策方法の中でも「正則化」について紹介していきます。
この記事で学べる内容は、以下のとおりです。
- 正則化について
- Pythonを使ったLasso回帰・Ridge回帰の実装方法
正則化の概念は、機械学習を勉強していく上で、過学習とセットで大事な概念になります。しっかりおさえておきましょう。
この記事は、機械学習を体系的に学べる「機械学習入門講座」の1つです。
機械学習を勉強しようと思ったとき、以下のような悩みを持ったことはないですか?
- 機械学習を学んでみたいけど、何から勉強したら良いのか分からない
- 機械学習の本を買ってみたけど、よく分からなくて挫折した
- そもそも本を買うのではなく、まずは「無料」で学習してみたい
上記のような、「機械学習を勉強し始める前の悩み」を解決するために、この機械学習講座を作成しました。
「プログラミングスクールは高いし、参考書は途中で飽きちゃうし…」といった方は、ぜひ第1回から学習して頂けたらと思います(*’ω’*)
※ブログ記事なので、無料です。でも、作成に手は抜いていません。
Contents
正則化とは?

正則化とは、過学習を回避するための手法です。
具体例で確認する正則化
たとえば、以前学習した多項式モデルは、以下のような精度になっていました。
Train Score | Test Score | |
二次元モデル | 0.56 | 0.51 |
五次元モデル | 0.61 | 0.50 |
十次元モデル | 0.61 | 0.45 |
これは訓練データでは上手く学習できているけど、未知のデータ(=テストデータ)を与えると、モデルが上手く機能していないことを表しています。
この現象を回避して、未知のデータに対しても上手く機能できるようにするのが、「正則化」という手法になります。
正則化のイメージ
そもそも、過学習が発生する原因は、訓練データに過剰に適合してしまうためです。
「教科書や参考書は完ペキだけど、センター試験では上手く問題が解けなかった…」といった場合、これは教科書に過学習していたことになります。
少し言い換えると、過学習とは「一部のパラメータ\(w\)の絶対値」が大きくなりすぎているということになります。
実際に、『【機械学習入門⑥】過学習とは?回避・対策する3つの方法も解説』で実装した、多項式モデルのパラメータ\(w\)を見てみましょう。
\(w_1\) | -90657.12 |
\(w_2\) | -69130.40 |
\(w_3\) | 93877.48 |
\(w_4\) | -45895.36 |
\(w_5\) | 12792.76 |
\(w_6\) | -2252.20 |
\(w_7\) | 256.06 |
\(w_8\) | -18.31 |
\(w_9\) | 0.75 |
\(w_{10}\) | -0.01 |
こうやってみると、\(w_1\)〜\(w_5\)のパラメータが、他と比較して明らかに大きすぎます。
この大きくなりすぎているパラメーターを抑制するのが、正則化の持つ役割になります。
正則化の目標は、テストデータの精度(=今回なら決定係数)を向上させることです。
正則化の種類
正則化には、「L1正則化」と「L2正則化」があります。
各正則化手法のカンタンなイメージが、以下のとおりです。
- L1正則化 : 不要な特徴量を減らして、過学習を回避する方法
- L2正則化 : 特徴量を減らさずに、過学習を回避する方法
要するに『特徴量を減らすか否かが違う』ってことになります。
なお、各正則化手法を使った線形モデルには名前が付いています。
- L1正則化した線形モデル : Lasso回帰(ラッソ回帰)
- L2正則化した線形モデル : Ridge回帰(リッジ回帰)
たまに「Ridge回帰を使って予測しよう」といった文面が出てきますが、それは「L2正則化した線形モデルのことを言っているんだな」と認識しておけば大丈夫です。
Pythonを使ったLasso回帰とRidge回帰の実装方法【Scikit-learn】
それでは、Lasso回帰とRidge回帰を実装して、両者の違いを確認していきましょう。
今回使うソースコードは、以下のとおりです。
# ### 2.1 ライブラリのインポート import numpy as np import pandas as pd from sklearn.datasets import load_boston from sklearn.linear_model import LinearRegression, Lasso, Ridge from sklearn.model_selection import train_test_split from sklearn.preprocessing import PolynomialFeatures import matplotlib.pyplot as plt import seaborn as sns sns.set() sns.set_style("ticks") # ### 2.2 データの読み込み boston = load_boston() df = pd.DataFrame(boston.data, columns=boston.feature_names) df['MEDV'] = boston.target print(df.head()) # 散布図で確認 plt.figure(figsize=(10, 6)) sns.scatterplot(x=df.RM, y=df.MEDV, alpha=0.7) plt.show() # ### 2.3 データの準備 x = df['RM'] X = np.array(x).reshape(-1, 1) y = df['MEDV'] print('-'*10 + '特徴量とターゲットに分割' + '-'*10) print('X:', X.shape) print('y:', y.shape) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0) # ### 2.4 Lasso回帰・Ridge回帰の実装 def get_polynomial_features(degree:int, data): pf = PolynomialFeatures(degree=degree, include_bias=False) return pf.fit_transform(data) X_train_10 = get_polynomial_features(10, X_train) X_test_10 = get_polynomial_features(10, X_test) # 多項式モデルの作成 lm_10deg = LinearRegression() lm_10deg.fit(X_train_10,y_train) print('Poly') print('Train Score:{:.3f}' .format(lm_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(lm_10deg.score(X_test_10, y_test))) # Ridge回帰を使用した多項式モデルの作成 ridge_10deg = Ridge(alpha=1.0, random_state=0) ridge_10deg.fit(X_train_10,y_train) print('Ridge') print('Train Score:{:.3f}' .format(ridge_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(ridge_10deg.score(X_test_10, y_test))) # Lasso回帰を使用した多項式モデルの作成 lasso_10deg = Lasso(alpha=0.01, max_iter=2000, random_state=0) lasso_10deg.fit(X_train_10,y_train) print('Lasso') print('Train Score:{:.3f}' .format(lasso_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(lasso_10deg.score(X_test_10, y_test))) # ### 2.5 学習結果の可視化 X_test_a = np.array([np.sort(X_test[:,0])]).reshape(len(X_test),1) X_test_10 = get_polynomial_features(10, X_test_a) y_pred_10deg = lm_10deg.predict(X_test_10) y_pred_10deg_lasso = lasso_10deg.predict(X_test_10) y_pred_10deg_ridge = ridge_10deg.predict(X_test_10) plt.figure(figsize=(10, 6)) sns.scatterplot(X.reshape(len(X), ), y, alpha=0.7) sns.lineplot(x=X_test_a.reshape(len(X_test_a), ), y=y_pred_10deg, linewidth=2.5, label='Poly 10') sns.lineplot(x=X_test_a.reshape(len(X_test_a), ), y=y_pred_10deg_ridge, linewidth=2.5, label='Ridge') sns.lineplot(x=X_test_a.reshape(len(X_test_a), ), y=y_pred_10deg_lasso, linewidth=2.5, label='Lasso') plt.xlabel('RM') plt.ylabel('MEDV') plt.show() print(list(lm_10deg.coef_)) print(list(ridge_10deg.coef_))
順番に中身のソースコードを確認していきましょう。
STEP① : ライブラリの読み込み
import numpy as np import pandas as pd from sklearn.datasets import load_boston from sklearn.linear_model import LinearRegression, Lasso, Ridge from sklearn.model_selection import train_test_split from sklearn.preprocessing import PolynomialFeatures import matplotlib.pyplot as plt import seaborn as sns sns.set() sns.set_style("ticks")
今回使うライブラリは上記です。
- Numpy : 配列計算用ライブラリ
- Pandas : データ操作用ライブラリ
- Scikit-learn(sklearn) : データの準備・機械学習モデルの作成用ライブラリ
- Matplotlib : 作図用ライブラリ①
- Seaborn : 作図用ライブラリ②
主にScikit-learnを使って、Lasso回帰とRidge回帰を実装していきます。
STEP② : データの読み込み
boston = load_boston() df = pd.DataFrame(boston.data, columns=boston.feature_names) df['MEDV'] = boston.target print(df.head()) # 散布図で確認 plt.figure(figsize=(10, 6)) sns.scatterplot(x=df.RM, y=df.MEDV, alpha=0.7) plt.show()
今回もボストンの住宅価格を使います。
再掲 : ボストン住宅価格データセットの中身
変数名 | データの説明 |
CRIM | 犯罪発生率 |
ZN | 住居区画の密集度 |
INDUS | 非小売業の土地割合 |
CHAS | チャールズ川の周辺か否か |
NOX | NOx濃度 |
RM | 住居に含まれる平均部屋数 |
AGE | 1940年より前に建てられた物件の割合 |
DIS | ボストン市の5つの雇用施設からの重み付き距離 |
RAD | 大きな道路へのアクセスのしやすさ |
TAX | 1万ドルあたりの不動産税率の総計 |
PTRATIO | 教師あたりの生徒数 |
B | 対象地域に住む黒人の比率 |
LSTAT | 低所得者の割合 |
df.head()
を使うことで、これから使うデータの上から5行を、Excelのようにキレイな形で確認できます。

※Jupyter notebookの場合です。基本的にJupyter notebookの使用をおすすめします。
もう見慣れたかもしれませんが、、、一応散布図も可視化しておきましょう。
# 散布図を使った可視化 plt.figure(figsize=(10, 6)) sns.scatterplot(x=df.RM, y=df.MEDV, alpha=0.7) plt.show()

このデータに対して、Lasso回帰とRidge回帰を実装していきます。
STEP③ : データの準備
x = df['RM'] X = np.array(x).reshape(-1, 1) y = df['MEDV'] print('-'*10 + '特徴量とターゲットに分割' + '-'*10) print('X:', X.shape) print('y:', y.shape) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
Lasso回帰とRidge回帰を使ったモデルを作成するための準備をしていきます。
特徴量はRM、予測対象はMEDVですね。これを、train_test_split()
で学習用データとテスト用データに分割します。
STEP④ : Lasso回帰・Ridge回帰の実装
def get_polynomial_features(degree:int, data): pf = PolynomialFeatures(degree=degree, include_bias=False) return pf.fit_transform(data) X_train_10 = get_polynomial_features(10, X_train) X_test_10 = get_polynomial_features(10, X_test) # 多項式モデルの作成 lm_10deg = LinearRegression() lm_10deg.fit(X_train_10,y_train) print('Poly') print('Train Score:{:.3f}' .format(lm_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(lm_10deg.score(X_test_10, y_test))) # Ridge回帰を使用した多項式モデルの作成 ridge_10deg = Ridge(alpha=1.0, random_state=0) ridge_10deg.fit(X_train_10,y_train) print('Ridge') print('Train Score:{:.3f}' .format(ridge_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(ridge_10deg.score(X_test_10, y_test))) # Lasso回帰を使用した多項式モデルの作成 lasso_10deg = Lasso(alpha=0.01, max_iter=2000, random_state=0) lasso_10deg.fit(X_train_10,y_train) print('Lasso') print('Train Score:{:.3f}' .format(lasso_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(lasso_10deg.score(X_test_10, y_test)))
Lasso回帰とRidge回帰を実装するには、多項式モデルと同様に以下の手順が必要です。
- ①十次元の特徴量を作成する(※今回は十次元で実装するので)
- ②多項式モデルと同じ手順でモデルを作る
Ridge回帰やLasso回帰を実装する場合、モデルの式は多項式モデルと変わりません。
なので、多項式モデル\(y = w_0 + w_1 x + w_2 x^2 + \cdots w_{10} x^{10}\)の式で、\(x\)以外の①で不足している特徴量を、まずは作成していきます。
STEP① : 十次元の特徴量を作成する
def get_polynomial_features(degree:int, data): pf = PolynomialFeatures(degree=degree, include_bias=False) return pf.fit_transform(data) X_train_10 = get_polynomial_features(10, X_train) X_test_10 = get_polynomial_features(10, X_test)
上記のように、scikit-learnに入っているPolynomialFeatures()
を使って、十次元の特徴量を作成します。
以前、多項式モデルを作成したときは、以下のような二次元の特徴量を作成しました。

これが、今回は十次元まで特徴量を増やした形になります。
この特徴量作成が完了したら、あとはモデルを作成するだけです。
STEP② : 多項式モデルと同じ手順でモデルを作る
lm_10deg = LinearRegression() lm_10deg.fit(X_train_10,y_train) print('Poly') print('Train Score:{:.3f}' .format(lm_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(lm_10deg.score(X_test_10, y_test))) # Ridge回帰を使用した多項式モデルの作成 ridge_10deg = Ridge(alpha=1.0, random_state=0) ridge_10deg.fit(X_train_10,y_train) print('Ridge') print('Train Score:{:.3f}' .format(ridge_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(ridge_10deg.score(X_test_10, y_test))) # Lasso回帰を使用した多項式モデルの作成 lasso_10deg = Lasso(alpha=0.01, max_iter=2000, random_state=0) lasso_10deg.fit(X_train_10,y_train) print('Lasso') print('Train Score:{:.3f}' .format(lasso_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(lasso_10deg.score(X_test_10, y_test)))
あとは、多項式モデルと同様に、LinearRegression()
を使えばOKです。
念のため、前回作成した多項式モデルも一緒に、モデル作成しておきます。
それでは、学習結果を見てみましょう。
lm.score()
を使えばモデルの決定係数を確認できます。
print('Poly') print('Train Score:{:.3f}' .format(lm_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(lm_10deg.score(X_test_10, y_test))) print('Ridge') print('Train Score:{:.3f}' .format(ridge_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(ridge_10deg.score(X_test_10, y_test))) print('Lasso') print('Train Score:{:.3f}' .format(lasso_10deg.score(X_train_10, y_train))) print('Test Score:{:.3f}' .format(lasso_10deg.score(X_test_10, y_test)))
- Poly
- Train Score:0.615
- Test Score:0.448
- Ridge
- Train Score:0.609
- Test Score:0.495
- Lasso
- Train Score:0.599
- Test Score:0.489
上記のような結果になりました。「テストデータに対する精度は、Ridge回帰が最も良い」という結果になりましたね。
ちなみに、Lasso回帰だけmax_iter=2000
を指定しています。
これは「更新数」のことで、なぜ必要になるのかといえば、それは解析的に解が求まらないからです。
「解析的に解が求まらない」というのは、式変形だけでは数式が解けないということです。
※少し込み入った話になるので、今回は「よく分からないけど、簡単には解けないんだ〜」と認識しておけばOKです。
STEP⑤ : 学習結果の可視化
X_test_a = np.array([np.sort(X_test[:,0])]).reshape(len(X_test),1) X_test_10 = get_polynomial_features(10, X_test_a) y_pred_10deg = lm_10deg.predict(X_test_10) y_pred_10deg_lasso = lasso_10deg.predict(X_test_10) y_pred_10deg_ridge = ridge_10deg.predict(X_test_10) plt.figure(figsize=(10, 6)) sns.scatterplot(X.reshape(len(X), ), y, alpha=0.7) sns.lineplot(x=X_test_a.reshape(len(X_test_a), ), y=y_pred_10deg, linewidth=2.5, label='Poly 10') sns.lineplot(x=X_test_a.reshape(len(X_test_a), ), y=y_pred_10deg_ridge, linewidth=2.5, label='Ridge') sns.lineplot(x=X_test_a.reshape(len(X_test_a), ), y=y_pred_10deg_lasso, linewidth=2.5, label='Lasso') plt.xlabel('RM') plt.ylabel('MEDV') plt.show() print(list(lm_10deg.coef_)) print(list(ridge_10deg.coef_))
元データと、各モデルの予測結果を確認してみます。
- 多項式モデル : 青線
- Ridge回帰 : オレンジ線
- Lasso回帰 : 緑線

元の多項式モデルだと、RMが4〜6の範囲は、かなりの悪影響を受けていました。
でも、正則化することで外れ値からの悪影響を解消できていますね。
正則化は、大きくなりすぎているモデルのパラメータを抑制する手法でした。なので、最後にモデルのパラメータを確認しましょう。
print(list(lm_10deg.coef_)) print(list(ridge_10deg.coef_))
パラメータ | 多項式モデル | Ridge回帰 |
\(w_1\) | -90657.12 | -0.16 |
\(w_2\) | -69130.40 | -0.60 |
\(w_3\) | 93877.48 | -1.30 |
\(w_4\) | -45895.36 | -1.39 |
\(w_5\) | 12792.76 | 0.29 |
\(w_6\) | -2252.20 | 0.24 |
\(w_7\) | 256.06 | -0.10 |
\(w_8\) | -18.31 | 0.02 |
\(w_9\) | 0.75 | -0.001 |
\(w_{10}\) | -0.01 | 3.21 |
多項式モデルでは、かなり大きかったモデルパラメーターが、Ridge回帰することで大きくても3.2くらいまで抑えられるようになりました。
ほとんどのパラメーターの絶対値は、0〜1の範囲におさまっているので、しっかりと正則化できていることが分かりますね!
【機械学習入門⑦】正則化まとめ
というわけで、今回は以上です。
正則化について、理解できたのではないでしょうか。
次回は、もう少し正則化を深掘りして、ElasticNetについて学習していきます。
https://tech-diary.net/elastic-net/
ElasticNetとは、Lasso回帰とRidge回帰を混ぜ合わせた正規化手法です。
詳しくは次回解説しますね!
この記事が良いと思ったら、ぜひ次の『機械学習入門⑧』も学んで頂けたらと思います(*’ω’*)
なお、『機械学習入門シリーズ』は長いので、休みながら学習を進めてみてくださいね。
休憩するときは、記事をツイートしておくと便利ですよ!
※メンションに気が付いたら、拡散する可能性高いです(`・ω・´)!
おすすめの記事
https://tech-diary.net/elastic-net/