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)
来一起研究一下。红色的是预测错误的
现在我就想看看预测错的错误数据。
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()
准确率就是这样慢慢试出来的,你也不知道怎么样他就会蹦高。但对于手写字体,经过我的实验,加了权重以后,准确率会高不少
当然如果没有一台好电脑,你也会像我一样崩溃的。。。。。
重构代码,增加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()
这样可能看不出来两种有啥不同。我们画在一张图上
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()
当然每一次运行结果可能都会不一样,因为train_test_split分割每一个的数据也是不一样的。这里只是相对而言比不加权重高一点。
但是对于机器学习建模来说,测试样本的准确率只是一方面。也许这个样本准确度高,换一个样本集,就不行了呢。。。所以要多试试吧。今天就先这么多。希望大家一起学习交流。这个算法还是很简单的!