ラビットチャレンジ（機械学習）

#要点のまとめ
## 1. 線形回帰モデル
####＜概要＞
回帰問題とは？
　　ある入力から出力を予測する問題
　　直線で予測　⇒　線形回帰
　　曲線で予測　⇒　非線形回帰

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

　　入力（説明変数、特徴量）　⇒　m次元のベクトル
　　出力（目的変数）　⇒　スカラー値

　　入力をx，パラメータをw，予測値をŷとすると

```math
{ŷ =w^Tx+w_0=\sum_{j=1}^{m}w_jx_j+w_0
}
```

・説明変数が1次元の場合(m=1)は単回帰となり、2次元以上の場合(m>1)は重回帰となる。
・学習データの平均二乗誤差を最小となるようなパラメータを最小二乗誤差で推定する。

**最小二乗法**
・平均二乗誤差とはデータとモデル出力の二乗誤差の和。
・最小二乗法とは、学習データの平均二乗誤差を最小とするようにパラメータを推定すること。
・平均二乗誤差の最小化は、その勾配が0になる点を求めれば良い。
　　（ｗに対して微分したものが0となるｗの点を求める）

　　平均二乗誤差(MSE)の式

```math
MSE_{\rm train}=\frac{1}{n_{\rm train}}\sum_{k=1}^{n_{\rm train}}\Big(\hat{y_k}-y_k\Big)^2
```

　　最小二乗法

```math
\frac{\partial}{\partial w}MSE_{\rm train}=0
```

・重み(w)は最小二乗法により求めることができ、解析的に解くことができる。

```math
{\hat{\boldsymbol{w}} = (X^TX)^{-1}X^T\boldsymbol{y}
}
```


## 2. 非線形回帰モデル
#### ＜概要＞
・複雑な非線形構造のデータでは線形回帰モデルでは捉えられないため非線形回帰モデルで表現する。
・非線形回帰モデルは回帰関数として、基底関数と呼ばれる既知の非線形関数とパラメータベクトルの線型結合を使用。
・基底関数は多項式関数やガウス型基底関数などを用いるこどが多い。
・未知パラメータは線形回帰モデルと同様に最小2乗法や最尤法により推定する。

　　多項式関数

```math
\phi_j(x)=x^j
```

　　ガウス型基底関数

```math
\phi_j(x)=\exp \Bigg(\frac{(x-\mu_j)^2}{2h_j}\Bigg)
```

**未学習と過学習**
・回帰を実行するにあたり、未学習と過学習の対策が必要
・未学習とは学習データに対して、十分小さな誤差が得られないこと。
　　対策　⇒　モデルの表現力が低いため、表現力の高いモデルを利用する
・過学習とは学習データへの当てはまりが良すぎて、逆にテストデータに対する誤差が大きいこと。
　　対策　⇒　学習データの数を増やす
　　　　　⇒　不要な基底関数(変数)を削除して表現力を抑止
　　　　　⇒　正則化法を利用して表現力を抑止

**正則化**
・正則化とは、損失関数に罰則項を組み込むことで、次数が大きくなった時のパラメータを抑えモデルの複雑さを緩和すること。
・複雑なモデルが過学習を起こす原因として、学習パラメータが極端に大きい（または小さい）ことが挙げられる。
・罰則項は、学習パラメータの絶対値が大きくなることに対して、損失が大きくなるような罰則を与える役割を持っている。
・L1ノルムを利用する場合の回帰をLasso回帰、L2ノルムを利用する場合の回帰をRidge回帰と呼ぶ

　　L1ノルムを利用　⇒　Lasso推定量（いくつかのパラメータを正確に0に推定）

```math
R(w)=\sum_{k=1}^m|w_k|=|w_1|+|w_2|+|w_3|+\cdots +|w_m|
```

　　L2ノルムを利用　⇒　Ridge推定量（パラメータを0に近づけるよう推定）

```math
R(w)=\sqrt{\sum_{k=1}^m w_k^2}=\sqrt{w_1^2+w_2^2+w_3^2+\cdots +w_m^2}
```

**データの分割**
・データを学習用データと検証用データに分割
　　（分割する目的）
　　　汎化性能を測定するため　　
　　　学習データに対する当てはまりの良さだけでなく、未知のデータに対する精度を測定する

**汎化性能の検証**
・作成されたモデルの汎化性能を検証するためにホールドアウト法や交差検証法が存在する。
・学習データや検証データの相性が偶然にもよいまたは悪い場合があるため、交差検証を行うことが推奨される。

