MLエンジニアへの道 #1 - 線形回帰モデル

Last Edited: 7/13/2024

この記事は線形回帰モデルや勾配降下法を紹介します。

ML

前回の記事「機械学習の真の姿とはどのようなものか」では、線形回帰について触れました。この記事では、コーディングに取り組み、 機械学習の重要な概念についてさらに詳しく説明します。

振り返り

前回の記事では、基本的な機械学習パイプラインについて説明しました。この記事では、同じパイプラインを通じて線形回帰モデルがどのように 使用できるかを示します。この記事を読む際には、Google Colabノートブックを開いて一緒にコードを書き進めることを強くお勧めします。 ノートブックの一番上に、以下のコードをコピー&ペーストしてください:

import sklearn
import pandas as pd
import seaborn as sns

上記のコードにより、Pythonで有用な機械学習パッケージにアクセスできます。

線形回帰モデル

ステップ 1. データ探索

機械学習の分野で広く使用されているデータセットがいくつかあり、アヤメデータセット(Iris Dataset) もその一つです。このデータセットには、 アヤメ属の3つの種、すなわちIris Setosa、Iris Versicolor、Iris Virginicaのデータがそれぞれ50行含まれています。 データセットを読み込むには、以下のコードを使用してください:

# sklearnからデータセットをインポート
from sklearn.datasets import load_iris
 
# アヤメデータを読み込む
iris = load_iris()
 
# pd DataFramesを作成
# 以下は、種のカテゴリを数値から文字に変換するコードです
iris_df = pd.DataFrame(data= iris.data, columns= iris.feature_names)
target_df = pd.DataFrame(data= iris.target, columns= ['species'])
def converter(specie):
    if specie == 0:
        return 'setosa'
    elif specie == 1:
        return 'versicolor'
    else:
        return 'virginica'
target_df['species'] = target_df['species'].apply(converter)
 
# DataFramesを結合する
iris_df = pd.concat([iris_df, target_df], axis= 1)
 
# データを表示する
iris_df

上記のコードをGoogle Colabで実行すると、以下のようなものが表示されるはずです:

Iris data

我々のタスクは、他の変数からがく片の長さ(sepal length)を予測することだとしましょう。前回と同様に、データを視覚化して分析してみましょう。 これを行うには、以下のコードを使用できます:

sns.pairplot(iris_df, hue= 'species')
Iris distribution

がく片の長さは種によって異なる分布を持ち、他の変数とも緩やかな正の相関があることが観察できます。この情報を使用して、どの変数をモデルに使用するかを選択できます。

ステップ 2. データ前処理

前のステップで得た情報に基づいて、データ前処理を進めましょう。すべての変数を使用することでがく片の長さをよりよく予測できることが分かりました。 したがって、データから列を削除する必要はありませんが、まだいくつかの作業が残っています。

まず、種の変数を数値変数に戻す必要があります。また、予測したい変数(この場合はがく片の長さ)を分離する必要があります。

# 種を数値に変換する
iris_df.drop('species', axis= 1, inplace= True)
target_df = pd.DataFrame(columns= ['species'], data= iris.target)
iris_df = pd.concat([iris_df, target_df], axis= 1)
 
# がく片の長さを分離する
X= iris_df.drop(labels= 'sepal length (cm)', axis= 1)
y= iris_df['sepal length (cm)']

独立変数にはX、予測したい従属変数にはyを使用するのが一般的です。さらに、データセットを訓練データとテストデータに分割する必要があります。

from sklearn.model_selection import train_test_split
 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size= 0.33, random_state= 101)

上記のコードでは、データセットをランダムに分割し、訓練データとテストデータがそれぞれ元のデータセットの67%と33%になるようにしています。

ステップ 3. モデル

便利なことに、sklearnには線形回帰の多くの作業を抽象化するLinearRegressionという事前定義されたオブジェクトがあります。 ただし、数学は基本的に前回の記事で説明したものと同じです。今回は複数のパラメータがあります:

sl=f(sw,pl,pw,s,ϕ)=ϕ1sw+ϕ2pl+ϕ2pw+ϕ3s+ϕ4 sl = f(sw, pl, pw, s, \phi) \\ = \phi_1sw + \phi_2pl+ \phi_2 pw + \phi_3 s + \phi_4

私たちは、偏微分を用いてより多くの係数を最適化し、方程式系を解く必要があります。以下のコードは、これを実現する方法です。

from sklearn.linear_model import LinearRegression
# LinearRegression()モデルを初期化
lr = LinearRegression()
 
# モデルをトレーニング/フィット
lr.fit(X_train, y_train)

前回の記事で説明したように、モデルのトレーニングには訓練データセットのみを使用します。

ステップ 4. モデルの評価

モデルのトレーニングが完了したので、テストする準備が整いました。まず、モデルを使用してがく片の長さを予測し、それを実際の値と比較する必要があります。

# 予測を行う
pred = lr.predict(X_test)

タスクの評価指標としてMSE(平均二乗誤差)とMAE(平均絶対誤差)の両方を使用しましょう。

from sklearn.metrics import mean_absolute_error, mean_squared_error
 
print('Mean Absolute Error:', mean_absolute_error(y_test, pred))
print('Mean Squared Error:', mean_squared_error(y_test, pred))
 
# 結果
# Mean Absolute Error: 0.26498350887555133
# Mean Squared Error: 0.10652500975036944

これで線形回帰モデルは終わりです。とても簡単ですね。

勾配降下法による線形回帰

