ふらふら Diary (仮)

興味のあることを適当に書いていく感じです

irisでロジスティック回帰

はじめに

ロジスティック回帰は、教師あり学習における分類を行うアルゴリズムである。scikit-learnライブラリでロジスティック回帰をする方法についてirisデータセットを使ってまとめてみた。

二値分類

ロジスティック回帰は、二値分類のアルゴリズムである。教師あり学習には以下の二つがある。

  • 回帰: 数値を予測する
  • 分類: カテゴリを予測する
アルゴリズム

基本的な考え方は線形回帰と同様で、データ \mathbf{x}に対して重みベクトル \mathbf{w}を掛けてバイアス w_0を加えた \mathbf{w}^T\mathbf{x}+w_0を計算する。ロジスティック回帰では確率を計算するため出力の範囲を0以上1以下に制限する必要がある。そのため、シグモイド関数を用いることで0から1の間の数値を返している。シグモイド関数は以下のようになる。
 \displaystyle \sigma (z) = \frac{1}{1+e^{-z}}
シグモイド関数を用いることで0から1の間の数値を返している。シグモイド関数をグラフに書くと次のような形状になる。

import math

# シグモイド関数
def sigmoid(x):
    y = 1 / (1 + math.e**(-x))
    return y

プロットしてみる。

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-10, 10, 100)
y = sigmoid(x)
plt.plot(x, y)
plt.show()

f:id:jetarinA:20200802102455p:plain
このシグモイド関数を用いて、データ  \mathbf{x} が与えられたときに、そのラベルが  y である確率  p p = \sigma (\mathbf{w}^T \mathbf{x} + w_0) で計算する。二値分類であれば  pが0.5未満のとき0、0.5以上のとき1というように分類を行う。
学習では、ロジスティック損失を誤差関数として用い、これを最小化する。最小化では勾配降下法を用いる。

具体例

irisのデータセットでロジスティック回帰をしてみる。

import pandas as pd
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

# irisデータセットの読み込み
data = load_iris()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.DataFrame(data.target, columns=['Species'])
df = pd.concat([X, y], axis=1)
df.head()
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) Species
0 5.1 3.5 1.4 0.2 0
1 4.9 3.0 1.4 0.2 0
2 4.7 3.2 1.3 0.2 0
0 4.6 3.1 1.5 0.2 0
0 5.0 3.6 1.4 0.2 0

irisのデータセットにはあやめの sepal length(がくの長さ)、sepal width(がくの幅)、petal length(花弁の長さ)、petal width(花弁の幅)に加え、それぞれの品種(setosa、versicolor、virginica)の情報がある。
今回は品種 setosa、versicolor のpatal length、petal widthのデータを使用してロジスティック回帰をしてみる。

# 品種 setosa、versicolorを抽出
df2 = df[(df['Species']==0) | (df['Species']==1)]
# 説明変数
X = df2.iloc[:, [2, 3]]
# 目的変数
y = df2.iloc[:, 4]
# 学習データと検証データを分割
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
model = LogisticRegression()
# 学習
model.fit(X_train, y_train)

切片と傾きを確認する。

# 切片
print(model.intercept_)
[-7.3018626]
# 傾き
print(model.coef_)
[[2.41092643, 1.01214654]]

ここで、決定境界をプロットしてみる。決定境界とは、分類結果が切り替わる境目のことで、ロジスティック回帰の場合、決定境界は確率を計算した結果がちょうど50%になる箇所を指す。
まず、以下の通り定義する。
 x_1: petal length (cm)
 x_2: petal width (cm)
バイアス:  w_0
重みベクトル \mathbf{w} = \left(\begin{array}{c}w_1 \\w_2 \end{array} \right)
データ \mathbf{x} = \left(\begin{array}{c}x_1 \\x_2 \end{array} \right)

データ \mathbf{x} が与えられたとき、そのラベルが  y である確率  p
 \displaystyle \sigma (z) = \frac {1}{1+e^{-z}}であるから
 p = \sigma(\mathbf{w}^T \mathbf{x} + w_0)
確率0.5のとき決定境界が求まるので
 \displaystyle \begin{eqnarray} \frac{1}{2} &=& \sigma (\mathbf{w}^T \mathbf{x} + w_0) \\ 
&=& \frac{1}{1 + e^{-(\mathbf{w}^T \mathbf{x} + w_0)}} \\
e^{-\mathbf{w}^T \mathbf{x} + w_0} &=& 1 \\
\mathbf{w}^T \mathbf{x} + w_0 &=& 0 \\
\left( \begin{array}{cc}w_1 & w_2 \end{array} \right) \left( \begin{array}{c} x_1 \\ x_2 \end{array} \right) + w_0 &=& 0 \\
w_1 x_1 + w_2 x_2 + w_0 &=& 0 \\
x_2 &=& -\frac{1}{w_2} (w_1 x_1 + w_0)
\end{eqnarray}
となり、これが平面の場合の決定境界となる。
Pythonでプロットしてみる。

w_0 = model.intercept_
w_1 = model.coef_[0, 0]
w_2 = model.coef_[0, 1]

x1 = np.linspace(0, 6, 30)
x2 = (-w_1 * x1 - w_0) / w_2
# プロット
plt.plot(x1, x2, color='gray')
plt.scatter(X.iloc[:, 0][y==0], X.iloc[:, 1][y==0], color='lightskyblue', label=data.target_names[0])
plt.scatter(X.iloc[:, 0][y==1], X.iloc[:, 1][y==1], color='sandybrown', label=data.target_names[1])
plt.ylim(-0.25, 2)
plt.xlabel(X.columns[0])
plt.ylabel(X.columns[1])
plt.legend()
plt.show()

f:id:jetarinA:20200807101705p:plain
今回はもともときれいに分かれていたので、決定境界もいい感じに引けていた。

特徴量の解釈について

ロジスティック回帰では各特徴量の係数を見ることができる。目的変数としてsetosaを0、versicolorを1として用いているとき、各特徴量は以下のようになる。

petal length (cm) petal width (cm)
2.41092643 1.01214654

先ほどのグラフの軸petal length、petal widthの重みは正の値だったため、どちらも大きくなるとversicolorである割合が大きくなることが分かる。
各特徴量の係数の符号を見ることで、正の影響を与えているのか負の影響を与えているのかが解釈できる。

おわりに

scikit-learnライブラリでロジスティック回帰をする方法についてirisデータセットを使って記述した。ロジスティック損失なども記述していけたらと思う。