import numpy as np
import struct
from sklearn.decomposition import PCA
from keras.datasets import mnist
为了方便,这里我们使用的MNIST数据集来自keras内置的keras.datasets,当然也可以去Yann Lecun的官网下载,只需要在之后的代码中稍加改动。
我们从keras内置的数据集中读取MNIST数据集,从下面一段代码的运行结果可以看出,我们导入的MNIST数据集共有60000个训练图片和10000个测试图片,每张图片均为28*28的大小。
(train_data_ori, train_label), (test_data_ori, test_label) = mnist.load_data()
print ("mnist data loaded")
print ("original training data shape:",train_data_ori.shape)
print ("original testing data shape:",test_data_ori.shape)
附上这段代码的运行结果:
mnist data loaded
original training data shape: (60000, 28, 28)
original testing data shape: (10000, 28, 28)
显然,这里我们得到的图片是28*28的二维数组,为了方便接下来的操作,我们将每张图片reshape为一维数组:
train_data=train_data_ori.reshape(60000,784)
test_data=test_data_ori.reshape(10000,784)
print ("training data shape after reshape:",train_data.shape)
print ("testing data shape after reshape:",test_data.shape)
附上这段代码的运行结果:
training data shape after reshape: (60000, 784) testing data shape
after reshape: (10000, 784)
可以看出所有的数据图片都变成了大小为784的一维数组。
在这里,降维的主要原因是在原有数据的784维特征空间内进行KNN聚类的计算开销过大,并且没有必要。因此,我们可以采用PCA算法提取出原有数据的主要特征。在这里,我提取了原有图片的100个主要特征,并构建了100维的特征空间。这一部分的代码如下:
pca = PCA(n_components = 100)
pca.fit(train_data) #fit PCA with training data instead of the whole dataset
train_data_pca = pca.transform(train_data)
test_data_pca = pca.transform(test_data)
print("PCA completed with 100 components")
print ("training data shape after PCA:",train_data_pca.shape)
print ("testing data shape after PCA:",test_data_pca.shape)
附上运行结果:
PCA completed with 100 components
training data shape after PCA: (60000, 100)
testing data shape after PCA: (10000, 100)
从上面的结果,我们可以分析出经过处理的数据集有着如下特点:
到这一步为止,我们已经将用于训练和测试的所有数据全部处理好。
实现KNN的代码如下所示,其中,test_data1是待聚类的数据(1维,大小:100),train_data_pca表示的是整个训练集(2维,大小:60000*100),train_label是训练集内数据对应的标签(2维,大小:60000*1),k是KNN的参数K的值,p则表示计算距离所使用的范数类型(通常我们计算的是欧几里得距离,取2)。
该函数返回值为聚类结果。
def KNN(test_data1,train_data_pca,train_label,k,p):
subMat = train_data_pca - np.tile(test_data1,(60000,1))
subMat = np.abs(subMat)
distance = subMat**p
distance = np.sum(distance,axis=1)
distance = distance**(1.0/p)
distanceIndex = np.argsort(distance)
classCount = [0,0,0,0,0,0,0,0,0,0]
for i in range(k):
label = train_label[distanceIndex[i]]
classCount[label] = classCount[label] + 1
return np.argmax(classCount)
定义测试函数如下,其中参数k为KNN算法的K值,p表示范数类型。
函数返回准确度和混淆矩阵。
def test(k,p):
print("testing with K= %d and lp norm p=%d"%(k,p))
m,n = np.shape(test_data_pca)
correctCount = 0
M = np.zeros((10,10),int)
for i in range(m):
test_data1 = test_data_pca[i,:]
predict_label = KNN(test_data1,train_data_pca,train_label, k, p)
true_label = test_label[i]
M[true_label][predict_label] += 1
# print("predict:%d,true:%d" % (predict_label,true_label)) use this line for debugging
if true_label == predict_label:
correctCount += 1
print("The accuracy is: %f" % (float(correctCount)/m))
print("Confusion matrix:",M)
在我们测试的过程中,使用k=3和欧几里得距离,如下:
test(3,2)
得到结果:
testing with K= 3 and lp norm p=2
The accuracy is: 0.973500
Confusion matrix: [[ 974 1 1 0 0 1 2 1 0 0]
[ 0 1131 3 0 0 0 1 0 0 0]
[ 7 4 1004 1 1 0 0 13 2 0]
[ 1 1 4 979 1 9 0 7 5 3]
[ 2 5 0 0 949 0 4 3 0 19]
[ 4 1 0 10 2 865 3 1 2 4]
[ 4 3 0 0 2 4 945 0 0 0]
[ 0 17 6 0 2 0 0 996 0 7]
[ 5 1 4 17 5 10 5 3 921 3]
[ 5 5 2 8 8 2 1 6 1 971]]
最终得到了超过97%的准确率,并且我们还可以根据混淆矩阵分析出一些其他有意思的结论。
代码同时也可以在我的github上面找到,顺便求一波star^_^。