2017年2月22日水曜日

ガウスカーネルによる最小二乗回帰によるパターン認識

二値分類を最小二乗回帰により分類することができる。
以下では0~9の文字を一対他法により分類している。

1

2

3

4

5

6

7

8

9

10

%matplotlib inline

import numpy as np

import matplotlib.pyplot as plt

import pandas as pd

from pandas import Series, DataFrame

from sklearn import cross_validation

from numpy.random import normal

from sklearn.utils import shuffle

from sklearn.metrics import f1_score

import itertools

訓練データの読み込み
0~9
の文字はそれぞれ500個ずつある。

1

2

3

4

5

6

7

8

9

10

#一行一行が一文字

train = DataFrame()

for i in range(10):

    train = pd.concat([train, pd.read_csv("/Users/Hirono/data_science/Data/digit_csv/digit_train%i.csv"% i, header =None)])

train_target = []

for i in range(10):

    for j in itertools.repeat(i, 500):

            train_target.append(j)

train_data, train_target = shuffle(train, train_target)

train_data = train_data.reset_index().drop("index", axis = 1)  #indexのコラムをおとす

テストデータの読み込み
0~9
の文字はそれぞれ200個ずつある。

データはふつうはMNistとかからとってくればok(前章参照)

1

2

3

4

5

6

7

8

9

test = DataFrame()

for i in range(10):

    test = pd.concat([test, pd.read_csv("/Users/Hirono/data_science/Data/digit_csv/digit_test%i.csv"% i, header =None)])

test_target = []

for i in range(10):

    for j in itertools.repeat(i, 200):

            test_target.append(j)

test_data, test_target = shuffle(test, test_target)

test_data = test_data.reset_index().drop("index", axis = 1)

1

2

3

4

#パラメータ

h = 0.2     #カーネル関数のパラメータ

N = 1000   #traindataを使う数

u = 0.1   #正則化パラメータ

 

いつものように訓練データからカーネルマトリックスをもとめる

1

2

3

4

5

6

7

8

9

10

11

12

13

#カーネル関数

def K(x,y):

    return np.exp(- np.dot((x-y),(x-y)) / (2 * h ** 2))

 

kernel_matrix = DataFrame()

series = []

for i in range(N):

    series = []

    for x in train_data[:N].iterrows():

        series.append(K(x[1], train_data.ix[i]))

    series = Series(series)

    kernel_matrix = pd.concat([kernel_matrix, series], axis = 1)

kernel_matrix.columns = range(N)

下は、たとえばs = 3としたときに、testdata3かそうでない数字かを返してくれる関数
3
だったら1, 3でなければ-1がかえってくる。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

def one_versus_the_other(s, test_data):

 

    def posi_or_nega(x):

        if x == s:

            x = 1

            return x

        else:

            x = -1

            return x

 

    t = Series(train_target[:N])

    t  = t.apply(lambda x: posi_or_nega(x) )

    tmp = np.linalg.inv(np.dot(kernel_matrix,kernel_matrix) + u * np.eye(N) )

    ws = np.dot(np.dot(tmp, kernel_matrix), t)  #もとめたいθ

 

    def sign(x):   #0-1にいれたいので、自分で定義

        if x > 0:

            return 1

        else:

            return -1

 

    def f(x):    #モデル関数

        y = 0

        for i, w in enumerate(ws):

            y += w * K(x, train_data.ix[i])

        return sign(y)

 

    return f(test_data)

2000個のtestdata0~9の数字に分類する。

1

2

3

4

5

6

classification = []

for j in range(2000):

    arr = []

    for i in range(10):

        arr.append(one_versus_the_other(i, test_data.ix[j]))

    classification.append(np.argmax(arr))

正解率は92%だった。

1

2

3

4

classification = Series(classification)

test_target = Series(test_target)

classification[classification == test_target].value_counts().sum() /2000.

#=> 0.92

具体的な集計結果

1

2

3

4

5

pred_and_target = pd.concat([classification, test_target], axis = 1)

pred_and_target.columns = ["pred", "target"]

pred_and_target = DataFrame(pred_and_target.groupby(["pred", "target"]).size()).reset_index()

pred_and_target = pred_and_target.rename(columns={0: 'count'})   #0は文字列じゃなくて数字なので、rename時に注意

pred_and_target.pivot(index="pred", columns="target").fillna(0)

表の読み方は、たとえば、0だと予想したものが実際は5であったものが19個あったという意味である。

計算時間がかなりかかってしまったので、今回は訓練データは1000個しかつかっていない。
だから、もっと訓練データを増やせば正解率をあげることができると考えられる。
(
実際に、訓練データを500から1000に増やすことで正解率が0.88→0.92に向上した。) そのうち計算時間が短縮されるようにコードを改善したい。

pythonメモ

1

2

shuffle([1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5])

#=> [[2, 5, 3, 4, 1], [2, 5, 3, 4, 1], [2, 5, 3, 4, 1]]

1

2

itertools.repeat(10, 3)

=> 10, 10 ,10

Seriesの値それぞれに関数適用

1

Series.apply(lambda x: posi_or_nega(x))

1

2

"%s %i" % ("aaaa", 4.0)

#'aaaa 4'

 

1

2

map(lambda x: x[1], [[1,2], [3,4]])

#[2, 4]

indexという名前のコラムをおとす

1

df.drop("index", axis = 1)

dataframe nan0でうめる

1

df.fillna(0)

columnkeyvaluerenameする

1

df.rename(columns={0: 'count'})

f値をもとめる。もし二値分類ならaverage = 'binary'とする

1

f1_score(classification, test_target, average='macro' )

seriesvalueの数を数える

1

series.value_counts()

seriesindex順に並び替える

1

series.sort_index()

最大値をとる配列のindexをかえす。最大値が二つ以上あるときは最初のindexのほうを返す。

1

2

3

4

np.array([0, 1, 2, 3, 10, 5, 6, 7, 8, 9]).argmax()

#=>4

np.array([0, 1, 2, 3, 10, 10, 6, 7, 8, 9]).argmax()

#=>4

最後の集計の流れよくつかいそうだからもう一回

1

2

3

4

5

pred_and_target = pd.concat([classification, test_target], axis = 1)

pred_and_target.columns = ["pred", "target"] #1

pred_and_target = DataFrame(pred_and_target.groupby(["pred", "target"]).size()) #2

pred_and_target = pred_and_target.reset_index().rename(columns={0: 'count'})    #3

pred_and_target.pivot(index="pred", columns="target").fillna(0)  #4

1

2

3

4

 

0 件のコメント:

コメントを投稿