コンピュータサイエンス系勉強ノート

計算機科学に限らず日々学んだことを色々まとめていきます

MNISTの手書き数字データで主成分分析の練習(Python)

今回はMNISTの手書き数字データを使って数字識別をやってみたいと思います.Pythonではscikit-learn内の関数を呼び出すことで簡単にデータをダウンロードできます.画像サイズは28×28ピクセルです.ソースコードは適当です.

ダウンロード用のコードは以下の通り.

from sklearn.datasets import fetch_mldata

mnist = fetch_mldata('MNIST original', data_home=".")
data = mnist["data"]
target = mnist["target"]

print(data)
print(target)


主成分分析をかけて主成分50個抜き出し,画像として表示してみます.

from sklearn.datasets import fetch_mldata
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

mnist = fetch_mldata('MNIST original', data_home=".")
data = mnist["data"]
target = mnist["target"]

pca = PCA(n_components=50)
pca.fit(data)
components = pca.components_

for i in range(5):
    for j in range(10):
        plt.subplot(5, 10, 1 + 10 * i + j)
        plt.imshow(components[1 + 5 * i + j, :].reshape(28, 28))
plt.show()

f:id:clientver2:20151111010927p:plain


各数字ごとの主成分も表示してみます.

from sklearn.datasets import fetch_mldata
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
from sklearn.cross_validation import train_test_split

mnist = fetch_mldata('MNIST original', data_home=".")
data = mnist["data"]
target = mnist["target"]
train, test, train_label, test_label = train_test_split(data, target)

for i in range(10):
    number_data = data[target == i]
    pca = PCA(n_components=5)
    pca.fit(number_data)
    components = pca.components_

    for j in range(5):
        plt.subplot(10, 5, 5 * i + j)
        plt.imshow(components[j, :].reshape(28, 28))
plt.show()

f:id:clientver2:20151111010935p:plain

次に画像データを2次元にまで落として散布図を作成してみます

from sklearn.datasets import fetch_mldata
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
from sklearn.cross_validation import train_test_split

mnist = fetch_mldata('MNIST original', data_home=".")
data = mnist["data"]
target = mnist["target"]
train, test, train_label, test_label = train_test_split(data, target)

pca = PCA(n_components=2)
pca.fit(data)

vectors = pca.transform(train)
plt.plot(vectors[train_label==0, 1], vectors[train_label==0, 0], "o")
plt.plot(vectors[train_label==1, 1], vectors[train_label==1, 0], "o")
plt.plot(vectors[train_label==2, 1], vectors[train_label==2, 0], "o")
plt.plot(vectors[train_label==3, 1], vectors[train_label==3, 0], "o")
plt.plot(vectors[train_label==4, 1], vectors[train_label==4, 0], "o")
plt.plot(vectors[train_label==5, 1], vectors[train_label==5, 0], "o")
plt.plot(vectors[train_label==6, 1], vectors[train_label==6, 0], "o")
plt.plot(vectors[train_label==7, 1], vectors[train_label==7, 0], "o")
plt.plot(vectors[train_label==8, 1], vectors[train_label==8, 0], "o")
plt.plot(vectors[train_label==9, 1], vectors[train_label==9, 0], "o")
plt.show()

f:id:clientver2:20151111010940p:plain

点が重なりまくって意味が不明です.あとplotだらけで超不細工です.

次に訓練データからテストデータの識別ができるか試してみます.やり方は簡単で,各数字毎の訓練データを主成分分析にかけて50個の基底ベクトルを取り出します.この基底ベクトルは50次元の空間を構成してて,この空間に各数字データを射影してあげます.例えば,ある数字の訓練データがN個あるなら50次元のベクトルがN個できます.このN個のベクトルの平均を全ての数字について求めてあげます.今回は0~9の数字なので10個の平均ベクトルが求まりますね.すると入力を50次元の空間に射影してあげると,どの平均ベクトルに近いかが計算できます.一番近い平均ベクトルのラベルをその入力のラベルとします.

import numpy as np
from sklearn.datasets import fetch_mldata
from sklearn.decomposition import PCA
from sklearn.cross_validation import train_test_split

class Identificate_Number(object):
    def __init__(self, data, target, n_components=50):
        self.data = data
        self.target = target
        self.pca = PCA(n_components)
        self.pca.fit(self.data)
        self.vectors = self.pca.transform(self.data)
        self.calc_mean()

    def calc_mean(self):
        means = []
        for i in range(10):
            mean = np.mean(self.vectors[self.target == i], axis=0)
            means.append(mean)
        self.means = np.array(means)

    def identification(self, train_data):
        labels = []
        data = self.pca.transform(train_data)

        for vec in data:
            distance = np.sqrt(np.sum((self.means - vec) ** 2, axis=1))
            labels.append(np.argmin(distance))
        labels = np.array(labels)
        return labels

def main():
    mnist = fetch_mldata('MNIST original', data_home=".")
    data = mnist["data"]
    target = mnist["target"]
    train, test, train_label, test_label = train_test_split(data, target)
    identification = Identificate_Number(train, train_label)
    label = identification.identification(train)
    success = (label == train_label).mean() * 100
    print(success)

main()

このやり方で試したところ大体8割くらいは識別できたようです.