k-近邻(knn)算法的简介和实现
最近小阿奇在学习机器学习算法,所以决定把相关原理和代码整理出来和小伙伴们一起分享
一、k近邻算法的概述
k-近邻算法(k-Nearest Neighbour algorithm),又称为KNN算法。KNN的工作原理:给定一个已知标签类别的训练数据集,输入没有标签的新数据后,在训练数据集中找到与新数据最邻近的k个实例,如果这k个实例的多数属于某个类别,那么新数据就属于这个类别。可以简单理解为:由那些离X最近的k个点来投票决定X归为哪一类。
如上图所示
当k的范围改变,绿色圆点会有不一样的类别
当k=3时(实线范围),绿色圆点属于红色三角这种类别;
当k=5时(虚线范围),绿色圆点属于蓝色方块这种类别。
那如何判断一部电影属于爱情片还是动作片呢?
电影名称 | 打斗镜头 | 接吻镜头 | 电影类型 |
---|---|---|---|
无问西东 | 1 | 101 | 爱情片 |
后来的我们 | 5 | 89 | 爱情片 |
前任3 | 12 | 97 | 爱情片 |
红海行动 | 108 | 5 | 动作片 |
唐人街探案 | 112 | 9 | 动作片 |
战狼2 | 115 | 8 | 动作片 |
新电影 | 24 | 67 | ? |
我们将表中已有数据作为样本数据,打斗镜头和接吻镜头作为数据特征。如何用knn算法给出新电影的电影类型呢?
将数据放在坐标图中可以发现,新电影更加偏向于爱情片。那么从k近邻角度来说,它主要通过未知点和已知数据集中的数据点之间的距离进行判断得出最终结果。
在二维平面角度距离公式:
那么在多维空间里,欧式距离公式可以帮我们很好地解决:
k近邻算法的主要步骤:
(1) 计算已知类别数据集中的点与当前点之间的距离;
(2) 按照距离递增次序排序;
(3) 选取与当前点距离最小的k个点;
(4) 确定前k个点所在类别的出现频率;
(5) 返回前k个点出现频率最高的类别作为当前点的预测类别。
当k=4时,那么在这个电影例子中,把距离按照升序排列,距离绿点电影最近的前4个的电影分别是《后来的我们》、《前任3》、《无问西东》和《红海行动》,这四部电影的类别统计为爱情片:动作片=3:1,出现频率最高的类别为爱情片,所以在k=4时,绿点电影的类别为爱情片。这个判别过程就是k-近邻算法。
然而k的选取得不同也得到不一样的结果,那么如何确定k的值呢?
李航书上讲到,我们一般选取一个较小的数值,通常采取交叉验证法来选取最优的k值。(也就是说,选取k值很重要的关键是实验调参,类似于神经网络选取多少层这种,通过调整超参数来得到一个较好的结果)
二、用python实现k近邻算法
1.算法实现
import pandas as pd
#导入数据,将其转换成dataframe格式
rowdata={'电影名称':['无问西东','后来的我们','前任3','红海行动','唐人街探案','战狼2'],
'打斗镜头':[1,5,12,108,112,115],
'接吻镜头':[101,89,97,5,9,8],
'电影类型':['爱情片','爱情片','爱情片','动作片','动作片','动作片']}
movie_data= pd.DataFrame(rowdata)
#根据上面距离公式,求出距离
new_data = [24,67]
dist = list((((movie_data.iloc[:6,1:3]-new_data)**2).sum(1))**0.5)
#首先对数据进行切分,然后升序排列求出前四
dist_l = pd.DataFrame({'dist': dist, 'labels': (movie_data.iloc[:6, 3])})
dr = dist_l.sort_values(by = 'dist')[: 4]
#统计前k个出现的类别频率
re = dr.loc[:,'labels'].value_counts()
#选择频率最高的输出
result = []
result.append(re.index[0])
print(result)
2.封装函数
def classify0(inX,dataSet,k):
result = []
dist = list((((dataSet.iloc[:,1:3]-inX)**2).sum(1))**0.5)
dist_l = pd.DataFrame({'dist':dist,'labels':(dataSet.iloc[:, 3])})
dr = dist_l.sort_values(by = 'dist')[: k]
re = dr.loc[:, 'labels'].value_counts()
result.append(re.index[0])
return result
inX = new_data
dataSet = movie_data
k = 3
print(classify0(inX,dataSet,k))
三、k近邻算法案例应用
import matplotlib as mpl
import matplotlib.pyplot as plt
datingTest=pd.read_table('datingTestSet.txt',header=None) #导入数据
#把不同标签用颜色区分
Colors = []
for i in range(datingTest.shape[0]):
m = datingTest.iloc[i,-1]
if m=='didntLike':
Colors.append('black')
if m=='smallDoses':
Colors.append('orange')
if m=='largeDoses':
Colors.append('red')
#绘制两两特征之间的散点图
plt.rcParams['font.sans-serif']=['Simhei'] #图中字体设置为黑体
pl=plt.figure(figsize=(12,8))
fig1=pl.add_subplot(221)
plt.scatter(datingTest.iloc[:,1],datingTest.iloc[:,2],marker='.',c=Colors)
plt.xlabel('玩游戏视频所占时间比')
plt.ylabel('每周消费冰淇淋公升数')
fig2=pl.add_subplot(222)
plt.scatter(datingTest.iloc[:,0],datingTest.iloc[:,1],marker='.',c=Colors)
plt.xlabel('每年飞行公里数')
plt.ylabel('消费冰淇淋公斤数')
fig3=pl.add_subplot(223)
plt.scatter(datingTest.iloc[:,0],datingTest.iloc[:,2],marker='.',c=Colors)
plt.xlabel('每年飞行公里数')
plt.ylabel('玩游戏所占时间比')
plt.show()
#数据归一化'''
def minmax(dataSet):
minDF=dataSet.min()
maxDF=dataSet.max()
normSet=(dataSet-minDF)/(maxDF-minDF)
return normSet
datingT=pd.concat([minmax(datingTest.iloc[:,:3]),datingTest.iloc[:,3]],axis=1) #对数据进行切分结合,axis=1保留纵轴
#数据进行切分
"""
将数据切分为训练集和测试集
dataSet:原始数据集
rate:训练集所占比例
返回切分好的训练集和测试集
"""
def randSplit(dataSet,rate=0.9):
n=dataSet.shape[0]
m=int(n*rate)
train=dataSet.iloc[:m,:]
test=dataSet.iloc[m:,:]
test.index=range(test.shape[0]) #test数据进行排序
return train,test
train,test=randSplit(datingT)
#分类器
def datingclassify(train,test,k):
n=train.shape[1]-1 #列数
m=train.shape[0] #行数
result=[]
for i in range(m):
dist=list((((train.iloc[:,:n]-test.iloc[i,:n])**2).sum(1))**5) #欧式距离公式
dist_l=pd.DataFrame({'dist':dist,'labels':(train.iloc[:,n])}) #进行标签处理
dr=dist_l.sort_values(by ='dist')[:k] #升序排列
re=dr.loc[:,'labels'].value_counts()
result.append(re.index[0]) #进行排序
result=pd.Series(result)
test['predict']=result #增加新的预测值列
acc=(test.iloc[:,-1]==test.iloc[:,-2]).mean() #确认准确率mean取巧的过程
print(f'模型预测准确率为{acc}')
return test
print(datingclassify(train,test,5))
在距离公式中差值最大的属性对计算结果的影响最大,原因仅仅是因为它的数值比较大,所以我们要进行数值归一化的处理,使得这三个特征的权重相等。
数据归一化的处理方法有很多种,比如0-1标准化、Z-score标准化、Sigmoid压缩法等等,在这里我们使用最简单
的0-1标准化,公式如下:
四、总结
1.k近邻算法核心思想是,即是给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例,这K个实例的多数属于某个类,就把该输入实例分类到这个类中。
2.与该实例最近邻的k个实例,这个最近邻的定义是通过不同距离函数来定义,我们最常用的是欧式距离。
3.为了保证每个特征同等重要性,我们这里对每个特征进行归一化。
4.k值的选取,既不能太大,也不能太小,何值为最好,需要实验调整参数确定!
希望对大家有所帮助!
参考:
机器学习(周志华),机器学习实战,李航《统计学习方法》,橘安酱的机器学习