ロジスティック回帰に対するloglossを偏微分して機械学習の学習フェーズの流れを学ぶ


## 前書き

筆者はこの分野を専門に学んでいたわけではないのですごく詳しいというわけではないです。
素人がこの分野を理解するにあたってこのような体系で理解しましたという一例と思って読んでください。

この記事では、偏微分などを扱うので、大学数学(解析学や線形代数)の基礎ぐらいの素養を持っている読者を想定して書いていますが、高校数学までの知識でも十分理解できる範囲かと思います。
<font color="Silver">ぶっちゃけ筆者も大学数学に造詣が深いわけではないのでそこまで高度なことは扱えません。</font>

なお、誤りの指摘は歓迎します。

## a. 機械学習における学習の流れ

はじめに、機械学習の学習の全体の流れを簡単に説明しておく。
必ずしも全ての場合に厳密にこの流れになるわけではないが、大枠は下図のようになる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74604/15243308-9380-1166-7f96-f6a2e4559f39.png" width=1200>

まず、全体として、モデル作成フェーズと、モデル活用フェーズに分かれる。

モデル作成フェーズでは、
1. 特徴量の作成などを行い、特徴量と正解ラベルから成るデータセットを用意する。(本記事では扱わないが実際はここが一番重要な工程)
   出来上がったデータセットを分割し、①学習に用いる教師データと、モデルを評価するためのバリデーションデータを用意する。
2. 予測に使う②モデルの種類を決定する。
3. ①の教師データを学習させ、③モデル(学習済)を作成する。
4. ①のバリデーションデータを③モデル(学習済)に入力し、バリデーションデータに対する④予測値を計算する。
5. ④の予測値から⑤評価値を算出し、③モデル(学習済)の良し悪しを判定する。
6. ⑤の評価値が不十分な場合は、①特徴量の追加の検討や、②モデル、モデルの学習のさせ方などを見直し、再度③モデル(学習済)を作成する。
   これを十分な評価値が得られるまで繰り返す。
7. ⑤の評価値が十分だと判断できる場合は③モデル(学習済)を⑥モデル(本番)として採用し、モデル活用フェーズに進む。

モデル活用フェーズでは、正解ラベルが分かってない、予測対象の⑦実データを⑥モデル(本番)に入力し⑧予測値を算出し、これを活用する。
上記の流れをコードで示すと次のようなイメージになる。

```python
# モデル作成フェーズ

from sklearn.model_selection import train_test_split

# prep_dfを特徴量などが作成済みのデータセット
# Survivedはこのデータセットにおける正解ラベル相当
df_x = prep_df.drop(columns=['Survived'])
df_y = prep_df['Survived']

# 1. ①の学習用データとバリデーションデータを用意
train_x, valid_x, train_y, valid_y = train_test_split(df_x, df_y, test_size=0.25, random_state=42)

# 2. ②のモデルの種類を選択
# ここではロジスティック回帰モデル
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(penalty='l2', C=1.0, random_state=42, max_iter=100)

# 3. モデルの学習を行いモデル(学習済)を得る
model.fit(train_x, train_y)

# 4. ①のバリデーションデータを③モデル(学習済)に入力とし④予測値を計算
y_pred = model.predict_proba(valid_x)

# 5. ④の予測値から⑤評価値を算出
# ここでは評価指標はlogloss
from sklearn.metrics import log_loss
logloss_valid = log_loss(y_true=valid_y, y_pred=y_pred)

# 6.logloss_validを見て評価値が十分かどうか検討する
# 十分だと判断した場合はこのモデルを⑥モデル(本番)とし、ファイルに保存しておく
import joblib
joblib.dump(model, 'model.joblib')
```

```python
# モデル活用フェーズ
# 予測値を使う本番環境で行う操作

# ⑥モデル(本番)をファイルから復元する
import joblib
model = joblib.load('model.joblib')

# ⑦の実データ
# これは正解ラベルが未知なのでモデルを使って予測値を算出する。
df

# ⑧の予測値を計算
prediction_df = model.predict_proba(df)
```

