機械学習

# モデル選択・評価
## データの分割
- 学習データ：モデルを学習させるデータ
- 検証データ：学習済みのモデルの精度を検証するためのデータ

何故か
学習済みのモデルの凡化性を測定するため
凡化性：データへ当てはまる良さではなくて、未知のデータへ当てはまる性質

## 未学習(underfit) ・ 過学習(overfit)
### 未学習
学習データに対して、十分小さい誤差が得られない
対策：表現力の高いモデルを利用する（学習パラメータ数を増やす）

### 過学習
学習データに対して、十分小さい誤差が得られたが、
検証データの誤差が学習データの誤差の差が大きい
対策①：学習データを増やす
対策②：モデルの表現力を抑止する（学習パラメータ数を減らす）
対策③：正則化法を利用する

![スクリーンショット 2021-04-16 15.37.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/0ce127cd-6f98-836d-7dd2-1914c4897063.png)
![スクリーンショット 2021-04-20 10.54.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/3ac20d7d-02d9-5d1c-6c8a-7702c064890e.png)

#### 正則化法
##### 正則化項（罰則項）
```math:
 \mathbf{y} = \mathbf{X}\boldsymbol{\beta} + \boldsymbol{\epsilon} \qquad 
\boldsymbol{\beta} = \begin{pmatrix} \beta_{1} \\ \beta_{2} \\ \vdots \\ \beta_{n} \end{pmatrix} 
```
回帰

```math:
\hat{\boldsymbol{\beta}} = \arg \min \left\{||\mathbf{y} -  \mathbf{X}\boldsymbol{\beta}||^{2}_{2} \right\}
```

Lasso

```math:
||\boldsymbol{\beta}||_{1}^{1} = |\beta_{1}| + |\beta_{2}| + \cdots + |\beta_{n}|\\
\hat{\boldsymbol{\beta}} = \arg \min \left\{ ||\mathbf{y} -  \mathbf{X}\boldsymbol{\beta}||^{2}_{2} + \lambda ||\boldsymbol{\beta}||_{1}^{1}  \right\}
```
Ridge

```math:
||\boldsymbol{\beta}||_{2}^{2} = |\beta_{1}|^{2} + |\beta_{2}|^{2} + \cdots + |\beta_{n}|^{2}\\
\hat{\boldsymbol{\beta}} = \arg \min \left\{ ||\mathbf{y} -  \mathbf{X}\boldsymbol{\beta}||^{2}_{2} + \lambda ||\boldsymbol{\beta}||_{2}^{2}  \right\}

```
![スクリーンショット 2021-04-19 14.58.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/86c9a7d9-5df0-42b7-51e6-b1faadc2ee30.png)
λが小さい→普通の回帰になる
λが大きい→製薬効果が高くなる

##### 参考
https://qiita.com/kenmatsu4/items/cecb466437da33df2870

https://stats.biopapyrus.jp/sparse-modeling/ridge.html

https://ja.wikipedia.org/wiki/ラグランジュの未定乗数法
#### ホールドアウト法
データを学習用と検証用の２つに分割する方法
大量のデータが必須

#### クロスバリデーション法（交差検証）
データを学習用と検証用に分割し
検証用と学習用のデータを交代して、学習、評価する
![スクリーンショット 2021-04-20 11.05.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/1316d40a-0674-e2c4-75a3-9b5c751bf4b2.png)

#### ホールドアウト法とクロスバリデーション法
ホールドアウトで検証したところ70％の精度、クロスバリデーションで65％の精度の場合、凡化精度の推定としてクロスバリデーション65％を採用する

#### グリッドサーチ
適切なハイパーパラメータを選ぶ一つの方法
![スクリーンショット 2021-04-20 11.11.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/753997c6-11c2-0f0a-4625-cd9e06b68dd2.png)

#### 混同行列ーconfusion matrix
![スクリーンショット 2021-04-20 11.16.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/8f8ec439-8379-b16d-d184-35333db6e8f2.png)

#### 分類の評価方法
##### 正解率の問題
正解した数/予測対象となった全データ数
分類したいクラスには偏りが多い場合、正解率の意味が薄くなる。
例：
メールのスパム分類：
スパム数が80件・普通のメール20件のデータの場合、
分類器は<strong>全て</strong>のメールをスパムとすると、正解率が80％になる、???
##### 再現率ーRecall

