E資格 機械学習 レポート

## はじめに
ラビット・チャレンジ Deep Learning 「機械学習講座」の受験レポート
要点のまとめと、ハンズオンの結果
講座受講中は手元のノートに記載している。
ここでは、振り返りながらメモレベルで表す。

## 線形回帰モデル
### 要点まとめ
とある入力から出力(連続値)を予測する。（回帰）
線形回帰は直線で予測するシンプルなモデル。
正解との差（誤差）を足し合わせる平均二乗誤差によりパラメータ(w)を推定。
パラメタを求めるときに偏微分を行うが、線形回帰だと行列の微分で求めれる様子。AI入門で学習した連鎖率を使用する方法しか知らなかったので、行列の微分の法則も頭に入れておきたい。

### ハンズオン
ボストンの家賃データセット（よく見るやつ）でモデルを作成し、値を変えて結果を見てみる。

#### 準備

```
# importなど
from sklearn.datasets import load_boston
from pandas import DataFrame
import numpy as np

boston = load_boston()

df = DataFrame(data=boston.data, columns = boston.feature_names)

from sklearn.linear_model import LinearRegression

```

```
# 犯罪率・部屋数から
data2 = df.loc[:, ['CRIM', 'RM']].values
# 家賃を予測したい
target2 = df.loc[:, 'PRICE'].values

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

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

#### 色々と数値を変えて予測してみる
犯罪率0.02、部屋数6

```
model2.predict([[0.02, 6]])
```
> array([21.09639176])

犯罪率1、部屋数6
非常に治安が悪いが、家賃は大きく変わっていない
犯罪率が家賃に与える影響は少なく見積もられていると考えられる

```
model2.predict([[1, 6]])
```
> array([20.83677678])


## 非線形回帰
### 要点まとめ
非線形（曲線）で表す。
線形ではxとしていたところを任意の関数φ(x)を用いる。
wについては線形のまま。
y=w0+Σwφ(x)
#### 基底関数
基底関数φ

多項式(1～9乗)
φj=x^j
同じxiに対してw1～w9までx^1からx^9をかける

ガウス型基底
exp{(x-μj)^2 / 2hj}
2hjで"広がり"をコントロール

基底展開法も線形回帰と同じ枠組みで推定可能

#### 未学習・過学習
未学習：学習データに対して十分小さな誤差が得られないモデル

過学習：小さな誤差は得られたけどテスト集合誤差との差が大きいモデル
対策１学習データの数を増やす
対策２不要な基底関数を削除して表現力を抑止
対策３正則化法を利用して表現力を抑止

#### 正則化
正則化項
Ridge推定量：L2ノルム
Lasso推定量：L1ノルム

正則化パラメータ
小さく：制約面が大きく
大きく：制約面が小さく

#### 汎化性能
精度の計算方法：MSE

ホールドアウト法
手元にデータが少ないときに問題が起こりやすい。
外れ値が検証データに含まれると、汎化性能の低いモデルが選ばれてしまう

クロスバリデーション（交差検証）
それぞれCV値をとって、一番小さいモデルを採用

最適化
グリッドサーチ
すべてのチューニングパラメータの組み合わせで評価値を算出
→NNだとベイズ最適化で最適化が行われている


### ハンズオン
import初期設定

```
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)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/c9d12c38-a638-62f1-1a34-0765d1e80ceb.png)


線形回帰モデルを作成
線形では表現できないことがわかる

```
from sklearn.linear_model import LinearRegression

clf = LinearRegression()
data = data.reshape(-1,1)
target = target.reshape(-1,1)
clf.fit(data, target)

p_lin = clf.predict(data)

plt.scatter(data, target, label='data')
plt.plot(data, p_lin, color='darkorange', marker='', linestyle='-', linewidth=1, markersize=6, label='linear regression')
plt.legend()
print(clf.score(data, target))
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/e82eda13-4d5a-ed5a-0f06-7d66ef8ec9b2.png)


非線形モデル(KernelRidge)で表現。
ある程度特徴をつかんだモデルとなっている


```
from sklearn.kernel_ridge import KernelRidge

