以下代码来自《机器学习实战》一书
代码由多个函数构成,每个函数封装一种功能。
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)))

练习①:
测试分类器
练习②:
查看文本转换之后的数据形式:
约会网站数据
测试归一化函数是否正常:
测试归一化函数
测试约会网站函数:约会网站测试
分类错误率
在线测试约会网站分类:
在线测试约会网站
练习③:
查看手写数字文件的转换:
测试文件转换函数
测试手写数字识别代码:
测试手写数字识别代码
分类错误率

用到的所有数据文件联系我获取。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