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()
各数字ごとの主成分も表示してみます.
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()
次に画像データを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()
点が重なりまくって意味が不明です.あと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割くらいは識別できたようです.