＜ホールドアウト＞
・データを学習用とテスト用の2つに分割し、学習データで学習し検証データで予測精度をチェックする。
・データを1回で2つに分けてしまうためデータが多くなければならない。
・外れ値が学習用データに入ると、外れ値にフィッティングしてしまう。

＜クロスバリデーション（交差検証）＞
・データをいくつかのブロックに分割し、1つのブロックを検証データ、他を学習データとする。
・学習データでモデルを学習させ、検証データで各モデルの精度を計測する、ということを分割数だけ繰り返す。
・精度評価は、各モデルの結果の平均を行う。
・外れ値の影響を受けづらい。


## 3. ロジスティック回帰モデル
#### ＜概要＞
・分類問題を解くための教師あり学習
・ロジスティック回帰はある事象が起こる確率を学習し、ある事象が起こる/起こらないの２値分類を行うモデル。
・入力とm次元パラメータの入力をシグモイド関数に入力すると、y=1となる確率の値を出力する。

　　ロジスティック回帰モデル：

```math
{P(Y=1|X=x)=\sigma(w_o+\sum_{j=1}^dx_jw_j)
}
```

シグモイド関数が用いられる理由は
　　シグモイド関数の出力が０～１となるため確率として扱える。
　　シグモイド関数の微分は、シグモイド関数自身で表現することが可能。

　　シグモイド関数の式：

```math
\sigma(x)=\frac{1}{1+\exp(-ax)}=\frac{1}{1+e^{-ax}}
```
　　シグモイド関数の微分：

```math
\sigma^{\prime}(x)=a\sigma(x)\big(1-\sigma(x) \big)
```
**最尤推定**
・ロジスティック回帰では、未知のパラメータｗを推定する際、最尤推定を使用する。
・以下の尤度関数が最大になるパラメータを探索することで求めることができる。
・ロジスティック回帰モデルではベルヌーイ分布を利用する。

　　ロジスティック回帰の尤度関数：

```math
{\boldsymbol{L}(\boldsymbol{w})=\prod_{i=1}^n \hat{y}_{i}^{y_i}(1-\hat{y}_{i})^{(1-y_i)}
}
```

・実際の計算では、この式の対数をとるほうが積から和に変換されるため、簡単である。
・対数をとったものを対数尤度関数という。

　　対数尤度関数：

```math
{E(\boldsymbol{w})=-\log{\boldsymbol{L}(\boldsymbol{w})}=-\sum_{i=1}^n (y_{i}\log{\hat{y}_{i}}+(1-y_{i})\log{(1-\hat{y}_{i})})
}
```

・上記の対数尤度関数をパラメータでして0になる値を求める必要があるが
　解析的に求めるのは難しいため、勾配降下法などにより求める。

**勾配降下法**
・勾配降下法は反復学習によりパラメータを逐次的に更新するアプローチのひとつ。
・勾配降下法ではパラメータを更新するのにすべてのデータに対する和を求める必要があるため、
膨大な計算資源が必要になることがある。
そのため、データを１つずつランダムに選んでパラメータを更新する「確率的勾配降下法」を用いて、効率よく最適な解を求めるアプローチを通常とる。

**モデルの評価**
分類の評価方法
(1) 正解率（Accuracy）

```math
\frac{TP+TN}{TP+FN+FP+TN}
```

