機械学習の概要（E資格ラビットチャレンジレポート2）


この記事はJDLA E資格認定講座のラビットチャレンジの成果レポートである。


（応用数学の概要（E資格ラビットチャレンジレポート1））
　https://qiita.com/AiTengu/items/b70de84dc11220b1494e

#１．線形回帰モデル
###（1）概要
　●**回帰分析**とは、入力ベクトル$x_i$を連続的な出力$y$に変換する関数$f(x_i)$を入力と出力の汲みの学習データ$D^t$から学習する教師あり学習のことである。
　●線形回帰は特に回帰問題の手法の中で最も基本的な回帰モデルである。線形回帰とは、
　　**入力をパラメータwで重み付けした和（重み付け線形和）＋バイアス**
　　によって出力が決定されるモデルのことである。 
              $y^=w_0+w^Tx=w_0+w_1x_1+⋯+w_ix_i$
   入力重み付け和$ ∑_iw_i X_i $ はベクトルの内積$w^Tx$ でも表現できる。　
 ●また、線形回帰モデルの学習は、①外れ値の影響を強く受けること、②多重共線性に留意が必要。
 ●最小2乗法とは、学習データの最小2乗誤差を最小とするようにパラメータを推定することである。　
   
　　　  $MSE= 1/n ∑ (y− \hat y )^2$

 ●訓練データが多いほど過学習が起きやすいが、訓練データが少ないと未学習が起きやすい。
 ●回帰問題ではデータに対してモデルの表現力（自由度）が不釣り合いに高いと過学習が発生しやすくなる。

###（2）Python実装演習

```python
from sklearn.datasets import load_boston
from pandas import DataFrame
import numpy as np

boston = load_boston()

print(boston['DESCR'])
print(boston['feature_names'])
print(boston['data'])
print(boston['target'])

```
まずデータをインポートしてデータの中身を見る。それぞれの内容は以下の通り。
・DESCR…データの詳細情報(インスタンスが506あるなど)
・feature_names…説明変数の名前（'CRIM' 'ZN' 'INDUS' 'CHAS' 'NOX' 'RM' 'AGE' 'DIS' 'RAD' 'TAX' 'PTRATIO' 'B' 'LSTAT'）
・data…説明変数のデータ（例：[6.3200e-03 1.8000e+01 2.3100e+00 ... 1.5300e+01 3.9690e+02 4.9800e+00]）
・target…目的変数のデータ（例：24.  21.6 34.7 33.4 36.2 ）
やはり実際にどんなデータが入っているのか実物を見てみることが重要。

```python

#　説明変数らをDataFrameへ変換
df = DataFrame(data=boston.data, columns = boston.feature_names)

# 目的変数をDataFrameへ追加
df['PRICE'] = np.array(boston.target)

# 最初の5行を表示
df.head(5)

```
説明変数・目的変数からデータフレームを作成する。
出力してみると以下のような一覧。（データ件数は506）ぱっとみて分かりづらいのでラベルは日本語表示に対応してくれるとありがたい。
　CRIM	　　ZN	INDUS	CHAS	NOX	RM	AGE	DIS	RAD	TAX	PTRATIO	B	LSTAT	PRICE
0　0.00632	18.0	2.31	0.0　0.538	6.575	65.2	4.0900	1.0	296.0	15.3	396.90	4.98	24.0

```python

# 説明変数
data = df.loc[:, ['RM']].values

# 目的変数
target = df.loc[:, 'PRICE'].values

## sklearnモジュールからLinearRegressionをインポート
from sklearn.linear_model import LinearRegression

# オブジェクト生成
model = LinearRegression()

# fit関数でパラメータ推定
model.fit(data, target)

#予測
model.predict([[1]])

# 説明変数
data2 = df.loc[:, ['CRIM', 'RM']].values
# 目的変数
target2 = df.loc[:, 'PRICE'].values

# オブジェクト生成
model2 = LinearRegression()

# fit関数でパラメータ推定
model2.fit(data2, target2)

```

