k-近邻算法(k-Nearest Neighbors,kNN)
knn算法属于监督类型算法。首先我们有样本训练集,知道每一条数据的分类。继而,我们输入没有分类的新数据,将新数据的每个特征与样本集中的对应项进行比较,提取样本集中最相思的数据,这样我们可以获得该数据的分类。一般来说,我们只选择样本集中前k个最相似的数据,通常k不大于20.最后,选择k个相似数据中出现最多的分类,作为新数据的分类,此为k近邻算法。
优点:
精度高,对异常数值不敏感,无数据输入假定。
缺点:计算复杂度高,空间复杂度高。无法给出任何数据的基础结构信息,因此我们也无法知晓平均
实例样本和典型实例样本具有什么特征
适用数据范围:数值型,标称型(numeric values,nominal values)
伪代码:
(1)计算样本数据集中的点与当前带分类数据的点之间的距离(计算相似程度)
(2)按照距离递增排序
(3)选取与当前距离最小的前K个点
(4)确定k个点所在类别的出现频率
(5)返回出现频率最高的类别作为预测分类
python代码:
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
#intX:待分类数据(向量),dataSet,labels:样本训练集,k
def classi0(intX, dataSet, labels, k):
dataSetSize = dataSet.shape[0]#训练集大小
diffMat = tile(intX,(dataSetSize,1)) - dataSet#把待分类数据向量复制成与训练集等阶,对应项求差
sqDiffMat = diffMat**2#各项平方
sqDistances = sqDiffMat.sum(axis=1)#axis=1,将一个矩阵的每一行向量相加
distances = sqDistances**0.5#开方,求出距离
sortedDistIndicies = distances.argsort()#从小到大排序,返回数组的索引
classCount = {}
for i in range(k):#遍历前K个样本训练集
voteILabel = labels[sortedDistIndicies[i]]#对应分类
classCount[voteILabel] = classCount.get(voteILabel,0) + 1#计数
sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)#排序
return sortedClassCount[0][0]#返回统计最多的分类
测试分类器:
错误率是常用的检验分类器的方法:
通过大量已知分类的测试数据,计算出分类器给出错误答案的次数除以测试的总次数。
准备数据:python获取文本数据:
40920 8.3269760.953952 largeDoses
14488 7.1534691.673904 smallDoses
26052 1.4418710.805124 didntLike
假设文本数据保存在datingTestSet.txt文本中
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines()#获取所有数据,以行为单位切割
numberOfLines = len(arrayOLines)
returnMat = zeros((numberOfLines,3))#构建矩阵,以0填充
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split('\t')#以tab分割为数组
returnMat[index,:] = listFromLine[0:3]#copy数据
classLabelVector.append(listFromLine[-1])#保存对应的分类
index += 1
return returnMat,classLabelVector
使用Matplotlib创建散点图分析数据:
import matplotlib
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)#add_subplot(mnp)添加子轴、图。subplot(m,n,p)或者subplot(mnp)此函数最常用:subplot是将多个图画到一个平面上的工具。其中,m表示是图排成m行,n表示图排成n列,也就是整个figure中有n个图是排成一行的,一共m行,如果第一个数字是2就是表示2行图。p是指你现在要把曲线画到figure中哪个图上,最后一个如果是1表示是从左到右第一个位置。
ax.scatter(datingDataMat[:,1], datingDataMat[:,2],15.0*array(map(int,datingLabels)),15.0*array(map(int,datingLabels)))#以第二列和第三列为x,y轴画出散列点,给予不同的颜色和大小
#scatter(x,y,s=1,c="g",marker="s",linewidths=0)
#s:散列点的大小,c:散列点的颜色,marker:形状,linewidths:边框宽度
plt.show()
归一化数据值
为了减少特征值之间因度量不同带来权重的偏差,需要将数据归一化。所谓的归一化,是讲数值范围处理在0~1或-1~1之间。
可以用如下公式:
newValue = (oldValue-min)/(max-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
通常只用90%的数据来训练分类器,剩余数据去测试分类器,获取正确/错误率。
def datingClassTest():
hoRatio = 0.10
datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
normMat, ranges, minVals = autoNorm(datingDataMat)
m = normMat.shape[0]
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classi0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
print "the classifier came back with: %s, the real answer is: %s" % (classifierResult, datingLabels[i])
if (classifierResult != datingLabels[i]): errorCount += 1.0
print "the total error rate is: %f" % (errorCount/float(numTestVecs))
(1)收集数据
提供文字的文本文件,大小都为32*32,每个数字大概有200个样本
(2)准备数据,把数据转换为上文中分类器可用的一维vector向量,从32*32变为1*24
def img2vector(filename):
returnVect = zeros((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 = 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 = 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 answer is: %d" % (classifierResult, classNumStr)
if (classifierResult != classNumStr): errorCount += 1.0
print "\nthe total number of errors is: %d" % errorCount
print "\nthe total error rate is: %f" % (errorCount/float(mTest))