算法概述
KNN算法,也叫做K近邻算法,是机器学习中一种简单的分类算法。是一种监督学习算法 。
算法原理
存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输人没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
算法推导
判断未知点(绿色)属于蓝色还是红色类别时,首先要确定K值,选择距离未知点最近的K个点,选择出现最高频率的点的类别作为未知点的类别。
K值的选择
K值选择过小,会出现与最近邻算法一样的问题,容易过拟合,会受数据噪声得影响
K值选择过大,容易发生欠拟合,对未知样本得判断不准确
算法流程——伪代码或者文字描述
Step.1–准备数据,分析数据,对数据进行预处理,归一化处理;
Step.2–划分训练集和测试集;
Step.3–计算未知样本和每个训练集样本的距离;
Step.4–设定参数,k值;
Step.5–将距离升序排列;
Step.6–选取距离最小的k个点;
Step.7–统计前k个最近邻样本点所在类别出现的次数;
Step.8–多数表决,选择出现频率最大的类别作为未知样本的类别。
算法实例——Python实现
电影类别分类
图形表示:
import matplotlib.pyplot as plt
fight=(3,2,1,101,99,98,18)
kiss=(104,100,81,10,5,2,90)
filmtype=(1,1,1,2,2,2,3)
plt.scatter(fight,kiss,c=filmtype)
计算距离:
距离的表示方法有多种,这里用欧拉距离来表示,也就是两点间直线距离
d
i
s
=
(
x
0
−
x
1
)
2
+
(
y
0
−
y
1
)
2
dis=\sqrt{(x_0-x_1)^2+(y_0-y_1)^2}
dis=(x0−x1)2+(y0−y1)2
代码实现
#构建数据集
import numpy as np
import matplotlib.pyplot as plt
fight=[3,2,1,101,99,98]
kiss=[104,100,81,10,5,2]
filmtype=[1,1,1,2,2,2]
plt.scatter(fight,kiss,c=filmtype)
plt.xlabel('fight')
plt.ylabel('kiss')
plt.title("movie")
plt.show()
x=np.array([fight,kiss])
x=x.T
y=np.array(filmtype)
xx=np.array([18,90])#测试数据
dist=(((x-xx)**2).sum(1))**0.5#np求和公式,按行求和
sorteddist=dist.argsort()#返回排序后索引
k=4#确定K值
classcount={}
for i in range(k):#统计前K种电影类型出现次数
votelabel=y[sorteddist[i]]
classcount[votelabel]=classcount.get(votelabel,0)+1
maxtype=0
maxcount=-1
for key,value in classcount.items():
if(value>maxcount):
maxtype=key
maxcount=value
print("电影类型",maxtype)
代码实现
封装成函数,方便日后调用
#封装成函数,方便日后调用
import numpy as np
def knn(inx,dataset,labels,k):
#inx 测试集
#dataset 训练集
#label 分类标签
#k 距离最近的k个点
dist=(((dataset-inx)**2).sum(1))**0.5
sorteddist=dist.argsort()
classcount={}
for i in range(k):#统计前K种电影类型出现次数
votelabel=labels[sorteddist[i]]
classcount[votelabel]=classcount.get(votelabel,0)+1
maxtype=0
maxcount=-1
for key,value in classcount.items():
if(value>maxcount):
maxtype=key
maxcount=value
return maxtype
约会网站配对数据
根据三项特性,为海伦女士预测自己对约会男生的态度。
数据集:百度网盘自取
提取码:tept
代码实现
# -*- coding: utf-8 -*-
#KNN算法进行约会网站数据匹配
#按照男生的三种特性:每年出行的里程数、玩游戏的时间比、每周吃冰淇凌公升数来预测
#海伦女士对该男生的态度:不喜欢、魅力一般、极具魅力
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']#设置图中可显示中文
plt.rcParams['axes.unicode_minus'] = False#设置图中可显示中文
def knn(inx,dataset,labels,k):#整体函数的功能是从前k个距离最近的点中找出出现次数最多的点,并返回其类型(海伦女士的态度)
#knn算法参数为
#inx 测试集
#dataset 训练例子
#label 分类标签
#k 距离最近的k个点
dist=(((dataset-inx)**2).sum(1))**0.5#计算欧式距离
sorteddist=dist.argsort()#按照出现次数排序,返回索引
classcount={}#创建字典
for i in range(k):#统计前K种电影类型出现次数
votelabel=labels[sorteddist[i]]
classcount[votelabel]=classcount.get(votelabel,0)+1#此votelabel类型的键值+1
maxtype=0
maxcount=-1
for key,value in classcount.items():#遍历字典,也就是存前K个近的点
if(value>maxcount):#比较找出类型出现最多的key 和values
maxtype=key
maxcount=value
return maxtype
#文件出理函数
def file2matrix(filename):#文件读取数据
fr=open(filename)#读取txt文件
numberoflines=len(fr.readlines())#按行读取,numberoflines为行数
returnmat=np.zeros((numberoflines,3))#创造一个与txt文件数据格式相同的空队列
classlabelvector=[]#用来存数据的结果也就是下面的1,2,3
fr=open(filename)#重新打开,指针复到初位
index=0
for line in fr.readlines():#遍历每一行
line=line.strip()
listfromline=line.split("\t")#按照文件中的空格划分出数据
returnmat[index,:]=listfromline[0:3]#将数据赋值给列表returnmat
#由于数据结果有三种情况,我们这里用1,2,3来代替数据中的结果
if listfromline[-1]=="didntLike":
classlabelvector.append(1)
elif listfromline[-1]=="smallDoses":
classlabelvector.append(2)
elif listfromline[-1]=="largeDoses":
classlabelvector.append(3)
index+=1#没遍历一行index+1
fr.close()#关闭文件
return returnmat,classlabelvector#返回测试集和label
dataingdatamat,datinglabels=file2matrix("datingTestSet.txt")#调用函数处理txt文件,注意,应将代码与txt文件保存在同一目录下,否则就用txt文件的系统路径
#分析数据
#两两分析,此处分析的为里程数与游戏时间
plt.scatter(dataingdatamat[:,0],dataingdatamat[:,1],c=datinglabels)
plt.xlabel(" 里程数")
plt.ylabel("游戏时间")
plt.figure(1)
plt.show()
##两两分析,此处分析的为里程数与每周吃冰淇淋公升数
#plt.scatter(dataingdatamat[:,0],dataingdatamat[:,2],c=datinglabels)
#plt.xlabel(" 里程数")
#plt.ylabel("每周吃冰淇淋公升数")
#plt.figure(2)
#plt.show()
##两两分析,此处分析的为游戏时间与每周吃冰淇淋公升数
#plt.scatter(dataingdatamat[:,1],dataingdatamat[:,2],c=datinglabels)
#plt.xlabel(" 游戏时间")
#plt.ylabel("每周吃冰淇淋公升数")
#plt.figure(3)
#plt.show()
#由于里程数的数据过大,导致剩余的两个特征的参考价值不大
#进行数据归一化操作(0-1归一化)
#(x-x_min)/(x_max-x_min)
#按照列进行归一化
def autonorm(dataset):#0-1归一化函数,由于有一些数据的范围较大,比重较大,所以做0-1归一化处理
#0-1归一化步骤
minvals=dataset.min(0)
maxvals=dataset.max(0)
normdataset=np.zeros(dataset.shape)
normdataset=(dataset-minvals)/(maxvals-minvals)
return normdataset
dataset=autonorm(dataingdatamat)
m=0.8#表示选出前80%的数据用做训练集
datasize=dataset.shape[0]
trainsize=int(m*datasize)#训练集数量
testsize=int((1-m)*datasize)#测试集数量
d=5#选择分类数目,就是分成几类
error=0#记录测试错误的个数
for i in range(testsize):
result=knn(dataset[trainsize+i-1,:],dataset[0:trainsize,:],datinglabels[0:trainsize],d)#循环调用上面的knn函数,将每个测试例子带入验证其正确性
if result!=datinglabels[trainsize+i-1]:#判断通过knn函数得到的结果,是否与真正的结果相同
error=error+1
print("学习比例:",m*100,"%")
print("测试比例:",100-100*m,"%")
print("错误率",error/testsize)#输出分类错误率
鸢尾花卉分类
数据集:鸢尾花(iris)数据集
提取码:gs5i
打乱处理过后的数据集:鸢尾花(iris)数据集2
提取码:w7qq
代码实现
import numpy as np
import matplotlib.pyplot as plt
import KKNNF as k
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def file2matrix(filename):#文件读取数据
fr=open(filename)
numberoflines=len(fr.readlines())#z指针迭代问题,需要重新打开文件
# print(numberoflines)
returnmat=np.zeros((numberoflines,4))
classlabelvector=[]
fr=open(filename)#z指针迭代问题,需要重新打开文件
index=0
for line in fr.readlines():
line=line.strip()
# print(line)
listfromline=line.split(",")
if len(listfromline)==1:
break
# print(len(listfromline))
# print(listfromline)
returnmat[index,:]=listfromline[0:4]
if listfromline[-1]=="Iris-setosa":
classlabelvector.append(1)
elif listfromline[-1]=="Iris-versicolor":
classlabelvector.append(2)
elif listfromline[-1]=="Iris-virginica":
classlabelvector.append(3)
index+=1
fr.close()
return returnmat,classlabelvector#返回测试集和label
dataingdatamat,datinglabels=file2matrix("C:\\Users\\98306\\Desktop\\iris2.txt")
def autonorm(dataset):#0-1归一化函数
minvals=dataset.min(0)
maxvals=dataset.max(0)
normdataset=np.zeros(dataset.shape)
normdataset=(dataset-minvals)/(maxvals-minvals)
return normdataset
dataset=autonorm(dataingdatamat)
m=0.8
datasize=dataset.shape[0]
trainsize=int(m*datasize)
testsize=int((1-m)*datasize)
d=50
error=0
for i in range(testsize):
result=k.knn(dataset[trainsize+i-1,:],dataset[0:trainsize,:],datinglabels[0:trainsize],d)
if result!=datinglabels[trainsize+i-1]:
error=error+1
print("错误率",error/testsize)
算法评价
了解KNN算法的优势和劣势,可以帮助我们在选择学习算法的时候做出更加明智的决定。那我们就来看看KNN算法都有哪些优势以及其缺陷所在!
KNN算法优点
1.简单易用,相比其他算法,KNN算是比较简洁明了的算法。即使没有很高的2.数学基础也能搞清楚它的原理。
3.模型训练时间快,上面说到KNN算法是惰性的,这里也就不再过多讲述。
4.预测效果好。
5.对异常值不敏感
6.多分类
KNN算法缺点
1.对内存要求较高,因为该算法存储了所有训练数据
2.预测阶段可能很慢
3.对不相关的功能和数据规模敏感