機械学習レシピ#5 最初の分類器を書こう

https://www.youtube.com/watch?v=AoeEHqVSNOw&list=PLOU2XLYxmsIIuiBfYad6rFYQU_jL2ryal&index=3

K近傍法の簡易版の分類器を自分で一から作る。

前回のコードをベースにする。
前回は分類器をインポートして利用していたが、ここではそれらをコメントアウトする。

```
# import a dataset
from sklearn import datasets
iris = datasets.load_iris()

x = iris.data
y = iris.target

from sklearn.cross_validation import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x,y,test_size = .5)

#この部分を置き換える。
#from sklearn.neighbors import KNeighborsClassifier
#my_classifier = KNeighborsClassifier()

my_classifier.fit(x_train,y_train)

predictions = my_classifier.predict(x_test)
print predictions

from sklearn.metrics import accuracy_score
print accuracy_score(y_test,predictions)
```

#パイプラインを機能させ、２つのメソッド(fit,predict)が何をするか理解する
分類器のインタフェースを見ると、配慮すべき２つが見える。
1. 学習するfit
2. 予測するpredict
そのため、この２つを実装する。
上記の分類器を書く前にもっとシンプルなランダム分類器（ラベルを推量するだけ）を作成することから始めてみる。

```
#各行の予測をするために
import random

class ScrappyKNN():
   #学習セットの特徴とラベルを入力とする。
   def fit(self,X_train,y_train):
　　　　　　　　　　　　#学習データを格納する。
      self.x_train = x_train
      self.y_train = y_train

   #テストデータの特徴を受取り、出力としてラベルの予測を返す
   #パラメータx_listは２次元配列もしくはリストのリスト.各行には1つのテストの特徴が入る
   def predict(self,x_test):
      #予測のリストを返す
　　　　　　　　　　　　predictions = []
      #各行には１つのテスト例の特徴が入る。
　　　　　　　　　　　　for row in x_test:
         #学習データからランダムにラベルを選びそれを予測に付ける。
         label = random.choice(self.y_train)
         predictions.append(label)
      return predictions
```
アイリスデータセットでは異なる花の種類は３つなので精度は約33%のはずで、実際実行してみると0.346666666667だった。精度が悪いので改善させる。

#K近傍法に基づいた自作分類器を実装
近傍法ではテスト点に最も近い学習点を見つける。この点が同じラベルになると予測する。Kは予測するとき考慮する近傍の数。Kが1なら最近傍の学習点しかみない。Kが3なら３つの近傍をみて、過半数以上のラベルになる。

まずは最近傍を見つける方法が必要。
そのために２点間の直線距離を定規で測るのと同じように計測する。
ユークリッド距離(Euclidean Distance)というそのための計算式がある。
ピタゴラスの定理に似たような感じで、計算する距離は斜辺の長さ。
ユークリッド距離は次元の数に関わりなく同様に機能することができる。（特徴が多くなれば等式に項を足せばよいだけ）

ユークリッド距離を求める機能をさっきのコードに追加する。

```
#ユークリッド距離を求めるためにscipyライブラリをインポート
from scipy.spatial import distance

#a,bは数の特徴のリスト.aは学習データからの点、bはテストデータからの点
#この関数はそれらの間の距離を返す
def euc(a,b):
   return distance.euclidean(a,b)
```

今回はK近傍法のKを1にハードコードして行う。
以下に最近傍分類器を実装する。
テスト点の予測をするために学習点全部に対する距離を求める。
そしてテスト点が最も近い点と同じラベルになると予測する。

```receipe5.py
from scipy.spatial import distance

def euc(a,b):
   return distance.euclidean(a,b)
   
class ScrappyKNN():
   #学習セットの特徴とラベルを入力としセット
   def fit(self,x_train,y_train):
      self.x_train = x_train #特徴
      self.y_train = y_train #ラベル

   #テスト用データを入力としラベルを予測
   def predict(self,x_test):
      predictions = []
      for row in x_test:
　　　　　　　　　　　　　　　　　　　#もっとも近い点をラベルにする関数closestを利用
         label = self.closest(row)
         predictions.append(label)
      return predictions

   def closest(self, row):
　　　　　　　　　　　　#テスト点から最初の学習点までの距離を計算する。
　　　　　　　　　　　　#この変数を使って見つけた最短距離を追跡する
      best_dist = euc(row, self.x_train[0])
      best_index = 0
　　　　　　　　　　　　#最も近い学習点のインデックスを追跡。他の学習点全てに繰り返し処理する。
      for i in range(1, len(self.x_train)):
         dist = euc(row,self.x_train[i])
　　　　　　　　　　　　　　　　　　　#より近いものが見つかるたびに変数を更新する。
         if dist < best_dist:
            best_dist = dist
            best_index = i
　　　　　　　　　　　　　#一番近いインデックスを使って最も近い学習例のラベルを返す
      return self.y_train[best_index]

# import a dataset
from sklearn import datasets
iris = datasets.load_iris()

x = iris.data
y = iris.target

from sklearn.cross_validation import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x,y,test_size = .5)

#from sklearn.neighbors import KNeighborsClassifier
my_classifier = ScrappyKNN()

my_classifier.fit(x_train,y_train)

predictions = my_classifier.predict(x_test)
#print predictions

from sklearn.metrics import accuracy_score
print accuracy_score(y_test,predictions)
```

これで単純な分類器を一から作ることができた。