**前言: ** 学习笔记,记录下对于一些问题的记录和理解,复习和加深记忆用,挖坑补坑用。
参考:李航 《统计学习方法》
0. 基本内容
k近邻算法(kNN)是一种基本分类和回归方法。其不具有显式的学习过程,而是通过给定的训练集合数据,通过k个最近邻的训练实例通过多数表决的方式,进行预测。所以k-NN由三个基本要素决定,即距离度量、k值选择和 分类决策规则。通常对应选择为:欧氏距离,k值根据具体情况选择,多数表决方式。
1. 问题与理解
-
几种距离
特征空间中两“点” x i = ( x i ( 1 ) , x i ( 2 ) , . . . , x i ( n ) ) T , x j = ( x j ( 1 ) , x j ( 2 ) , . . . , x j ( n ) ) T x_i = (x_i^{(1)}, x_i^{(2)},..., x_i^{(n)})^T , x_j = (x_j^{(1)}, x_j^{(2)},..., x_j^{(n)})^T xi=(xi(1),xi(2),...,xi(n))T,xj=(xj(1),xj(2),...,xj(n))T,则两点间的 L p L_p Lp距离定义为:
L p ( x i , x j ) = ( ∑ k = 1 n ∣ x i ( k ) − x j ( k ) ∣ p ) 1 p L_p(x_i, x_j) = (\sum_{k=1}^{n} |x_i^{(k)} - x_j^{(k)}|^p)^{\frac{1}{p}} Lp(xi,xj)=(k=1∑n∣xi(k)−xj(k)∣p)p1- 当p = 1时,称为曼哈顿距离(Manhattan distance)
- 当p = 2时,称为欧氏距离(Euclidean distance),另欧式空间,欧式范数。
- 当p = ∞时,取值为各个坐标的最大值
-
kd树
- kNN是个简单易理解的算法,可预想到的难点在于,当进行分类时,如何找到最近的k个近邻。一种方法是逐点扫描,计算距离,然后通过比较找到最近的k个点,但显而易见的是,如果训练数据很多,这种方法的计算量会是巨大的。事实上,有很多数据点是不需要考虑和进行计算的。第二种方法kd树便为解决这些问题而生的。
2. 具体实例与算法实现
-
实例
使用k-NN算法,实现mnist手写数字集识别。
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import numpy as np
def loadData():
# 读取mnist手写数字集数据
mnist_dir = '/home/zhangwei/data/database/MNIST'
mnist = input_data.read_data_sets(mnist_dir, one_hot = True)
# 图片数据
train_images = mnist.train.images # (55000, 784)
val_images = mnist.validation.images # (5000, 784)
test_images = mnist.test.images # (10000, 784)
# 标签信息
train_labels = mnist.train.labels # (55000, 10)
val_labels = mnist.validation.labels
test_labels = mnist.test.labels
return train_images,train_labels,test_images,test_labels
def kNN(train_images,train_labels,test_images,test_labels, k = 10, sample_size = 55000):
# k:近邻系数
# 截取部分数据进行kNN测试
train_img = train_images[0:sample_size, :]
train_label = train_labels[0:sample_size, :]
test_img = test_images
test_label = test_labels
# 使用逐行扫描的方法,使用欧氏距离度量和多数表决策略
rNum = 0 # 预测正确数
wNum = 0 # 预测错误数
batch = 100 # 测试100个test_img,查看准确率
for i in range(batch):
Diff = np.tile(test_img[i], (train_img.shape[0], 1)) - train_img
sqDiff = Diff ** 2
sqDistance = sqDiff.sum(axis = 1)
d = sqDistance ** 0.5
index = d.argsort() # 返回排序后的索引
classList = np.zeros((1, 10))
for j in index[0:k]:
classList = train_label[j] + classList
predict = classList.argsort()[0,-1]
fact = test_label[i].argsort()[-1]
if(predict == fact):
rNum = rNum + 1
else:
wNum = wNum + 1
accuracy = rNum / float(wNum + rNum) # 准确率
return accuracy
if __name__ == '__main__':
train_images,train_labels,test_images,test_labels = loadData()
# k取不同值时,计算精度acc
k = np.arange(1, 21)
acc = []
for n,i in enumerate(k):
acc.append(kNN(train_images,train_labels,test_images,test_labels, i,10000))
print(n+1, '/', len(k))
plt.plot(k, acc, 'o')
plt.show()
结果展示:
选取样本数固定时,不同k(横坐标)值时,acc(纵坐标)的变化图