●単回帰分析
　部屋数（RM）から住宅価格（PRICE）を予測する。しかし、部屋数1に対する予測結果は-25.5685118であり、このモデルはうまく作動していない。

●重回帰分析（2変数）
　次に犯罪率（CRIM）と部屋数（RM）から住宅価格（PRICE）を予測する。今度の予測結果は29.43977562。


#２．非線形回帰モデル
###（1）概要
　●複雑な非線形構造のデータでは線形回帰モデルでは捉えられないため**非線形回帰モデル**で表現する。
　●非線形回帰モデルは回帰関数として、基底関数と呼ばれる既知の非線形関数とパラメータベクトルの線型結合を使用しており、学習パラメータwと目的変数との関係が線形になっていないモデルである。
　●指数関数や反比例の分母に重みがついている場合は非線形回帰であるが、多項式は重みがxの累乗にかかっているだけであり線形な関係が成り立っているため多項式回帰は線形回帰に分類される。
　(非線形回帰の例)　$y=exp^x$　　$y=w_0+w_1x_1^2$
　(線形回帰（多項式回帰）の例)　$y=w_0+w_1x_1+w_2x_1^2$

　●基底関数は多項式関数やガウス型基底関数などを用いるこどが多い。
　●未知のパラメータは線形回帰モデルと同様に最小2乗法や最尤法により推定する。
 　(多項式関数)   $ϕ_j(x)=x^j$
 
　●未学習と過学習
　・回帰を実行するにあたって、未学習と過学習への対策が必要となる。
　・**未学習**とは学習データに対して、十分小さな誤差が得られないことである。
　　（対策）
　　　・モデルの表現力が低いため、表現力の高いモデルを利用する
  ・**過学習**とは学習データへの当てはまりが良すぎて、逆にテストデータに対する誤差が大きいこと。
　　（対策）
　　　・学習データの数を増やす
　　　・不要な基底関数(変数)を削除して表現力を抑止する
　　　・正則化法を利用して表現力を抑止する

###（2）Python実装演習

```python
# Googleドライブのマウント
from google.colab import drive
drive.mount('/content/drive')

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline

#seaborn設定
sns.set()
#背景変更
sns.set_style("darkgrid", {'grid.linestyle': '--'})
#大きさ(スケール変更)
sns.set_context("paper")

n=100

def true_func(x):
    z = 1-48*x+218*x**2-315*x**3+145*x**4
    return z 

def linear_func(x):
    z = x
    return z 

# 真の関数からノイズを伴うデータを生成

# 真の関数からデータ生成
data = np.random.rand(n).astype(np.float32)
data = np.sort(data)
target = true_func(data)

# 　ノイズを加える
noise = 0.5 * np.random.randn(n) 
target = target  + noise

# ノイズ付きデータを描画
plt.scatter(data, target)

plt.title('NonLinear Regression')
plt.legend(loc=2)

```

#３．ロジスティック回帰モデル
###（1）概要
　●ロジスティック回帰は主に2クラス分類で使用する。
　●モデルの出力$\hat y $は、入力xがy=1で表現されるクラスに属する条件付き確率$p(y=1|x)$を表わしている。
　n個目のデータに対する目的関数を$y_n$、モデルの出力を$\hat y_n $とする。
　●ロジスティック回帰では、予測の良さを測るために尤度（あるモデルのもとでそのデータが得られるであろう確率のこと。）を使用する。y=1なら尤度は$p(y=1|x)$、y=0なら尤度は$p(y=0|x)$となる。この場合分けをまとめて書くと、
　　$p(y＝1│x)^y p(y＝0│x)^(1-y)$
　となる。これが2クラス分類における1つのデータに対する尤度となる。
　このため、全データに対する尤度は、確率の積は、
　　$∏(p(y_n=1│x_n )^(y_n ) p(y_n=0│x_n )^(1-y_n ))$
 となる。ロジスティック回帰では、これを最大にするようにパラメータを学習する。

