本記事について
ラビットチャレンジの機械学習のレポートです。
ラビットチャレンジはStudy-AI開講のE資格受験資格を得られる認定プログラムとなっています。
線形回帰モデル
- 線形回帰モデルとは
- 線形とは比例
- 方程式の式変形
- 線形とは比例
{\displaystyle \begin{align} y &= a_{0} + a_{1}x_{1} + a_{2}x_{2} + … + a_{n-1}x_{n-1}\\ &= a_{0} + \sum_{i=1}^{n-1}a_{i}x_{i} \\ &= \sum_{i=0}^{n-1}a_{i}x_{i} (ただしx_{0} = 1) \\ &= \boldsymbol{a^T}\boldsymbol{x} \end{align}}
-
回帰問題
- ある入力(離散あるいは連続値)から出力(連続値)を予測する問題
- 直線で予測:線形回帰
- 曲線で予測:非線形回帰
- ある入力(離散あるいは連続値)から出力(連続値)を予測する問題
-
回帰で扱うデータ
- 入力(各要素を説明変数もしくは特徴量と呼ぶ)
- m次元のベクトル(m=1の場合はスカラ)
- 出力(目的変数)
- スカラー値(目的変数)
-
説明変数
${\displaystyle \boldsymbol{x} = (x_{1}, x_{2}, ... , x_{m}, )^T \in \mathbb{R} }$
($\mathbb{R} $は実数全体の集合を表す) -
目的変数
$y \in \mathbb{R} $
-
- スカラー値(目的変数)
- 入力(各要素を説明変数もしくは特徴量と呼ぶ)
-
線形回帰モデル
-
線形問題を解くための機械学習モデルの一つ
-
教師あり学習
- 教師データ
${(x_{i}, y_{i});i = 1, ... , n}$
-
入力とm次元パラメータの線形結合を出力するモデル
-
慣例として予測値にはハットを付ける(正解データとは異なるため)
-
パラメータ
$ {\displaystyle \boldsymbol{w} = (w_{1}, w_{2}, ... , w_{m}, )^T \in \mathbb{R^m} }$ -
線形結合
${\displaystyle \hat{y} = \boldsymbol{w^Tx} + w_{0} = \sum_{j=1}^{m}w_{j}x_{j} + w_{0}}$
-
-
-
-
線形結合
- 入力ベクトルと未知のパラメータの各要素を掛け足し合わせたもの
- 入力ベクトルとの線形結合に加え、切片も足し合わせる
- 入力のベクトル型次元でも出力ア1次元(スカラ)となる
-
モデルのパラメータ
-
特徴量が予測値に対してどのように影響を与えるかを決定する重みの集合
- 正の(負の)重みを付ける場合、その特徴量を増加させると、予測の値が増加(減少)する
- 重みが大きければその特徴量は予測に大きな影響力を持つ
- 重みが0の場合はその特徴量は予測に全く影響しない
-
切片
- y軸との交点を示す
-
パラメータは最小二乗法によって推定する
-
-
パラメータの決定方法
-
説明変数が1次元の場合
- 単回帰モデルと呼ぶ
-
データへの仮定
-
データは回帰直線に誤差が加わり観測されていると仮定する
- モデル数式
$y = w_{0} + w_{1}x_{1} + ε$
$y$:目的変数(既知)
$w_{0}$:切片(未知)
$w_{1}$:回帰係数(未知)
$x_{1}$:説明変数(既知)
$ε$:誤差
- モデル数式
-
-
-
連立方程式と行列の変換
- 連立方程式
{\displaystyle \begin{align} y_{1} &= w_{0} + w_{1}x_{1} + ε_{1} \\ y_{2} &= w_{0} + w_{1}x_{2} + ε_{2} \\ y_{n} &= w_{0} + w_{1}x_{n} + ε_{n} \\y_{i} &= w_{0} + w_{1}x_{i} + ε_{i}\\ &= \begin{pmatrix} 1 & x \end{pmatrix} \begin{pmatrix} w_{0} \\ w_{1} \end{pmatrix} + ε_{i}\end{align}}
行列に変換する
$ \boldsymbol{y =Xw + ε} $
誤差を無視すると下記のように変形できる
$$\boldsymbol{y =Xw}$$
{\displaystyle\begin{align} \begin{pmatrix} y_{1} \\ y_{2} \\ ... \\ y_{n}\end{pmatrix} &= \begin{pmatrix} 1 & x_{0} \\ 1 & x_{1} \\ ... & ...\\ 1 & x_{n} \end{pmatrix} \begin{pmatrix} w_{0} \\ w_{1} \end{pmatrix} \end{align}}
-
重回帰モデル
-
説明変数が多次元の場合
- 重回帰モデルと呼ぶ
- 重回帰は曲面で示される
-
データへの仮定
- データは回帰曲面に誤差が加わり観測されていると仮定する
- モデル数式
$y = w_{0} + w_{1}x_{1} + w_{2}x_{2} + ε$
$y$:目的変数(既知)
$w_{0}$:切片(未知)
$w_{1}$:回帰係数(未知)
$x_{1}$:説明変数(既知)
$w_{2}$:回帰係数(未知)
$x_{2}$:説明変数(既知)
$ε$:誤差
- モデル数式
- データは回帰曲面に誤差が加わり観測されていると仮定する
-
-
データの分割とモデルの汎化性能測定
- データの分割
- 学習用データ:機械学習モデルの学習に利用するデータ
- 検証用データ:学習済みモデルの精度を検証するためのデータ
- なぜ分割するか
- モデルの汎化性能を測定するため
- データへの当てはまりの良さではなく、未知のデータに対してどれくらい精度が高いかを測りたい
- データの分割
-
最小二乗法
- 平均二乗誤差
- データとモデルの出力の二乗誤差の和
- 小さいほど直線とデータの距離が近い
- パラメータのみに依存する関数
- データは既知の値でパラメータのみ未知
- データとモデルの出力の二乗誤差の和
- 平均二乗誤差
-
最小二乗法
- 学習データの平均二乗誤差が最小になるようなパラメータを探索
- 学習データの平均二乗誤差の最小化は、その勾配が0になる点を求めればよい
- 一般的に外れ値に弱い
- 損失関数を学ぶのにおすすめ書籍「イラストで学ぶ機械学習」
- 平均二乗誤差は下記の式で求められる
$${\displaystyle MSE_{train} = \frac{1}{n_{train}} \sum_{i=1}^{n_{train}}(\hat{y_{i}}^{(train)}-y_{i}^{(train)})^2}$$ - MSEを最小にするような$w$(m次元)は下記の式
$${\displaystyle \hat{w} = arg \quad min\quad MSE_{train}} \quad (w \in \mathbb{R^{m+1}})$$ - MSEを$w$に関して微分したものが0となる点を求めるには下記の式
$$ \frac{\partial}{\partial w}MSE_{train} = 0 $$
上の式を計算していくと下記のようになる
{\displaystyle \begin{align} \boldsymbol{\hat{w}} = arg \quad min\quad MSE_{train} \\
\frac{\partial}{\partial w}MSE_{train} \end{align}}
{\displaystyle \begin{align}
&\Rightarrow \frac{\partial}{\partial w} \Biggl( \frac{1}{n_{train}} \sum_{i=1}^{n_{train}} (\hat{y_{i}}^{(train)}-y_{i}^{(train)})^2\Biggr) = 0 \\
&\Rightarrow \frac{\partial}{\partial w} \Biggl( \frac{1}{n_{train}} \sum_{i=1}^{n_{train}} (\boldsymbol{X_{i}^{(train)T}w} - y_{i}^{(train)})^2\Biggr) = 0 \\
&\Rightarrow \frac{\partial}{\partial w} \Biggl(\frac{1}{n_{train}}(\boldsymbol{X^{(train)}w - y^{(train)}})^T (\boldsymbol{X^{(train)}w - y^{(train)}})\Biggr) = 0 \\
&\Rightarrow \frac{1}{n_{train}} \frac{\partial}{\partial w} \Biggl(\boldsymbol{X^{(train)T}w^T X^{(train)}w} -2 \boldsymbol{X^{(train)T}w^T y^{(train)}} + \boldsymbol{y^{(train)T} y^{(train)}}\Biggr) = 0 \\
&\Rightarrow 2\boldsymbol{X^{(train)T}X^{(train)}w} -2 \boldsymbol{X^{(train)T}y^{(train)}} = 0 \\
&\Rightarrow \boldsymbol{\hat{w}} = (X^{(train)T}X^{(train)})^{-1}X^{(train)T}y^{(train)}
\end{align}}
よって回帰係数は
$$ {\displaystyle\boldsymbol{\hat{w}} = (X^{(train)T}X^{(train)})^{-1}X^{(train)T}y^{(train)}} $$
予測値は$y = Xw$であるので
$$ {\displaystyle\boldsymbol{\hat{y}} = X(X^{(train)T}X^{(train)})^{-1}X^{(train)T}y^{(train)}} $$
ハンズオン
-
設定
- ボストンの住宅データセットを線形回帰モデルで分析
- 適切な査定結果が必要
- 高すぎても安すぎても会社に損害がある
-
課題
- 部屋数が4で犯罪率が0.3の物件はいくらになるか
-
実施結果
# 必要なモジュールをインポート
from sklearn.datasets import load_boston
from pandas import DataFrame
import numpy as np
# ボストンデータをbostonというインスタンスにインポート
boston = load_boston()
# データの中身を確認
# カラム'target'は住宅価格であるから必ず正の値になる。業務などではこのあたりの確認(前処理)やデータの上限などの確認が重要。
print(boston['target'])
# > [24. 21.6 34.7 33.4 36.2 ... 17.5 20.2 18.2 13.6 19.6 15.2 14.5 15.6 13.9 16.6 14.8 ... 11.9]
# データフレームの作成
# カラムに特徴量の名称、データにデータ内容を持つデータフレームを作成
df = DataFrame(data=boston.data, columns=boston.feature_names)
# 目的変数をDataFrameに追加
df['PRICE'] = np.array(boston.target)
# 最初の12行を出力
# ぱっと見でおかしなデータがないか確認してみる
df.head(12)
線形単回帰分析(変数が1つ)
# カラムを指定してデータを表示
# RMは部屋数
df[['RM']].head(5)
# RMを説明変数としてdataに保存
# loc[取得する行(:は全部), 取得する列]
data = df.loc[:, ['RM']].values
# dataリストの表示
data[0:5]
# > array([[6.575],[6.421],[7.185],[6.998], [7.147]])
# 目的変数を格納
target = df.loc[:, ['PRICE']].values
# 線形回帰を行うためモジュールをインポートする
from sklearn.linear_model import LinearRegression
# 線形回帰用のオブジェクトを生成
model = LinearRegression()
model.get_params()
# fit関数を用いてパラメータ(講義内のw)を推定 -> これで学習が終了
model.fit(data, target)
# 予測を行う
# 部屋数1の場合
model.predict([[1]])
# > array([[-25.5685118]])
重回帰分析(変数が2つ)
# 犯罪率と部屋数という二つの指標の値を表示
df[['CRIM', 'RM']].head()
# 説明変数に'CRIM'と'RM'を指定
data2 = df.loc[:, ['CRIM', 'RM']].values
# 目的変数を設定
target2 = df.loc[:, 'PRICE'].values
# オブジェクトを生成
model2 = LinearRegression()
# パラメータの推定
model2.fit(data2, target2)
# 【問題】
# 犯罪率0.3、部屋数4の値を予測
model2.predict([[0.3, 4]])
# > array([4.24007956])
- 所感
実行自体は簡単に行うことができた。推定はうまくできなかった場合もあった。部屋数によってはデータ群に十分なデータ量がない場合があるんだと思う。データの妥当性を考えるということが重要であり難しいことが分かった。
非線形回帰モデル
-
非線形回帰モデル
-
複雑な非線形構造を内在する減少に対して、非線形回帰モデリングを実施
- データの構造を線形で捉えられる場合は限られる
例) $ y= w_{0} + w_{1}x + w_{2}x^2 + w_{3}x^3 $
$\Rightarrow x$の代わりに$\phi(x)$を利用する
※$w$が線形であることは変わらない
予測モデル:$\hat{y} = w_{0} + w_{1}\phi_{1}(x) + w_{2}\phi_{1}(x) + w_{3}\phi_{1}(x)$
→これは重み$w$について線形(linear-in-paramater) - 非線形な構造を捉えられる仕組みが必要
- データの構造を線形で捉えられる場合は限られる
-
基底展開法
- 回帰関数として、基底関数と呼ばれる既知の非線形関数とパラメータベクトルの線形結合を使用
- 未知パラメータは線形回帰モデルと同様に最小二乗法や最尤法により推定
$$ {\displaystyle y_{i} = f(x_{i})+ \epsilon}$$
$$ {\displaystyle y_{i} = w_{0} + \sum_{j=1}^{m}w_{j}\phi_{j}(x_{i}) + \epsilon_{i}}$$
-
よく使われる基底関数
- 多項式関数
- ガウス型基底関数
- スプライン関数/Bスプライン関数
-
1次元の基底関数に基づく非線形回帰
- 多項式関数
基底関数$\phi(x)$に多項式関数$\phi_{j} = x^j$を考える
$${\displaystyle \begin{align} \hat{y} &= w_{0} + w_{1}\phi_{1}(x) + w_{2}\phi_{2}(x) + \cdots + w_{9}\phi_{9}(x) \ &= w_{0} + w_{1}x_{1} + w_{2}x_{2} + \cdots + w_{9}x_{9}\end{align}}$$ - 多項式関数
$$ {\displaystyle \phi_{j}(x) = \exp \Biggl(\frac{(x-\mu_{j})^2}{2h_{j}}\Biggl)}$$
- 多項式関数
-
求め方
- 説明変数
$x_{i} = (x_{i1}, x_{i2}, \cdots ,x_{im}) \in \mathbb{R}^m$ - 非線形関数ベクトル
$\phi(x_{i}) = (\phi_{1}(x_{i}),\phi_{2}(x_{i}), \cdots, \phi_{k}(x_{i}))^T \in \mathbb{R}^k$ - 非線形関数の計画行列
$\phi^{(train)} = (\phi(x_{1}),\phi(x_{2}), \cdots, \phi(x_{n}))^T \in \mathbb{R}^{n×k}$ - 最尤法による予測値
${\displaystyle\boldsymbol{\hat{y}} = \Phi(\Phi^{(train)T}\Phi^{(train)})^{-1}\Phi^{(train)T}y^{(train)}}$
※$\Phi$の中身は$X$であるため基底展開法も線形回帰と同じ枠組で推定可能
- 説明変数
-
未学習(underfitting)と過学習(overfitting)
- 未学習
学習データに対して十分な誤差が得られないモデル- (対策)モデルの表現力が低いため、表現力の高いモデルを利用する
- 過学習
学習データに対する誤差は小さいが、テスト集合誤差との差が大きいモデル
※こちらが起こりやすい- (対策1)学習データの数を増やす
- (対策2)不要な基底関数(変数)を削除して表現力を抑制
- (対策3)正則化法を利用して表現力を抑制
適切な表現力のモデルを作成することが大切
- 未学習
-
対策2:不要な基底関数を削除
- 基底関数の数、位置やバンド幅によりモデルの複雑さが変化
- 解きたい問題に対して多くの基底関数を用意してしまうと過学習の問題が起こるため適切な基底関数を用意(CVなどで選択)
-
対策3:正則化法(罰則化法)
- 「モデルの複雑さに伴って、その値が大きくなる正則化項(罰則項)を課した関数」を最小化
- $MSE$を求める際に元々の$w$が大きいとき$w$を抑えるため実施する
- 正則化項(罰則化項)
- 形状によっていくつもの種類があり、それぞれの推定量の性質が異なる
- 正則化(平滑化)パラメータ
- モデルの曲線の滑らかさを調節するため、適切に決める必要がある
$$ S_{\gamma} = (y=\Phi w)^T(y=\Phi w) + \gamma R(w) \quad \gamma(>0)$$
※基底関数の数(k)が増加するとパラメータが増加し、残差は減少(モデルは複雑化)
- モデルの曲線の滑らかさを調節するため、適切に決める必要がある
-
正則化項の役割
- 正則化項がない場合、最小二乗推定量(資料内の図の●)
- L2ノルムを利用する場合はRidge推定量
- L1ノルムを利用する場合はLasso推定量
※ノルムとは実数上のベクトル空間$V$に対しては任意の $x,y\in V$ と任意の実数 $a$ に対して以下の3つの性質を満たす関数 ||*|| のことである。
① $ ||\vec{x}|| = 0 \Leftrightarrow \vec{x} = 0$
② $ ||a\vec{x}|| = |a|||\vec{x}||$
③ $ ||\vec{x}|| + ||\vec{y}|| \geq ||\vec{x}+\vec{y}||$
$L^p$ノルムは代表的なノルムで、$\sqrt[p]{|x_1|^p+|x_2|^p+\cdots +|x_n|^p}$は上記の3つの性質を満たす。そのためL1ノルムはひし形、L2ノルムは円となる。
Lasso推定量はパラメータが0になりやすい。
-
-
汎化性能
- 学習に使用した入力だけでなく、新たな入力に対する予測性能
- (学習誤差ではなく)汎化誤差(テスト誤差)が小さいほど良い性能のモデルと言える
- 汎化誤差は通常、学習データとは罰に収集された検証データでの性能を測ることで推定する
- 手元のデータが未学習しているか過学習しているか
- 訓練誤差もテスト誤差もどちらも小さい→汎化しているモデルの可能性
- 訓練誤差は小さいがテスト誤差が大きい→過学習
- 訓練誤差も学習誤差も小さくならない→未学習
- 検証方法
- 【ホールドアウト法】
- 有限のデータを学習用とテスト用の二つに分割し、「予測精度」や「誤り率」を推定する
- 学習用を増やすとテスト用が減り学習制度はよくなるが、性能評価の精度は下がる
- テスト用を増やすと学習用が減少し、学習そのものの精度が悪くなる
- 手元にデータが大量にある場合を除いて、良い性能評価を与えないという欠点がある
- 外れ値が検証に含まれていた場合、外れ値にフィットするモデルが選ばれてしまう
- 基底展開法に基づく非線形回帰モデルでは、基底関数の数、位置、バンドの幅の値とチューニングパラメータをホールドアウト値を小さくするモデルで決定する
- 有限のデータを学習用とテスト用の二つに分割し、「予測精度」や「誤り率」を推定する
- 【交差検証法(クロスバリデーション)】
- 学習データ、テストデータの分割を複数回行い、それそれで学習と評価を行う
- 【グリッドサーチ】
- すべてのチューニングパラメータの組み合わせで評価値を算出
- 最もよいい評価値を持つチューニングパラメータを持つ組み合わせを、「良いモデルのパラメータ」として採用
- 【ホールドアウト法】
- 学習に使用した入力だけでなく、新たな入力に対する予測性能
ロジスティック回帰モデル
- 分類問題
- ある入力(数値)からクラスに分類する問題
- 分類で扱うデータ
- 入力(各要素を説明変数もしくは特徴量と呼ぶ)
- m次元のベクトル(m=1の場合はスカラ)
- 出力(目的変数)
- 0もしくは1の値
- 例:タイタニックデータ、IRISデータなど
- 説明変数
$$\boldsymbol{x} = (x_{1}, x_{2}, \cdots , x_{m})^T \in \mathbb{R}$$ - 目的変数
$$y \in {0,1}$$
- 入力(各要素を説明変数もしくは特徴量と呼ぶ)
- 分類に対する主要なアプローチ
- 識別的アプローチ
$p(C_{k}|\boldsymbol{x})$を直接モデル化する - 生成的アプローチ
$p(C_{k})$と$p(\boldsymbol{x}|C_{k})$をモデル化し、その後ベイズの定理を用いて$p(C_{k}|\boldsymbol{x})$を求める
$$ {\displaystyle \begin{align} p(C_{k}|\boldsymbol{x}) &= \frac{p(C_{k}, \boldsymbol{x})}{p(\boldsymbol{x})} \
&= \frac{p(\boldsymbol{x}|C_{k})p(C_{k})}{p(\boldsymbol{x})}
\end{align}} $$ - 識別関数の構成
関数の結果を作成して分類する
- 識別的アプローチ
- ロジスティック回帰モデル
- 分類問題を解くための教師あり機械学習モデル(教師データから学習)
- 入力とm次元パラメータの線形結合(今までにで求めていた$\hat{y}$)をシグモイド関数に入力→実数全体を0から1の値に変換
- 出力は$y=1$になる確率の値
- 分類問題を解くための教師あり機械学習モデル(教師データから学習)
- シグモイド関数
- シグモイド関数とは
- 入力は実数で出力は0~1の値になる
- クラス1に分類される確率を表現
- 単調増加関数
$$p_{i} = \sigma(w_{0}+w_{1}x_{i1}+w_{2}x_{i2} + \cdots +w_{m}x_{im})$$
- パラメータが変わるとシグモイド関数の形が変わる
- aを増加させるとx=0付近での曲線の勾配が増加
- aを極めて大きくすると単位step関数($x<0$で$f(x)=0$、$x>0$で$f(x)=1$となるような関数)に近づく
- バイアスの変化は段差の位置
- シグモイド関数とは
{\displaystyle \begin{align} \sigma(x) &= \frac{1}{1+\exp(-ax)} \\
&= \frac{\exp(ax)}{1+\exp(ax)}
\end{align}}
- シグモイド関数の性質
- シグモイド関数の微分はシグモイド関数自身で表現可能
- 尤度関数の微分を行う際にこの事実を利用すると計算が容易
- 表記は下記のようになる
{\displaystyle \begin{align} a(z) &= \frac{1}{1+\exp(-z)} \\ &= ({1+\exp(-z)})^{-1}
\end{align}}
{\displaystyle \begin{align} \frac{\partial \sigma(z)}{\partial z} &= -1(1+\exp(-z))^{-2} × \exp(-z)×(-1) \\
&= \frac{1}{1+\exp(-z)}×\frac{\exp(-z)}{1+\exp(-z)} \\
&= \sigma(z)×\frac{1+\exp(-z)-1}{1+\exp(-z)} \\
&= \sigma(z)(1-\sigma(x))
\end{align}}
$$p_{i} = \sigma(w_{0}+w_{1}x_{i1}+w_{2}x_{i2} + \cdots +w_{m}x_{im})$$
- 最尤推定
- データから、そのデータを生成したであろうもっともらしい分布(パラメータ)を推定する
- 尤度関数
$$P(y_{1},y_{2},\cdots, y_{n};p) = \prod_{i=1}^{n}p^{y_{i}}(1-p)^{1-y_{i}}$$ - ロジスティック回帰モデルの最尤推定
- 確率$p$はシグモイド関数となるため、推定するパラメータは重みパラメータとなる
- $(x_{1}, y_{1}),(x_{2}, y_{2}),\cdots,(x_{n}, y_{n})$を生成するに至ったもっともらしいパラメータを探す
- 尤度関数は下記の式で表され、下記尤度関数を最大化するパラメータを探す
\displaystyle \begin{align}
P(y_{1},y_{2},\cdots, y_{n}|w_{1},w_{2},\cdots, w_{n}) &= \prod_{i=1}^{n}p^{y_{i}}_{i}(1-p_{i})^{1-y_{i}} \\
&= \prod_{i=1}^{n}\sigma(w^T x_{i})^{y_{i}}_{i}(1-\sigma(w^T x_{i}))^{1-y_{i}} \\
&= L(w)
\end{align}
- 尤度関数を最大とするパラメータを探す(推定)
- 対数を取ると微分の計算が簡単
- 同時確率の積を和に変換可能
- 指数が積の演算に変換可能
- 対数尤度関数が最大になる点と尤度関数が最大になる点は同じ
- 対数関数は単調増加であるため
- 「尤度関数にマイナスをかけたものを最小化」し、「最小二乗法の最小化」と合せる
- 尤度関数の桁落ちを防ぐためには対数の利用が必須
- 対数を取ると微分の計算が簡単
\displaystyle \begin{align}
E(w_{1},w_{2},\cdots, w_{n}) &= -\log{L}(w_{1},w_{2},\cdots, w_{n}) \\
&= -\sum_{i=1}^{n}\Biggl(y_{i}\log{p_{i}} + (1-y_{i})\log{(1-p_{i})}\Biggl)
\end{align}
-
確率的勾配法
- パラメータが更新されなくなった場合、それは勾配が0になったということ
- 少なくとも反復学習で探索した範囲では最適解が求められたことになる
- 勾配下降法ではパラメータを更新するのにN個すべてのデータに対する和を求める必要がある
- nが巨大になったときにデータをオンメモリに乗せる容量が足りない、計算時間が膨大になるなどの問題があるため確率的勾配下降法を利用して解決
- 確率的勾配下降法とは
- データを一つずつランダムに選んでパラメータを更新
- 勾配下降法でパラメータを1回更新するのと同じ計算量でパラメータをn回更新できるため効率よく最適解を探索可能
-
混同行列
- 各検証データに対するモデルの予測結果を4つの観点で分類し、それぞれに当てはまる予測結果の個数をまとめた表
検証用 positive |
検証用 negative |
|
---|---|---|
モデル positive |
真陽性(True Positive) 正しくpositiveと判別した個数 |
偽陰性(False Positive) 間違えてpositiveと判別した個数 |
モデル negative |
偽陽性(False Negative) 間違えてnegativeと判別した個数 |
真陰性(True Negative) 正しくnegativeと判別した個数 |
- 分類の評価方法
- 正解率
- よく利用される
- 「正解した数/予測対象となった全データ数」で求められる
- 分類したいクラスに偏りがある場合はあまり意味をなさない
- 再現率(Recall)
- 本当にPositiveなものの中からPositiveと予測できる割合
- 誤りが多くても抜け漏れが少ない予測をしたい場合(病気の診断など)に利用
- $\frac{TP}{TP + FN}$で求められる
- 適合率(Precision)
- モデルがPositiveと予測した物の中で本当にPositiveである割合
- 見逃し(False Negative)が多くてもより正確な予測をしたい場合に利用
- $\frac{TP}{TP + FP}$で求められる
- F値
- 理想的にはどちらも高いモデルがいいモデルであるが、両者はトレードオフの関係になあり、どちらかを小さくするともう片方の値が大きくなってしまう
- 正解率
ハンズオン
-
設定
- タイタニックの乗客データを利用しロジスティック回帰モデルを作成
- 特徴量抽出をしてみる
-
課題
- 年齢が30歳で男の乗客は生き残れるか?
-
実施結果
# 必要なモジュールをインポート
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
# タイタニックデータの取得
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
# trainデータはSurvivedの項目があるため分離
y = train['Survived']
train = train[[col for col in train.columns if col != 'Survived']]
# train とtestを結合
train_and_test = pd.concat([train, test], axis = 0)
display(train_and_test)
# 欠損値がどれだけあるか確認
train_and_test.isnull().sum()
# 文字列を数字に置き換える
# 'Sex'列の男性を0、女性を1に変換
def code_transform_sex(x):
if x == 'male':
y = 0
else:
y = 1
return y
train_and_test['Sex'] = train_and_test['Sex'].apply(lambda x:code_transform_sex(x))
# 'Embarked'列をCなら0、Qなら1、Sなら2に置き換え
def code_transform_embarked(x):
if x == 'C':
y = 0
elif x == 'Q':
y = 1
else:
y = 2
return y
train_and_test['Embarked'] = train_and_test['Embarked'].apply(lambda x: code_transform_embarked(x))
# 数値に変換できない文字列の行(Name、Ticket、Cabin)と数字の列では別の前処理を行う
# 数字のみの列と文字のみの列を分離
numerical_col = [col for col in train_and_test.columns if train_and_test[col].dtype != 'object']
categorical_col = [col for col in train_and_test.columns if train_and_test[col].dtype == 'object']
train_and_test_num = train_and_test[numerical_col]
train_and_test_cat = train_and_test[categorical_col]
# 数値列の前処理を行う
# 欠損値はそれぞれの列の中央値で埋める
train_and_test_num.fillna(train_and_test_num.median(), inplace=True)
# 文字列の前処理を行う
# 欠損値には'missing'の文字を入れる
train_and_test_cat.fillna(value='missing', inplace=True)
# One-Hot-Encodingをして文字列を全て数値に変換
train_and_test_cat = pd.get_dummies(train_and_test_cat)
# 欠損値がなくなり数値のみのデータになったため結合して全体データに戻す
train_and_test_total = pd.concat([train_and_test_num, train_and_test_cat], axis=1)
train_and_test_total.head()
# 今回の解析では「Age」と「Sex」のみ利用するためデータを抽出
X = train_and_test_total.loc[:,['Age','Sex']]
# 上記のtrain_and_test_totalはテストデータも含むため学習データのみ抽出
train_rows = train.shape[0]
X = X[:train_rows]
# データを標準化
std = StandardScaler()
X = std.fit_transform(X)
# モデルの作成
logreg = LogisticRegression(class_weight='balanced')
logreg.fit(X, y)
# Age=30、Sex=0の課題データを作成
X_target = pd.DataFrame(data =[[30, 0]], columns = ['Age','Sex'])
# 課題データを標準化
X_target = std.fit_transform(X_target)
30歳男性の生存率を求める
# 生存確率(y = Survived = 1になる確率)を求める
y_proba = logreg.predict_proba(X_target)[: , 1]
print(y_proba)
# > [0.47524046]
# 0.5よりも大きければ1を、小さければ0を振り分け('Survived'列に合う形にする)
y_pred = logreg.predict(X_target)
print(y_pred)
# > [0]
- 所感
データの加工(説明変数の準備)がかなり大変なことがわかった。実際には自分でどの変数が必要なのか考えなければならないので、学習させたい事象について、ある程度知っておく必要がありそうだと思った。また、説明変数の数がより多くなればもう少し正確に生存率を計算できるのではないかと思うので、時間があるときに試したいと思う。 - 参考にしたサイト
「Kaggle:Titanic(Logistic回帰)」https://qiita.com/y-sh-ml/items/25f59bff177a779d2d65
主成分分析
- 多変量データの持つ構造をより少数個の指標に圧縮する方法
- 変量の個数を減らすことに伴う情報量の消失をなるべく小さくしたい
- 少数変数を利用した分析や可視化(2,3次元の場合)が可能
- 係数ベクトルが変わると線形返還後の値が変化する
- 情報の量を分散の大きさととらえる
- 線形返還後の変数の分散が最大となる射影軸を探す
ハンズオン
- 設定
- 乳がん検査データを利用しロジスティック回帰モデルを作成
- 主成分を利用し2次元空間上に次元圧縮
- 課題
- 32次元のデータを2次元上に次元圧縮した際に、うまく判別できるかを確認
# ライブラリのインポート
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
# データセットのロード
original_df = pd.read_csv('data.csv')
# すべてのデータがNaNの'Unnamed'という列があるため削除
original_df.drop('Unnamed: 32', axis=1, inplace=True)
original_df
出力結果(一部抜粋)
「diagnosis」が診断結果であるから目的変数、3列目以降の計測値は説明変数である。
# diagnosisの各値の数を調べる(良性:B、悪性:M)
original_df.diagnosis.value_counts()
# >B 357
# >M 212
# >Name: diagnosis, dtype: int64
# 目的変数の抽出
y = original_df.diagnosis.apply(lambda d: 1 if d == 'M' else 0)
# 説明変数の抽出
X = original_df.loc[:, 'radius_mean':]
# 学習用とテスト用でデータを分離
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
# 標準化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# ロジスティック回帰で学習
logistic = LogisticRegression(class_weight='balanced')
logistic.fit(X_train_scaled, y_train)
# スコアと混同行列を検証
print('Train score: {:.3f}'.format(logistic.score(X_train_scaled, y_train)))
print('Test score: {:.3f}'.format(logistic.score(X_test_scaled, y_test)))
print('Confustion matrix:\n{}'.format(confusion_matrix(y_true=y_test, y_pred=logistic.predict(X_test_scaled))))
# >Train score: 0.986
# >Test score: 0.951
# >Confustion matrix:
# >[[86 4]
# > [ 3 50]]
# 説明変数間の相関係数が高いかどうかを確認
# ヒートマップを作成
plt.figure(figsize=(17, 17))
# .corr()で相関係数を求められる。ヒートマップにプロット
seaborn.heatmap(pd.DataFrame(X_train_scaled).corr(), annot=True)
セルの色が薄いところは相関係数が高い部分であり、相関係数が高い部分があるとうことは次元圧縮が有効であることを示す。
# 主成分分析を用いて次元数2まで圧縮
pca = PCA(n_components=2)
X_train_pca = pca.fit_transform(X_train_scaled)
# 寄与率を求める
print('explained variance ratio: {}'.format(pca.explained_variance_ratio_))
# >explained variance ratio: [ 0.43315126 0.19586506] → 2次元だと約62.9%となる
# 散布図にプロット
temp = pd.DataFrame(X_train_pca)
temp['Outcome'] = y_train.values # 'diagnosis'を数値化した列を追加
b = temp[temp['Outcome'] == 0]
m = temp[temp['Outcome'] == 1]
plt.scatter(x=b[0], y=b[1], marker='o') # 良性は○でマーク
plt.scatter(x=m[0], y=m[1], marker='^') # 悪性は△でマーク
plt.xlabel('PC 1') # 第1主成分をx軸
plt.ylabel('PC 2') # 第2主成分をy軸
散布図
散布図より左上が良性、右下が悪性のように直線で分離できそうである。
# 次元削減した説明変数で学習、テストする
logistic.fit(X_train_pca, y_train)
# 検証
print('Test score: {:.3f}'.format(logistic.score(X_train_pca, y_train)))
# >Test score: 0.944
- 所感
32次元のデータを主成分分析で2次元に圧縮してもスコアに大きな変化はなかったため、必要な情報はある程度保持した上で次元圧縮ができているのだと感じた。他のデータでも主成分分析がどの程度有効なのか試したいと思った。 - 参考にしたサイト
「scikit-learnで乳がんデータセットを主成分分析する」https://ohke.hateblo.jp/entry/2017/08/11/230000
k近傍法
- 分類問題のための機械学習手法
- 最近傍のデータを$k$個取ってきて、それらが最も多く所属するクラスに識別する
- kの数を変えると結果も変わることに注意する
k-means
- k-meansとは
- 教師なし学習
- クラスタリング手法
- 与えられたデータを$k$このクラスタに分類する
- k-meansのアルゴリズム
- 各クラスタ中心の初期値を設定
- 各データの点に対して、各クラスタ中心との距離を計算し、最も距離が近いクラスタを割り当てる
- 各クラスタの平均ベクトル(中心)を計算する
- 収束するまで2,3の処理を繰り返す
- k-meansの注意点
- 中心の初期値が変わるとクラスタリング結果も変わりうることに注意
- 初期値が近いとうまくクラスタリングできないことに注意
サポートベクターマシン
- サポートベクタマシン(SVM)とは
- 機械学習モデルの一種
- 分類と回帰を扱うことができ、主に分類タスクに利用される
- マージン最大化
- 入力の各データ点との距離が最大となるような境界線を求める
- 扱うデータは高次元であり、超平面で考える必要がある
- データが線形分類できない(直線で分類できない)
- 計算式
- マージンを最大化するには分類境界$f(x)=0$と分類境界から最も近くにあるデータ$x_{i}$の距離を最大化する必要がある
- 反対に、分類境界の決定に関わっているのは分類境界に最も近い$x_{i}$のみで、その他のデータは関係しない
- この$x_{i}$はサポートベクトルと呼ばれる
- $f(x)=0$と$x_{i}$の距離は次式であらわされる
$$\frac{|f(x_{i})|}{||w||} = \frac{|w^{T}x_{i} + b|}{||w||}$$
($||w||$は$w$のL2ノルム)
- スラック変数
- 上記では訓練データーを分類できる決定関数$f(x)=0$が存在すると仮定したが、実際は線形分離ができない事も多い
- そこで多少の分類誤りは許すこととし(ソフトマージン)、マージン内のデータや誤分類されたデータに対する誤差を表すようにする
- この誤差をスラック変数と呼ぶ
- ソフトマージンでは、$f(x)=1$と$f(x)=-1$の間の距離をマージンと解釈する
- カーネル関数
- 上記の通りデータが線形分類できないため、何をもってマージン最大化となるか考える必要がある
- そこでデータをあえて高次元に写像する事でその写像後の空間で線形分類できるようにするという手法がとられている
- この写像に利用する関数をカーネル関数と呼ぶ
- また、このような手法をカーネルトリックと呼ぶ
ハンズオン
- 設定
- アヤメのデータセットを利用してモデルを構築する
- 課題
- 特徴量を評価し、花の品種を3つのうちに分類する
# モジュールのインポート
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn import datasets, model_selection, svm, metrics
# Irisの測定データの読み込み
iris = datasets.load_iris()
# データの中身を確認
data1 = pd.DataFrame(data= np.c_[iris['data'], iris['target']],
columns= iris['feature_names'] + ['target'])
data1.head()
出力結果
特徴量は下記の四つである。
・Sepal Length(がく片の長さ)
・Sepal Width(がく片の幅)
・Petal Length(花びらの長さ)
・Petal Width(花びらの幅)
また、正解ラベルは下記三つである。
・0: Setosa
・1: Versicolor
・2: Versinica
# 特徴量を保存
iris_data = pd.DataFrame(data=iris.data, columns=iris.feature_names)
# 分類ラベルを保存
iris_label = pd.Series(data=iris.target)
# データを学習データと検証データに分割
data_train, data_test, label_train, label_test = model_selection.train_test_split(iris_data, iris_label)
# SVMで学習
clf = svm.SVC()
clf.fit(data_train, label_train)
predict = clf.predict(data_test)
print('予測結果:',predict)
# >予測結果: [1 2 1 2 0 0 0 0 0 0 2 0 2 0 1 0 1 0 2 1 2 1 2 2 2 1 1 0 0 1 1 0 0 1 2 0 1 1]
# 結果と実際のラベルを比較し正解率を表示
ac_score = metrics.accuracy_score(label_test, predict)
print(ac_score)
# >0.9736842105263158
- 参考にしたサイト
「サポートベクターマシン(SVM)を少し覗いて実装してみる」https://nanjamonja.net/archives/811