机器学习之knn算法----手写数字识别mnist

机器学习之knn算法----手写数字识别mnist

knn介绍

KNeighborsClassifier(n_neighbors=5, weights=‘uniform’, algorithm=‘auto’, leaf_size=30, p=2, metric=‘minkowski’, metric_params=None, n_jobs=1, **kwargs)
Parameters

n_neighbors : int, (default = 5)—k的值

weights : str or callable, optional (default = ‘uniform’)

- 'uniform' : 默认投票是权重一致
- 'distance' : 距离越近权重越高

algorithm : {‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, optional

- 'ball_tree' 球树,创建球树,减少计算次数
- 'kd_tree' `KDTree`,创建二叉树,减少计算次数
- 'brute' 暴力法
- 'auto' 自动选择。当然数量比较少时,还是使用暴力法

leaf_size :

-int, optional (default = 30)
-当使用 BallTree or KDTree.  这个是跟节点的数量,默认是30,当数据量很大时,可以加深节点

p : integer, optional (default = 2)

- p=1曼哈顿距离。p=2欧氏距离
- 应该都知道,没什么介绍的。一般都用欧氏距离
- 

metric : string or callable, default ‘minkowski’

- 闵可夫斯基,这就是一个公式,不同的p值对应不同的距离。p=2就是欧式

n_jobs : int, optional (default = 1)

-  让几个人去计算,-1就是你电脑有几个核就用几个。默认就是一个

进入正题

这些参数到底怎么影响knn的正确率呢?我们来看看。。。。

这里使用的是.mat的二进制文件。网上有下载,我后面也会上传

导入模块

import scipy.io as spio

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt
%matplotlib inline

from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split

因为是mat文件,所以用scipy来获取:

mnist = spio.loadmat("./data/mnist.mat")
display(mnist)

输出:

{'__header__': b'MATLAB 5.0 MAT-file Platform: posix, Created on: Sun Mar 30 03:19:02 2014',
 '__version__': '1.0',
 '__globals__': [],
 'mldata_descr_ordering': array([[array(['label'], dtype='

这是一种MATLAB的格式文件,有兴趣的可以自行百度,在这不做重点
可以看出数据都是在data里,目标值在label中。
获取数据:

#因为直接获取到的数据shape为(784,70000),所以要进行转置
#每一个图片的格式都是28*28
data = mnist["data"].T
taget = mnist["label"].T
taget = taget.reshape(70000,)

数据我处理成了一份图片格式的保存在本地,一共七万张图片。后面我也会上传。
代码就不上了,比较简单。需要的也可以私信我。

数据切分和加载算法:

X_train,x_test,Y_train,y_test = train_test_split(data,taget,test_size=0.2)
#使用默认参数
knn = KNeighborsClassifier()
knn.fit(X_train,Y_train)

计算得分

knn.score(x_test,y_test)

输出:0.9715714285714285

这是默认参数的准确率。那么他到底预测了哪些是错的哪些的对的呢?我怎么能看到?
先接收一下他预测出来的数据

y_ = knn.predict(x_test)

好,我们来画个图:

plt.figure(figsize=(10*3,10*4))
#因为测试样本有一万四千个,我们取100个,每隔100个取一个
for i in range(100):
    plt.subplot(10,10,i+1)
    a = x_test[i*100].reshape(28,28)
    plt.imshow(a,cmap="gray")
    c="b"
    if(y_test[i*100]!=y_[i*100]):c="r"
    plt.title("real:{}predict:{}".format(y_test[i*100],y_[i*100]),color=c)

来一起研究一下。红色的是预测错误的
机器学习之knn算法----手写数字识别mnist_第1张图片
现在我就想看看预测错的错误数据。

result = y_==y_test

x_c=x_test[~result]
y_real = y_test[~result]
y_c = y_[~result]
plt.figure(figsize=(10*3,40*4))
for i in range(y_test[~result].size):

    plt.subplot(40,10,i+1)
    plt.imshow(x_c[i].reshape(28,28),cmap="gray")
    
    plt.title("real:{},predict:{}".format(y_real[i],y_c[i]))
    

这个结果有398张,太大太长。我就不显示了。


现在我更改一下算法

#kd树来构建knn
knn = KNeighborsClassifier(algorithm='kd_tree')
knn.fit(X_train,Y_train)
knn.score(x_test,y_test)

输出:0.9715714285714285

我再更改一下算法

#选择用球树来构建
knn = KNeighborsClassifier(algorithm='ball_tree')
knn.fit(X_train,Y_train)
knn.score(x_test,y_test)

输出:0.9715714285714285

最终不会因为算法的改变而改变准确率。
经过画图也看出,他们的准确率和计算错误的样本都是一样的,错都错在那几张图

kdtree和球树只是检索计算的时候用到,可能会比暴力计算法(全部计算一遍)要快那么一点点。当然对于这个案例来说,我喜欢用kdtree。还是会快一点的。另外大家运算的时候,最少要让n_jobs=2.不然真的太慢了。可能是我电脑的原因吧。

那么K的值到底取多少好?我还能不能在通过其他参数来提升一点正确率呢?
我们来试一试,先改变一下K值看看

这段代码真的很费时间!!!可以看出knn的时间空间复杂度真的很大

#用于放置准确率的列表,后期画图用
socre_list = []
for k in range(3,14):
    t1 = time.time()
    knn = KNeighborsClassifier(n_neighbors=k,n_jobs=2,algorithm='kd_tree')
    knn.fit(x_train,y_train)
    y_ = knn.predict(x_test)
    result = y_==y_test
    score = y_test[result].size/14000
    socre_list.append(score)
    t2 = time.time()
    print("k的值为:{},用时{:.2f}秒,此时的正确率为:{:.10f}".format(k,t2-t1,score))

我的电脑运行内部一次循环用时500到560秒左右
一共循环十几次,可想而知
。。。。。。。

dispaly(socre_list)

输出:

socre_list =[0.9719285714,0.9717857143,0.9715714286,0.9722857143,0.9715000000,0.9701428571,0.9702857143,0.9698571429,0.9694285714,0.9684285714,0.9677857143]

以上就是最后的到的准确率

#画图展示
plt.figure(figsize=(12,9))
plt.plot(list1,color="b",marker='o',lw=2,ls='-',label="正确率")
plt.title("近邻算法K值所影响的手写数字识别正确率:")
plt.legend(loc="best")
plt.xticks(range(10),np.arange(4,14))
plt.xlabel("k值")
plt.ylabel("正确率")
plt.grid(True)
plt.show()

机器学习之knn算法----手写数字识别mnist_第2张图片
图中可以知道:当k取6时,准确率是最高的。


准确率就是这样慢慢试出来的,你也不知道怎么样他就会蹦高。但对于手写字体,经过我的实验,加了权重以后,准确率会高不少
当然如果没有一台好电脑,你也会像我一样崩溃的。。。。。
重构代码,增加distance权重

socre_list1 = []
for k in range(3,14):
    t1 = time.time()
    knn = KNeighborsClassifier(n_neighbors=k,n_jobs=2,algorithm='kd_tree',weights='distance')
    knn.fit(x_train,y_train)
    y_ = knn.predict(x_test)
    result = y_==y_test
    score = y_test[result].size/14000
    socre_list1.append(score)
    t2 = time.time()
    print("k的值为:{},用时{:.2f}秒,此时的正确率为:{:.10f}".format(k,t2-t1,score))

输出:

k的值为:3,用时528.99秒,此时的正确率为:0.9730000000
k的值为:4,用时560.37秒,此时的正确率为:0.9741428571
k的值为:5,用时560.62秒,此时的正确率为:0.9730714286
k的值为:6,用时519.32秒,此时的正确率为:0.9735000000
k的值为:7,用时522.16秒,此时的正确率为:0.9722857143
k的值为:8,用时520.83秒,此时的正确率为:0.9726428571
k的值为:9,用时521.36秒,此时的正确率为:0.9710714286
k的值为:10,用时518.88秒,此时的正确率为:0.9717142857
k的值为:11,用时521.65秒,此时的正确率为:0.9704285714
k的值为:12,用时520.72秒,此时的正确率为:0.9702142857
k的值为:13,用时521.22秒,此时的正确率为:0.9689285714

画图:

plt.figure(figsize=(12,9))
plt.plot(socre_list1,color="b",marker='o',lw=2,ls='-',label="正确率")
plt.title("近邻算法K值所影响的手写数字识别正确率:")
plt.legend(loc="best")
plt.xticks(range(11),np.arange(3,14))
plt.xlabel("k值")
plt.ylabel("正确率")
plt.grid(True)
plt.show()

机器学习之knn算法----手写数字识别mnist_第3张图片

这样可能看不出来两种有啥不同。我们画在一张图上

plt.figure(figsize=(9,6))
plt.plot(socre_list1,color="r",marker='o',lw=2,ls='-',label="加了权重distance的正确率")
# plt.xticks(range(11),np.arange(3,14))

plt.plot(list1,color="b",marker='o',lw=2,ls='-',label="默认权重的正确率")
plt.xticks(range(11),np.arange(3,14))
plt.grid(True)
plt.legend(loc="best")
plt.title("近邻算法K值所影响的手写数字识别正确率:")

plt.show()

机器学习之knn算法----手写数字识别mnist_第4张图片
当然每一次运行结果可能都会不一样,因为train_test_split分割每一个的数据也是不一样的。这里只是相对而言比不加权重高一点。
但是对于机器学习建模来说,测试样本的准确率只是一方面。也许这个样本准确度高,换一个样本集,就不行了呢。。。所以要多试试吧。今天就先这么多。希望大家一起学习交流。这个算法还是很简单的!

你可能感兴趣的:(机器学习,python,人工智能,大数据)