sklearnの事前定義されたモデルを使用しましたが、自分で構築することもできます。ここでは、異なる学習アルゴリズムである勾配降下法を使用して自分のモデルを作成してみましょう。 すでに気づいたかもしれませんが、パラメータの数が増えるにつれて最適なパラメータセットを見つけるための方程式系を解くことがますます難しくなります。 複雑なケースにも使用できる最適なパラメータセットを得るための簡単な方法のその1つが勾配降下法です。

勾配降下法とは?

コスト関数が次のようであると仮定しましょう。

私たちが見つけたいのは、最小点に導くパラメータです。まずランダムなパラメータを選んだところ8.5であったとします。勾配を計算すると、8.5に触れる線、つまり次のような接線を導くことができます。

ここで、左に行き、傾斜を下ることで局所最小値に近づくことができることに気が付いたでしょうか。傾斜が負の場合、右に行って傾斜を下ることで局所最小値に近づくことができます。 つまり、傾斜の反対方向に進むことで常に局所最小値に近づくことができます。(傾斜が正の場合は負の方向に進み、その逆も同様です。)この手順を繰り返し行うことで、 局所最小値にかなり近づくことができます。これが勾配降下法の基本的な考え方です。これを次の式で表すことができます。

ϕt=ϕt1ddϕJ(ϕ) \phi_t = \phi_{t-1} - \frac{d}{d\phi}J(\phi)

ここで、ttはタイムステップであり、JJはコスト関数です。しかし、反対方向に遠くに行きすぎると、曲線の反対側に交差してしまい、 局所最小値に到達できなくなります。よって、反対方向に小さなステップを踏んで、徐々に局所最小値に降下することの方が理想的です。 これは学習率(α\alpha)を調整することで実現されます。学習率を勾配に乗じることで、ステップが小さくなります。

ϕt=ϕt1αddϕJ(ϕ) \phi_t = \phi_{t-1} - \alpha\frac{d}{d\phi}J(\phi)

勾配の計算

では、関数の勾配をどのように求めるのでしょうか?パラメータに関して偏微分を行うことで求めることができます。MSEをコスト関数として使用し、 偏微分を求めてみましょう。MSEの式は次の通りです:

MSE=1ni=1n(yif(xi,ϕ))2=J MSE = \frac{1}{n}\sum_{i=1}^{n}(y_i-f(x_i,\phi))^2 = J

チェーンルール(連鎖律)を使用して偏微分を取りましょう。

ϕJ=2ni=1n(yif(xi,ϕ))ddϕf(xi,ϕ) \frac{\partial}{\partial\phi} J = \frac{2}{n}\sum_{i=1}^{n}(y_i-f(x_i,\phi))\frac{d}{d\phi}f(x_i,\phi)

ffは線形関数であるため、ffの偏微分は傾きに対してはϕ\phi、切片に対しては1です。したがって、偏微分は次のようになります: 傾きに対しては:

ϕJ=2ni=1n(yif(x,ϕ))xi \frac{\partial}{\partial\phi} J = \frac{2}{n}\sum_{i=1}^{n}(y_i-f(x,\phi))x_i

切片に対しては:

ϕJ=2ni=1n(yif(x,ϕ)) \frac{\partial}{\partial\phi} J = \frac{2}{n}\sum_{i=1}^{n}(y_i-f(x,\phi))

これらの値に学習率を乗じて、パラメータから差し引くことでパラメータを最適化できます。

コードの実装

上記の概念を使用して、勾配降下法を用いた線形回帰モデルを自作することができます。

class LinearRegressionGD():
  def __init__(self, lr=0.01):
    self.W = np.zeros(X.shape[1])
    self.b = 0
    self.lr = lr # Learning rate
    self.history = [] # History of loss
 
  def predict(self, X):
    return np.sum(self.W*X + self.b, axis=1)
 
  def fit(self, X, y, epochs=100):
    for i in range(epochs):
      pred = self.predict(X)
      n = len(y)
 
      self.history.append(mean_squared_error(y, pred))
 
      diff = pred - y
      grad_W = np.sum((1/n)*diff[:, np.newaxis]*X, axis=0)
      grad_b = np.sum((1/n)*diff)
 
      self.W -= self.lr * grad_W
      self.b -= self.lr * grad_b
    return self.history

上記のモデルに訓練データを学習させて結果を見てみましょう。

# LinearRegressionGD() モデルの初期化
lrgd = LinearRegressionGD()
 
# モデルをトレーニング/フィット
history = lrgd.fit(X_train, y_train)

エポックごとのMSEを追跡し、勾配降下法がどのように機能しているかをプロットしてみましょう。

import matplotlib.pyplot as plt
plt.plot(history)
plt.title("MSE vs Epoch")
plt.ylabel("MSE")
plt.xlabel("Epoch")
plt.show()
Iris GD plot

上記のプロットから、エポックごとにMSEが徐々に減少していることがわかります。これは、勾配降下法が機能している良いサインです。 モデルをMSEとMAEで評価してみましょう。

pred = lrgd.predict(X_test)
print('Mean Absolute Error:', mean_absolute_error(y_test, pred))
print('Mean Squared Error:', mean_squared_error(y_test, pred))
 
# 結果
# Mean Absolute Error: 0.2809225334352517
# Mean Squared Error: 0.11678672963320082

LinearRegressionGDのMAEとMSEがLinearRegressionに近いことがわかります。方程式系を解く必要がないため、勾配降下法は、 これから学習するより複雑なモデルに対処する際に役立ちますので、この概念を完全に理解しておくべき概念といえます。

リソース