今天在网上买的《机器学习实战》这本书到了,以前学习的都是理论知识,很多算法没有实现过。理论终究要落实到实践中去,以后应该理论知识和代码实现一起学习。
KNN算法的工作原理:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。
2018.1.24本人python3.6版本,在学习过程,借鉴和学习了https://www.cnblogs.com/vrfighters/articles/4715527.html中的内容。对代码进行了一定自己的理解和修改。
2018.1.25在昨天基础上,对KNN算法进行了实际应用的学习。借鉴和学习了https://www.zhihu.com/question/37146648中的内容。其中遇到了中文显示不正常的问题,在http://blog.youkuaiyun.com/ac540101928/article/details/51273291中得到了解决。
代码如下:
from numpy import *
import operator #运算符模块。
import matplotlib
import matplotlib.pyplot as plt
import os
#创建数据集
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
#算法核心
#inX:用于分类的输入向量。即将对其进行分类。
#dataSet:训练样本集
#labels:标签向量
def classfy0(inX,dataSet,labels,k): #距离计算
dataSetSize =dataSet.shape[0]#得到数组的行数。即知道有几个训练数据,第一维用1,为列数,第二维用0,为行的数。可理解为0表示列有多大,1表示行有多大
diffMat =tile(inX,(dataSetSize,1))-dataSet#tile:numpy中的函数。tile将原来的一个数组,扩充成了4个一样的数组。diffMat得到了目标与训练数值之间的差值。
sqDiffMat =diffMat**2#各个元素分别平方
sqDistances =sqDiffMat.sum(axis=1)#对应行相加,axis=1表示对应行相加。axis=0表示列相加。一维数组是一个列元素阵列
distances =sqDistances**0.5#开方,得到距离。
sortedDistIndicies=distances.argsort()#升序排列,得到的sortedDistIndicies是按照距离大小,得到的是原数组中的下标
classCount={}
for i in range(k):
voteIlabel=labels[sortedDistIndicies[i]] #根据下标,找到原来的分类集
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1#如果没有这个分类集,则返回0,有则返回键值并且加1
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) #.item将字典变为元组,key按照元组中第二个元素进行比较。reverse是降序排列。
return sortedClassCount[0][0]
def file2matrix(filename): #读取数据
fr = open(filename)
numberOfLines = len(fr.readlines()) #得到了数据的行数
returnMat = zeros((numberOfLines,3)) #zeros函数,得到numberOfLines行,3列的矩阵
classLabelVector = []
fr = open(filename)
index = 0
for line in fr.readlines():
line = line.strip() #除去行末尾的空格
listFromLine = line.split('\t') #以空格分隔
returnMat[index,:] = listFromLine[0:3]#[index,:]表示第index行的所有数据赋值,其实就是三个数据
classLabelVector.append(int(listFromLine[-1])) #将listFromLine最后一个元素存储
index += 1
return returnMat,classLabelVector
def figure(): #散点图
a,b=file2matrix("datingTestSet2.txt")
fig=plt.figure(figsize=(10,8))
ax1=fig.add_subplot(211) #分割成2行1列第一块。add_subplot(n,m,p),将画布分割成n行m列放在第p块
type1_x = []
type1_y = []
type2_x = []
type2_y = []
type3_x = []
type3_y = []
for i in range(len(b)):
if b[i] == 1: # 不喜欢
type1_x.append(a[i][1])
type1_y.append(a[i][2])
if b[i] == 2: # 魅力一般
type2_x.append(a[i][1])
type2_y.append(a[i][2])
if b[i] == 3: # 极具魅力
type3_x.append(a[i][1])
type3_y.append(a[i][2])
type1 = ax1.scatter(type1_x, type1_y, s=20, c='red')
type2 = ax1.scatter(type2_x, type2_y, s=40, c='green')
type3 = ax1.scatter(type3_x, type3_y, s=50, c='blue')
plt.xlabel('玩视频游戏所消耗的事件百分比')
plt.ylabel('每周所消费的冰淇淋公升数')
ax1.legend((type1, type2, type3), ('1', '2', '3'))
ax2=fig.add_subplot(212) #分割成2行1列第2块。add_subplot(n,m,p),将画布分割成n行m列放在第p块
type1_x = []
type1_y = []
type2_x = []
type2_y = []
type3_x = []
type3_y = []
for i in range(len(b)):
if b[i] == 1: # 不喜欢
type1_x.append(a[i][0])
type1_y.append(a[i][1])
if b[i] == 2: # 魅力一般
type2_x.append(a[i][0])
type2_y.append(a[i][1])
if b[i] == 3: # 极具魅力
type3_x.append(a[i][0])
type3_y.append(a[i][1])
type1 = ax2.scatter(type1_x, type1_y, s=20, c='red')
type2 = ax2.scatter(type2_x, type2_y, s=40, c='green')
type3 = ax2.scatter(type3_x, type3_y, s=50, c='blue')
plt.xlabel('每年获得的飞行常客里程数')
plt.ylabel('玩视频游戏所消耗的事件百分比')
ax2.legend((type1, type2, type3), ('不喜欢', '魅力一般', '极具魅力'))
plt.show()
def autoNorm(dataSet): #归一化处理,dataSet为一个1000*3的矩阵
minVals = dataSet.min(0) #选取每一列中的最小值,0为列,1为行,1*3的矩阵
maxVals = dataSet.max(0) #选取每一列中的最大值,1*3的矩阵
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet)) #zeros(1000,3),得到1000*3的全0矩阵
m = dataSet.shape[0] #m=行数
normDataSet = dataSet - tile(minVals, (m,1)) #扩充成和dataset相同行数。
normDataSet = normDataSet/tile(ranges, (m,1)) #每一行都除以最大和最小的差值。
return normDataSet, ranges, minVals
#tile(array,(m,n)).array是一维数组,则将扩充为m行和n倍。二维的则复制m行,每行的数组都复制n倍。按原来的排列。具体看https://www.cnblogs.com/zibu1234/p/4210521.html
def datingClassTest():
hoRatio = 0.10 #设定10%的数据同于测试
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') #加载数据
normMat, ranges, minVals = autoNorm(datingDataMat) #归一化数据处理
m = normMat.shape[0] #得到数据的量
numTestVecs = int(m*hoRatio) #因为数组里面的[]是Int,所以要转换类型
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classfy0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3) #第一个参数是前10%的数据,第二个是后90%的数据,第三个是标签,第四个是取前三个最近的点。
print ("分类结果 %d, 实际结果: %d" % (classifierResult, datingLabels[i]))
if (classifierResult != datingLabels[i]):
errorCount += 1.0
print("Error!")
print ("总错误率: %f" % (errorCount/float(numTestVecs)))
def classifyPerson():
resultList=['不喜欢','有点魅力','很有魅力']
percentTats=float(input("玩视频游戏所消耗的事件百分比:"))
ffMiles=float(input("每年获得的飞行常客里程数:"))
iceCream=float(input("每周所消费的冰淇淋公升数:"))
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
normMat,ranges,minVals=autoNorm(datingDataMat)
inArr=array([ffMiles,percentTats,iceCream])
classifierResult=classfy0((inArr-minVals)/ranges,normMat,datingLabels,3)#第一个参数是归一化后的新数据
print("这个人给你的感觉:",resultList[classifierResult-1])
'''手写识别系统'''
def img2vector(filename):
returnVect = zeros((1,1024)) #形成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 = os.listdir('trainingDigits') #获取训练目录里文件的文件名
m = len(trainingFileList)
trainingMat = zeros((m,1024)) #扩充成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 =os.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 = classfy0(vectorUnderTest, trainingMat, hwLabels, 3)
print ("训练集分类: %d, 实际类别: %d" % (classifierResult, classNumStr))
if (classifierResult != classNumStr): errorCount += 1.0
print( "\n总错误数: %d" % errorCount)
print ("\n错误率: %f" % (errorCount/float(mTest)))