以降では主にモデル作成フェーズについて解説する。


## b. 教師データに関する図

この記事ではそれなりに数式に踏み込もうと思うので①の教師データと数式中で用いる記号の対応を最初に図示しておく。
なお、バリデーションデータについてもデータの形式は同じである。
![dataset.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74604/713aebfc-2351-681c-48e0-94b5f7b72227.png)


## c. 2値分類におけるロジスティック回帰モデル

ここでは、②のモデルの種類の1つとして、ロジスティック回帰モデルを取り上げる。
ロジスティック回帰はシンプルなモデルであるため、複雑な問題設定のデータに対する予測の精度は出づらいが、計算量が比較的少ないため、予測時の応答時間にシビアな2値分類のアプリケーションで利用されることがある。

2値分類では、目的変数は正例/負例のいずれかとなる。
正例を1、負例を0とすると、目的変数を表す確率変数$Y$は、$Y \in \\{0, 1\\}$ となる。 

$i$番目のレコードの目的変数の値を$y_i$、特徴量ベクトルを$\mathbf{x_i}$とする。
ロジスティック回帰モデルは、$y_i$が正例である確率 $Pr(y_i = 1|\mathbf{x_i})　を予測値 \widehat{y_i}$ としたとき、負例である確率$1 - \widehat{y_i}$ とのオッズ比の対数を線形結合でモデル化したものである。[^1]

つまり、　

```math
\begin{equation}
\ln(\frac{\widehat{y_i}}{1-\widehat{y_i}}) = w_0 + w_1x_{i1} + w_2x_{i2} + ... = w_0 + \sum_{j=1}^M w_j x_{ij}
\end{equation}
```

で表される。

右辺 $w_0 + \sum_{j=1}^M w_j x_{ij} = \theta_i$ とおくと、

```math
\begin{equation}
\frac{\widehat{y_i}}{1-\widehat{y_i}} = e^{\theta_i}
\end{equation}
```

これを$\widehat{y_i}$について解くと、

```math
\begin{equation}
\widehat{y_i} = \frac{1}{1 + e^{-\theta_i}}
\end{equation}
```

となり、シグモイド関数[^2]で表される。
<font color="Silver">これは中間層なしで活性関数にシグモイド関数を使ったニューラルネットワークと同じ...?</font>

## d. 教師あり学習における学習とは

教師あり学習では、教師データとして、特徴量ベクトル$\mathbf{x_i}$とそれに対して観測された正解ラベル$y_i$の組が複数与えられる。
学習とは、与えられた学習データから予測した予測値と正解ラベルとの関係性を評価する**目的関数**を最良にする**モデルパラメータ**を決定する工程である。

前項c.で紹介したロジスティック回帰モデルにおけるモデルパラメータは、各特徴量の重みを表す係数$(w_0, w_1, w_2, ...)$である。

冒頭の図のように、$i$番目のレコードの正解ラベルの値を$y_i$、モデルから算出される予測値を$\widehat{y_i}$とする。
2値分類では目的関数に**logloss**を用いることが多いため、これを目的関数にしたと仮定すると、次のような式で表される。

```math
\begin{eqnarray}
logloss &=& -\frac{1}{N} \sum_{i=1}^N (y_i log \widehat{y_i} + (1 - y_i)log (1 - \widehat{y_i})) \\ 
&=& -\frac{1}{N}  \sum_{i=1}^N (y_i log \frac{1}{1 + e^{-(w_0 +  \sum_{j=1}^M w_j x_{ij})}} + (1 - y_i)log(1 - \frac{1}{1 + e^{-(w_0 + \sum_{j=1}^M w_j x_{ij})}}))
\end{eqnarray}
```

