1.KNN最邻近算法
K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一,有监督算法。该方法的思路是:如果一个样本在特征空间中的k个最相似的样本中的大多数属于某一个类别,则该样本也属于这个类别。KNN算法由你的邻居来推断出你的类别,KNN算法就是用距离来衡量样本之间的相似度。
如果K = 3,绿色圆点的最近的3个邻居是2个红色小三角形和1个蓝色小正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于红色的三角形一类。
如果K = 5,绿色圆点的最近的5个邻居是2个红色三角形和3个蓝色的正方形,还是少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。
K 值的选择,距离度量和分类决策规则是该算法的三个基本要素。K值的选择一般低于样本数据的平方根,一般是不大于20的整数。距离度量常用的有欧式距离,曼哈顿距离,余弦距离等,一般使用欧氏距离,对于文本分类,常用余弦距离。分类决策就是“少数服从多数”的策略。
2.KNN算法步骤:
- 对于未知类别的数据(对象,点),计算已知类别数据集中的点到该点的距离。
- 按照距离由小到大排序
- 选取与当前点距离最小的K个点
- 确定前K个点所在类别出现的概率
- 返回当前K个点出现频率最高的类别作为当前点预测分类
3.KNN算法复杂度:
KNN 分类的计算复杂度和训练集中的文档数目成正比,也就是说,如果训练集中文档总数为 n,那么 KNN 的分类时间复杂度为O(n)
4.KNN问题:
该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的 K 个邻居中大容量类的样本占多数。解决:可以采用权值的方法,根据和该样本距离的远近,对近邻进行加权,距离越小的邻居权值大,权重一般为距离平方的倒数。
5.KNN数据归一化:
为了防止某一维度的数据的数值大小对距离计算产生影响,保证多个维度的特征是等权重的,最终结果不能被数值的大小影响,应该将各个维度进行数据的归一化,把数据归一化到[0,1]区间上。
归一化公式:
6.距离度量
欧式距离
也称欧几里得距离,在一个N维度的空间里,求两个点的距离,这个距离肯定是一个大于等于零的数字,那么这个距离需要用两个点在各自维度上的坐标相减,平方后加和再开方。一维,二维,三维的欧式距离计算方法
一维
平方欧式距离
就是欧式距离的平方。
7.KNN案例
import numpy as np
import operator
def classify(normData,dataSet,labels,k):
dataSetSize = dataSet.shape[0]
# print 'dataSetSize 长度 =',dataSetSize
#当前点到所有点的坐标差值
diffMat = np.tile(normData, (dataSetSize,1)) - dataSet
#对每个坐标差值平方
sqDiffMat = diffMat ** 2
#对于二维数组 sqDiffMat.sum(axis=0)指定对数组b对每列求和,sqDiffMat.sum(axis=1)是对每行求和
sqDistances = sqDiffMat.sum(axis = 1)
#欧式距离 最后开方
distance = sqDistances ** 0.5
#argsort() 将x中的元素从小到大排序,提取其对应的index 索引,返回数组
sortedDistIndicies = distance.argsort()
# classCount保存的K是魅力类型 V:在K个近邻中某一个类型的次数
classCount = {}
for i in range(k):
#获取对应的下标的类别
voteLabel = labels[sortedDistIndicies[i]]
#给相同的类别次数计数
classCount[voteLabel] = classCount.get(voteLabel,0) + 1
sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
def file2matrix(filename):
fr = open(filename)
# readlines:是一次性将这个文本的内容全部加载到内存中(列表)
arrayOflines = fr.readlines()
numOfLines = len(arrayOflines)
# print "numOfLines = " , numOfLines
#numpy.zeros 创建给定类型的矩阵 numOfLines 行 ,3列
returnMat = np.zeros((numOfLines,3))
classLabelVector = []
index = 0
for line in arrayOflines:
#去掉一行的头尾空格
line = line.strip()
listFromline = line.split('\t')
returnMat[index,:] = listFromline[0:3]
classLabelVector.append(int(listFromline[-1]))
index += 1
return returnMat,classLabelVector
'''
将训练集中的数据进行归一化
归一化的目的:
训练集中飞行公里数这一维度中的值是非常大,那么这个纬度值对于最终的计算结果(两点的距离)影响是非常大,
远远超过其他的两个维度对于最终结果的影响
实际约会姑娘认为这三个特征是同等重要的
下面使用最大最小值归一化的方式将训练集中的数据进行归一化
'''
def autoNorm(dataSet):
# dataSet.min(0) 代表的是统计这个矩阵中每一列的最小值 返回值是一个矩阵1*3矩阵
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
#dataSet.shape[0] 计算行数, shape[1] 计算列数
m = dataSet.shape[0]
# normDataSet存储归一化后的数据
# normDataSet = np.zeros(np.shape(dataSet))
#np.tile(minVals,(m,1)) 在行的方向上重复 minVals m次 即复制m行,在列的方向上重复munVals 1次,即复制1列
normDataSet = dataSet - np.tile(minVals,(m,1))
normDataSet = normDataSet / np.tile(ranges,(m,1))
return normDataSet,ranges,minVals
def datingClassTest():
hoRatio = 0.1
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
#将数据归一化
normMat,ranges,minVals = autoNorm(datingDataMat)
# m 为行数 = 1000
m = normMat.shape[0]
# print 'm =%d 行'%m
#取出100行数据测试
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
#normMat[i,:] 取出数据的第i行,normMat[numTestVecs:m,:]取出数据中的100行到1000行 作为训练集,datingLabels[numTestVecs:m] 取出数据中100行到1000行的类别,4是K
classifierResult = classify(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],4)
print '模型预测值: %d ,真实值 : %d' %(classifierResult,datingLabels[i])
if (classifierResult != datingLabels[i]):
errorCount += 1.0
errorRate = errorCount / float(numTestVecs)
print '正确率 : %f' %(1-errorRate)
return 1-errorRate
def classifyperson():
resultList = ['没感觉', '看起来还行','极具魅力']
input_man= [30000,3,0.1]
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
result = classify((input_man - minVals)/ranges,normMat,datingLabels,3)
print '你即将约会的人是:' , resultList[result-1]
if __name__ == '__main__':
acc = datingClassTest()
if(acc > 0.9):
classifyperson()
调用python 中 Scikit-learn 实现KNN算法:
from sklearn.neighbors import NearestNeighbors
import numpy as np
from KNNDateOnHand import *
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
t作为训练集拟合模型
nbrs = NearestNeighbors(n_neighbors=3).fit(normMat)
input_man= [30000,3,2]
#数据归一化
S = (input_man - minVals)/ranges
#找到当前点的K个临近点,也就是找到临近的3个点
distances, indices = nbrs.kneighbors(S)
print indices
print distances
# classCount K:类别名 V:这个类别中的样本出现的次数
classCount = {}
for i in range(3):应的索引的类别号
voteLabel = datingLabels[indices[0][i]]
classCount[voteLabel] = classCount.get(voteLabel,0) + 1
sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
resultList = ['没感觉', '看起来还行','极具魅力']
print resultList[sortedClassCount[0][0]-1]