clf = KernelRidge(alpha=0.0002, kernel='rbf')
clf.fit(data, target)

p_kridge = clf.predict(data)

plt.scatter(data, target, color='blue', label='data')

plt.plot(data, p_kridge, color='orange', linestyle='-', linewidth=3, markersize=6, label='kernel ridge')
plt.legend()
#plt.plot(data, p, color='orange', marker='o', linestyle='-', linewidth=1, markersize=6)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/49713a57-4ad4-afdf-8f40-1f2c74699400.png)

## ロジスティック回帰
### 要点まとめ
分類で使用するモデル
0か1かしか返さない
タイタニックデータ、IRISデータ
sigmoid関数を使用し、実数全体から[0,1]へつぶす
sigmoid関数の微分は、sigmoid関数自身で表現できる
sigmoid関数により出力が確率に対応

最尤推定
ロジスティック回帰ではベルヌーイ分布を利用する。
尤度関数：データを固定しパラメータを変化させる
尤度関数を最大化するようなパラメータを選ぶ推定方法を最尤推定という

確率的勾配降下法(SGD)
データを一つずつランダムに(「確率的」に)選んでパラメータを更新する

混合行列
TP(True Positive)、FP(False Positive)
FN(False Negative)、TN(True Negative)

再現率(TP/TP+FN)
「誤り(False Positive)が多少多くても抜け漏れは少ない」予測をしたい

適合率(TP/TP+FP)
見逃し(False Negative)が多くてもより正確な」予測をしたい際に利用

F値
バランスを示す

### ハンズオン
import、ファイルリード

```
#from モジュール名 import クラス名（もしくは関数名や変数名）
import pandas as pd
from pandas import DataFrame
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

#matplotlibをinlineで表示するためのおまじない (plt.show()しなくていい)
%matplotlib inline

# titanic data csvファイルの読み込み
titanic_df = pd.read_csv('/content/drive/MyDrive/Pythonライブラリ基礎講座/data/titanic_train.csv')
```

前処理

```
titanic_df.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1, inplace=True)
#Ageカラムのnullを中央値で補完

titanic_df['AgeFill'] = titanic_df['Age'].fillna(titanic_df['Age'].mean())

```

実装(チケット価格から生死を判別)

```
#運賃だけのリストを作成
data1 = titanic_df.loc[:, ["Fare"]].values

#生死フラグのみのリストを作成
label1 =  titanic_df.loc[:,["Survived"]].values

from sklearn.linear_model import LogisticRegression

model=LogisticRegression()

model.fit(data1, label1)

X_test_value = model.decision_function(data1) 

```


```
w_0 = model.intercept_[0]
w_1 = model.coef_[0,0]

# def normal_sigmoid(x):
#     return 1 / (1+np.exp(-x))

def sigmoid(x):
    return 1 / (1+np.exp(-(w_1*x+w_0)))

x_range = np.linspace(-1, 500, 3000)

plt.figure(figsize=(9,5))
#plt.xkcd()
plt.legend(loc=2)


# plt.ylim(-0.1, 1.1)
# plt.xlim(-10, 10)

# plt.plot([-10,10],[0,0], "k", lw=1)
# plt.plot([0,0],[-1,1.5], "k", lw=1)
plt.plot(data1,np.zeros(len(data1)), 'o')
plt.plot(data1, model.predict_proba(data1), 'o')
plt.plot(x_range, sigmoid(x_range), '-')
#plt.plot(x_range, normal_sigmoid(x_range), '-')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/f11e33d9-27c5-b209-787c-e94c0e08738c.png)


2変数で分析
結果として、女性、年齢が若いほど生存率が高いことがわかる

```
data2 = titanic_df.loc[:, ["AgeFill", "Pclass_Gender"]].values
label2 =  titanic_df.loc[:,["Survived"]].values
model2 = LogisticRegression()
model2.fit(data2, label2)