「本当のPositiveなもの」の中からPositiveと予測した割合
誤りなものなにPositiveと予測したFalse Positive事情を考えない（多くてもいい）
例：病気の診断で陽性であるものを陰性（<strong>False Negative</strong>)と誤診としてしまうのを避けたい。
陰性であるものを陽性と誤診としてしまっても(False Positive)、再度、検査すれば、大丈夫からです。
※False Negativeを少なくしたい時

![スクリーンショット 2021-04-20 14.43.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/4e841c80-d78d-d07d-a977-a5d68ba8be9f.png)

##### 適合率ーPrecision
Positiveと予測したものの中から、本当のPositiveである割合
本当のものなのに、Negativeと予測したFalse Negative事情を考えない（多くてもいい）
例：大事なメールをスパム(<strong>False Positive</strong>)と誤検してしまうのを避けたい。
スパムであるものを普通のメールと誤検しても(False Negative)、自分でやればいい
※False Position を少なくしたい
![スクリーンショット 2021-04-20 14.45.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/ff9a7ad4-51b6-21e7-6a43-183d00b48fde.png)

##### F値
RecallとPrecisionはトレードオフの関係あり、どちらを小さくすると、片方の値が大きくなってします。
RecallとPrecisionの調和平均
RecallとPrecisionのバランスを示している。高ければ高いほど、RecallとPrecisionともに高くなる
![スクリーンショット 2021-04-20 15.36.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/e4c27fda-8da2-5c22-e861-5585efab73b5.png)

# 回帰問題について
ある入力値から出力値を予測する
例：賃貸の広さ、周辺状況を元に、家賃を予測する

直線で予測できる場合、線形回帰
曲線で予測できる場合、非線形回帰
# 線形回帰
## 単回帰
![スクリーンショット 2021-04-13 13.49.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/42365aa6-bf7e-006d-4c30-b9bf9ed1c3f2.png)
### 説明
![IMG_1763 (1).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/70bb2d4f-701a-81ae-f043-44bed3fec94e.jpeg)
![IMG_1764 (1).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/ff7fa84e-4931-f12f-6684-5900299f830d.jpeg)

### 参考
https://www.sist.ac.jp/~kanakubo/research/statistic/tankaiki.html

### データ準備
```python:
n_sample = 100
var = .2

def linear_func(x):
    return 2 * x + 5

def add_noise(y_true, var):
    return y_true + np.random.normal(scale=var, size=y_true.shape)

def plt_result(xs_train, ys_true, ys_train):
    plt.scatter(xs_train, ys_train, facecolor="none", edgecolor="r", s=50,  label="training data")
    plt.plot(xs_train, ys_true, label="$2 x + 5$")
    plt.legend()

xs = np.linspace(0, 1, n_sample)
ys_true = linear_func(xs)
ys = add_noise(ys_true, var)
```
### 学習
![スクリーンショット 2021-04-13 15.10.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/2bbe8ad1-c7fd-a5c9-d496-b557d615c257.png)

### 予測
![スクリーンショット 2021-04-13 15.15.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/a8b00e8a-d528-2672-3871-f5a678d31c8f.png)

## 重回帰分析
![スクリーンショット 2021-04-14 10.31.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/03eeb64a-463e-a4e9-fe29-acb7b32abb51.png)
### データ準備
```python:
n_sample = 100
var = .2

def mul_linear_func(x):
    ww = [1., 0.5, 2., 1.]
    return ww[0] + ww[1] * x[:, 0] + ww[2] * x[:, 1] + ww[3] * x[:, 2]

def add_noise(y_true, var):
    return y_true + np.random.normal(scale=var, size=y_true.shape)

def plt_result(xs_train, ys_true, ys_train):
    plt.scatter(xs_train, ys_train, facecolor="none", edgecolor="b", s=50,  label="training data")
    plt.plot(xs_train, ys_true, label="$1 + 0.5x_1 + 2x_2 + x_3$")
    plt.legend()
x_dim = 3

X = np.random.random((n_sample, x_dim))
ys_true = mul_linear_func(X)
ys = add_noise(ys_true, var)
xs = np.linspace(0, 1, n_sample)
plt_result(xs, ys_true, ys)
```
### 学習
![スクリーンショット 2021-04-14 10.34.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/140f74d1-e041-3678-c2a4-94877d02cb44.png)

### 予測
![スクリーンショット 2021-04-14 10.35.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/6094124c-88cb-ab58-4a58-4594741f7667.png)

### 考察
inputデータの次元が１に
Wの次元が２にしたら、単回帰になる

