PCA + KNN 手写数字识别

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27668313/article/details/78990432
KNN的原理可参考博客:

https://www.cnblogs.com/21207-iHome/p/6084670.html


本文采用PCA+KNN的方法进行手写数字识别,训练数据来源于kaggle,已上传到我的CSDN博客,训练数据共有42000行,每行代表一幅数字图片,共有784列(一副数字图像是28*28像素,将一副图像展开为一行即784),更多关于Digit Recognizer项目的介绍https://www.kaggle.com/c/digit-recognizer

由于训练数据量太大,直接采用KNN非常耗时,在个人PC上半小时都跑不完全部数据,采用PCA降维的方法,选取25个维度,跑完全部数据只需200秒左右。下面代码采用 python3.X,代码中的predict()可以不用管,这个是用于预测test.csv数据的。

对于PCA维度的选取:在多次尝试后,采用25个维度的精确度已能达到96%,效果较好。需要注意的是,PCA处理后的训练数据和原始数据是不同的,所以采用PCA处理数据后,并不是选取的维度越多精确度就越好。 我觉得可以这样理解,采用PCA处理后的数据,数据可以分为主要成分和次要成分,只有那些主要成分对图像的贡献度最大,次要成分可以看成是噪声。如果选取的维度太多,噪声也就多了,这样识别率就低了,实际实验中显示采用PCA后选用784维,精确度只有23%。而没有经过PCA处理的数据,每个维度的重要性都是一样的,不存在主要成分和次要成分之分(实际上原始数据还是存在次要成分的,只不过没有经过处理后,维度之间的重要性无法区分开来,经过PCA处理后,就好像把原来的数据从一个空间映射到另一个空间,在这个空间中,主要成分和次要成分就区分开来了),没有采用PCA处理数据,直接用KNN计算全部的784维,精确度接近97%。

以下是采用PCA后,尝试不同选取维度后得到的精确度。
维度数 精确度
784       23.2%
400       26.7%
300       38.1%
200       60.5%
100       85.7%
40         92.5%
30         94%
20         93.3%
10         90%
本次采用样本数:8000
原始样本数:42000 

import numpy as np
from numpy import *
import operator
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
import time


time_start = time.time()


def loadDataSet(filedir):
    DateSet = pd.read_csv(filedir)
    Data = DateSet.iloc[1:42001, 1:].as_matrix()  # dataframe格式转成元组
    # 降维处理
    pca = PCA(n_components=25, whiten=True)
    Data = pca.fit_transform(Data)
    print('Data size: ', shape(Data))
    label = DateSet.iloc[1:42001, :1].as_matrix()
    # 分配训练数据和测试数据
    train_data, test_data, train_label, test_label = train_test_split(Data, label, test_size=0.3, random_state=0)
    return train_data, test_data, train_label, test_label


def kNN_classify(classify_data, train_data, train_label, k):
    # 计算欧式距离
    m = shape(train_data)[0]
    diff = tile(classify_data, (m, 1)) - train_data
    dist = sqrt(sum(diff**2, axis=1))
    # print('m ', m)
    data_index = argsort(dist)  # 升序排序,得到索引值
    # print('data_index: ', data_index)
    # print('data_index: ', data_index)
    # 取前k个与classify_data距离最小的训练数据的标签
    data_label = []
    for i in range(k):
        data_label.append(train_label[data_index[i]][0])
    # print('data_label: ', data_label)

    # 选取数量最多的标签作为classify_data的分类标签
    classify_label = {}
    for j in range(k):
        votelabel = data_label[j]
        classify_label[votelabel] = classify_label.get(votelabel, 0) + 1
    # print('classify_label: ', classify_label)
    maxValue = 0
    class_label = None
    for label, value in classify_label.items():
        if value > maxValue:
            maxValue = value
            class_label = label
    return class_label


def train_model(train_data, test_data, train_label, test_label, k=5):
    m = shape(test_data)[0]
    err_num = 0
    for i in range(m):
        result = kNN_classify(test_data[i], train_data, train_label, k)
        if result != test_label[i]:
            err_num += 1
    accuracy = (m - err_num)/m
    return accuracy

# 这个函数可以注释掉
def predict(filedir, train_data, train_label, k):
    predict_data = pd.read_csv(filedir)
    predict_data = predict_data.iloc[1:100, :].as_matrix()
    m = shape(predict_data)[0]
    result = zeros((m, 1))
    for i in range(m):
        result[i] = kNN_classify(predict_data[i], train_data, train_label, k=5)
    df = pd.DataFrame(result)
    df.index.name = ['ImageId']
    df.index += 1
    df.columns = ['Label']
    df.to_csv('E:\ProgramData\Python3Project\Digit Recognizer/result.csv', header=True)
    print('predict done!')


filedir = 'E:\ProgramData\Python3Project\Digit Recognizer/train.csv'
train_data, test_data, train_label, test_label = loadDataSet(filedir)
accuracy = train_model(train_data, test_data, train_label, test_label, 5)
print('accuracy: %f' % accuracy)
time_end = time.time()
print('totally cost:', time_end - time_start)
print('Done')

猜你喜欢

转载自blog.csdn.net/qq_27668313/article/details/78990432