分類アルゴリズム
- 目的変数0/1の2値として扱う(2値分類の場合)
- 回帰だと予測値が0~1の範囲を超えてしまうため、そのまま使うことができない
- 多クラス分類の場合、目的変数を0, 1, 2のように量的変数のように使うことができない
- なぜなら、量的変数にすると、数値間で距離、順序があると捉えられるから
ロジスティック回帰について
- 2値分類の基本的なアルゴリズムである. (回帰と書いてあるが、分類のアルゴリズム)
- いくつかの要因(説明変数)から、目的変数[0/1]が起こる確率を予測するアルゴリズム
例) 患者の情報(年齢、体重、血圧など)から、ある病気にかかるかどうかを予測する
回帰をそのまま分類に使うと、0~1の範囲を超えてしまう問題がある。どうにかして0~1の範囲に収める必要がある。そこで、シグモイド(ロジスティック)関数を用いる。シグモイド関数は以下のような数式である。 $$ f(x) = \frac{1}{1 + \exp(-x)} $$ このシグモイド関数をグラフに表すと、下のようになる。
このシグモイド関数のxに線形回帰を代入すれば、上記のように0~1の範囲に収めることができる。
出力した値は0~1になるから、確率して解釈することができる。
損失関数
損失関数とは、モデルが出力した予測値と正解値のズレ(差)を計算する関数である。
機械学習や深層学習では、損失関数の値を最小化するように学習を行っていく。
ロジスティック回帰の損失関数は、交差エントロピー(Cross entropy)を用いる。
まず、ロジスティック回帰は以下のように定義しておく。
$$ \hat{y_i} = \frac{1}{1 + \exp (-(\theta_0 + \theta_1 x))} $$
次に、データiにおける損失を$Cost(y, \hat{y_i})$とすると、交差エントロピーは以下のような数式で表される。
$$
Cost(y_i, \hat{y_i}) = -(y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i))
$$
これは、$y=0$と$y=1$で別々に考えるとわかりやすい。
- $y = 0$のとき、つまり、正解データが0であるとき
上記の式に、$y_i = 0$を代入すると、以下の式になる。
$$
Cost(y_i, \hat{y_i}) = -\log(1 - \hat{y}_i)
$$
例えば、予測した結果$ y_i = 0 $だった場合、正解データと同じだから、損失0であることはグラフや数式からわかる。
一方で、予測した結果が正解データ0より遠ければ遠いほど、的外れな予測をしているため、損失が大きいことがわかる。
- $y = 1$のとき、つまり、正解データが1であるとき
上記の式に、$y_i = 1$を代入すると、以下の式にある
$$
Cost(y_i, \hat{y_i}) = -\log(\hat{y}_i)
$$
$ y = 0$の時と同様に、予測した結果が$y_i = 1$だった場合、正解データと同じだから、損失が0であることはグラフや数式からわかる。
一方で、予測した結果が正解データ1より遠ければ遠いほど、的外れな予測をしているため、損失が大きいことがわかる。
しかし、先ほどの交差エントロピーの数式はあるデータ$i$の損失を求める式である。そして、データ数N個あるときの損失を求める式は以下のようになる。
$$
L(\theta) = -\frac{1}{N} \sum_{i=1}^{N} \left[ y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i) \right]
$$
最急降下法を用いて、最適なパラメータを見つける
損失関数を定義したら、最急降下法を用いて、最適なパラメータを見つけることができる。パラメータが$\theta_0, \theta_1$の2つを最急降下法を用いて、最適化すると、以下のような数式になる。
$$
\theta_0 := \theta_0 - \alpha \frac{\partial}{\partial \theta_0} L(\theta_0, \theta_1)
$$
$$
\theta_1 := \theta_1 - \alpha \frac{\partial}{\partial \theta_1} L(\theta_0, \theta_1)
$$
なお、学習率を$\alpha$とする。
そしてロジスティック回帰の式を$ \hat{y} = \frac{1}{1 + \exp(-(\theta_0 + \theta_1x))} $とすると、最急降下法で最適なパラメータ$\theta$を最適化する式は、以下のようになる。
まず、$\frac{\partial L}{\partial \theta_0} $は以下のようになる。
\frac{\partial L}{\partial \theta_0} =\frac{\partial L}{\partial \hat{y}_i}\frac{\partial \hat{y}_i}{\partial z}\frac{\partial z}{\partial \theta_0}
次に、それぞれの、$\frac{\partial L}{\partial \hat{y}_i}, \frac{\partial \hat{y}_i}{\partial z}, \frac{\partial z}{\partial \theta_0} $を求める。
\frac{\partial L}{\partial \hat{y}_i}= -\frac{1}{N} \sum_{i=1}^{N} \left(-\frac{y_i}{\hat{y}_i} + \frac{1 - y_i}{1 - \hat{y}_i} \right
)
\frac{\partial \hat{y}_i}{\partial z} = \sigma(z)(1 - \sigma(z))= \hat{y}_i(1- \hat{y}_i)
\frac{\partial z}{\partial \theta_0} = 1
そうすると、$\frac{\partial L}{\partial \theta_0}$は 次のようになる。
\begin{align*}
\frac{\partial L}{\partial \theta_0} &= \frac{\partial L}{\partial \hat{y}_i}\frac{\partial \hat{y}_i}{\partial z}\frac{\partial z}{\partial \theta_0} \\
&= -\frac{1}{N} \sum_{i=1}^{N} \left( \frac{y_i}{\hat{y}_i} - \frac{1 - y_i}{1 - \hat{y}_i} \right) \cdot \hat{y}_i(1 - \hat{y}_i) \\
&= -\frac{1}{N} \sum_{i=1}^{N} (y_i - \hat{y}_i)
\end{align*}
この$(y_i - \hat{y}_i)$は正解値と予測値のさになっていることがわかる。
同様に、$\frac{\partial L}{\partial \theta_1}$は以下のようになる。
\frac{\partial L}{\partial \theta_1}= -\frac{1}{N} \sum_{i=1}^{N} (y_i - \hat{y}_i)x_i
したがって、パラメータ$\theta_0, \theta_1$を更新する式は以下のようになる。
$$
\theta_0 := \theta_0 + \alpha\frac{1}{N} \sum_{i=1}^{N} (y_i - \hat{y}_i)
$$
$$
\theta_1 := \theta_1 + \alpha\frac{1}{N} \sum_{i=1}^{N} (y_i - \hat{y}_i)x_i
$$
実際にロジスティック回帰を使って、学習してみる
実際にロジスティック回帰を使って、学習してみる
今回はタイタニックのデータを使って、ロジスティック回帰を行う。単にロジスティック回帰を使うこととして、より精度の高い手法があると思うが、それは一旦置いておく。
# ライブラリ
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# 評価指標
from sklearn.metrics import log_loss, accuracy_score
# Titanicデータセットの読み込み
df = sns.load_dataset("titanic")
# 今回は欠損値のある行は削除
df.dropna(inplace=True)
# 説明変数と目的変数を設定
X = df.loc[:, (df.columns!='alive') & (df.columns!='survived')]
X = pd.get_dummies(X, drop_first=True)
y = df['survived']
# 学習データとテストデータに分ける
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
model = LogisticRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)
# 評価
logloss = log_loss(y_test, y_pred_proba)
accuracy = accuracy_score(y_test, y_pred)
print('logloss:', logloss)
print('accuracy:', accuracy)
出力結果
logloss: 0.41115807724466186
accuracy: 0.7636363636363637
まとめ
- 線形回帰の結果をシグモイド関数を使って0~1の範囲に収める
- 損失関数は交差エントロピーを用いる
参考
[1] https://datawokagaku.com/logistic_reg/
[2] https://www.udemy.com/course/mlpython-2/