一、KNN算法分析
k最近邻(KNN)分类算法应该是机器学习分类算法中最简单的算法了,它采用测量不同特征值之间的距离方法进行分类。它的思想很简单:如果一个样本在特征空间中的K个最相似(即特征空间中最邻近)的样本的大多数属于某一个类别,则该样本也属于这个类别。
KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类策略上只依据最近邻的一个或者多个样本的类别来决定待分类样本所属的类别。由于KNN方法主要靠周围有限的近邻的样本,而不是靠判别类域的方法类确定所属类别的,因此对于类域的交叉或者重叠较多的待分类样本集来说,KNN方法较其他方法更为合适。
该算法在分类时有个主要的不足是,当样本不平衡时,如果一个类的样本容量很大,而其他样本容量很小时,可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占大多数。因此可以采用权值的方法(和该样本的距离小的邻居权值大)来改进。该方法的另一个不足之处是计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。目前常用的解决方法是实现对已知样本点进行剪裁,事先去除对分类作用不大的样本。该算法比较适合于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生无法。
总的来说就是我们已经存在了一个带标签的数据库,然后输入没有标签的新数据后,将新数据的每个特征与样本集中的数据对应的特征进行比较,然后算法提取样本集中特征最相似(计算上是最相近)的样本的标签。一般来说,只考虑样本集合中前K个最相似的数据。最后,选择K个最相似的数据中出现次数最多的分类。其算法描述如下:
1) 计算已知类别数据集中的点与当前点之间的距离;
2) 按照距离递增次序排序;
3) 选取与当前点距离最小的K个点;
4) 确定前K个点所在的类别的出现频率;
5) 返回前K个点出现频率最高的类别作为当前点的预测分类。
二、python实现
对于机器学习来讲,python需要安装三个插件,分别是numpy,scipy,matplotlib。其中,前两个是用于数值计算的,最后一个是用于画图的。安装过程可参考网上的博客。
2.1 、KNN基础实践
我们用《机器学习实战》中第二单元的第一个例子,所用的数据可以到http://download.csdn.net/detail/hechenghai/9017133下载
#KNN算法
#input: inX:表示目标样本
# dataSet:已知所属类型的样本集
# labels:dataSet中样本的所属类别
# K:用于比较的邻居的数量
#导入numpy
from numpy import *
import operator
#创建数据集
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
#用KNN算法进行分类
def classify0(inX,dataSet,labels,k):
dataSetSize = dataSet.shape[0]#shape[0]表示行数,shape[1]表示列数
#tile(A,reps)表示将A重复reps次创建一个矩阵
diffMat = tile(inX,(dataSetSize,1)) - dataSet
sqDiffMat = diffMat **2
sqDistances = sqDiffMat.sum(axis=1)
distances =sqDistances**0.5
#对距离进行排序
sortedDistIndices = distances.argsort()
classCount={}#定义一个字典,可以增加元素
voteIlabel = 0
for i in range(k):
voteIlabel = labels[sortedDistIndices[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0)+1
#sorted(iterable[,cmp,[,key[,reverse=True]]])
# iterable:是可迭代类型;
#cmp:用于比较的函数,比较什么由key决定,有默认值,迭代集合中的一项;
#key:用列表元素的某个属性和函数进行作为关键字,有默认值,迭代集合中的一项;
#reverse:排序规则. reverse = True 或者 reverse = False,有默认值。
#返回值:是一个经过排序的可迭代类型,与iterable一样。
# itemgetter函数用于获取对象的哪些维的数据,参数为一些序号(即需要获取的数据在对象中的序号)
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
这里我再对python的sorted函数进行说明:
>>> help(sorted)
Help on built-in function sorted in module__builtin__:
sorted(...)
sorted(iterable,cmp=None, key=None, reverse=False) --> new sorted list
iterable:是可迭代类型;
cmp:用于比较的函数,比较什么由key决定;
key:用列表元素的某个属性或函数进行作为关键字,有默认值,迭代集合中的一项;
reverse:排序规则. reverse =True 降序 或者 reverse = False 升序,有默认值。
返回值:是一个经过排序的可迭代类型,与iterable一样。
参数说明:
(1) cmp参数
cmp接受一个函数,拿整形举例,形式为:
def f(a,b):
return a-b
如果排序的元素是其他类型的,如果a逻辑小于b,函数返回负数;a逻辑等于b,函数返回0;a逻辑大于b,函数返回正数就行了
(2) key参数
key也是接受一个函数,不同的是,这个函数只接受一个元素,形式如下
def f(a):
return len(a)
key接受的函数返回值,表示此元素的权值,sort将按照权值大小进行排序
(3) reverse参数
接受False 或者True 表示是否逆序
operator.itemgetter函数
operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号(即需要获取的数据在对象中的序号),下面看例子。
a = [1,2,3]
>>> b=operator.itemgetter(1) //定义函数b,获取对象的第1个域的值
>>> b(a)
2
>>>b=operator.itemgetter(1,0) //定义函数b,获取对象的第1个域和第0个的值
>>> b(a)
(2, 1)
要注意,operator.itemgetter函数获取的不是值,而是定义了一个函数,通过该函数作用到对象上才能获取值。
在上述代码中:
sortedClassCount =sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
由于classCount是一个map所以需要取第二维的值作比较。
2.2、KNN实战提高
本节中,我们使用K近邻分类器的手写识别系统。为了简单起见,这里构造的系统只能识别数字0到9,如下图所示。需要识别的数字已经使用图形处理软件,处理成具有相同的色彩和大小:宽高是32像素*32像素的黑白图像。
#读取文件数据,将数据转化为一个向量
def img2vector(filename):
returnVect = zeros((1,1024))#申请一个1*1024的矩阵
fr = open(filename)#读取文件数据
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVect[0,32*i+j]= int(lineStr[j])
return returnVect
def handwritingClassTest():
hwLabels = []#每个文件的标签
trainingFileList = os.listdir('trainingDigits')#获得文件夹下的文件列表
m = len(trainingFileList)#获取文件夹下的文件数目
trainingMat =zeros((m,1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])#获取文件标签(根据文件名获取)
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector('trainingDigits/%s'%fileNameStr)
testFileList = os.listdir('testDigits')#获取测试用例
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
vectorUnderTest = img2vector('testDigits/%s'%fileNameStr)
classifierResult =classify0(vectorUnderTest,trainingMat,hwLabels,3)
print("the classifier came back with: %d, the real answeris : %d" %(classifierResult,classNumStr))
if(classifierResult!=classNumStr):
errorCount+=1.0
print("\n the total number of errors is: %d"%errorCount)
print("\n the total error rate is: %f"%(errorCount/float(mTest)))