机器学习实战--KNN

这本书的好就不多说的,其实如果不是因为机器学习那门学位课的作业是这个,我想我会错过这本书0.0

knn

优 点:精度高,对异常值不敏感,无数据输入假定
缺 点:计算复杂度高,空间复杂度高,无法给出数据的内在含义
使用数据范围:数值型和标称型

————————————————————————————-下面进入正题————————————————————————————-

kNN.py

1
2
3
from numpy import *  
import operator
from os import listdir

createDataSet()

1
2
3
4
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

classify0()

inX用于分类的输入向量,是一个向量
dataSet输入的训练样本集,是一个矩阵
labels标签向量
k用于选择最近邻居的数目
labels数目与dataSet的行数相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def classify0(inX, dataSet, labels, k):
dataSetSize = dataSet.shape[0] #返回的是dataSet的行数,行数就是样本的数量
diffMat = tile(inX, (dataSetSize,1)) - dataSet #矩阵相减
#inX是个向量,而dataset是个矩阵,两者之间要进行相减的运算,需要把这个向量也补成一个和dataset有相同行数列数的矩阵,
#tile()的第二个参数,就是(datasetsize,1),这个参数的意思就是把inX补成有datasetsize行数的矩阵。
#假如inX是(1,2),datasetsize =3,那么经过tile()转换后产生了一个这样的矩阵([1,2],[1,2],[1,2])
sqDiffMat = diffMat**2 #平方
sqDistances = sqDiffMat.sum(axis=1) #按行求和
# sqdiffMat是([1,2],[0,1],[3,4]),axis这个参数影响了对矩阵求和时候的顺序,axis=0是按照列求和,结果为([3.1.7])
# axis=1是按照行进行求和,结果是([4,7])。
distances = sqDistances**0.5 #开方,得到欧氏距离
sortedDistIndicies = distances.argsort() #把向量中每个元素进行排序,结果是元素的索引形成的向量
#例子distance([1,4,3]),经过distance.argsort()之后的结果是([0,2,1]
classCount={} #存放最终的分类结果及相应的结果投票数
#投票过程,就是统计前k个最近的样本所属类别包含的样本个数
for i in range(k):
# index = sortedDistIndicies[i]是第i个最相近的元素索引,即样本下标
# voteIlabel = labels[index]是样本index对应的分类结果('A' or 'B')
voteIlabel = labels[sortedDistIndicies[i]]
# classCount.get(voteIlabel, 0)返回voteIlabel的值,如果不存在,则返回0
# 然后将票数增1
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
# 把分类结果进行排序,然后返回得票数最多的分类结果
# key=operator.itemgetter(1)的意思是按照字典里的第一个排序
#例子a = [1, 2, 3],b = operator.itemgetter(1),b(a)返回为2
#b = operator.itemgetter(1, 0),b(a),定义函数b,获取对象的第1个域和第0个的值,返回 (2, 1)
# reverse=True是降序排序
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0] #返回类别最多的类别

file2matrix()

将文本记录转换为NumPy
将文本记录转换为NumPy的解析程序
输入为矩阵,输出为训练样本矩阵和类标签向量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def file2matrix(filename):
fr = open(filename) #打开文档
numberOfLines = len(fr.readlines()) #得到文件行数
#fr.readlines()读取行数,存在数组中,导入后每行中用\t隔开,两行之间用\n换行得到文件行数
returnMat = zeros((numberOfLines,3)) #创建返回NumPy矩阵,numberoflines行,3列的初始化零的矩阵
classLabelVector = []#定义一个空的数组
fr = open(filename)
index = 0
for line in fr.readlines():
line = line.strip() #删除()中的内容,这里表示删除空格
listFromLine = line.split('\t')#以\t分割
#print(listFromLine)
returnMat[index,:] = listFromLine[0:3]#把每行前三个元素存入returnMat矩阵中,每行中存储三个
classLabelVector.append(int(listFromLine[-1]))#存储第四列元素即标签,在数组中append添加,-1表示最后一列
index += 1
return returnMat,classLabelVector

autoNorm()

归一化数值,避免某特征值过大,使得权重比例不均匀,对计算结果产生影响。
autoNorm可以自动将数字特征值转化为0到1区间

1
2
3
4
5
6
7
8
9
10
11
12
13
def autoNorm(dataSet):
minVals = dataSet.min(0)#一维数组,值为各项特征(列)中的最小值。参数0使得函数从列中选取最小值
#print(minVals)
maxVals = dataSet.max(0)
#print(maxVals)
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet)) #创建与样本集一样大小的零矩阵
#print(normDataSet)
m = dataSet.shape[0]#dataSet的行数
normDataSet = dataSet - tile(minVals, (m,1))#矩阵中所有的值减去最小值
#tile将原来的一个数组minVals,扩充成了m行1列的数组
normDataSet = normDataSet/tile(ranges, (m,1)) #矩阵中所有的值除以最大取值范围进行归一化
return normDataSet, ranges, minVals