h = 0.02
xmin, xmax = -5, 85
ymin, ymax = 0.5, 4.5
xx, yy = np.meshgrid(np.arange(xmin, xmax, h), np.arange(ymin, ymax, h))
Z = model2.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
Z = Z.reshape(xx.shape)

fig, ax = plt.subplots()
levels = np.linspace(0, 1.0)
cm = plt.cm.RdBu
cm_bright = ListedColormap(['#FF0000', '#0000FF'])
#contour = ax.contourf(xx, yy, Z, cmap=cm, levels=levels, alpha=0.5)

sc = ax.scatter(titanic_df.loc[index_survived, 'AgeFill'],
                titanic_df.loc[index_survived, 'Pclass_Gender']+(np.random.rand(len(index_survived))-0.5)*0.1,
                color='r', label='Not Survived', alpha=0.3)
sc = ax.scatter(titanic_df.loc[index_notsurvived, 'AgeFill'],
                titanic_df.loc[index_notsurvived, 'Pclass_Gender']+(np.random.rand(len(index_notsurvived))-0.5)*0.1,
                color='b', label='Survived', alpha=0.3)

ax.set_xlabel('AgeFill')
ax.set_ylabel('Pclass_Gender')
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
#fig.colorbar(contour)

x1 = xmin
x2 = xmax
y1 = -1*(model2.intercept_[0]+model2.coef_[0][0]*xmin)/model2.coef_[0][1]
y2 = -1*(model2.intercept_[0]+model2.coef_[0][0]*xmax)/model2.coef_[0][1]
ax.plot([x1, x2] ,[y1, y2], 'k--')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/d0b3375a-0074-2a2a-c269-92ab71b76612.png)


## 主成分分析
### 要点まとめ
多次元のデータを低次元に圧縮し、人間がわかるようにする
線形変換後の変数の分散が最大になる射影軸を探索
学習データから平均を求め、平均から分散共分散行列を求める。

係数ベクトルが変われば線形変換後の値が変化する。
変換後の分散が一番大きくなる射影軸を求める。

目的関数と制約条件（ノルムが１など）と組み合わせる
ラグランジュ関数：目的関数ー制約条件

元の分散共分散行列の固有値と固有ベクトルが、制約付き最適化問題の解になる。

寄与率：全体の軸の中の第k主成分の割合（情報量の割合）

### ハンズオン
import

```
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegressionCV
from sklearn.metrics import confusion_matrix
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
%matplotlib inline
```

PCA 次元数を２まで圧縮
寄与率43%
もともと9割以上で分類できていたことを考えると、情報としては落ちているが、人間の目で見てわかる状態になった

```
# PCA
# 次元数2まで圧縮
pca = PCA(n_components=2)
X_train_pca = pca.fit_transform(X_train_scaled)
print('X_train_pca shape: {}'.format(X_train_pca.shape))
# X_train_pca shape: (426, 2)

# 寄与率
print('explained variance ratio: {}'.format(pca.explained_variance_ratio_))
# explained variance ratio: [ 0.43315126  0.19586506]

# 散布図にプロット
temp = pd.DataFrame(X_train_pca)
temp['Outcome'] = y_train.values
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軸
```

> explained variance ratio: [0.43315126 0.19586506]

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/45b9a3fb-c19e-88bc-4f7d-f29c220613db.png)


## k近傍法
### 要点まとめ
教師なし学習
最近傍のデータをk個撮ってきて、それらが最も多く所属するクラスに識別
kを変化させると結果も変わる。
kをいくつにするのかが現実的に問題となる。
kを大きくすると決定協会はなめらかとなる。


### ハンズオン
importとテストデータ作成

```
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

def gen_data():
    x0 = np.random.normal(size=50).reshape(-1, 2) - 1
    x1 = np.random.normal(size=50).reshape(-1, 2) + 1.
    x_train = np.concatenate([x0, x1])
    y_train = np.concatenate([np.zeros(25), np.ones(25)]).astype(np.int)
    return x_train, y_train
```

データをプロット