loglossは小さい方が良い指標なので、与えられた教師データに対し、これを最小にするようなモデルパラメータ$(w_0, w_1, w_2, ...)$の値を求める。
この結果として、③のモデル(学習済)が得られる。


## e. 過学習と正則化項

#### 過学習

モデルが教師データに過剰に適合して、汎化性能を失ってしまうことを**過学習**という。
このトピックに関しては[PRML 1章](https://www.amazon.co.jp/%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E8%AA%8D%E8%AD%98%E3%81%A8%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92-%E4%B8%8A-C-M-%E3%83%93%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97/dp/4621061224)がわかりやすいので、この内容をベースに説明する。

:::note warn
この節の説明は2値分類ではなく、回帰をベースにした説明になっているので注意
:::

下図は、緑線で表される真のモデルに、正規分布に従うランダムノイズを乗せて擬似的に生成した学習データ(青丸)を用いてモデルを学習した際に、どのようなモデル(赤線)が生成されたかを示した図になっている。
ここまでの説明との対応をとると、緑線は正解ラベル$y$、横軸$x$は特徴量の値、赤線はモデルの予測値$\widehat{y}$に相当する。　
*M*は、書籍内ではモデルの次数を表すものとなっているが、ここでは「*M*が大きいほどモデルの複雑性が高い」ぐらいに理解しておけばよい。　
![overfitting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74604/13d5164b-9896-b62d-d39a-9f965e98186a.png)

ここで、訓練誤差と汎化誤差に着目して各モデルを見てみる。

- 訓練誤差: 学習データと予測値の誤差。つまり青丸と赤線の距離。
- 汎化誤差: 予測値と真の値の誤差。つまり赤線と緑線の距離。
![losses.001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74604/96be1ac5-bc89-db1d-899a-e7a1e05e1120.png)


*M*=0や*M*=1の場合、モデルの表現力が不十分で、訓練誤差、汎化誤差が共に大きい状態になっている。
*M*=3の場合が最も真のモデルに近いモデルが生成されており、汎化誤差が最も小さくなっている。
*M*=9の場合、訓練データにほぼ完全に適合したモデルが生成されている。訓練誤差はほぼ0に近い一方で、M=3の場合と比べると汎化誤差が大きくなっている。

前項d.で述べたように、教師あり学習では目的関数を改善するように学習を進める。
そのため、目的関数がloglossの場合、学習データとの誤差がなるべく小さくなる、可能な限り青丸の近くを通るようなモデルを学習しようとする。
*M*=9の場合のように、学習データの量に対してモデルの表現力が高すぎると、学習データのノイズに対しても訓練誤算を小さくすることができてしまうため、真のモデルと乖離したモデルが生成されてしまう場合がある。

この場合のように、訓練誤差は小さいが、汎化誤差が大きくなっている状態のことを **過学習(overfit)** という。
機械学習の目的は未知のデータを正しく予測することなので、**汎化誤差**が小さいことが望ましい状態である。そのため、過学習が起こらないように注意する必要がある。

#### 正則化

過学習を抑制するための手段の一つに**正則化**がある。
前節の*M*=9の場合のように、モデルが学習データに過剰に適合している状態では、訓練誤差を小さくするためにモデルパラメータ$(w_0, w_1, w_2, ...)$が大きい状態になる。
正則化は、目的関数に、「各モデルパラメータ$(w_0, w_1, w_2, ...)$の値が過剰に大きくなっていない方が良い」という観点を加えることで、過学習を抑制しようという発想のものである。

**正則化項** $R(\mathbf{w})$を導入して、次のように目的関数$\Phi(\mathbf{w})$を定義する。

```math
\Phi(\mathbf{w})　= logloss(\mathbf{w}) + \lambda R(\mathbf{w})
```
$\mathbf{w}$はモデルパラメータのベクトルで、$\mathbf{w} = (w_0, w_1, w_2, ...)^{\top}$である。
$\lambda$は、正則化項の重みを調整する定数で、**ハイパーパラメータ**と呼ばれるものである。これについては後の項で扱う。

正則化には、L1正則化と、L2正則化がありそれぞ正則化項は次の式で表される。

L1正則化の場合、

```math
R(\mathbf{w}) = \sum_{j=0}^M |w_j|
```

L2正則化の場合、
```math
R(\mathbf{w}) = \sum_{j=0}^M w_j^2
```

いずれについてもモデルパラメータが小さいほど小さくなるものになっている。
L1正則化は特定のモデルパラメータを0にしやすい性質があるため、特徴選択(予測に寄与しない特徴量を削ること)に用いられることも多い。


## f. モデルパラメータの最適化

最適化には様々な方法があるが、ここでは最も単純な例として **勾配降下法(最急降下法)** を紹介する。
過学習を抑制するため、前項e.で説明したL2正則化を採用した場合、目的関数$\Phi(\mathbf{w})$は次の式で表される。

```math
\begin{eqnarray}
\Phi(\mathbf{w}) &=& logloss(\mathbf{w}) + \lambda R(\mathbf{w}) \\
&=& -\frac{1}{N}  \sum_{i=1}^N (y_i log \frac{1}{1 + e^{-(w_0 + \sum_{j=1}^M w_j x_{ij})}} + (1 - y_i)log(1 - \frac{1}{1 + e^{-(w_0 + \sum_{j=1}^M w_j x_{ij})}})) + \lambda \sum_{j=0}^M w_j^2
\end{eqnarray}
```

ここで、$x_{ij}$、$y_i$は学習データから与えられる定数で、パラメータはモデルパラメータである$(w_0, w_1, w_2, ...)$であることに注意する。
この目的関数$\Phi(\mathbf{w})$が最小になるような、$\mathbf{w}$を求める。

<font color="Silver">高校数学までは、パラメータは基本$x$の方だったため、係数に見える$\mathbf{w}$側がパラメータであるというところは困惑するところかもしれない... ちなみに筆者はかなり混乱しました。</font>


#### 勾配降下法

$\mathbf{w}$に初期値を与え、$\Phi(\mathbf{w})$を小さくする方向に反復して$\mathbf{w}$を更新することで$\Phi(\mathbf{w})$を最小化するというのが最適化の基本的な考え方になる。
$\mathbf{w}$は、多次元のベクトルなので、$\Phi(\mathbf{w})$は多次元のスカラー場になるが、この関数が小さくなる方向を求める際に **勾配(gradient)** を利用するものを、**勾配降下法**という。
勾配[^3]は、対象の関数の値の変化量が最も大きい方向を向くベクトルで、その成分は関数を各次元方向に偏微分したものになる。

```math
\nabla \Phi(\mathbf{w}) = (\frac{\partial \Phi(\mathbf{w})}{\partial w_0}, \frac{\partial \Phi(\mathbf{w})}{\partial w_1}, \frac{\partial \Phi(\mathbf{w})}{\partial w_2}, ...)^{\top}
```

これを用いて次のような更新式でモデルパラメータを更新することを繰り返す。

```math
\mathbf{w^{(k+1)}} = \mathbf{w^{(k)}} - \eta \nabla \Phi(\mathbf{w})
```

この更新式の$j$番目の成分に着目すると、

```math
w_j^{(k+1)} = w_j^{(k)} - \eta \frac{\partial \Phi(\mathbf{w})}{\partial w_j}
```
と表される。

上記において、$\mathbf{w^{(k+1)}}$、$\mathbf{w^{(k)}}$は、それぞれ、k+1回目、k回目の更新が終わった時点でのモデルパラメータのベクトル。
$\eta$は **学習率**と呼ばれるもので、1回の反復でどの程度大きくモデルパラメータを更新するかを定める**ハイパーパラメーター**である。

下図[^4]は勾配降下法によるパラメータの更新の様子を図示したものになっている。
簡単のために1次元ではあるが、横軸wはモデルパラメータ $\mathbf{w}$に相当し、縦軸f(w)は目的関数$\Phi(\mathbf{w})$に相当するものである。

:::note warn math
$\mathbf{w}$は実際には多次元ベクトルなので、このような平面図では表現しきれない。    
f(w)についても実際の$\Phi(\mathbf{w})$ の外形を示しているものではないので注意。
:::
![gd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74604/1d98606e-1fd0-9eb0-9ee2-25c994322359.png)


$w$の初期値が$w=w^{(0)}$の黒丸の点で与えられたとき、パラメータwは$w=w^{(1)}, w^{(2)}, w^{(3)}$と順次更新されていく。
更新の大きさは黒矢印で示した通り、$-\eta\frac{\partial f(w)}{\partial w}$で表される。つまり、接線の傾きが大きいところほど更新の幅は大きくなり、方向は必ず接線の下り方向になる。
よって、適切な初期値と学習率$\eta$が設定できていれば、最適解に近づくにつれて更新幅が小さくなり、最終的には最適解に収束していく。

一方で、図中のf(w)のように多峰な関数だと、最適解に収束せず局所解に落ちてしまう場合がある。
図中の$w_{'}^{(0)}$のような初期値が与えられた場合、接線の下り方向は局所解の方向なので、最終的に収束するのは局所解の方になる。

学習率$\eta$も更新の幅に影響を与えるものである。これは、大きすぎると探索が発散してしまうが、小さすぎても探索にかかる時間が増加するため、適度な値を設定する必要がある。
目的関数の最適化もこういった様々な難しさがあるため、解法や、初期値の決め方、学習率の調整の仕方など含め様々な方法が提案されている。
この記事ではそれらの紹介はしないが、興味があれば調べてみると良い。<font color="Silver">というか筆者の守備範囲を超えているので他の記事に譲らせてください。</font>


ここで、目的関数をL2正則化付きのloglossとしたときの更新式を計算してみる。
勾配ベクトルのj番目の成分 $\frac{\partial \Phi(\mathbf{w})}{\partial w_j}$ は次のように計算できる。

```math
\begin{eqnarray}
\frac{\partial \Phi(\mathbf{w})}{\partial w_j} &=& -\frac{1}{N} \sum_{i=1}^N \frac{\partial}{\partial w_j} \{ y_i log \frac{1}{1 + e^{-(w_0 + \sum_{j=1}^M w_j x_{ij})}} + (1 - y_i)log(1 - \frac{1}{1 + e^{-(w_0 + \sum_{j=1}^M w_j x_{ij})}}) \} + \lambda  \sum_{k=0}^M \frac{\partial w_k^2}{\partial w_j} \\
&=& -\frac{1}{N} \sum_{i=1}^N \frac{\partial}{\partial w_j} \{ y_i log \frac{1}{1 + e^{-(w_0 + \sum_{j=1}^M w_j x_{ij})}} + (1 - y_i)log(1 - \frac{1}{1 + e^{-(w_0 + \sum_{j=1}^M w_j x_{ij})}}) \} + 2 \lambda w_j
\end{eqnarray}
```

ここで、計算を簡単にするために、
```math
\begin{eqnarray}
\theta_i &=& w_0 + \sum_{j=1}^M w_j x_{ij} \\
\widehat{y_i} &=& \frac{1}{1 + e^{-\theta_i}} 
\end{eqnarray}
``` 
とおくと、上の式は 

```math
\frac{\partial \Phi(\mathbf{w})}{\partial w_j} = -\frac{1}{N} \sum_{i=1}^N \frac{\partial}{\partial w_j} \{ y_i log \widehat{y_i} + (1 - y_i) log (1 - \widehat{y_i}) \} + 2 \lambda w_j
```

ここでさらに、
```math
L(\widehat{y_i}) = y_ilog\widehat{y_i} + (1 - y_i)log(1 - \widehat{y_i}) 
```
とおいたとき、目的関数は次のように表される。


```math
\Phi(\mathbf{w}) = -\frac{1}{N} \sum_{i=1}^N L(\widehat{y_i}) + \lambda \sum_{j=0}^M w_j^2 
```
このとき、合成関数の微分により、

```math
\begin{eqnarray}
\frac{\partial \Phi(\mathbf{w})}{\partial w_j} &=& -\frac{1}{N} \sum_{i=1}^N \frac{\partial L(\widehat{y_i})}{\partial w_j} + 2\lambda w_j \\
&=& -\frac{1}{N} \sum_{i=1}^N （\frac{\partial L(\widehat{y_i})}{\partial \widehat{y_i}} \cdot \frac{\partial \widehat{y_i}}{\partial \theta_i} \cdot \frac{\partial \theta_i}{\partial w_j}） + 2 \lambda w_j
\end{eqnarray}
```
となる。

各偏微分はそれぞれ、
```math
\begin{eqnarray}
\frac{\partial L(\widehat{y_i})}{\partial \widehat{y_i}} &=& \frac{y_i}{\widehat{y_i}} - \frac{1 - y_i}{1 - \widehat{y_i}} \\
\frac{\partial \widehat{y_i}}{\partial \theta_i} &=& \frac{e^{-\theta_i}}{(1 + e^{-\theta_i})^2} \\
\frac{\partial \theta_i}{\partial w_j} &=& x_{ij} (j \neq 0の場合) \\
\frac{\partial \theta_i}{\partial w_0} &=& 1
\end{eqnarray}
```
と計算でき、$\frac{\partial L(\widehat{y_i})}{\partial \widehat{y_i}} \cdot \frac{\partial \widehat{y_i}}{\partial \theta_i}$の値は、

```math
\begin{eqnarray}
\frac{\partial L(\widehat{y_i})}{\partial \widehat{y_i}} \cdot \frac{\partial \widehat{y_i}}{\partial \theta_i} &=& \{y_i(1 + e^{-\theta_i}) - \frac{(1 - y_i)(1 + e^{-\theta_i})}{e^{-\theta_i}} \} \cdot \frac{e^{-\theta_i}}{(1 + e^{-\theta_i})^2} \\
&=& \frac{y_ie^{-\theta_i}}{1 + e^{-\theta_i}} - \frac{1 - y_i}{1 + e^{-\theta_i}} \\
&=& \frac{(1 + e^{-\theta_i})y_i - 1}{1 + e^{-\theta_i}} \\
&=& y_i - \frac{1}{1 + e^{-\theta_i}} \\
&=& y_i - \widehat{y_i}
\end{eqnarray}
```

となる。よって、

ここまでの計算結果を代入すると、
```math
\begin{eqnarray}
\frac{\partial \Phi(\mathbf{w})}{\partial w_j} &=& \frac{1}{N} \sum_{i=1}^N (\widehat{y_i} - y_i)x_{ij} + 2 \lambda w_j (j \neq 0の場合) \\
\frac{\partial \Phi(\mathbf{w})}{\partial w_0} &=& \frac{1}{N} \sum_{i=1}^N (\widehat{y_i} - y_i) + 2 \lambda w_0
\end{eqnarray}
```

と求まる。これを用いてモデルパラメータの更新を行う。


## g. ハイパーパラメータ

学習によって、最適化されていく**モデルパラメータ**と異なり、予め決めておかないモデルの形や、学習の進め方などのパラメータを**ハイパーパラメータ**という。
ここまでで登場した、

- 正則化項の重み$\lambda$
- 学習時のモデルパラメータ更新の最大反復回数
- 学習率

などがハイパーパラメータに相当する。
これらは最終的なモデルの性能に影響を与えるので、モデルの作成とバリデーションを繰り返して最適なものを決める必要がある。

これらを決めるのに、最も単純な方法は**グリッドサーチ**と呼ばれるもので、ハイパーパラメータの値の組み合わせを適当な規則に則って作成し、それぞれについて汎化誤差を求め、最もスコアがよくなったハイパーパラメータを選ぶという方法である。ハイパーパラメータは相互作用があるものがあるので、組み合わせで探索する。

ロジスティック回帰は非常に単純なモデルなので、モデルの学習にかかる時間も短く、ハイパーパラメータの数も少ないため、グリッドサーチのような単純な方法でも比較的良いハイパーパラメータを求めやすいが、ニューラルネットワークなど、膨大なハイパーパラメータを持つモデルもあり、この調整に関しても様々な方法が提案されている。これらについてこの記事では扱わないが、興味があれば調べてみると良い。

<font color="Silver">
<br>これも筆者の守備範囲を超えているので他の記事に譲らせてください。<br>
複雑なモデルではもう少し探索の仕方を工夫する必要があり、<a href="https://optuna.org">oputuna</a>などを使って効率的に求めたりするようです。<br>
他にもGPUを使って1回のモデル作成にかかる時間を短縮したりする必要があるので、ある程度札束で殴るということが必要になる印象です。
</font>

## h. バリデーションとアーリーストップ

項f.の工程で、モデルは教師データに対する目的関数の値を可能な限り小さくするようなモデルパラメータを学習した。
機械学習の本来の目的は、未知の入力データに対して結果を予測することなので、学習したモデルが未知のデータに対しどの程度の予測性能を持っているかを評価する必要がある。
この工程のことを **バリデーション** という。

バリデーションを行う方法はいくつかあるが、最も単純なものは、正解ラベル付きのデータセットのうちの一部をバリデーションデータとして取り分け、残りのデータのみを教師データとして学習に用いる**ホールドアウト**と呼ばれる方法である。(項a.の図を参照)
これらを使って教師データに対する評価値である、**訓練誤差**と、バリデーションデータに対する評価値である**汎化誤差**を見比べることで主に、

- 汎用的なデータに対するモデルの予測性能が要求に対し十分な水準を満たしているか
- モデルが教師データを十分に学習できているか。
- 過学習が起きていないか

などを確認する。

前項f.で説明したように、モデルの学習はモデルパラメータを反復して更新することにより行われるので、各反復時のモデルに対する訓練誤差と、汎化誤差をプロットできる。
学習がうまく進んでモデルの性能が十分な場合には、下図のように訓練誤差、汎化誤差ともに小さい値に収束していく。
![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74604/d145eb8f-52fb-ab85-f9e6-736b83481f6a.png)
汎化誤差の収束値を見ることでモデルの性能が要求に対して十分かどうか確認する。

この例では、50回の反復を行っているが、10回未満の段階では、訓練誤差、汎化誤差ともに大きい状態で、反復ごとに評価値が改善される幅も大きい。
この10回未満の段階のような、訓練誤差、汎化誤差が大きいままの状態のことを **未学習(アンダーフィット)** という。
これを避けるためには、プロットを見て、まだ評価値の改善が大きい段階でモデルパラメータの更新を打ち切ってしまっていないかどうか確認すると良い。

別の例として、下図のようになることもある。
![download-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74604/231f685d-c32b-6c92-86bc-65bafbc68135.png)
この例では、訓練誤差は減少していっているが、汎化誤差は12回目の反復付近で上がり始めている。
この関係性が見られたときは、**過学習(オーバーフィット)** が発生していると判断できる。

このような場合は、程度を見つつ正則化項の重みを調整したり、**アーリーストップ**を設定して、汎化誤差が上がり始めた時点でモデルパラメータの更新を打ち切るなどの対応が考えられる。
アーリーストップではtoleranceという閾値を設け、$i$回目の反復時の汎化誤差$V_i$と$i+1$回目の反復時の汎化誤差$V_{i+1}$の差

```math
|V_{i + 1} - V_i| < tolerance
```
となった場合にモデルパラメータの更新を打ち切る。
これにより学習にかかる時間も短縮でき、過学習も抑制することができる。


## i. クロスバリデーション

前項h.ではバリデーションのためにホールドアウト法を説明したが、一般には **クロスバリデーション** という方法を使うことが多い。
この方法では、下図のように、正解ラベル付きデータセットをK個に分割して、分割したうちの一つをバリデーションデータ、残りを教師データとする操作を分割数分順繰りに繰り返す。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74604/70b16356-0cf9-2f82-f4ee-2ab5f7e9889e.png" width=1200>

この方法では、図のようにK個の異なるモデルとそれらそれぞれに対し評価値が算出される。
活用の際は、これらの評価値を総合的にみながらハイパーパラメータを調整し、改めて全データを用いてモデル(本番)を1つ作成する方法と、
各モデルをそのまま使って予測値をK個算出し、それらの平均を取る等して最終的な予測値を決定する**バギング**と呼ばれる方法がある。

## j. scikit-learnにおけるLogisticRegressionモデル

本記事で紹介した内容はほんの一部であるため、全ての対応を取れるわけではないが、
最後に本記事で登場した内容とscikit-learnにおけるLogistic RegressionモデルのAPIとの対応について触れておく。

[scikit-learn v1.2 LogisticRegression ](https://scikit-learn.org/1.2/modules/generated/sklearn.linear_model.LogisticRegression.html)

- penalty: 正則化の種類。本記事で紹介したL1, L2の他にelastic netも選択できる。
- C: 正則化項の重み $\lambda$の逆数。(つまり$C=\frac{1}{\lambda}$)
- tol: アーリーストップの基準値$\delta$
- max_iter: モデルパラメータ更新の最大反復回数。
- solver: モデルパラメータ最適化の方法。本記事では勾配降下法を紹介したが、scikit-learnで採用されているのはもう少し工夫がされているもの。

主に、ハイパーパラメータに相当するものがコンストラクタの引数になっていることがわかる。


## 終わりに

機械学習の学習フェーズをロジスティック回帰を例に数式にまで踏み込んで書いてみました。
近年は便利なライブラリがたくさんあるので、中身の動きを知らなくてもなんとなく実装できてしまうことが多いですが、中ではこういうことが起こっているのかというイメージを少しでも持っていただけると幸いです。

この記事中では、あまりコンペについては触れませんでしたが、実は社内での[Kaggleで勝つデータ分析の技術](https://www.amazon.co.jp/Kaggle%E3%81%A7%E5%8B%9D%E3%81%A4%E3%83%87%E3%83%BC%E3%82%BF%E5%88%86%E6%9E%90%E3%81%AE%E6%8A%80%E8%A1%93-%E9%96%80%E8%84%87-%E5%A4%A7%E8%BC%94/dp/4297108437) 4章の輪読会向けに作成したものです。
この記事では本書の内容を網羅していないので、より詳しく学びたい方はこちらも合わせて読んでいただくのが良いかと思います。

長い記事でしたが、読んでいただきありがとうございました。


[^1]: [wikipedia (ロジスティック回帰)](https://ja.wikipedia.org/wiki/%E3%83%AD%E3%82%B8%E3%82%B9%E3%83%86%E3%82%A3%E3%83%83%E3%82%AF%E5%9B%9E%E5%B8%B0)
[^2]: [wikipedia (シグモイド関数)](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%B0%E3%83%A2%E3%82%A4%E3%83%89%E9%96%A2%E6%95%B0)
[^3]: [wikipedia (勾配 (ベクトル解析))](https://ja.wikipedia.org/wiki/%E5%8B%BE%E9%85%8D_(%E3%83%99%E3%82%AF%E3%83%88%E3%83%AB%E8%A7%A3%E6%9E%90))
[^4]: [GeoGebra](https://www.geogebra.org/?lang=ja)を用いて作成。