KNN
- 是什么/原理
- 核心思想:“近朱者赤,近墨者黑”,由你的邻居来推断出你的类别
- python实现
- 算法不足之处
是什么/原理
- k近邻法(KNN)是一种基本分类与回归方法,是“懒惰学习”的代表,其算法的时间复杂度是O(n),一般适用于样本数较少的数据集。其输出可以是多类。
- k近邻法没有显式的学习过程,即没有训练阶段,数据集事先已有了分类和特征值,待收到新样本后直接进行处理。
- 相似度衡量方法:包括欧式距离(p=2)、曼哈顿距离(p=1)、夹角余弦。
- 简单应用中,一般使用欧氏距离,但对于文本分类来说,使用余弦来计算相似度就比欧式距离更合适。
- K近邻法的三个基本要素是:k值的选择、距离度量、分类决策规则。
核心思想
k近邻法中k值的选择会对k近邻法的结果产生重大影响。选取的k值若较小,就相当于用较小的领域中的训练实例进行预测,“学习”的近似误差会减小,只有与输入实例较近的训练实例才会对预测结果起作用,但缺点是“学习”的估计误差会增大,预测结果会对近邻的实例点非常敏感。反之,若选取的k值较大,其优点是可以减少学习的估计误差,但缺点是学习的近似误差会增大。K值的增大意味整体的模型变得简单(k值越小模型越复杂,k值较小可能产生过拟合的现象)。所以在应用中,k值一般取一个比较小的数值,通常采用交叉验证法来选取最优的k值。
Q:什么是近似误差和估计误差?
近似误差可以理解为模型估计值与实际值之间的差距。
估计误差可以理解为模型的估计系数与实际系数之间的差距。
近似误差,更关注于“训练”。最小化近似误差,即为使估计值尽量接近真实值,这里的真实值指的是训练样本,模型本身并不是最接近真实分布。换一组样本可能就不近似了,这种训练样本表现好,测试样本表现不好的现象称为过拟合。
估计误差,更关注于“测试”、“泛化”。与近似误差相反,最小化估计误差,即为使估计系数尽量接近真实系数,可能此时对于训练样本得到的估计值不一定最接近真实值,但模型肯呢个更加接近真实分布。
如图(图片来自百度百科)
K值若较小,这里取内圈,红色三角占2/3,则绿色的未知物被判断为红色三角;
K值若较大,这里取外圈,蓝色方块占3/5,则绿色未知物被判断为蓝色方块。
实现
from numpy import * # 引入科学计算包
import operator # 经典python函数库。运算符模块。
# 创建数据集
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
# 算法核心
# inX:用于分类的输入向量。即将对其进行分类。
# dataSet:训练样本集
# labels: 标签向量
def classfy0(inX, dataSet, labels, k):
# 距离计算(这里用的欧氏距离)
dataSetSize = dataSet.shape[0] # 得到数组的行数。即知道有几个训练数据
diffMat = tile(inX, (dataSetSize, 1)) - dataSet # tile:numpy中的函数。tile将原来的一个数组,扩充成了4个一样的数组。diffMat得到了目标与训练数值之间的差值。
sqDiffMat = diffMat ** 2 # 各个元素分别平方
sqDistances = sqDiffMat.sum(axis=1) # sum(axis=1)函数表示按行求和,一般默认
注释
axis=0即默认列求和
distances = sqDistances ** 0.5 # 开方,得到距离。
假如:(这里用的欧氏距离)
Newinput:[1,0,2]
Dataset:
[1,0,1]
[2,1,3]
[1,0,2]
计算过程即为:
1、求差
[1,0,1] – [1,0,2] = [0,0,-1]
[2,1,3] – [1,0,2] = [1,1,1]
[1,0,2] – [1,0,2] = [0,0,-1]
2、对差值平方
[0,0,1]
[1,1,1]
[0,0,1]
3、将平方后的差值累加
1
3
1
4、将上一步骤的值求开方,即得距离
1
1.73
1
这就是以上的步骤
代码块
sortedDistIndicies = distances.argsort() # 升序排列
# 选择距离最小的k个点。
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
# 排序
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #Python3.5中:iteritems变为items
return sortedClassCount[0][0]
if __name__== '__main__':
dataSet, labels = createDataSet()
input = array([1.1, 0.3])
K = 3
output = classfy0(input, dataSet, labels, K)
print("测试数据为:", input, "分类结果为:", output)
算法不足之处
- 样本不平衡容易导致结果错误;
- 计算量较大。