datingClassTest()

测试算法,样本集中百分之九十的数据用来训练样本,百分之十的样本用来测试分类器kNN.classify0()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def datingClassTest():
hoRatio = 0.10 #百分之十的数据用于测试分类器,更改该变量的值可更改参加测试分类器的数据量
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') #导入数据
normMat, ranges, minVals = autoNorm(datingDataMat) #归一化数值
m = normMat.shape[0] #得到总行数
numTestVecs = int(m*hoRatio) #测试总数据数量,m*hoRatio是一个浮点型,需转化成整形
errorCount = 0.0 #初试错误率为0
for i in range(numTestVecs):
classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
#分类器(需要测试的向量,训练样本集(90%),标签集合,K)
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))) #错误率
print(errorCount)

classifyPerson()

约会数据,对于未来的约会预测函数,输入飞行里程数,玩视频游戏的百分比和冰激凌公升数,可以得到一个是否对他感兴趣的预测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def classifyPerson():
resultList=['not at all','in samll doses','in large doses'] #三种感兴趣程度
percentTats=float(input("percentage of time spent playing video games?"))
ffMiles=floats=float(input("frequent flier miles earned per year?"))
iceCream=float(input("liters of ice cream consuned per year?"))#input键盘输入
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt') # 导入数据
normMat,ranges,minvals=autoNorm(datingDataMat) # 归一化,ranges是归一化的分母
inArr=array([ffMiles,percentTats,iceCream]) # inArr是归一化之前的datingDataMat数组中的行
classifierResult=classify0((inArr-minvals)/ranges,normMat,datingLabels,3)#先归一化,然后调用分类函数
#print(classifierResult)
print("you will probably like this person:%s"%resultList[classifierResult-1])
```

## img2vector()
> 图片转向量
手写体:32*32的黑白图像
图片转向量,将32*32的二进制图像矩阵转换为1*1024的向量

```python
def img2vector(filename):
returnVect = zeros((1,1024))
fr = open(filename)
for i in range(32):#循环读出文件的前32行
lineStr = fr.readline()
for j in range(32):#将每行的前32个字符存储在NumPy数组中
returnVect[0,32*i+j] = int(lineStr[j])
return returnVect#返回数组

handwritingClassTest()

手写体测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def handwritingClassTest():
hwLabels = []
trainingFileList = listdir('trainingDigits') #导入训练数据
#print(trainingFileList)
m = len(trainingFileList) #训练数据的总数
#print(m)
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) #将对应数据集下的文件一个个的转为向量
#print(trainingMat[i,:])
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) #利用训练的trainingMat测试
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)))

knn_main.py

1
2
3
4
5
6
7
8
9
10
11
import kNN  
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from imp import reload


group,labels=kNN.createDataSet()
#print(group)
#print(labels)
#print(kNN.classify0([0,0],group,labels,3))

散点图

1
2
3
4
5
6
7
8
9
fig=plt.figure()                #建立画板  
ax=fig.add_subplot(111) #添加一个子图,一行一列第一个子块,若括号内为349,则三行四列第9个子块
reload(kNN)
datingDataMat,datingLabels=kNN.file2matrix('datingTestSet2.txt')
#print(datingDataMat)
#ax.scatter(datingDataMat[:,1],datingDataMat[:,2]) # scatter绘制散点图,使用第二列第三列数据
#ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*np.array(datingLabels),15.0*np.array(datingLabels))
ax.scatter(datingDataMat[:,0],datingDataMat[:,1],15.0*np.array(datingLabels),15.0*np.array(datingLabels))
#plt.show()

归一化数值

1
2
3
4
normMat,ranges,minVals=kNN.autoNorm(datingDataMat)  
#print(normMat)
#print(ranges)
#print(minVals)

测试算法

1
#kNN.datingClassTest()

约会预测

1
2
3
#对于未来的约会预测函数,输入飞行里程数,玩视频游戏的百分比和冰激凌公升数,可以得到一个是否对他感兴趣的预测,  
#输入10 10000 0.5
#kNN.classifyPerson()

手写体

1
2
3
4
5
6
7
#trainingDigits包含大约2000个例子,每个数字约有200个样本  
#testDigits包含大约900个测试数据
testVector=kNN.img2vector('trainingDigits/0_13.txt')
#print(testVector[0,0:31])
#print(testVector[0,32:63])
#print(testVector[0,64:95])
kNN.handwritingClassTest()
-------------本文结束感谢您的阅读-------------
AmberWu wechat
欢迎大家扫码交流!