# 非線形回帰
## 多項式回帰
![スクリーンショット 2021-04-13 15.17.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/dfa2a4ba-01b5-01d9-bc6a-0f233d53337e.png)
### データ準備
```python:
n_sample = 10
var = .25

def sin_func(x):
    return np.sin(2 * np.pi * x) 

def add_noise(y_true, var):
    return y_true + np.random.normal(scale=var, size=y_true.shape)

def plt_result(xs, ys_true, ys):
    plt.scatter(xs, ys,facecolor="none", edgecolor="b", s=50,  label="training data")
    plt.plot(xs, ys_true, label="$\sin(2\pi x)$")
    plt.legend()
#データの作成
xs = np.linspace(0, 1, n_sample)
ys_true = sin_func(xs)
ys = add_noise(ys_true, var)

print("xs: {}".format(xs.shape))
print("ys_true: {}".format(ys_true.shape))
print("ys: {}".format(ys.shape))

#結果の描画
plt_result(xs, ys_true, ys)
```
### 学習
![スクリーンショット 2021-04-13 15.18.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/daec0f95-5137-fcd9-1241-6b92beed20ef.png)
### 予測
![スクリーンショット 2021-04-13 15.20.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/0ed48f16-e453-b684-c900-679cd048de65.png)

# 分類問題について
ある入力値に対して、クラスに分類する

# ロジックティック回帰
## Sigmoid（シグモイド）関数 
```math
\sigma(x) = \frac{1}{1 + \exp{(-ax)}}
```
![スクリーンショット 2021-04-15 11.38.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/ca43cdf4-d998-ebfa-208d-5ffb28554d6f.png)

微分
![IMG_1767 (1).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/56b398a6-dece-1cfd-157d-599c75bc51af.jpeg)

```math
p(y=1 | \boldsymbol{x}; \boldsymbol{w}) = \sigma (\boldsymbol{w}^{\mathrm{T}} \boldsymbol{x})
```
モデル
![スクリーンショット 2021-04-15 11.48.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/02ae38a0-5ee0-bc67-ec1a-d282275defc0.png)
説明
![IMG_1766 (1).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/2bae2c02-4e4f-9183-893a-c002aad97090.jpeg)

## 最尤推定
尤度関数を最大化するようにパラメータを選ぶ推定
![スクリーンショット 2021-04-15 13.29.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/e39ce2e6-8173-a379-7429-f21981056a82.png)
log対数を取る理由：
同時確率の積を和に変換可能
指数が積の演算に変換可能

対数尤度関数が最大値になる点と尤度関数が最大値になる点は同じ

−をかける理由：
「対数尤度関数の最小化」を「最小２乗法の最小化」と合わせる

## 勾配降下法・Gradient Descent
反復学習によりパラメータを逐次的に更新する
![スクリーンショット 2021-04-15 17.14.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/18a161fc-7b37-f483-0561-64beab4b7582.png)
なぜ必要？

```math
線形回帰の場合：MSEのw微分が０になる \hat{w}を求められるが、\\
複雑な関数になると、直接に、\hat{w}を求めるのが難しいため
```
問題：
勾配降下では、パラメータを更新するため、全てのデータに対する和を求めないといけない。
膨大すぎるデータの場合、データをメモリに載せきれない問題が発生する。
この問題を解決するため、確率的な勾配降下を利用する

## 確率的勾配降下法・Stochastic Gradient Descent
データを一つずつランダムに選んで、パラメータを更新
![スクリーンショット 2021-04-16 13.39.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/ad87f064-7178-8839-7de3-1c43b4be7af3.png)

