机器学习实战—— Chap02.kNN
一、k-邻近算法概述
k-邻近算法,即 K-Nearest Neighbors Algorithm
简单地说,k-近邻算法采用测量不同特征值之间的距离方法进行分类。
距离度量
这里提到距离度量
的概念:
距离度量是用来描述不同元素之间距离远近的标准,而这种“标准”不只有一种。
最一般使用的度量单位,也是一般k-临近算法使用的,是欧氏距离。
算法特点
- 优点:精度高、对异常值不敏感、无数据输入假定。
- 缺点:计算复杂度高、空间复杂度高。
- 适用数据范围:数值型和标称型。
工作原理
•存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每个数据与所属分类的对应关系。
•输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。
•一般来说,只选择样本数据集中前N个最相似的数据。K一般不大于20,最后,选择k个中出现次数最多的分类,作为新数据的分类
算法的一般流程
•收集数据:可以使用任何方法
•准备数据:距离计算所需要的数值,最后是结构化的数据格式。
•分析数据:可以使用任何方法
•训练算法:(此步骤kNN中不适用)
•测试算法:计算错误率
•使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。
K值选择
•如果选择较小的K值
-
“学习”的近似误差(approximation error)会减小,但 “学习”的估计误差(estimation error) 会增大,
-
噪声敏感
-
K值的减小就意味着整体模型变得复杂,容易发生过 拟合.
•如果选择较大的K值
- 减少学习的估计误差,但缺点是学习的近似误差会增大.
- K值的增大,就意味着整体的模型变得简单.
小结
由此,KNN算法的最重要的三个点可以总结为:
- K值的确定
- 权重设置:即虽然最后选择了K个数据作为参考依据,但K个数据与测试数据的相对位置各不相同,因此需要对K个数据进行不同的权重设置
- 距离的度量方式:计算距离是使用
欧氏距离
还是马氏距离
,巴氏距离
,曼哈顿距离
等等,亦或有其他算法。
二、kNN算法理想化实现
注意:这一部分是完全架空,在理想、抽象的条件下,为了实现算法而构建的部分。实际应用时,无论是调用诸如sk-learn中封装好的函数,还是重写、设定参数,都会比下面这个复杂且完善。
0.数据准备
- 导入了两个模块:第一个是科学计算包NumPy;第二个是运算符模块
from numpy import *
import operator
- 定义
createDataSet()
函数,用于创建数据集和标签
def createDataSet():
# 四组二维特征
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
# 四组特征的标签
labels = ['A','A','B','B']
return group, labels
1.算法实施
- kNN算法的伪代码
对未知类别属性的数据集中的每个点依次执行以下操作:
(1) 计算已知类别数据集中的点与当前点之间的距离;
(2) 按照距离递增次序排序;
(3) 选取与当前点距离最小的k个点;
(4) 确定前k个点所在类别的出现频率;
(5) 返回前k个点出现频率最高的类别作为当前点的预测分类。
- kNN算法实现
classify0()
函数
二维坐标下,两个向量点的欧式距离计算:
d = ( x A 0 − x B 0 ) 2 + ( x A 1 − x B 1 ) 2 d=\sqrt{(xA_0-xB_0)^2+(xA_1-xB_1)^2} d=(xA0−xB0)2+(xA1−xB1)2
def classify0(inX, dataSet, labels, k):
# numpy函数shape[0]返回dataSet的行数
dataSetSize = dataSet.shape[0]
# 将inX重复dataSetSize次并排成一列
diffMat = tile(inX, (dataSetSize,1)) - dataSet
# 二维特征相减后平方(用diffMat的转置乘diffMat)
sqDiffMat = diffMat ** 2
# sum()所有元素相加,sum(0)列相加,sum(1)行相加
sqDistances = sqDiffMat.sum(axis = 1)
# 开平方,计算出距离
distances = sqDistances ** 0.5
# argsort函数返回的是distances值从小到大的--索引值
sortedDistIndicies = distances.argsort()
# 定义一个记录类别次数的字典
classCount = {
}
# 选择距离最小的k个点
for i in range(k):
# 取出前k个元素的类别
voteIlabel = labels[sortedDistIndicies[i]]
# 字典的get()方法,返回指定键的值,如果值不在字典中返回0
# 计算类别次数
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
# python3中用items()替换python2中的iteritems()
# key = operator.itemgetter(1)根据字典的值进行排序
# key = operator.itemgetter(0)根据字典的键进行排序
# reverse降序排序字典
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
# 返回次数最多的类别,即所要分类的类别
return sortedClassCount[0][0]
简单测试:
classify0([0,0], group, labels, 3)
# 'B'
至此构造了第一个分类器,使用这个分类器可以完成很多分类任务。
三、实例一:改进约会网站的配对效果
问题描述
海伦使用约会网站寻找约会对象。经过一段时间之后,她发现曾交往过三种类型的人:
- 1:不喜欢的人
- 2:魅力一般的人
- 3:极具魅力的人
她希望:
- 不喜欢的人则直接排除掉
- 工作日与魅力一般的人约会
- 周末与极具魅力的人约会
现在她收集到了一些约会网站未曾记录的数据信息,这更有助于匹配对象的归类。
实现流程
(1) 收集数据
案例中提供了文本文件,但是有一个小插曲:
原始的数据文件 datingTestSet.txt,其数据记录是数字与字符混合的:
这导致了读文件时出现报错ValueError: invalid literal for int() with base 10: 'largeDoses'
。这种现象很常见,原始数据内部数据结构“混乱”,需要Data Cleaning。
……好在,还有一个datingTestSet2.txt文件,已经将三种不同的labels用数字1~3代替。更多时候需要我们实际修改。
海伦把这些约会对象的数据存放在文本文件 datingTestSet2.txt 中,总共有 1000 行。海伦约会的对象主要包含以下 3 种特征:
Col1
:每年获得的飞行常客里程数Col2
:玩视频游戏所耗时间百分比Col3
:每周消费的冰淇淋公升数
(2) 准备数据
使用Python解析文本文件。讲文本记录通过NumPy转化为分类器可以接受的格式。
构建file2matrix()
函数,以此来处理输入格式问题:
def file2matrix(filename):
# 打开文件
fr = open(filename)