本文为自己学习机器学习所记笔记,主要参考《统计学习方法》和《机器学习实战》两本书。
1.算法描述
输入:训练数据集 T={(x1,y1),(x2,y2)...(xN,yN)},xi为实例的特征向量,yi为类别(标签)
输出:实例x所属的类y
(1)根据给定的距离度量,在训练集T中找出与x最近的k个点
(2)在这k个点中根据分类决策规则(如多数表决),决定x的类别y
模型三个要素:距离度量,k值选择,分类决策规则。
优点:精度高,对异常值不敏感,无输入假定
缺点:计算复杂度高,空间复杂度高
2.kNN算法实现
# 分类函数,四个参数分别为:待分类特征向量x,训练数据集,标签,用于选取最近邻居的数目
def classify(inX,dataSet,labels,k):
dataSetSize=dataSet.shape[0] #获取样本数目
#计算待分类特征向量x与训练集中每一个样本的距离。此处为欧式距离
diffMat=tile(inX,(dataSetSize,1))-dataSet
#tile(a,(b,c)) 将a按行复制b次,按列复制c次
sqDiffMat=diffMat**2
sqDistances=sqDiffMat.sum(axis=1) #按行求和
distances=sqDistances**0.5
distIndicies=distances.argsort() #排序 返回从小到大的索引
classCount={} #建立一个字典,记录标签和出现的次数
for i in range(k):
voteILabel=labels[distIndicies[i]] #距离第i近的数据的标签
classCount[voteILabel]=classCount.get(voteILabel,0)+1
# 转化为元组,逆序排列
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0] #返回出现次数最大的标签
3.实例1:kNN用于约会网站配对
(1)从文本文件解析数据
文本文件如下,每一列分别为男嘉宾每年飞行里程数,玩游戏所占时间百分比,每周消耗冰淇淋公升数,最后一列为对男嘉宾喜欢程度。
def file2matrix(filename):
fr=open(filename)
numberOfLines=len(fr.readlines())
returnMat=zeros((numberOfLines,3))
labelsVec=[]
index=0
fr=open(filename)
for line in fr.readlines():
line=line.strip()
listFromLine=line.split('\t')
returnMat[index,:]=listFromLine[0:3]
labelsVec.append(int(listFromLine[-1]))
index+=1
fr.close()
return returnMat,labelsVec
用两个矩阵分别保存特征向量和标签。
(2)归一化特征值
由于不同特征值相差较大,需归一化至同一范围(0-1)
#归一化特征值
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)) # 对应元素相除。矩阵除法为linalg.solve(matA,matB)
return normDataset,ranges,minVals
(3)测试:
将带标签数据一部分作为训练数据,另一部分作为测试数据,测试分类器的错误率。
# 约会网站配对结果错误率测试
def datingClassTest():
ratio=0.5
dataset,labelsVec=file2matrix('D:\python\code\machinelearninginaction\Ch02\datingTestSet2.txt')
normData,ranges,minVals=autoNorm(dataset)
m=dataset.shape[0]
numTestVec=int(m*ratio)
errorCount=0
for i in range(numTestVec):
testResult=classify(normData[i,:],normData[numTestVec:m,:],labelsVec[numTestVec:m],3)
print('The result by classifier is:%s,the real result is:%s'%(testResult,labelsVec[i]))
if testResult!=labelsVec[i]:
errorCount+=1
print('the total error rate is:%f'%(errorCount/numTestVec))
#给定一个人的具体信息,预测喜欢程度
def datingPredict():
resultList=['not at all','in small doses','in large doses']
percentTats=float(input('percentage of time spent on video games:'))
ffMiles=float(input('frequent fliers miles earned per year: '))
iceCream=float(input('liters of icecream consumed per year:'))
inX=array([percentTats,ffMiles,iceCream])
dataSet,labelsVec=file2matrix('D:\python\code\machinelearninginaction\Ch02\datingTestSet2.txt')
normData,ranges,minVals=autoNorm(dataSet)
result=classify((inX-minVals)/ranges,normData,labelsVec,3)
print('You will probably like this person:',resultList[result-1])
4.实例2:kNN用于识别手写数字
所用数据集为32*32像素的黑白图片,已经处理为文本文件。如图
(1)将文本文件转换为测试向量。将原来32*32的图像用一个1024的数组来表示并作为特征向量
def img2vector(filename):
returnvec=zeros(1024)
fr=open(filename)
for i in range(32):
strLine=fr.readline()
for j in range(32):
returnvec[32*i+j]=strLine[j]
fr.close()
return returnvec
(2)准确率测试。
def handwritingClassTest():
hwLabels=[]
trainFlieList=listdir('D:\python\code\machinelearninginaction\Ch02\\trainingDigits') #获得该目录下的文件名的字符串列表形式
m=len(trainFlieList)
trainMat=zeros((m,1024))
for i in range(m):
fileNameStr=trainFlieList[i]
fileStr=fileNameStr.split('.')[0] #以‘.’分开,去掉后缀
classNum=int(fileStr.split('_')[0]) # 从文件名改获得数字标签
hwLabels.append(classNum)
trainMat[i,:]=img2vector('D:\python\code\machinelearninginaction\Ch02\\trainingDigits\%s'%fileNameStr)
testFileList=listdir('D:\python\code\machinelearninginaction\Ch02\\testDigits')
mTest=len(testFileList)
errorCount=0
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0] # 以‘.’分开,去掉后缀
classNum = int(fileStr.split('_')[0]) # 从文件名改获得数字标签
testVev=img2vector('D:\python\code\machinelearninginaction\Ch02\\testDigits\%s'%fileNameStr)
classifierResult=classify(testVev,trainMat,hwLabels,3)
print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNum))
if (classifierResult != classNum):
errorCount += 1.0
print('\nthe total error rate is:%f'%(errorCount/mTest))
文章所用数据集:
https://github.com/pbharrin/machinelearninginaction/tree/master/Ch02