## データの準備
```python:
n_sample = 100
harf_n_sample = 50
var = .2

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

def plt_data(x_train, y_train):
    plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train, facecolor="none", edgecolor="b", s=50, label="training data")
    plt.legend()
#データ作成
x_train, y_train = gen_data(n_sample, harf_n_sample)
#データ表示
plt_data(x_train, y_train)
def add_one(x):
    return np.concatenate([np.ones(len(x))[:, None], x], axis=1)
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
```
## 勾配降下法・Gradient Descentの実装
```python:
def gd(X_train, max_iter, eta):
    w = np.zeros(X_train.shape[1])
    for _ in range(max_iter):
        w_prev = np.copy(w)
        sigma = sigmoid(np.dot(X_train, w))
        grad = np.dot(X_train.T, (sigma - y_train))
        w -= eta * grad
        if np.allclose(w, w_prev):
            return w
    return w
X_train = add_one(x_train)
max_iter=100
eta = 0.001
w_gd = gd(X_train, max_iter, eta)
```
## 確率的勾配降下法・Stochastic Gradient Descentの実装
```python:
def get_ramdom_batch(x, y, size=1):
  return_x = []
  return_y = []
  index_arr = [np.random.randint(0,y.shape[0]) for _ in range(0, size)]
  for index in index_arr:
    return_x.append(x[index])
    return_y.append(y[index])
  return np.array(return_x), np.array(return_y)
def sgd(X_train, max_iter, eta):
  w = np.zeros(X_train.shape[1])
  for _ in range(max_iter):
    w_prev = np.copy(w)
    x_batch, y_batch = get_ramdom_batch(X_train, y_train, 2)
    sigma = sigmoid(np.dot(x_batch, w))
    grad = np.dot(x_batch.T, (sigma - y_batch))
    w -= eta * grad
    if np.allclose(w, w_prev):
      return w
  return w
X_train = add_one(x_train)
max_iter=100
eta = 0.001
w_sgd = sgd(X_train, max_iter, eta)
```
## 予測
```python:
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 = add_one(xx)
proba = sigmoid(np.dot(X_test, w_gd))
y_pred = (proba > 0.5).astype(np.int)
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
```
![スクリーンショット 2021-04-16 13.59.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/d7ae74f9-bc67-8798-6039-924d2a1df243.png)

# 主成分分析
## データ
![スクリーンショット 2021-04-21 10.38.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/e5ad749e-6701-4c63-6d88-1f659c4ea6fd.png)

## 平均-Mean
![スクリーンショット 2021-04-21 10.39.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/09f0d133-7bc1-7692-5b00-01bf3341c6c0.png)
![スクリーンショット 2021-04-21 10.39.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/b4ce5f74-a38b-603a-bc7a-c071cae783f9.png)

## 分散共分散行列
![スクリーンショット 2021-04-21 10.42.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/4a04b7e5-070e-0c8e-9d44-d4ba0876fe72.png)
![スクリーンショット 2021-04-21 10.42.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/9ca268ae-19cf-beef-6e29-5276909f2149.png)
![スクリーンショット 2021-04-21 10.43.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/40e3ebbb-13fd-f333-e29e-6178ce0977d6.png)
![スクリーンショット 2021-04-21 10.43.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/a7ad0fb7-e72a-4369-fe87-0a1523091d43.png)


## 線形変化後のベクトル
![スクリーンショット 2021-04-21 10.46.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/42af38e1-7972-1149-6e05-08b842c6a743.png)
![スクリーンショット 2021-04-21 10.46.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/fe597268-3121-a562-9272-8dceb9e2f1b1.png)
<strong>aj:射影軸</strong>

## 線形変化後の分散
情報の量を分散の大きさを取り
分散を最大となる射影軸ajを探索
![スクリーンショット 2021-04-21 10.52.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/d025f308-8ba6-8756-c5ce-7a219a22fcd3.png)

## 線形変化後の分散の最適化
![スクリーンショット 2021-04-21 10.54.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/7b33cff0-ff57-6921-b357-43f5aeadc950.png)

![スクリーンショット 2021-04-21 10.55.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/a1b33fa3-90ce-558d-2596-143ffbeb16ed.png)

## 主成分分析の求め方
![スクリーンショット 2021-04-21 10.56.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/7f4aea04-4763-ee1e-4dfd-aa5013b7ac8d.png)

## 寄与率
![スクリーンショット 2021-04-21 10.58.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/f39d0307-a281-0f4c-8ebb-3cc07dc4c78b.png)

## 実装
### 33次元のデータで学習

```python:
# 学習用とテスト用でデータを分離
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))))
```
![スクリーンショット 2021-04-21 11.50.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/aa436d1a-bbd6-0765-c306-ac5d1d724a64.png)

### 2次元へ圧縮したデータで学習