(2) 再現率(Recall）
　　　本当にPositiveであるものの中からPositveと予測できている割合

```math
\frac{TP}{TP+FN}
```

(3) 適合率(Precision）
　　　モデルがPositiveと予想したものの中で本当にPositiveであったものの割合

```math
\frac{TP}{TP+FP}
```

(4) F値
　　　再現率と適合率はトレードオフ
　　　再現率と適合率の調和平均

```math
\frac{2}{\frac{1}{Recall}+\frac{1}{Precision}}=\frac{2(Precision)(Recall)}{Recall+Precision}
```


## 4. 主成分分析
#### ＜概要＞
・データの変数を削減する手法の一つ。対象データにおける方向と重要度を見つける。
・多変量データをより少数個の指標に圧縮
・情報の損失をなるべく小さくしつつ少数個の変数に圧縮
・可視化がしやすくなる
・主成分ごとに計算される固有値を固有値の総和で割ると、主成分の重要度を割合で表すことが可能。
　　寄与率：第k主成分の分散の全分散に対する割合（第k主成分が持つ情報量の割合）
　　累積寄与率：第1-k主成分まで圧縮した際の情報損失量の割合

・主成分分析で計算される第一主成分は分散を最大化するような軸になっている。

＜主成分分析の手順＞
　①分散共分散行列を求める
　②分散共分散行列に対して、固有値問題を解き、固有ベクトル・固有値を求める
　③各主成分方向にデータを表現する

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

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

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

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

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

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




## 6. サポートベクターマシン
#### ＜概要＞
・2クラス分類のための機械学習手法
・線形判別関数（決定境界）と最も近いデータ点との距離（マージン）が最大化されるように線形判別関数を求める。

```math
{y=w^Tx+b=\sum_{j=1}^mw_jx_j+b
}
```

　（マージン）
　　線形判別関数ともっとも近いデータ点との距離をマージンという
　（サポートベクター）
　　データを分割する直線に最も近いデータ点
　　分離超平面を構成する学習データは、サポートベクターだけで残りのデータは不要
・ハードマージン・ソフトマージン
　　ハードマージンはマージンの内側にデータが入り込むのを許容しない
　　ソフトマージンは一部のデータがマージンの内側に入ることを許容する
　　ソフトマージンを導入することで、線形分離可能でないデータについても学習可能になる。
・非線形分離
　　線形分離できないとき特徴空間に写像し、その空間で線形に分類する
　　カーネルトリックを利用することで、複雑な決定境界を学習することができる。


＜目的関数の求め方＞

各点と決定境界との距離

```math
{\frac{|w^Tx_i+b|}{||w||}=\frac{t_i(w^Tx_i+b)}{||w||}
}
```

したがって、マージンとは決定境界と最も距離の近い点との距離なので次の式となる

```math
{\min_i\frac{t_i(w^Tx_i+b)}{||w||}
}
```

サポートベクターマシーンは、マージンを最大化することが目標なので目的関数は次の式となる

```math
{\max_{w,b}[\min_i\frac{t_i(w^Tx_i+b)}{||w||}]
}
```

また、マージン上の点においては　${t_i(w^Tx_i+b)\geqq 1
}$　の条件が成り立つため、
目的関数は以下のようになる。

```math
{\max_{w,b}\frac{1}{||w||}
}
```

#実装演習

## 1. 線形回帰モデル
＜ボストンの住宅データセットを線形回帰モデルで分析＞

課題：部屋数が4で犯罪率が0.3の物件はいくらになるか
結果：部屋数が4で犯罪率が0.3の物件の価格は4240ドルと予想される。

######コード
```python
#必要モジュールとデータのインポート
from sklearn.datasets import load_boston
from pandas import DataFrame
import numpy as np

# ボストンデータを"boston"というインスタンスにインポート
boston = load_boston()

#データフレームの作成
#　説明変数をDataFrameへ変換
df = DataFrame(data=boston.data, columns = boston.feature_names)
# 目的変数をDataFrameへ追加
df['PRICE'] = np.array(boston.target)

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

##重回帰分析(2変数)

#カラムを指定してデータを表示
df[['CRIM', 'RM']].head()

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

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

# fit関数でパラメータ推定
model2.fit(data2, target2)
model2.predict([[0.3, 4]])
```
######実行結果
array([4.24007956])
＜結果＞
部屋数が4で犯罪率が0.3の物件の価格は4240ドル


## 2. 非線形回帰モデル
課題： コードを変更し出力結果の違いを確認する。
######コード
```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)

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/b557e004-ef77-6c66-21b5-623778236e55.png)

```python
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/1593495/cda3fc68-bb35-ebb4-82b8-339212917134.png)

```python
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/1593495/210bfdd2-ab1a-5c6a-ea9a-2124ec3cccdc.png)


######実行結果



## 3. ロジスティック回帰モデル
タイタニックの乗客データを利用しロジスティック回帰モデルを作成、特徴量抽出をしてみる

課題：年齢が30歳で男の乗客は生き残れるか？
結果：30歳男性の生存確率は約19%で生き残ることは困難
　　　30歳女性の生存確率は約73%となった。なぜだろう？

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

#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

# タイタニックデータ csvファイルの読み込み
titanic_df = pd.read_csv('/content/drive/My Drive/study_ai_ml_google/data/titanic_train.csv')

#不要なデータの削除・欠損値の補完
#予測に不要と考えるからうをドロップ (本当はここの情報もしっかり使うべきだと思っています)
titanic_df.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

#一部カラムをドロップしたデータを表示
#titanic_df.head()
#nullを含んでいる行を表示
#titanic_df[titanic_df.isnull().any(1)].head(10)

#Ageカラムのnullを中央値で補完
titanic_df['AgeFill'] = titanic_df['Age'].fillna(titanic_df['Age'].mean())

#再度nullを含んでいる行を表示 (Ageのnullは補完されている)
#titanic_df[titanic_df.isnull().any(1)]

#titanic_df.dtypes

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

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

from sklearn.linear_model import LogisticRegression

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

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

print("チケット価格 61ドル")
print(model.predict([[61]]))
print(model.predict_proba([[61]]))

print("チケット価格 62ドル")
print(model.predict([[62]]))
print(model.predict_proba([[62]]))

########## 実装(年齢、性別から生死を判別) ##########

#年齢、性別のリストを作成
titanic_df['Gender'] = titanic_df['Sex'].map({'female': 0, 'male': 1}).astype(int)
data2 = titanic_df.loc[:, ['Gender','AgeFill']].values
#生死フラグのみのリストを作成
label2 =  titanic_df.loc[:,'Survived'].values

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

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

print("男性　30歳")
print(model2.predict([[1,30]]))
print(model2.predict_proba([[1,30]]))

print("女性　30歳")
print(model2.predict([[0,30]]))
print(model2.predict_proba([[0,30]]))

```
######実行結果
チケット価格 61ドル
[0]
[[0.50358033 0.49641967]]
チケット価格 62ドル
[1]
[[0.49978123 0.50021877]]

男性　30歳
[0]
[[0.80668102 0.19331898]]
女性　30歳
[1]
[[0.26744115 0.73255885]]

＜結果＞
61ドルのチケットは生存×
61ドルのチケットは生存〇
30歳男性の生存は×
30歳女性の生存は〇

## 4. 主成分分析
乳がん検査データを利用しロジスティック回帰モデルを作成
主成分を利用し2次元空間上に次元圧縮してみる

課題：32次元のデータを2次元上に次元圧縮した際に、うまく判別できるかを確認する
結果：2次元になったことで識別しやすい形になった。
　　　第2主成分までの累積寄与率は60%超。

######コード

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

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

## sys.pathの設定
cancer_df = pd.read_csv('/content/drive/My Drive/study_ai_ml_google/data/cancer.csv')

print('cancer df shape: {}'.format(cancer_df.shape))

#データを確認すると、欠損の列があるため、削除する。
cancer_df.drop('Unnamed: 32', axis=1, inplace=True)

print('cancer df shape: {}'.format(cancer_df.shape))
```
cancer df shape: (569, 32)
データは569件、次元数は32。

```python
# 目的変数の抽出
y = cancer_df.diagnosis.apply(lambda d: 1 if d == 'M' else 0)

# 説明変数の抽出
X = cancer_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 = LogisticRegressionCV(cv=10, random_state=0)
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.988
Test score: 0.972

```python
#PCAによる次元削減（次元数30）
pca = PCA(n_components=30)
pca.fit(X_train_scaled)
plt.bar([n for n in range(1, len(pca.explained_variance_ratio_)+1)], pca.explained_variance_ratio_)

```
PCAによる次元削減（次元数30）
第2主成分までで60%を超えている。
第4主成分までで80%を超えている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/b686d0e3-5547-e642-814c-7aa94241d4ca.png)

