以下代码来自《机器学习实战》一书
代码由多个函数构成,每个函数封装一种功能。
classify0():分类器函数,实现KNN分类功能
creatDataSet():创建数据,用来测试分类器是否可以分类
file2matrix():将文本文件的内容转存到数组中
autoNorm(dataSet):用于对特征值的归一化
datingClassTest():针对约会网站的测试函数
classifyPreson():约会网站的预测函数,可以通过用户输入特征值进行在线预测
img2vector(filename):读取图片文件(手写数字的图片,已经转换成了文本存储),这里将3232的文本转存到11024的数组中
handwritingClassTest():用于手写数字的测试函数
本代码除了对KNN算法进行练习,还展示了如何处理文本文件数据,将数据处理成分类器能够接收的格式。
KNN算法练习分为三个部分:
①创建简单的数据对分类器功能进行测试,用到的函数有:creatDataSet()、classify0()
②对约会网站数据进行分类测试,用到的函数有:file2matrix()、autoNorm(dataSet)、datingClassTest()、classifyPreson()、classify0()
③对手写数字数据进行分类测试,用到的函数有:img2vector(filename)、handwritingClassTest()、classify0()
from numpy import *
import operator
from os import listdir #函数listdir可以列出给定目录下的文件名
def creatDataSet():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]) #数组
labels = ['A','A','B','B'] #列表
return group, labels
#k-近邻算法
def classify0(inX, dataSet, labels, k):
dataSetSize = dataSet.shape[0]
diffMat = tile(inX, (dataSetSize,1)) - dataSet #把inX按照(dataSetSize,1)重复,结果是个个数组 为了实现预测样本和训练样本元素对应相减
sqDiffMat = diffMat**2 #元素平方
sqDistances = sqDiffMat.sum(axis=1) #按行求和
distances = sqDistances**0.5 #开平方
sortedDistIndicies = distances.argsort() #从小到达排序返回索引值
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]] #取出前k个索引值对应的类别
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 #存入字典,重复的+1
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #对选出的k个类别进行频率的排序
return sortedClassCount[0][0]
#将文本记录转换为NimPy的解析程序
def file2matrix(filename):
fr = open(filename) #打开文件
arrayOLines = fr.readlines() #按行读取到列表中,每一行是一个元素。每次按行读取整个文件内容,将读取到的内容放到一个列表中,返回list类型。
numberOfLines = len(arrayOLines) #样本数
returnMat = zeros((numberOfLines,3)) #创建二维数组,并用0填充。这里用0填充之后,下面赋值时,会自动把数据存为整型(个人理解)
classLabelVector = [] #存放标签
index = 0
for line in arrayOLines: #遍历数据的每一行
line = line.strip() #去掉回车符
listFormLine = line.split('\t') #以tab字符把每一行分割成一个元素列表
returnMat[index,:] = listFormLine[0:3] #把数据存放到returnMat中,一行一行存,前三列为特征
labels = {'didntLike':1,'smallDoses':2,'largeDoses':3} #每一类别对应一个整数
classLabelVector.append(labels[listFormLine[-1]]) #取出最后一列的元素,并将其对应的值存入classLabelVector中
index += 1 #处理下一行数据
return returnMat,classLabelVector
#归一化特征值
def autoNorm(dataSet):
minVals = dataSet.min(0) #取出每一列的最小值,0代表从列中选取
maxVals = dataSet.max(0) #取出每一列的最大值
ranges = maxVals - minVals #最大值减去最小值
normDataSet = zeros(shape(dataSet)) #创建与原始数据同样大小的数组
m = dataSet.shape[0] #样本数
normDataSet = dataSet - tile(minVals, (m,1)) #各个特征值减去最小值,这里扩充minVals(1*3)方便做减法
normDataSet = normDataSet/tile(ranges, (m,1)) #各个特征值除去取值范围
return normDataSet, ranges, minVals
#分类器针对约会网站的测试代码
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 = classify0(normMat[i,:],normMat[numTestVecs:m,:] #normMat[i,:]取出第i个测试样本,normMat[numTestVecs:m,:]取出划分测试集剩下的训练集样本
,datingLabels[numTestVecs:m],3) #datingLabels[numTestVecs:m]训练集的标签,numTestVecs:m代表在这之间所有的行
print("分类器返回结果:%d, 真正的结果:%d" % (classifierResult, datingLabels[i]))
if (classifierResult != datingLabels[i]):
errorCount += 1.0 #记录错误分类的数量
print("分类总计错误率为:%f" % (errorCount/float(numTestVecs)))
#约会网站预测函数
def classifyPreson():
resultList = ['不喜欢的人', '魅力一般的人', '极具魅力的人']
percenTats = float(input("打游戏所花费时间的比例?")) #input()其接收任意任性输入,将所有输入默认为字符串处理,并返回字符串类型。
ffMiles = float(input("每年获得的飞行常客里程数?"))
iceCream = float(input("每周消费的冰淇淋的公升数?"))
datingDataMat, datingLabels = file2matrix('datingTestSet.txt') #读取数据
normMat, ranges, minVals = autoNorm(datingDataMat) #归一化数据
inArr = array([percenTats, ffMiles, iceCream]) #输入的样本数组
classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels,3) #(inArr-minVals)/ranges对输入的数据归一化
print("这个人对你来说可能是:", resultList[classifierResult-1])
#读取图片文本文件
def img2vector(filename):
returnVect = zeros((1,1024)) #创建1*1024的数组存放数据
fr = open(filename) #打开文件
for i in range(32): #控制行
lineStr = fr.readline() #每次只读取文件的一行,通常是把读取到的一行内容放到一个字符串变量中,返回str类型。
#.read() 每次读取整个文件,它通常将读取到底文件内容放到一个字符串变量中,也就是说 .read() 生成文件内容是一个字符串类型。
for j in range(32): #控制列
returnVect[0,32*i+j] = int(lineStr[j]) #将读取到的行数据一个个存放到数组中
return returnVect
#手写数字识别系统的测试代码
def handwritingClassTest():
hwLabels = [] #存放标签
trainingFileList = listdir('digits/trainingDigits') #列出给定目录下的文件名
m = len(trainingFileList) #训练样本的数量
trainingMat = zeros((m,1024)) #存放训练样本的矩阵
for i in range(1,m):
fileNameStr = trainingFileList[i] #读取文件名
fileStr = fileNameStr.split('.')[0] #以'.'分割字符串,并返回索引为0位置的字符串,此时的字符串为去掉后缀名的那部分
classNumStr = int(fileStr.split('_')[0]) #以'_'分割字符串,并返回索引为0位置的字符串,此时的字符串对应该样本文件的标签
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector('digits/trainingDigits/%s' % fileNameStr) #读取样本文件,存到数组中
testFileList = listdir('digits/testDigits') #列出给定目录下的文件名
errorCount = 0.0 #分类错误率
mTest = len(testFileList) #测试样本的数量
for i in range(1,mTest):
fileNameStr = testFileList[i] #读取文件名
fileStr = fileNameStr.split('.')[0] #以'.'分割字符串,并返回索引为0位置的字符串,此时的字符串为去掉后缀名的那部分
classNumStr = int(fileStr.split('_')[0]) #以'_'分割字符串,并返回索引为0位置的字符串,此时的字符串对应该样本文件的标签
vectorUnderTest = img2vector('digits/testDigits/%s' % fileNameStr) #读取测试样本文件
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) #把测试样本输入到分类器中进行分类,返回分类结果
print("分类器返回结果:%d, 真正的结果:%d" % (classifierResult, classNumStr))
if (classifierResult != classNumStr):
errorCount += 1.0 #分类错误就+1
print("总共错误的个数为:%d" % errorCount)
print("分类总计错误率为:%f" % (errorCount/float(mTest)))
练习①:
练习②:
查看文本转换之后的数据形式:
测试归一化函数是否正常:
测试约会网站函数:
在线测试约会网站分类:
练习③:
查看手写数字文件的转换:
测试手写数字识别代码:
用到的所有数据文件联系我获取。