k-近邻算法(k-Nearest Neighbour algorithm),又称为KNN算法,是数据挖掘技术中原理最简单的算法。
KNN的工作原理:给定一个已知标签类别的训练数据集,输入没有标签的新数据后,在训练数据集中找到与新数据最邻
近的k个实例,如果这k个实例的多数属于某个类别,那么新数据就属于这个类别。可以简单理解为:由那些离X最
近的k个点来投票决定X归为哪一类。
k-近邻算法步骤:
1.计算已知类别数据集中的点与当前点之间的距离
2.按照距离递增次序排序
3.选取与当前点距离最小的k个点
4.确定前k个点所在类别的出现频率
5.返回前k个点出现频率最高的类别作为当前点的预测类别
没听懂?我们借图说话。
图中有红色三角和蓝色方块两种类别,我们现在需要判断绿色圆点属于哪种类别。
当k=3时,绿色圆点属于红色三角这种类别;
当k=5时,绿色圆点属于蓝色方块这种类别(蓝色方块数量为3,大于红色三角的数量)。
是不是很简单?
我们再举个栗子
电影类型分类
通过电影中出现的打斗镜头和接吻镜头的数量来预测该电影的类型。
1.准备数据
import pandas as pd
rowdata = {'电影名称':['无问西东','后来的我们','前任3','红海行动','唐人街探案','战狼2'],\
'打斗镜头':[13,5,32,38,65,78],\
'接吻镜头':[20,42,40,26,9,28],\
'电影类型':['爱情片','爱情片','爱情片','动作片','动作片','动作片']}
movie_data = pd.DataFrame(rowdata)
movie_data
2.使用Matplotlib进行可视化(非必要过程)
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
#把不同标签用颜色区分
Colors = []
df = pd.DataFrame(data=movie_data)
for i in range(movie_data.shape[0]):
m = df['电影类型'][i]
if m=='爱情片':
Colors.append('red')
if m=='动作片':
Colors.append('blue')
#绘制两两特征之间的散点图
plt.rcParams['font.sans-serif']=['Simhei'] #图中字体设置为黑体
pl=plt.figure(figsize=(18,13))
fig1=pl.add_subplot(221)
plt.scatter(df['打斗镜头'],df['接吻镜头'],marker='p',c=Colors)
plt.scatter(30,30,marker='x',c='black')
plt.xlabel('打斗镜头')
plt.ylabel('接吻镜头')
plt.title('预测电影类型')
plt.show()
3.创建KNN分类器进行预测
import pandas as pd
"""
函数功能:KNN分类器
参数说明:
inX:需要预测分类的数据集
dataSet:已知分类标签的数据集(训练集)
k:k-近邻算法参数,选择距离最小的k个点
返回:
result:分类结果
"""
def classify0(inX,dataSet,k):
result = []
dist = list((((dataSet.iloc[:,1:3]-inX)**2).sum(1))**0.5) #将各个点的位置带入计算公式,求距离 .iloc为截取数据片段
dist_l = pd.DataFrame({'dist':dist,'labels':(dataSet.iloc[:, 3])}) #对计算出的距离进行对应,以DataFrame格式赋给dist_1
dr = dist_l.sort_values(by = 'dist')[: k] #对距离进行升序排序,并截取前k个
re = dr.loc[:, 'labels'].value_counts() #对距离最短的前k个值的labels的数量进行统计,求出k范围内数量最多的标签
#.loc可按条件进行选择'labels'一列的值
result.append(re.index[0]) #取数量最多的标签并将标签添加到result
return result
new_data = [24,67] #这里我们假设预测点为[24,67],读者可自行修改
inX = new_data
dataSet = movie_data
k = 3
print('该电影的类型预测结果为:%s'%classify0(inX,dataSet,k)[0])
输出结果:
是不是很简单?上面的代码和KNN分类器都是我纯手工打造,你也可以根据我讲的原理自己设计KNN分类器,这样效果会更好。
接下来我再将一个例子,逐步讲解k-近邻算法的详细步骤。
约会网站配对效果判定
根据以往的交往数据来进行预测对约会对象的喜欢程度,是didntLike,smallDoses,还是largeDoses。
1.准备数据
附上数据下载地址
datingTest = pd.read_table('datingTestSet.txt',header=None)
datingTest.head()
datingTest.shape
datingTest.info()
2.分析数据
使用 Matplotlib 创建散点图,查看各数据的分布情况
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
#把不同标签用颜色区分
Colors = []
for i in range(datingTest.shape[0]):
m = datingTest.iloc[i,-1]
if m=='didntLike':
Colors.append('blue')
if m=='smallDoses':
Colors.append('orange')
if m=='largeDoses':
Colors.append('red')
#绘制两两特征之间的散点图
plt.rcParams['font.sans-serif']=['Simhei'] #图中字体设置为黑体
pl=plt.figure(figsize=(14,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()
图中,蓝色的点代表不喜欢,橙色代表有点喜欢,红色代表非常喜欢。
3.数据归一化
什么是归一化?请看下面这张图。在上面的例子中,打斗镜头和接吻镜头数量有的很大,有的很小,数据参差不齐,很难对数据进行精确的预测,那怎么办呢?这里介绍一种常用的方法就是将数据统一归到0-1之间。
数据归一化后的效果:
数据归一化的处理方法很多种,比如0-1标准化、Sigmoid压缩法等等。而我们这里讲的0-1标准化的公式如下:
该方法实现对原始数据的等比例缩放,其中Xscale为归一化后的数据,X为原始数据,Xmax、Xmin分别为原始数据集的最大值和最小值。
"""
函数功能:归一化
参数说明:
dataSet:原始数据集
返回:0-1标准化之后的数据集
"""
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)
datingT.head()
4. 划分训练集和测试集
"""
函数功能:切分训练集和测试集
参数说明:
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])
return train,test
train,test = randSplit(datingT)
print(train,test) #打印训练集
5.创建针对于约会网站的KNN分类器
"""函数功能:k-近邻算法分类器
参数说明:
train:训练集
test:测试集
k:k-近邻参数,即选择距离最小的k个点
返回:预测好分类的测试集
"""
def datingClass(train,test,k):
n = train.shape[1] - 1
m = test.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()
print(f'模型预测准确率为{acc}')
return test
datingClass(train,test,5)
输出结果为:
准确率还不错 n_n
看到这里,是不是觉得机器学习十大算法之一—k-近邻算法不太难?不过,学习k-近邻算法还有很长路要走,这里我只是用简单的例子和代码来引领大家走进机器学习的大门,也是我做这一期博客的初衷,也希望大家多多支持和鼓励,点个赞,加个关注,我会持续更新机器学习十大算法。
机器学习-十大算法系列欢迎访问我的主页,我会持续更新,谢谢。