　●識別モデルとして $p(y=1|x;w)=σ(w^Tx)$ を用いる。
   ただし、$σ(⋅) $はシグモイド関数であり、$σ(h)=1 / (1+exp(−h))$で定義される。

###（2）Python実装演習

```python

import pandas as pd
from pandas import DataFrame
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import LogisticRegression

titanic_df = pd.read_csv('[格納場所のパス]/titanic_train.csv')
titanic_df.head(10)

```

ここで使うデータは有名な「タイタニックデータ」。タイタニック号に乗った人に関する特徴量（年齢や客室のグレード等）と、目的関数（生存できたか）が記録されている。
このデータから、ある特徴量を持つ人は生存できるかできないかを予測する（生存する確率を求める）ことができるモデルを作成する。
まずはおなじみのお決まりフレーズをセットし、データを表示する。「Survived」が1なら生存、0なら死亡を示す。

![](https://camo.qiitausercontent.com/1b5e297fb60bbfc8d60c5c70b9e4cd38285cd62d/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f313039303337332f65373434356532332d386138382d643137652d663830342d3736303631333964363435352e706e67)

次に、上記のデータを見て、目的の予測を行うにはどのようなデータにしなければならないかを考える。まずは今回の目的と関係なさそうなデータが多いこと、nullが含まれるデータが多いことに気がつく。
そこで、データの前処理を行う。処理としては、今回必要のないデータを削除して、必要なデータ（例えば性別）を数値に変換すること、nullが含まれるデータにfillna()関数を用いて年齢がnullの場合にデータ全体の年齢の平均で埋めることの2つである。

```python

#不要なデータの削除
titanic_df.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

#性別データを数値（女性なら0、男性なら1）に変換
titanic_df['Gender'] = titanic_df['Sex'].map({'female': 0, 'male': 1}).astype(int)

#Ageカラムのnullを平均で補完（Ageに直接上書きせずに、新たな列AgeFillを作る）
titanic_df['AgeFill'] = titanic_df['Age'].fillna(titanic_df['Age'].mean())

```

１変数のロジスティック回帰
まず誰もが思いつくチケットの価格（Fare）から生死を予測する。

```python

data1 = titanic_df.loc[:, ["Fare"]].values        # 運賃リスト
label1 =  titanic_df.loc[:,["Survived"]].values   # 生死リスト

model=LogisticRegression()
model.fit(data1, label1)

for i in [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300]:
  a = model.predict([[i]])
  if a == 0:
    print('運賃が{}の時乗客は死亡する'.format(i))
  else:
    print('運賃が{}の時乗客は生存する'.format(i))

```

ロジスティック回帰のオブジェクトを作成して、データを入れて予測する。（最尤推定により推定値を決定）
上記で予測したモデルを用いて運賃の高低により生存できるのかどうかを検証すると、運賃60以下は死亡、70以上は生存という結果になった。金持ちほど生存確率が高くなると言う直感に近い結果が出て意外感はないが一安心。

#４．主成分分析
###（1）概要
>主成分分析は、元の入力よりも次元が低い表現を学習する。また、成分が互いに線形な相関を持たない表現を学習する。（「深層学習」Goodfellow p106）

　●主成分分析の理解には下記サイトの説明と図が秀逸であるので引用。
>
主成分分析(Principle Component Analysis)とは，どういったものなのかを説明したいと思います．主成分分析は多次元のデータを次元圧縮（データは減らない）する方法です．
主成分分析とは直接は関係ありませんが，次元圧縮の一例として例えばプログラマのスキルとしてpython,Java,給料でプログラマのの熟練度を測るためにGithub上でのstar⭐️の数でスキル(2次元とする)を評価できると仮定しましょう．それはつまり３次元のデータを2次元に要約（圧縮）したことになります．次に３次元から２次元への写像（f:R3→R2f:R3→R2）を考えた主成分分析とは、座標で考えると，例えば３次元のデータ（x,y,z座標）を二次元のデータ(l,m座標)に要約（圧縮）するようなものです．この時，第n主成分を分散の大きい順に，l(エル)を第１主成分，mを第２主成分と呼びます．イメージとしては，三次元空間にある赤い点を主成分軸（この場合第１・第２主成分）にして2次元で表すということです．

![](https://camo.qiitausercontent.com/c5f4fd97f5094aff16f578bf5c83ef9aac77d6b5/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f33333332392f37643439393532662d663166632d366331332d313464622d3566383537323031643735302e706e67)
https://qiita.com/NoriakiOshita/items/460247bb57c22973a5f0

　●データに対して線形変換を行い、次元削減を実現する手法。この線形変換を行うための行列は、データの分散共分散行列の固有ベクトルから構成される。
　●また、各固有ベクトルに対応する固有値は、それぞれの固有ベクトル方向の分散を意味する。主成分分析は分散が大きな方向ほど情報を多く含むよい方向としてその方向に新たな座標軸を設定する。
　●最も固有値が大きな固有ベクトルを第1主成分、以後固有値が大きな順に第2主成分、第3主成分と呼ぶ。
  ●訓練データ $X=[x_1,x_2,...,x_n]^T $に対して $E[x]=0$ となるように変換する。
  すると、不偏共分散行列は $Var[x]=1/(n−1)X^TX$ と書ける。$Var[x]$ を固有値分解し、固有値の大きい順に対応する固有ベクトルを第1主成分( $w_1$ ), 第2主成分( $w_2$ ), ...とよぶ。

###（2）Python実装演習


https://colab.research.google.com/drive/1a5TSi4uxuRoSvJRHdb4QTErha_9md6NZ?usp=sharing

※Google Colbの共有方法を使う方法がようやく理解できたので、今後は実装演習はQiitaにコードを貼り付けず、共有したコードを添付するちょっと大人なやり方でスマートに展開。


![](https://camo.qiitausercontent.com/ffe904f0e7e5e54391ef1b37247f7c3dbbe8ac7d/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323233353432352f38366336383932322d643964312d373864342d633162662d3339356462633463373233312e706e67)

上記のアウトプットはいかにも「機械学習をやっている」という感覚を見ていてくれるお気に入り。ちなみに、セルの色が薄い部分は相関係数が高い部分であって次元圧縮が有効であることを示している。

（参考）「scikit-learnで乳がんデータセットを主成分分析する」https://ohke.hateblo.jp/entry/2017/08/11/230000

#５．アルゴリズム

###1．近傍法
###（1）概要
・分類問題のための機械学習手法
・最近傍のデータをk個取り、それらが最も多く属するクラスに識別する。
・kにより結果が変わる。k=1の場合を最近傍法と呼ぶ。
・kを大きくすると決定境界は滑らかになる。

＜k近傍法のアルゴリズム＞
　①入力データと学習データの距離を計算する。
　②入力データに近いほうからk個の学習データを取得する。
　③学習データのラベルで多数決を行い、分類結果とする。

・２値分類の場合にはkを奇数にとり、多数決の結果がどちらかに決まるようにすることが一般的。

###（2）Python実装演習
https://colab.research.google.com/drive/1p2S7S3cef4svLcfqRH5LQr9JlKJ1j-3j?usp=sharing

・k近傍法は訓練データから獲得するパラメータはないため学習ステップはない。「陽に訓練ステップはない」という表現が微妙に分かりづらかったが、調べてみたところ「陽に～ない」と言う表現は物理学等で普通に使用されているらしく以下の説明が分かりやすかった。

>
通常の意味では、Qという物理量が
　Q=f(t)
というｔの関数として直接表されている場合を「ｔを陽に含む」、
　Q=f(v)　（v=v(t)）
のように、Qは一見ｖの関数だが、ｖがｔの関数になっている場合、Qの式にｔは現れないがｖを通してｔの関数となっているという意味で「ｔを陰に含む」と言います。
この場合も
　Q=f(v(t))
と丁寧に書けばｔが見えるようになりますが、これをもって「陽に示した」とはあまり言わず、ｖに具体的にv(t)の式を代入して式の中のｖを全てtの式で置き換えた時、「陽に示した」と言います。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1112315636
>

![](https://camo.qiitausercontent.com/464d1405677fe52fb97bc0d758ca6bffa84afa50/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f313130383039322f64323437343034642d326464612d396465332d313864632d3638383362626161343133352e706e67)
上記の図を見ると、今回の3-NNでは境界付近で誤って分類されたものが存在することが分かる。


###2．k-means
###（1）概要
・教師なし学習
・クラスタリング手法
・与えられたデータをk個のクラスタに分類する

＜k-means法のアルゴリズム＞
　①各クラスタの中心の初期値を設定する
　②各データ点に対して、各クラスタ中心との距離を計算し、最も距離が近いクラスタを割り当てる
　③ 各クラスタの平均ベクトル（中心）を計算する
　④終息するまで2), 3)の処理を繰り返す

・初期値が近いとうまくクラスタリングできない。
・k-means++により初期値をうまく決めることができる

###（2）Python実装演習
https://colab.research.google.com/drive/1Hy2OpqyUgq4D_VDA4ci6Ui3kO27q4ZQS?usp=sharing

・178件データがあり、説明変数は13個あることが分かる。
・3つのクラスタに分けてみるが、あまりうまく分かれていないことが理解できる。

#６．サポートベクターマシン
###（1）概要
　●2クラス分類のための機械学習手法
　●線形判別関数（決定境界）と最も近いデータ点との距離（マージン）が最大化されるように線形判別関数を求める。
        $ y=w^Tx+b=∑ w_jx_j+b $

　●線形判別関数ともっとも近いデータ点との距離を**マージン**という。
　●データを分割する直線に最も近いデータ点を**サポートベクトル**という。分離超平面を構成する学習データは、サポートベクターだけで残りのデータは不要である。
　●**ハードマージン**はマージンの内側にデータが入り込むのを許容しない。一方、**ソフトマージン**は一部のデータがマージンの内側に入ることを許容する。ソフトマージンを導入することで、線形分離可能でないデータについても学習可能になる。
　●線形分離できない場合は、特徴空間に写像しその空間で線形に分類する**カーネルトリック**を利用することで、複雑な決定境界を学習することができる。データをより高次元空間に写像し、写像先の空間で識別する手法があるが計算コストが大きくなる。高次元空間における識別面は内積 $ϕ(x)^Tϕ(x^`)$の結果さえ分かれば構成できるため、わざわざ変換φを計算しなくても良い。そこで、内積の結果をある関数kによって得られることを考える工夫をカーネルトリックという。

###（2）Python実装演習

https://colab.research.google.com/drive/1QKb1DvwoKGyE7jpUb3yHyZTxzxCI7hKk?usp=sharing

・訓練データが線形分離可能な場合
　線形分離が可能なように2つの分類のデータを標準正規分布に沿った乱数で作成する。データ及びマージン、決定境界をプロットすると決定境界できちんと分かれていることが理解できる。
・訓練データが線形分離不可能な場合
　データ及びマージン、決定境界をプロットすると、非線形においても決定境界で分かれていることが分かる。
・訓練データに重なりがある場合（ソフトマージンSVM）
2つの分類のデータを重なりがある状態になるように標準正規分布に沿った乱数で作成する。データ及びマージン、決定境界をプロットすると、ソフトマージンSVMでは重なりがあっても決定境界で分けられていることがわかる。


#参考文献
　●JDLA　E資格シラバス最新版（2020）
　https://www.jdla.org/wp-content/uploads/2019/09/JDLA_E%E3%82%B7%E3%83%A9%E3%83%90%E3%82%B9_2020%E7%89%88.pdf
　●「深層学習」(Ian Goodfellow,Yoshua Bengio,Aaron Courville,2016)
　●「ゼロからつくるPyhon機械学習プログラミング入門」（八谷大岳・講談社）
　●「徹底攻略ディープラーニングE資格問題集　第2版」（インプレス）