工作原理
存在一个样本数据集合,并且样本集合中每个数据都存在标签,即知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据的分类标签
**一般k不大于20
特点
–优点:(1)精度高
(2)对异常值不敏感
(3)无数据输入假定
–缺点:计算复杂度高、空间复杂度高
–适用数据范围:数值型和标称型
注:
标称型数据:一般在有限的数据中取,而且只存在“是”和“否”两种不同的结果,一般用于分类问题
数值型数据:可以在无限的数据中取,而且数据比较具体化,一般用于回归问题
一般流程
(1)收集数据:可以使用任何方法
(2)准备数据:距离计算所需要的数值,最好是结构化的数据格式
(3)分析数据:可以使用任何方法
(4)训练方法:可略过
(5)测试算法:计算错误率
(6)使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理
使用算法过程
对未知类别属性的数据集中的每个点依次执行以下操作:
(1)计算已知类别数据集中的点与当前点之间的距离
(2)按照距离递增次序排序
(3)选取与当前点距离最小的k个点
(4)确定前k个点所在类别的出现频率
(5)返回前k个点出现频率最高的类别作为当前点的预测分类
示例1
假设有4个点,(1,1.1)、(1,1)、(0,0)、(0,0.1),假设有一个新的点,对这个新点进行k-近邻分类
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
def classify0(inX,dataSet,labels,k):
dataSetSize = dataSet.shape[0] # 获取有多少个数据
# 算出距离
diffMat = tile(inX,(dataSetSize,1)) - dataSet #tile(A,n) 将数组A重复n次,以此算出差值即计算出(X1-X2)和(Y1-Y2)
sqDiffMat = diffMat ** 2
sqDistance = sqDiffMat.sum(axis=1) #算出 (X1-X2)^2 + (Y1-Y2)^2
distances = sqDistance ** 0.5
sortedDistIndices = distances.argsort()
# 截取前k个
classCount = {}
for i in range(k):
voteLabel = labels[sortedDistIndices[i]]
classCount [voteLabel] = classCount.get(voteLabel,0) + 1
# 选出频率最高的
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
if __name__ == '__main__':
group,labels = createDataSet()
inX = [0,0]
print(classify0(inX=inX,dataSet=group,labels=labels,k=3))
如果新点为[0,0](举例),则分类结果为B:
若新点为[7,8](举例),则分类结果为A:
示例2
使用k-近邻算法改进约会网站的配对效果
一般流程
(1)收集数据:提供文本文件
(2)准备数据:使用Python解析文本文件
(3)分析数据:使用matplotlib画图
(4)训练算法:略过
(5)测试算法:使用所提供的的部分数据作为测试样本
(6)使用算法:产生简单的命令行程序,而后自行进行测试
数据:
数据存放在txt中
数据格式:每年获得的飞行常客里程数;玩视频游戏所消耗时间百分比;每周消费的冰激凌公升数;类别分类
类别分为3类:1-不喜欢的人;2-魅力一般的人;3-极具魅力的人
from numpy import *
import matplotlib
import matplotlib.pyplot as plt
def file2matrix(filename):
fr = open(filename)
arrayLines = fr.readlines()
numberOfLines = len(arrayLines)
returnMat = zeros((numberOfLines,3))
classLabelVector = []
index = 0
for line in arrayLines:
line = line.strip().split('\t')
returnMat[index,:] = line[0:3]
classLabelVector.append(int(line[-1]))
index += 1
return returnMat,classLabelVector
if __name__ == '__main__':
datingDataMat,datingDataLabel = file2matrix('datingTestSet2.txt')
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,1],datingDataMat[:,2]) # 使用datingDataMat矩阵的第二、第三列数据
plt.show()
最终画出来的数据点是这样的…
为了分出不同类别的数据,采用以下代码:
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingDataLabel),15.0*array(datingDataLabel))
结果是这样的…
如果选取第一、第二列的特征,是这样的…
如果选取的是第一、第三列,结果是这样的…
∴ 选取第一、第二个特征比较好~
在datingTestSet2.txt文件上测试分类器
from numpy import *
import operator
"""
Fuction:
将txt分解为特征值和标签值两个矩阵
Patameters:
filename:数据文件
Output/Return:
returnMat:特征矩阵
classLabelVector:标签值
"""
def file2matrix(filename):
fr = open(filename)
arrayLines = fr.readlines()
numberOfLines = len(arrayLines)
returnMat = zeros((numberOfLines,3))
classLabelVector = []
index = 0
for line in arrayLines:
line = line.strip().split('\t')
returnMat[index,:] = line[0:3]
classLabelVector.append(int(line[-1]))
index += 1
return returnMat,classLabelVector
"""
Funtion:
数据归一化
Parameters:
dataSet:特征矩阵
Output/Return:
normDataSet:归一化后的特征矩阵
ranges:变化范围
minVals:每一特征的最小值
"""
def autonorm(dataSet):
minVals = dataSet.min()
maxVals = dataSet.max()
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
"""
Funtion:
k-近邻分类器
Parameters:
inX:测试样本的特征矩阵
dataSet:训练样本的特征矩阵
labels:训练样本的标签
k:截取前k个“答案”值
Output/Return:
sortedClassCount:根据各个标签在答案中的出现频率,选择最高的频率对应的标签作为分类答案
"""
def classify0(inX,dataSet,labels,k):
dataSetSize = dataSet.shape[0] # 获取有多少个数据
# 算出距离
diffMat = tile(inX,(dataSetSize,1)) - dataSet #tile(A,n) 将数组A重复n次,以此算出差值即计算出(X1-X2)和(Y1-Y2)
sqDiffMat = diffMat ** 2
sqDistance = sqDiffMat.sum(axis=1) #算出 (X1-X2)^2 + (Y1-Y2)^2
distances = sqDistance ** 0.5
sortedDistIndices = distances.argsort()
# 截取前k个
classCount = {}
for i in range(k):
voteLabel = labels[sortedDistIndices[i]]
classCount [voteLabel] = classCount.get(voteLabel,0) + 1
# 选出频率最高的
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
"""
Function:
测试分类器性能
Output:
错误率
"""
def datingClassTest():
ratio = 0.1
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
normMat,ranges,minVals = autonorm(datingDataMat)
m = normMat.shape[0]
numTestVec = int(m * ratio) # 按比例划分测试集
errorCount = 0.0 # 设置成浮点数,若是整数,则计算百分比只能计算得出0(整数除法--截取),如果是浮点数,则可正确得到百分比
for i in range(numTestVec):
classifierResult = classify0(normMat[i,:],normMat[numTestVec:m,:],datingLabels[numTestVec: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 / numTestVec))
if __name__ == '__main__':
datingClassTest()
结果:
错误率:
示例3
手写识别系统
训练数据和测试数据的格式是txt文件,用1和0两个数值表示,以此来显现不同的手写字体
from numpy import *
import operator
import os
"""
Funtion:
将txt文件中的数据存放在数组中
Parameters:
filename: 存放手写字体的txt文件
Output/Return:
returnVec: 返回存储的数组
"""
def img2vector(filename):
returnVec = zeros((1,1024))
fr = open(filename)
for i in range(32):
line = fr.readline()
for j in range(32):
returnVec[0,32 * i + j] = int(line[j])
return returnVec
"""
Funtion:
k-近邻分类器
Parameters:
inX:测试样本的特征矩阵
dataSet:训练样本的特征矩阵
labels:训练样本的标签
k:截取前k个“答案”值
Output/Return:
sortedClassCount:根据各个标签在答案中的出现频率,选择最高的频率对应的标签作为分类答案
"""
def classify0(inX,dataSet,labels,k):
dataSetSize = dataSet.shape[0] # 获取有多少个数据
# 算出距离
diffMat = tile(inX,(dataSetSize,1)) - dataSet #tile(A,n) 将数组A重复n次,以此算出差值即计算出(X1-X2)和(Y1-Y2)
sqDiffMat = diffMat ** 2
sqDistance = sqDiffMat.sum(axis=1) #算出 (X1-X2)^2 + (Y1-Y2)^2
distances = sqDistance ** 0.5
sortedDistIndices = distances.argsort()
# 截取前k个
classCount = {}
for i in range(k):
voteLabel = labels[sortedDistIndices[i]]
classCount [voteLabel] = classCount.get(voteLabel,0) + 1
# 选出频率最高的
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
"""
Function:
手写字体识别
Output:
错误率
"""
def handwritingClassTest():
handwritingLabel = []
trainingFileList = os.listdir('trainingDigits') # 获取训练集文件夹下的内容
trainingNum = len(trainingFileList) # 获取训练集大小
trainingMat = zeros((trainingNum,1024))
for i in range(trainingNum):
fileName = trainingFileList[i]
fileStr = fileName.split('.')[0] # 获取文件夹的名字
classNum = int(fileStr.split('_')[0]) # 获取手写数字的具体数值
handwritingLabel.append(classNum)
trainingMat[i,:] = img2vector('trainingDigits/%s' % fileName) # 将训练文件中的值存储起来
testFileList = os.listdir('testDigits')
errorCount = 0.0
testNum = len(testFileList)
for i in range(testNum):
fileName = testFileList[i]
fileStr = fileName.split('.')[0]
classNum = int(fileStr.split('_')[0])
vectorTest = img2vector('testDigits/%s' % fileName)
classifierResult = classify0(vectorTest,trainingMat,handwritingLabel,3)
print("The classifier result is %d,the real answer is %d" % (classifierResult,classNum))
if classifierResult != classNum:
errorCount += 1.0
print("The number of wrong answer is %d" % (errorCount))
print("The error rate is %f" % (errorCount / testNum))
if __name__ == "__main__":
handwritingClassTest()
结果:
错误率: