2017年3月22日水曜日

CS231n 1

Notebook

CS231n

画像認識の授業.Nielsenのdeep learningが何となく合わないので数字認識から画像認識に手を伸ばしてみる.画像認識は様々な要因によって数字認識よりも困難.学習アルゴリズムが学習するデータをtrainig set, アルゴリズムの性能の評価に使うデータをtest set, hyperparameterの設定に使うデータをevaluation setという.

In [1]:
%matplotlib inline
import pickle
import numpy as np
import scipy
import matplotlib.pyplot as plt
from scipy  import spatial

def unpickle(file):
    fo = open(file, 'rb')
    dict = pickle.load(fo, encoding='latin1')
    fo.close()
    return dict

# mnistとcifar-10をダウンロードしてカレントディレクトリにおいておく.
data, labels = unpickle('data_batch_1')["data"], unpickle('data_batch_1')["labels"]
for i in range(2, 6):
    data = np.concatenate((data, unpickle("data_batch_{0}".format(i))["data"]))
    labels = np.concatenate((labels, unpickle("data_batch_{0}".format(i))["labels"]))
test_set, test_labels = unpickle('test_batch')["data"][:2000], unpickle('test_batch')["labels"][:2000]
mnist_data, mnist_labels = unpickle('mnist.pkl')[1][0], unpickle('mnist.pkl')[1][1] # mnist(ついで)

dataの第i要素は3072次元のベクトルで,labels[i]がそれに対応するラベル.適当に操作してplt.imshowに入れるとカラー画像が表示される.dataの要素はそれぞれ10個のクラスの内1つに対応していて,クラスに応じて0から9までの整数のラベルがつけられている.dataの要素をexample, それに対応するラベルをexampleのラベルと呼ぶことにする.読み込んだ学習に使うデータはは50000examplesで,計算量を抑えるためtest_dataは先頭2000examplesを切り取った.

In [3]:
plt.imshow(test_set[0].reshape(3, 32, 32).transpose(1, 2, 0)) 
plt.show() # リス?

L1/L2 distances, hyperparameter search, cross-validation

Nearest Neighbour Classifier

dataの要素I_1, I_2の $I_1, I_2$の距離関数を $$ d_p(I_1, I_2) = (\sum_{pixel} |I^{pixel}_1 - I^{pixel}_2|^p)^{1/p} $$ と定義する.つまり$L^p$ ノルムの導く距離.あるexample vが与えられた時,vとtraining set の全てのexampleとの距離を$ d_p$によって計算し,最短距離にあるk個を取る.そのk個のexamples のlabels のうち, 個数最大のものをv に対するアルゴリズムの推測とする.

In [4]:
from collections import Counter
class KNN:
    def train(self, X_train, y_train):
        '''
        X_train: [data1のベクトル, data2のベクトル, ...] というリストかnp.array
        y_train: [data1のラベル, data2のラベル, ...] というリストかnp.array
        '''
        self.x_train = X_train
        self.y_train = y_train
        
    def predict(self, X, k=1, p=1):
        '''
        X: [data1のベクトル, data2のベクトル, ...] というリストかnp.array,テストデータ
        k: 近傍の数
        p: 距離関数のパラメーター
        '''
        prediction = []
        for x in X:
            distances = np.array([scipy.spatial.distance.minkowski(x, u, p) for u in self.x_train])
            k_nearest_sorted_index = np.argsort(distances)[:k]
            k_nearest_labels = [self.y_train[i] for i in k_nearest_sorted_index]
            k_nearest_distances = np.array([distances[j] for j in k_nearest_sorted_index])
            
            counter = Counter(k_nearest_labels)
            labels = np.array([counter[j] for j in range(10)])
            top = np.where(labels==labels.max())[0] # k近傍のラベルたちで,最大個数のもののリスト
            
            if len(top)==1: # 最大個数となるラベルが1つだけなら,それがテストexampleのラベルとする.
                y_hat = top[0] 
            else: # 複数個のラベルがk近傍で最大個数となるなら,合計の距離が最小のラベルをテストexampleのラベルとする.
                distance_sum = np.zeros_like(top)
                for ind, j in enumerate(top):
                    for v in np.where(k_nearest_labels==j)[0]:
                        distance_sum[ind] += k_nearest_distances[v]
                y_hat = top[np.argmin(distance_sum)]
            prediction.append(y_hat)
        return prediction
    
    def evaluate(self, X_evaluate, y_evaluate, k=1, p=1):
        prediction = self.predict(X_evaluate, k, p)
        num_set = len(y_evaluate)
        num_error = len(np.where(np.array(prediction) - np.array(y_evaluate)!=0)[0])
        print("k = {0}, p = {1}, error ratio = {2}".format(k, p, num_error/num_set))

kやpのように,学習アルゴリズムに入力する学習データでない変数をhyperparameterというい,これの決め方には注意が必要.幾つかの候補のリストを作ってそれぞれ試していくのだが,overfittingの危険があるのでhyperparameterの性能評価にはtest setを使ってはならなず,validation setを使う.validation setはtraining set を一部切り出してつくり,当然validation setにした分は学習には使わない.

In [5]:
knn = KNN()
knn.train(mnist_data[:8500], mnist_labels[:8500]) # 先頭8500 examples を学習
In [6]:
for k in [1, 2, 3]:
    for p in [1, 2, 3]:
        knn.evaluate(mnist_data[8500:9000], mnist_labels[8500:9000], k, p) # validation set は 500 exampels
k = 1, p = 1, error ratio = 0.036
k = 1, p = 2, error ratio = 0.022
k = 1, p = 3, error ratio = 0.012
k = 2, p = 1, error ratio = 0.034
k = 2, p = 2, error ratio = 0.028
k = 2, p = 3, error ratio = 0.024
k = 3, p = 1, error ratio = 0.034
k = 3, p = 2, error ratio = 0.016
k = 3, p = 3, error ratio = 0.016

k=1, p=3 が成績が良かったので,これをtest set にかけてみる

In [7]:
knn.evaluate(mnist_data[9000:10000], mnist_labels[9000:10000], 1, 3)
k = 1, p = 3, error ratio = 0.026

なかなかよい成績だ.CIFAR-10にもkNNをかけてみよう.

In [8]:
knn.train(data, labels)
knn.evaluate(test_set, test_labels, 1, 1) # 死ぬほど時間がかかるのでハイパーパラメーターは決め打ち
k = 1, p = 1, error ratio = 0.71

非常に悪い成績だ.画像認識の困難さが伺える.
また,kNNは全てのtrain setを保持し,かつtest exampleを1つ与えられるごとにtrain setの全ての要素と距離を比較していかなければならないので,必要なメモリ,計算量が非常に大きくなる.

0 件のコメント:

コメントを投稿