机器学习-kNN
本节介绍机器学习中比较简单的一种监督学习算法,kNN算法,以及其Python实现。
部分内容引用自《Machine Learning in Action》
概述
监督学习
明确知道想要得到什么。例如判断某辆机动车的类型(小车,卡车,公交车),预测明天是下雨,天晴还是多云,对某个曲线做拟合等等。如果目标变量是离散型,则使用分类算法,如果目标变量是连续型,则可以使用回归算法。
无监督学习
不需要预测目标变量的值。如果需要将数据划分为离散的组,可以使用聚类算法。如果还需要估计数据与每个分组的相似程度,则需要使用密度估计算法。
kNN简介
kNN(k nearest neighbor)k近邻算法,是一种简单的监督学习算法,其可以非常高效的对目标变量做分组,从而判断其类型。基本思想是找出目标变量与数据集中最相似的k个数据,再找出这k个数据中次数最多的那个类别,该类别就是原目标变量的类别。
算法流程
- 计算目标变量和数据集中所有点的距离(空间中两个点的距离公式,略)
- 按照距离递增排序
- 选取前k个点
- 确定前k个点所在类别的出现概率
- 返回概率最高的那个点作为目标变量的类别
实例
假设二维空间中有四个点,[1, 1.1], [1, 1], [0, 0], [0, 0.1],其类别分别为 ['A', 'A', 'B', 'B'],求点 [0.9, 0.9]和点[0.2, 0.2]的类别。
先画一个图,直观查看这几个点的位置:
通过直觉,可以看出 [0.9, 0.9]距离右上两个点最近,而右上两个点的类别是A,所以 [0.9, 0.9]的类别应该也是A。同理,[0.2, 0.2]的类别应该是B。
下面通过Python实现kNN,
import operator
import matplotlib.pyplot as plt
import numpy as np
def create_dataset():
group = np.array([[1, 1.1], [1, 1], [0, 0], [0, 0.1]])
labels = ['A', 'A', 'B', 'B']
return group, labels
def classify(in_x, data_set, labels, k):
# shape returns a tuple which means the size of each dimensions,
# 0 represents the first dimension, 1 represents the 2nd dimension
data_set_size = data_set.shape[0]
if k >= data_set_size:
k = data_set_size
# np.tile() is used to extend a matrix on each dimension
tmp_mat = np.tile(in_x, (data_set_size, 1)) - data_set
tmp_mat = tmp_mat ** 2
# sum() is used to calculate sum on given dimension
tmp_mat = tmp_mat.sum(axis=1)
tmp_mat = tmp_mat ** 0.5
print("Distance of point %r to each data set: %r" % (in_x, tmp_mat))
# argsort() means sort elements, but returns key not the element
tmp_mat = tmp_mat.argsort()
label_count = {}
for i in range(k):
label = labels[tmp_mat[i]]
label_count[label] = label_count.get(label, 0) + 1
# print(label_count.items())
# => [('B', 2), ('A', 1)]
# Sort the elements in a dict by the second item of each element
sorted_label_count = sorted(label_count.items(), key=operator.itemgetter(1), reverse=True)
return sorted_label_count[0][0]
def test():
group, labels = create_dataset()
test_point1 = (0.9, 0.9)
label1 = classify(test_point1, group, labels, 3)
print("Lable of point %r is %r" % (test_point1, label1))
test_point2 = (0.2, 0.2)
label2 = classify(test_point2, group, labels, 3)
print("Lable of point %r is %r" % (test_point2, label2))
plt.title("kNN demo")
plt.xlabel("x")
plt.ylabel("y")
plt.plot(group[:, 0], group[:, 1], 'ob')
plt.plot(test_point1[0], test_point1[1], 'or')
plt.plot(test_point2[0], test_point2[1], 'oy')
plt.show()
if __name__ == '__main__':
test()
运行结果:
D:\work\python_workspace\machine_learning\venv\Scripts\python.exe D:/work/python_workspace/machine_learning/kNN/kNN.py
Distance of point (0.9, 0.9) to each data set: array([0.2236068 , 0.14142136, 1.27279221, 1.20415946])
Lable of point (0.9, 0.9) is 'A'
Distance of point (0.2, 0.2) to each data set: array([1.20415946, 1.13137085, 0.28284271, 0.2236068 ])
Lable of point (0.2, 0.2) is 'B'
Process finished with exit code 0
上面的实例仅仅展示了kNN的算法思想和Python实现,真实场景中不会出现上面实例中这么简单的数据。真实场景往往非常复杂,例如数据集可能包含成千上万的数据元素,每个元素的属性有很多(几十上百甚至上千),属性可能有多种类型(数值型,非数值型)等等。
对于数值型属性,往往需要做归一化处理,常见的一种方式是将数据转化到0和1之间,
对于非数值属性,需要对其编码,使其转换为数值类型。
归一化和编码是两个比较重要的话题,后面会专门展开讨论,这里就不再赘述了。
kNN优缺点
kNN是分类算法中最简单最有效的算法,无论是思想还是编码实现都很容易掌握,在数据量比较小的情况下是非常高效的。但它也存在一些缺点,例如(1)如果数据集很大,我们就必须使用很多的存储空间,(2)每次判断某个分类时都要计算到所有点的距离,因此需要很多的计算资源,(3)每次判断某个分类的计算都是独立的,无法给出数据集的任何基础结构信息。