```python
# 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軸
```
######実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/3378a549-1620-1950-83b3-0beeb428b3b1.png)



## 5. アルゴリズム
課題： コードを変更し出力結果の違いを確認する。
###  5-1 k近傍法
######コード
```python
%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/1593495/fcfa9221-12e9-af5f-bea8-3de61cdb8bfe.png)




```python
#学習なし

#予測
#予測するデータ点との、距離が最も近い k 個の、訓練データのラベルの最頻値を割り当てる

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 = 5
print('  K=',n_neighbors)
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)
```
######実行結果
境界線はn=3よりn=10の場合滑らかになっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/6e24ac19-d369-5f92-5efb-7bb8132647b6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/86aec658-8125-7607-7be2-341ac12c797d.png)



###  5-2 k-means法
######コード
```python
# k平均クラスタリング(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])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/a23ecefa-7441-baa1-379b-cf49b70e3b4e.png)


```python
## 学習

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)
```
######実行結果
各クラスタ中心をランダムに初期化しているため上手くひけない場合がある
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/ed0ca06e-c76f-fa01-5ed1-9c8a6f25849e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/d7fa50ca-413d-45d9-fdee-c61de91fc372.png)



## 6. サポートベクターマシン
課題： コードを変更し出力結果の違いを確認する。
#### ①線形分離可能
######コード
```python

%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/1593495/e92b1a6d-ec2c-326a-469b-a3a359847426.png)

```python
## 学習

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')
```
######実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/5911727e-2661-3c07-55ff-883d336bee2c.png)



#### ②線形分離不可能
######コード

```python
## 訓練データ生成② （線形分離不可能）

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/1593495/d3b9332f-c14f-86e6-42ec-74bdcf5db795.png)

```python
## 学習

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/1593495/a008b21d-353d-4de2-1559-7893ec549c1b.png)