KNN简介
KNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。属于监督学习。
通俗理解如下:
如下图,绿色圆要被决定赋予哪个类,是红色三角形还是蓝色四方形?如果K=3,由于红色三角形所占比例为2/3,绿色圆将被赋予红色三角形那个类,如果K=5,由于蓝色四方形比例为3/5,因此绿色圆被赋予蓝色四方形类。
由此也说明了KNN算法的结果很大程度取决于K的选择。
KNN算法流程
数据的所有特征都要做可比较的量化。
若是数据特征中存在非数值的类型,必须采取手段将其量化为数值。举个例子,若样本特征中包含颜色(红黑蓝)一项,颜色之间是没有距离可言的,可通过将颜色转换为灰度值来实现距离计算。另外,样本有多个参数,每一个参数都有自己的定义域和取值范围,他们对distance计算的影响也就不一样,如取值较大的影响力会盖过取值较小的参数。为了公平,样本参数必须做一些scale处理,最简单的方式就是所有特征的数值都采取归一化处置。
需要一个distance函数以计算两个样本之间的距离。
距离的定义有很多,如欧氏距离、余弦距离、汉明距离、曼哈顿距离等等。 一般情况下,选欧氏距离作为距离度量,但是这是只适用于连续变量。通常情况下,如果运用一些特殊的算法来计算度量的话,K近邻分类精度可显著提高,如运用大边缘最近邻法或者近邻成分分析法。
K值的确定,K是一个自定义的常数,K的值也直接影响最后的估计,一种选择K值得方法是使用 cross-validate(交叉验证)误差统计选择法。交叉验证的概念之前提过,就是数据样本的一部分作为训练样本,一部分作为测试样本,比如选择95%作为训练样本,剩下的用作测试样本。通过训练数据训练一个机器学习模型,然后利用测试数据测试其误差率。 cross-validate(交叉验证)误差统计选择法就是比较不同K值时的交叉验证平均误差率,选择误差率最小的那个K值。例如选择K=1,2,3,… , 对每个K=i做100次交叉验证,计算出平均误差,然后比较、选出最小的那个。
步骤如下:
- 计算测试数据与各个训练数据之间的距离;
- 按照距离的递增关系进行排序;
- 选取距离最小的K个点;
- 确定前K个点所在类别的出现频率;
- 返回前K个点中出现频率最高的类别作为测试数据的预测分类。
代码实现
我们需要使用KNN算法去识别mnist手写数字,具体步骤如下:
- 首先需要将手写数字做成0 1串,将原图中黑色像素点变成1,白色为0,写成TXT文件:
def img2vector(impath,savepath):
'''
convert the image to an numpy array
Black pixel set to 1,white pixel set to 0
'''
im = Image.open(impath)
im = im.transpose(Image.ROTATE_90)
im = im.transpose(Image.FLIP_TOP_BOTTOM)
rows = im.size[0]
cols = im.size[1]
imBinary = zeros((rows,cols))
for row in range(0,rows):
for col in range(0,cols):
imPixel = im.getpixel((row,col))[0:3]
if imPixel == (0,0,0):
imBinary[row,col] = 0
#save temp txt like 1_5.txt whiich represent the class is 1 and the index is 5
fp = open(savepath,'w')
for x in range(0,imBinary.shape[0]):
for y in range(0,imBinary.shape[1]):
fp.write(str(int(imBinary[x,y])))
fp.write('\n')
fp.close()
- 将所有的TXT文件中的0 1串变成行向量 :
def vectorOneLine(filename):
rows = 32
cols = 32
imgVector = zeros((1, rows * cols))
fileIn = open(filename)
for row in xrange(rows):
lineStr = fileIn.readline()
for col in xrange(cols):
imgVector[0, row * 32 + col] = int(lineStr[col])
return imgVector
- KNN识别 :
def kNNClassify(testImput, TrainingDataSet, TrainingLabels, k):
numSamples = dataSet.shape[0] # shape[0] stands for the num of row
#calculate the Euclidean distance
diff = tile(newInput, (numSamples, 1)) - dataSet # Subtract element-wise
squaredDiff = diff ** 2 # squared for the subtract
squaredDist = sum(squaredDiff, axis = 1) # sum is performed by row
distance = squaredDist ** 0.5
#sort the distance vector
sortedDistIndices = argsort(distance)
#choose k elements
classCount = {} # define a dictionary (can be append element)
for i in xrange(k):
voteLabel = labels[sortedDistIndices[i]]
#initial the dict
classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
#vote the label as final return
maxCount = 0
for key, value in classCount.items():
if value > maxCount:
maxCount = value
maxIndex = key
return maxIndex
KNN优缺点
优点:
- 简单,易于理解,易于实现,无需估计参数,无需训练
- 适合对稀有事件进行分类(例如当流失率很低时,比如低于0.5%,构造流失预测模型)
- 特别适合于多分类问题(multi-modal,对象具有多个类别标签),例如根据基因特征来判断其功能分类,kNN比SVM的表现要好
缺点:
- 计算时间和空间线性于训练集的规模(在一些场合不算太大),样本过大识别时间会很长。
k值比较难以确定。
用到的知识
- -