```
X_train, ys_train = gen_data()
plt.scatter(X_train[:, 0], X_train[:, 1], c=ys_train)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/f3262580-8618-4c79-e762-58dcb601a619.png)


境界を引くとほとんど分類できているが、２つ誤って分類されていることがわかる。

```
def distance(x1, x2):
    return np.sum((x1 - x2)**2, axis=1)

def knc_predict(n_neighbors, x_train, y_train, X_test):
    y_pred = np.empty(len(X_test), dtype=y_train.dtype)
    for i, x in enumerate(X_test):
        distances = distance(x, X_train)
        nearest_index = distances.argsort()[:n_neighbors]
        mode, _ = stats.mode(y_train[nearest_index])
        y_pred[i] = mode
    return y_pred

def plt_resut(x_train, y_train, y_pred):
    xx0, xx1 = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
    xx = np.array([xx0, xx1]).reshape(2, -1).T
    plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
    plt.contourf(xx0, xx1, y_pred.reshape(100, 100).astype(dtype=np.float), alpha=0.2, levels=np.linspace(0, 1, 3))

n_neighbors = 3

xx0, xx1 = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
X_test = np.array([xx0, xx1]).reshape(2, -1).T

y_pred = knc_predict(n_neighbors, X_train, ys_train, X_test)
plt_resut(X_train, ys_train, y_pred)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/8df04d9e-4efa-d266-950c-2ed1cfe5a0f6.png)


## k-means
### 要点まとめ
教師なし学習
与えられたデータをk個のクラスタに分類する。
各クラスタの中心を決定し、中心と書くクラスタとの距離が近いクラスタを割り当て、各クラスタの平均ベクトルを計算する
→収束するまで繰り返す
初期値が近いところに置かれるとうまくクラスタリングできない。



### ハンズオン
import

```
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import cluster, preprocessing, datasets

from sklearn.cluster import KMeans
```

ワインのデータセットをロード

```
wine = datasets.load_wine()

X = wine.data
y=wine.target
```

K-meansで分類を実行。
うまく分類できていない様子がわかる。

```
model = KMeans(n_clusters=3)
labels = model.fit_predict(X)
df = pd.DataFrame({'labels': labels})

def species_label(theta):
    if theta == 0:
        return wine.target_names[0]
    if theta == 1:
        return wine.target_names[1]
    if theta == 2:
        return wine.target_names[2]

df['species'] = [species_label(theta) for theta in wine.target]
pd.crosstab(df['labels'], df['species'])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/34e1e4e8-1dcc-257e-8746-c694bd1923d6.png)


## SVM
### 要点まとめ
教師あり学習。線形モデルの正負で二値分類を行う。

線型ハンベツカンスウと最も近いデータ点との距離をマージンといい、マージンが最大となる線形判別関数を求める。

目的関数と制約条件が決まったら、最適化問題をラグランジュ未定乗数法で解くことを考える。

ラグランジュ未定乗数法とは制約付き最適化問題を解くための手法。

KKT条件とは、制約付き最適化問題をにおいて最適解が満たす条件。

学習データはマージン上にあり予測に影響をあたえるデータ（サポートベクター）だけで、残りのデータは不要。

ソフトマージンSVM
サンプルを線形分離できないとき、誤差を許容し、誤差に対してペナルティを与える。
パラメータCの大小で決定境界が変化

非線形分離
線形分離できないとき、特徴空間に写像し、その空間で線形に分離する

カーネルトリック
特徴空間が高次元でも計算コストを抑えられる

非線形カーネル
非線形な分離が可能


### ハンズオン

importとテストデータの準備

```
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

def gen_data():
    x0 = np.random.normal(size=50).reshape(-1, 2) - 2.
    x1 = np.random.normal(size=50).reshape(-1, 2) + 2.
    X_train = np.concatenate([x0, x1])
    ys_train = np.concatenate([np.zeros(25), np.ones(25)]).astype(np.int)
    return X_train, ys_train

X_train, ys_train = gen_data()
plt.scatter(X_train[:, 0], X_train[:, 1], c=ys_train)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/cf54da67-9194-ac0c-8a2c-06cc833040db.png)

通常のSVMで線形に分離ができていることがわかる。

