《机器学习实战》
第二章.k-近邻算法
2.1 k-近邻算法概述
采用测量不同特征值之间的距离的方法进行分类
k-近邻算法 |
优点:精度高、对异常值不敏感、无数据输入假定。 缺点:计算复杂度高、空间复杂度高。 使用数据范围:数值型和标陈型。 |
2.2 工作原理
如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。存在一个训练样本集,并且样本集中每个数据都存在标签,知道对应关系。输入没有标签的新数据之后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似的数据的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
k-近邻算法中的k的选择十分重要
例子:
如果K=3,绿色圆点的最近的3个邻居是2个红色小三角形和1个蓝色小正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于红色的三角形一类。
如果K=5,绿色圆点的最近的5个邻居是2个红色三角形和3个蓝色的正方形,还是少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。
于此我们看到,当无法判定当前待分类点是从属于已知分类中的哪一类时,我们可以依据统计学的理论看它所处的位置特征,衡量它周围邻居的权重,而把它归为(或分配)到权重更大的那一类。这就是K近邻算法的核心思想。
2.2.1 k-近邻算法的特点
k-近邻算法没有显示的训练过程,是一种懒惰学习的著名代表。在训练阶段仅仅是把样本保存下来,训练时间开销为零,待收到测试样本后在进行处理,与之相反的再训练阶段就对样本进行学习的处理的方法成为急切学习。
2.3 工作流程
k-近邻算法的一般流程 |
(1) 收集数据:可以使用任何方法 (2) 准备数据:距离计算所需要的数值,最好是结构化的数据格式 (3) 分析数据:可以使用任何方法 (4) 训练算法:此步骤不适用于k-近邻算法 (5) 测试算法:计算错误率 (6) 使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判断输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。 |
2.4 实现k-近邻算法
伪代码:
1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。
1.创建kNN.py模块
创建数据集
creatDataSet()函数用于创建数据集和标签
保存之后:如果想在命令行终端运行,导入包之前需要添加路径
python
import sys
sys.path.append("F: \\ukNN")##路径
样例:
def createDataSet():
group = array([[1.0, 2.0], [1.2, 0.1], [0.1, 1.4], [0.3, 3.5]])#元组里面添加了四个列表,四个列表分别有两个元素
labels = ['A', 'A', 'B', 'B']
return group, labels
k-近邻算法核心函数
#k-近邻算法核心函数 def classify(input, dataSet, label, k): ''' 各变量的含义 inX:用于分类的输入向量 dataSet:训练样本集合 labels:标签向量 k:近邻算法中的k shape:是array的属性,描述一个多维数组的维度 tile(inX, (dataSetSize,1)):把inX二维数组化, dataSize表示生成数组后的行数, 1表示列的倍数。 整个这一行代码表示前一个二维数组矩阵的每一个元素减去后一个数组对应的元素值 这样就实现了矩阵之间的减法 axis=1:参数等于1的时候,表示矩阵中行之间的数的求和,等于0的时候表示列之间数的求和。 ''' dataSize = dataSet.shape[0] '''返回值为矩阵的维度例如上面样例中是4个例子,每个例子两个数字,所以是4 2. shape[0]返回第一个数字4,shape[1] = 2 print(dataSize) 计算欧式距离''' diff = tile(input, (dataSize, 1)) - dataSet###将元组中的元素分别与dataset中的值相减 print("diff = " + diff) '''dataSetSize此处为4,将输入向量inX复制4×1份 ###把输入的数字乘上4,然后与上面例子中的四个点相减。求出每个元素上的差值用来方便用欧氏距离公式 ###表示求平方''' sqdiff = diff ** 2 #直接将上面的矩阵平方 squareDist = sum(sqdiff, axis=1) ###将元组中的元素分别与dataset中的值相减,(2,1))#在列方向上重复input,datasize次,行1次
'''开根号之后相加求出输入点与其余点之间的距离
sum应该是默认的axis= 0
就是普通的相加,当加入axis= 1
以后就是将一个矩阵的每一行向量相加。'''
dist = squareDist ** 0.5
##对距离进行排序
sortedDistIndex = argsort(dist)
##argsort()根据元素的值从大到小对元素进行排序,返回下标
classCount = {}
for i in range(k):
voteLabel =label[sortedDistIndex[i]]
###对选取的K个样本所属的类别个数进行统计
classCount[voteLabel] =classCount.get(voteLabel, 0) + 1
'''get函数用来返回一个矩阵1.mat.getA()
将自身矩阵变量转化为ndarray类型的变量。'''
###选取出现的类别次数最多的类别
maxCount = 0
for key,value in classCount.items():
if value > maxCount:
maxCount = value
classes = key
return classes
将文本转换成numpy的解析程序
#将文本记录转换为Numpy解析的函数
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines()#读入函数
numberOflines = len(arrayOLines)
returnMat = zeros((numberOflines,3))#二维的一个numberOflines行,3列,0 元素矩阵
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip()#删除开头结尾处的空白字符
listFromLine = line.split('\t')#以换行符为切片点,将每行切片成一个单独的元素
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat,classLabelVector
归一化数值:由于方程种数字差值最大的属性对计算结果的影响最大,也就是说,每年获取的飞行常客里程数对于计算结果的影响远远大于其他的两个特征。
为解决此类问题采用将数值归一化,如将取值范围处理为0-1或者-1-1之间。
公式:NewValue = (oldValue - min) / (max - min)
#归一化特征值
def autoNorm(dataSet):
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - tile(minVals, (m,1))
normDataSet = normDataSet/tile(ranges,(m,1))
return normDataSet,ranges,minVals
实例:分类器针对约会网站的测试代码
def datingClassTest():
hoRatio = 0.10
datingDataMat,datingLabels = file2matrix('F:\\uknn\\datingTestSet.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
m = normMat.shape[0]
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
classifierResult =classify(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
print("the classifier came back with: %d, the real answer is: %d" % (classifierResult,datingLabels[i]))
if(classifierResult != datingLabels[i]):
errorCount += 1.0
print("the total error rate is: %f" %(errorCount/float(numTestVecs)))
def classifyPerson():
resultList = ['一点也不喜欢','有些喜欢','非常喜欢']
percentTats = float(input("玩游戏所消耗时间百分比?"))
ffMiles = float(input("在外工作时间?"))
iceCream = float(input("每周消费的金钱?"))
datingDataMat,datingLabels = file2matrix('F:\\ukNN\\datingTestSet2.txt')
normMat,ranges, minVals = autoNorm(datingDataMat)
inArr = array([ffMiles,percentTats,iceCream])
classifierResult = classify((inArr -minVals)/ranges,normMat,datingLabels,3)
print("你可能喜欢这个人的程度:",resultList[classifierResult - 1])
参考文档:
http://blog.csdn.net/qq_33323162/article/details/75103444
https://baike.baidu.com/item/k%E8%BF%91%E9%82%BB%E7%AE%97%E6%B3%95/9512781?fr=aladdin