```python:
# 次元数2まで圧縮
pca = PCA(n_components=2)
X_train_pca = pca.fit_transform(X_train_scaled)
# ロジスティック回帰で学習
logistic = LogisticRegressionCV(cv=10, random_state=0)
logistic.fit(X_train_pca, y_train)
X_test_pca = pca.transform(X_test_scaled)
# 検証
print('Train score: {:.3f}'.format(logistic.score(X_train_pca, y_train)))
print('Test score: {:.3f}'.format(logistic.score(X_test_pca, y_test)))
print('Confustion matrix:\n{}'.format(confusion_matrix(y_true=y_test, y_pred=logistic.predict(X_test_pca))))
```
![スクリーンショット 2021-04-21 11.54.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/d9d885a9-b452-389e-b317-72b5759b98bf.png)
### 考察
検証スコアは97％→93％になったが、
学習がかかる時間が短縮され、計算コストが減ったメリットもある。
２次元へ圧縮するのが、あまり極端になるため、スコアが4％も下がった。
15次元（半分）へ圧縮したら、
検証スコアが高くなる期待できる。

# K近傍法・KNN
分類の機械学習手法
最近傍のデータからK個を取って、それらがもっとも多く所属するクラスに識別

![スクリーンショット 2021-04-21 13.29.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/0a5b1007-1162-d425-bf3a-fa2216ccd451.png)

Kが多くすると、決定境界が滑らかになる
![スクリーンショット 2021-04-21 13.31.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/ceddda34-712c-2c5c-f0e8-33648231bb54.png)

## 実装
### データの準備

```python:
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-04-21 14.23.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/e12e166a-472c-03fa-c13c-47c20e53e4f7.png)

### 予測

```python:
from scipy import stats
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-04-21 14.24.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/ceb46451-75a9-f806-d44e-40e9a94bdb0a.png)

# K平均法・K-Means
教師なし学習
クラスタリング手法
与えられたデータをK個のクラスタに分類

※クラスタリング：特徴の似ているもの同士をグループ化

## アルゴリズム
### ①各クラスタ中心の初期値の設定
![スクリーンショット 2021-04-21 14.55.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/78cf019e-035d-af11-defb-8cc6ba54f56d.png)

### ②各データ点に対して、各クラスタ中心との距離を計算し、最も、距離が近いクラスタを割り当てる
![スクリーンショット 2021-04-21 14.58.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/1c2e51f8-c48e-627d-10da-06b37d709827.png)

### ③各クラスタの平均ベクトル（中心）を計算
![スクリーンショット 2021-04-21 15.00.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/ad234d74-e629-c664-c255-8922a029ff98.png)

### ④クラスタの再割り当てると、中心の更新を繰り返し
![スクリーンショット 2021-04-21 15.04.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/4a6d3449-3ffe-8b0c-d178-538dd17fafbb.png)

## 考察
初期値が近くとクラスタリングがうまくいかない
![スクリーンショット 2021-04-21 15.05.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/3df23ad1-1545-277c-c726-48cf56dda227.png)

Kの値を変えると、結果も変わる
![スクリーンショット 2021-04-21 15.06.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/cea3934e-9d84-3478-811a-e78b2011963a.png)

## 実装
### データ準備

```python:
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-04-21 15.20.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/b12dcb2a-8112-defa-891f-aa87faed756e.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
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-04-21 15.23.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/84b8bc0f-247d-3e6a-109f-0068f804abad.png)

# サポートベクターマシン（SVM）
2つクラス分類の手法
## 線形判別関数
![キャプチャ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/b3c9a675-3a40-0ab4-8739-7474952751a7.png)

## マージン
線形判別関数ともっとも近いデータ点との距離をマージンという
マージンを最大化するように線形判別関数を求める
![キャプチャ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/6519d922-bc86-5fdf-7663-684343248ebc.png)

## すべてデータ点をマージンと線形判別関数で表す
![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/73c1724d-eb12-db50-af4b-33e53c76926b.png)

## データ点から決定境界への距離
![キャプチャ3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/d1509c8d-ffc1-7c33-1db2-0cff780f8434.png)
![キャプチャ4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/1e2c2f43-f6e1-6b5c-875a-4b33b8c154e5.png)

マージンとは決定境界ともっとも近い点との距離
![キャプチャ5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/95a3ffd3-14c1-438f-f260-e18acc90cdc1.png)

## 目的関数
![キャプチャ6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/e353f74a-4e97-9537-7674-9a3d2bf7d17d.png)
![キャプチャ7.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/40947c79-f535-d775-530f-181992a26522.png)

上記の式は以下のように同等
これはSVMの目的関数と制約条件
（※）式とする
![キャプチャ8.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/b96611b4-30cc-8a18-1399-187349d38848.png)

## ラグランジュの未定乗数法
制約つき最適化問題を解く手法
![キャプチャ9.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/49e23616-2346-e93c-ac98-ba4ecd27b7b0.png)

## KKT条件
制約つき最適化問題が満たす条件
![キャプチャ10.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/5a1cf916-f6ef-7241-8f17-e5f68125f152.png)

## SVMのラグランジュの未定乗数法・主問題
（※）式をラグランジュの未定乗数法を利用し
![キャプチャ11.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/1ba86fac-1615-f930-0d11-a52d8e141d7f.png)

## 双対問題
w, bを消化
![IMG_1786 (1).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/0d3e2dbe-1f7a-bfe6-b7a6-661b90f95b1f.jpeg)
![キャプチャ12.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/f346813e-55b6-5d3d-6a6e-93d0d1d010f0.png)

## 主問題と双対問題
###主問題
w, b, a　に対して微分を求めて、各の微分式を0にイコールして、
連立法定式を利用して、最適なw, b, aを求める。

###双対問題
####aを求め方
![キャプチャ13.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/d32d9712-4f37-2ff2-9eb0-3c9d357a5844.png)
![キャプチャ14.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/762cef09-98c5-7a2d-3103-03752b1c270c.png)

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

```
####ｗを求め方
![キャプチャ15.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/40970aca-6b87-9e4b-19a0-2dc889cd160b.png)
####bを求め方
![キャプチャ16.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/20a06bfa-fa2e-a0ff-f49e-a06217603f86.png)
計算上以下のように利用されている。
![キャプチャ18.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/714a8dc3-3765-270d-81c6-2b9ce3e3b04f.png)

