E資格　機械学習

# はじめに
本記事は、日本ディープラーニング協会のDeep Learning資格試験（E資格）の受験に必要な、協会認定の講座プログラムのひとつである[ラビットチャレンジ](https://ai999.careers/rabbit/)のレポート記事（機械学習編）となります。

#1.線形回帰モデル

1-1. 回帰問題

- ある入力（離散あるいは連続値）から出力（連続値）を予測する問題
- 入力の各要素を説明変数または特徴量と呼ぶ
- 出力はスカラー値となり、目的変数と呼ぶ

1-2. 線形回帰モデル

- 回帰問題を解くための機械学習モデルの一つ
- 教師あり学習（教師データから学習）
- 入力とｍ次元パラメータの線形結合を出力するモデル

1-3. 線形結合

- 入力ベクトルと未知のパラメータの各要素を掛け算し、足し合わせたもの
- 入力ベクトルとの線形結合に加え、切片も足し合わせる


```math
\sum_{j=1}^{m}w_jx_j + w_0
```

```math
\vec{y} = X\vec{w} + \vec{ε}
```

1-4. パラメータの推定

- 線形回帰モデルのパラメータは最小二乗法で推定

```math
MSE_{train} = \frac{1}{n_{train}}\sum_{i=1}^{n_{train}}(\hat{y}_i^{(train)} - y_i^{(train)})^2
```

- MSEをパラメータｗに関して微分したものが0となるｗを求めると回帰係数と予測値は以下の様に表せる

```math

\hat{\boldsymbol{w}} = (X^{(train)T}X^{(train)})^{-1}X^{(train)T}\boldsymbol{y}^{(train)}
```
1-5.　実装演習

- Boston　Housingデータセット（ボストンの住宅価格データ）を用いる
- Boston　Housingデータセットは、「1970年代後半におけるボストンの住宅価格」の
表形式データセットである
- 以下の通り、「犯罪率、広さ、産業、川の隣、環境、部屋数、古さ、距離、道路、税金、生徒と先生、黒人、低所得」を含む説明変数と目的変数（住宅価格）からなる

参考：[Boston Housing](https://www.atmarkit.co.jp/ait/articles/2006/24/news033.html)

```
.. _boston_dataset:

Boston house prices dataset
---------------------------

**Data Set Characteristics:**  

    :Number of Instances: 506 

    :Number of Attributes: 13 numeric/categorical predictive. Median Value (attribute 14) is usually the target.

    :Attribute Information (in order):
        - CRIM     per capita crime rate by town
        - ZN       proportion of residential land zoned for lots over 25,000 sq.ft.
        - INDUS    proportion of non-retail business acres per town
        - CHAS     Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
        - NOX      nitric oxides concentration (parts per 10 million)
        - RM       average number of rooms per dwelling
        - AGE      proportion of owner-occupied units built prior to 1940
        - DIS      weighted distances to five Boston employment centres
        - RAD      index of accessibility to radial highways
        - TAX      full-value property-tax rate per $10,000
        - PTRATIO  pupil-teacher ratio by town
        - B        1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
        - LSTAT    % lower status of the population
        - MEDV     Median value of owner-occupied homes in $1000's

    :Missing Attribute Values: None

    :Creator: Harrison, D. and Rubinfeld, D.L.

This is a copy of UCI ML housing dataset.
https://archive.ics.uci.edu/ml/machine-learning-databases/housing/


This dataset was taken from the StatLib library which is maintained at Carnegie Mellon University.

The Boston house-price data of Harrison, D. and Rubinfeld, D.L. 'Hedonic
prices and the demand for clean air', J. Environ. Economics & Management,
vol.5, 81-102, 1978.   Used in Belsley, Kuh & Welsch, 'Regression diagnostics
...', Wiley, 1980.   N.B. Various transformations are used in the table on
pages 244-261 of the latter.

The Boston house-price data has been used in many machine learning papers that address regression
problems.   
     
.. topic:: References

   - Belsley, Kuh & Welsch, 'Regression diagnostics: Identifying Influential Data and Sources of Collinearity', Wiley, 1980. 244-261.
   - Quinlan,R. (1993). Combining Instance-Based and Model-Based Learning. In Proceedings on the Tenth International Conference of Machine Learning, 236-243, University of Massachusetts, Amherst. Morgan Kaufmann.

```

- sklearnモジュールからLinearRegressionをインポートし、広さ（RM）と住宅価格（MEDV）の関係を線形回帰モデルを利用して求めた結果は以下となる

```
## sklearnモジュールからLinearRegressionをインポート
from sklearn.linear_model import LinearRegression
# オブジェクト生成
model = LinearRegression()
# fit関数でパラメータ推定
model.fit(data, target)
#予測
print(model.predict([[1]]))
print(model.predict([[5]]))
print(model.predict([[10]]))
[-25.5685118]
[10.83992413]
[56.35046904]
```

- 上記の予測より、部屋の広さが大きくなるほど、住宅価格が上がることが見て取れる
- また、RM=1の際の予測結果がマイナス（実際にはありえない値）となっており、データセットにない予測はうまく予測できないということも見て取れる

#2.非線形回帰モデル

2-1.　非線形回帰モデル

- 複雑な非線形構造を内在する現象に対して、非線形回帰モデリングを実施
- 回帰関数として、基底関数と呼ばれる既知の非線形関数とパラメータベクトルの線形結合を使用する

2-2.　未学習と過学習

- 学習データに対して、十分小さな誤差が得られないモデルを未学習という
- 小さな誤差は得られたが、テスト集合誤差との差が大きいモデルを過学習という

2-3.　汎化性能

- 学習に使用した入力だけでなく、これまで見たことのない新たな入力に対する予測性能を汎化性能と呼ぶ
- 汎化誤差が小さいモデル＝良い性能を持ったモデルと言える
- 汎化誤差は通常、学習データとは別に収集された検証データでの性能を測ることで推定

2-4.　データの分割

- ホールドアウト法やクロスバリデーションを使ってデータを検証用データと学習用データに分割する

2-5.　実装演習

- 下記のような非線形のデータを用意する

![スクリーンショット 2021-06-13 16.05.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/4dcaf83f-ef3f-184e-7744-9877cd1ef94a.png)

```
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)
print(clf.score(data, target))
0.8298402977198136
```

- このように非線形回帰モデル（ガウスカーネルモデル）を利用することで、決定係数の大きい（決定係数が1に近いほどモデルがデータに当てはまっていることを表す）結果を得ることができた

![スクリーンショット 2021-06-13 16.07.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/339a7835-ae41-af67-b1f4-00e8c900c3fb.png)

#3.ロジスティック回帰モデル

3-1. 分類問題

- ある入力からクラスに分解する問題を分類問題と呼ぶ
- 入力の各要素を説明変数または特徴量、出力を目的変数と呼び、0　or　1の値で表す

3-2. ロジスティック線形回帰モデル

- ロジスティック回帰モデルとは、分類問題を解くための教師あり機械学習モデルの一つで、入力とｍ次元パラメータの線形結合をシグモイド関数に入力する
- 出力はy=1になる確率の値になる

3-3. 最尤推定

- ロジスティック回帰モデルではベルヌーイ分布を利用する

```math
　P(y) = p^y(1-p)^{1-y}
```
- データからそのデータを生成したであろう尤もらしい分布（パラメータ）を推定することを最尤推定という

3-4.　実装演習

- タイタニック号乗客車の生存状況の表形式データセットを用いる
- Titanic　datasetは「1912年に北大西洋で氷山に衝突して沈没したタイタニック号の乗客車の生存状況」の表形式データセットである

参考：[Titanic dataset](https://www.atmarkit.co.jp/ait/articles/2007/02/news016.html)

- データの前処理として不要なデータの削除・欠損値の補完を行う

```
#予測に不要と考えるカラムをドロップ
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)
print(model.predict([[50]]))
print(model.predict([[100]]))
[0]
[1]
```

- 予測の結果として、0 or 1の2値に分類ができていることがわかる

#4.主成分分析

4-1. 主成分分析

- 多変量のデータの持つ構造をより少数個の指標に圧縮することを主成分分析という
- 情報の量を分散の大きさと捉え、線形変換後の変数の分散が最大となる射影軸を探索する
- 線形変換後の分散は以下の式で表せる

```math
　Var(\boldsymbol{s}_j) = \frac{1}{n}\boldsymbol{s}^T_j\boldsymbol{s}_j = \frac{1}{n}(\bar{X}\boldsymbol{a}_j)^T(\bar{X}\boldsymbol{a}_j) = \boldsymbol{a}^T_jVar(\bar{X})\boldsymbol{a}_j
```

- 上記分散式にノルムが1となるような制約を加えて、ラグランジュ関数を最大にする係数ベクトルを探索すると、元データの分散共分散行列の固有値と固有ベクトルが、制約付き最適化問題の解となる

4-2.　実装演習

```
#ライブラリのインポート
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
#訓練データ生成
n_sample = 100

def gen_data(n_sample):
    mean = [0, 0]
    cov = [[2, 0.7], [0.7, 1]]
    return np.random.multivariate_normal(mean, cov, n_sample)

def plt_data(X):
    plt.scatter(X[:, 0], X[:, 1])
    plt.xlim(-5, 5)
    plt.ylim(-5, 5)
#訓練データの表示
X = gen_data(n_sample)
plt_data(X)
```
![スクリーンショット 2021-06-13 17.17.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/6283e4d0-023a-e686-1a6d-20e75e72a030.png)

```
#関数定義
n_components=2

def get_moments(X):
    mean = X.mean(axis=0)
    stan_cov = np.dot((X - mean).T, X - mean) / (len(X) - 1)
    return mean, stan_cov
    
def get_components(eigenvectors, n_components):![スクリーンショット 2021-06-13 17.20.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/d22f60d0-8dd2-7ba2-4a9f-d18fcb386e58.png)

    W = eigenvectors[:, ::-1][:, :n_components]
    return W.T

def plt_result(X, first, second):
    plt.scatter(X[:, 0], X[:, 1])
    plt.xlim(-5, 5)
    plt.ylim(-5, 5)
    # 第１主成分
    plt.quiver(0, 0, first[0], first[1], width=0.01, scale=6, color='red')
    # 第２主成分
    plt.quiver(0, 0, second[0], second[1], width=0.01, scale=6, color='green')

#分散共分散行列を標準化
meean, stan_cov = get_moments(X)
#固有値と固有ベクトルを計算
eigenvalues, eigenvectors = np.linalg.eigh(stan_cov)
components = get_components(eigenvectors, n_components)

plt_result(X, eigenvectors[0, :], eigenvectors[1, :])
```

![スクリーンショット 2021-06-13 17.20.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/20b04479-83ff-1261-90d0-fef82b2430b3.png)


#5.アルゴリズム

5-1. K近傍法（ｋNN）

- 分類問題のための機械学習手法
- 最近傍のデータをk個取ってきて、それらがもっとも多く所属するクラスに識別する
- ｋNNのアルゴリズムは以下のように表せる

```
kNNのアルゴリズム
1.既存のデータ（学習データ）をベクトル空間にプロット
2.未知データと既存データの距離を計算する
3.未知データと近いデータをk個抽出する
4.抽出したk個のデータで多数決をして、結果として出力する
```
参考：[kNN](https://nisshingeppo.com/ai/knn/)

5-2.　実装演習（kNN）

```
#ライブラリのインポート
%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)
```

![スクリーンショット 2021-06-13 17.28.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/a1fc6a58-7691-a44a-fb64-e4d5fc5a572e.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)
```

![スクリーンショット 2021-06-13 17.32.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/b724a490-a5de-39dd-19d5-59a6cc989628.png)


5-3. k-means

- 教師なし学習で用いられるクラスタリング手法で、与えられたデータをk個のクラスタに分類する
- k-meansのアルゴリズムは以下のように表せる

```
k-meansのアルゴリズム
1.k(≧1)を決める
2.ｋ個のランダムな配列（セントロイド）を生成する
3.セントロイドからの距離を近いものにラベル付けしていく
4.各ラベルの重心にセントロイドを移動させる
5.セントロイドが収束するまで3,4を繰り返す
```
参考：[k-means](https://qiita.com/masatojames/items/b33b7881b168f2c5495c)

5-4.　実装演習（k-means）

```
#　ライブラリのインポート
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
#　データ生成
def gen_data():
    x1 = np.random.normal(size=(100, 2)) + np.array([-5, -5])
    x2 = np.random.normal(size=(100, 2)) + np.array([5, -5])
    x3 = np.random.normal(size=(100, 2)) + np.array([0, 5])
    return np.vstack((x1, x2, x3))
#データ作成
X_train = gen_data()
#データ描画
plt.scatter(X_train[:, 0], X_train[:, 1])
```

![スクリーンショット 2021-06-13 17.39.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/f69d64a4-9317-e85b-f9ca-5e361cfa8624.png)

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

n_clusters = 3
iter_max = 100

# 各クラスタ中心をランダムに初期化
centers = X_train[np.random.choice(len(X_train), n_clusters, replace=False)]

for _ in range(iter_max):
    prev_centers = np.copy(centers)
    D = np.zeros((len(X_train), n_clusters))
    # 各データ点に対して、各クラスタ中心との距離を計算
    for i, x in enumerate(X_train):
        D[i] = distance(x, centers)
    # 各データ点に、最も距離が近いクラスタを割り当
    cluster_index = np.argmin(D, axis=1)
    # 各クラスタの中心を計算
    for k in range(n_clusters):
        index_k = cluster_index == k
        centers[k] = np.mean(X_train[index_k], axis=0)
    # 収束判定
    if np.allclose(prev_centers, centers):
        break

#クラスタリング結果を表示
def plt_result(X_train, centers, xx):
    #　データを可視化
    plt.scatter(X_train[:, 0], X_train[:, 1], c=y_pred, cmap='spring')
    # 中心を可視化
    plt.scatter(centers[:, 0], centers[:, 1], s=200, marker='X', lw=2, c='black', edgecolor="white")
    # 領域の可視化
    pred = np.empty(len(xx), dtype=int)
    for i, x in enumerate(xx):
        d = distance(x, centers)
        pred[i] = np.argmin(d)
    plt.contourf(xx0, xx1, pred.reshape(100, 100), alpha=0.2, cmap='spring')
y_pred = np.empty(len(X_train), dtype=int)
for i, x in enumerate(X_train):
    d = distance(x, centers)
    y_pred[i] = np.argmin(d)
xx0, xx1 = np.meshgrid(np.linspace(-10, 10, 100), np.linspace(-10, 10, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

plt_result(X_train, centers, xx)
```

![スクリーンショット 2021-06-13 17.42.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/d9eb74f3-0ade-a95d-dbea-e66a6f0e4e56.png)

#6.サポートベクターマシーン

6-1. サポートベクターマシーン（SVM）
 
- 2クラス分類のための機械学習手法で、線形モデルの正負で2値分類を行う
- 下記のような異なるカテゴリの黄色と紫のデータがあった場合に、これらをうまく分類するための線を引くことを考える

![スクリーンショット 2021-06-12 22.18.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/af8899d8-7fa6-dffb-d07a-183a06345068.png)

- サポートベクターマシーンでは、このようにお互いのカテゴリに近い点を基準にして分割線を引く
- 点線は、Negative-hyperplane、Positive-hyperplaneと呼ばれる（任意でポジティブ、ネガティブと呼ばれており、特に決まりはないらしい）
- 各点線から距離が等しくなるように引いた実線の分割線を超平面（hyperplane）という
- 超平面と最も近いデータ点との距離をマージンといい、マージンが最大となるような超平面を求める

![スクリーンショット 2021-06-12 22.24.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/0ef90413-eb42-143c-5959-a53dce06adc0.png)

- 2つの分類問題を考えた時、サポートベクターマシーンの特徴として、より曖昧なデータを使用することが挙げられる
- 多くのアルゴリズムが全てのデータを用いて分類を行うのに対して、SVMでは境界に近い値を使用する

参考：[SVM](https://yaakublog.com/svmsupport-vector-machine)

6-2.　実装演習

```
#ライブラリのインポート
%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)
```

![スクリーンショット 2021-06-13 17.46.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/fea7bade-52ed-bef5-fd47-e33946a7394f.png)

```
#学習
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=['--', '-', '--'])


# マージンと決定境界を可視化
plt.quiver(0, 0, 0.1, 0.35, width=0.01, scale=1, color='pink')
```

![スクリーンショット 2021-06-13 17.50.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1575602/52addf41-4661-c961-77b5-4e94db9c39e2.png)