```
t = np.where(ys_train == 1.0, 1.0, -1.0)

n_samples = len(X_train)
# 線形カーネル
K = X_train.dot(X_train.T)

eta1 = 0.01
eta2 = 0.001
n_iter = 500

H = np.outer(t, t) * K

a = np.ones(n_samples)
for _ in range(n_iter):
    grad = 1 - H.dot(a)
    a += eta1 * grad
    a -= eta2 * a.dot(t) * t
    a = np.where(a > 0, a, 0)


index = a > 1e-6
support_vectors = X_train[index]
support_vector_t = t[index]
support_vector_a = a[index]

term2 = K[index][:, index].dot(support_vector_a * support_vector_t)
b = (support_vector_t - term2).mean()


xx0, xx1 = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

X_test = xx
y_project = np.ones(len(X_test)) * b
for i in range(len(X_test)):
    for a, sv_t, sv in zip(support_vector_a, support_vector_t, support_vectors):
        y_project[i] += a * sv_t * sv.dot(X_test[i])
y_pred = np.sign(y_project)


# 訓練データを可視化
plt.scatter(X_train[:, 0], X_train[:, 1], c=ys_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
                    s=100, facecolors='none', edgecolors='k')
# 領域を可視化
#plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, y_project.reshape(100, 100), colors='k',
                     levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
```



```
factor = .2
n_samples = 50
linspace = np.linspace(0, 2 * np.pi, n_samples // 2 + 1)[:-1]
outer_circ_x = np.cos(linspace)
outer_circ_y = np.sin(linspace)
inner_circ_x = outer_circ_x * factor
inner_circ_y = outer_circ_y * factor

X = np.vstack((np.append(outer_circ_x, inner_circ_x),
               np.append(outer_circ_y, inner_circ_y))).T
y = np.hstack([np.zeros(n_samples // 2, dtype=np.intp),
               np.ones(n_samples // 2, dtype=np.intp)])
X += np.random.normal(scale=0.15, size=X.shape)
x_train = X
y_train = y

plt.scatter(x_train[:,0], x_train[:,1], c=y_train)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/039bab32-bc61-a27c-6200-b160a2e9068d.png)


カーネル法(RBFカーネル)を使用し、特徴空間上で線形分離する。
以下のように線形分離不可能なデータを分離できている。

```
def rbf(u, v):
        sigma = 0.8
        return np.exp(-0.5 * ((u - v)**2).sum() / sigma**2)

X_train = x_train
t = np.where(y_train == 1.0, 1.0, -1.0)

n_samples = len(X_train)
# RBFカーネル
K = np.zeros((n_samples, n_samples))
for i in range(n_samples):
    for j in range(n_samples):
        K[i, j] = rbf(X_train[i], X_train[j])

eta1 = 0.01
eta2 = 0.001
n_iter = 5000

H = np.outer(t, t) * K

a = np.ones(n_samples)
for _ in range(n_iter):
    grad = 1 - H.dot(a)
    a += eta1 * grad
    a -= eta2 * a.dot(t) * t
    a = np.where(a > 0, a, 0)


index = a > 1e-6
support_vectors = X_train[index]
support_vector_t = t[index]
support_vector_a = a[index]

term2 = K[index][:, index].dot(support_vector_a * support_vector_t)
b = (support_vector_t - term2).mean()


xx0, xx1 = np.meshgrid(np.linspace(-1.5, 1.5, 100), np.linspace(-1.5, 1.5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

X_test = xx
y_project = np.ones(len(X_test)) * b
for i in range(len(X_test)):
    for a, sv_t, sv in zip(support_vector_a, support_vector_t, support_vectors):
        y_project[i] += a * sv_t * rbf(X_test[i], sv)
y_pred = np.sign(y_project)


# 訓練データを可視化
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
                    s=100, facecolors='none', edgecolors='k')
# 領域を可視化
plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, y_project.reshape(100, 100), colors='k',
                     levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1344334/ee364d29-a659-1e6b-6972-bb7da9c3be98.png)