####予測
![キャプチャ17.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/a6feaae3-2653-8b61-e5ef-a19456fb17ea.png)

```python:
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')
```
![キャプチャ19.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/75c0f335-ca0e-d8c7-0e55-28e94a3a5bf7.png)

####サポートベクターのイメージ
![キャプチャ20.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/c0655c02-dda8-300b-4fd9-214bbbb9b317.png)

# ソフトマージンSVM
線形分類できない場合（同じクラスに属するデータは同じ側に在るが、たまに、反対クラスの側にあるデータ（孤立のデータ））
以下の写真には、黄色の一点がピンク色の点の側に在る

誤差を許容し、誤差に対して、ペナルティを与える
![スクリーンショット 2021-04-29 15.50.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/cdc0d22f-e887-b7e8-7254-67d08440bca6.png)
## 目的変数と制約条件
![スクリーンショット 2021-04-29 15.52.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/94bca713-3f03-7ccc-9d2d-731688e0c973.png)
## 双対変数への展開
![IMG_1802 (1).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/553621fd-83fd-9ca4-bf65-14ccd279add7.jpeg)
## 性質
基本、普通のSVMにパラメータCが追加された
パラメータCの大小で決定境界が変わる
![スクリーンショット 2021-04-29 15.59.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/99af1f5a-65fa-f587-06ba-daebba8225d7.png)
## 実装
### データの準備

```python:
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)
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
```
### 学習

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

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

C = 1
eta1 = 0.01
eta2 = 0.001
n_iter = 1000

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.clip(a, 0, C)
```
### 予測

```python:
index = a > 1e-8
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(-4, 4, 100), np.linspace(-4, 4, 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=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=['--', '-', '--'])
```
![スクリーンショット 2021-04-29 16.06.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/884394d9-1502-b402-9f7b-6a166b4f7548.png)

# カーネルSVM
線形でないデータの場合
特徴空間に写像し、その空間に、線形のデータになる
![スクリーンショット 2021-04-29 16.46.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/a9681a95-02ea-99e7-e9e7-6fbfa67a8bfc.png)
## 目的関数
![スクリーンショット 2021-04-29 16.48.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/1801925f-5779-7288-cfb9-323ab5929e91.png)
## カーネルトリック
![IMG_1805 (1).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/bb36647d-60fe-28fb-cf5f-507c0dc7edb7.jpeg)

高次元ベクトルの内積をスカラー関数で表現できる
特徴空間が高次元でも計算コストを抑えられる
## カーネル関数
![スクリーンショット 2021-04-29 17.02.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/23dc39e0-58c6-7b0a-949d-bfe31b381280.png)

https://scikit-learn.org/stable/modules/svm.html#svm-kernels
## 実装
### データ準備

```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)
```
### 学習

```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)
```
### 予測

```python:
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=['--', '-', '--'])
```
![スクリーンショット 2021-04-29 17.06.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/a9c19e44-0bfc-5b75-eca9-532386aaa59d.png)