• Python版本: Python3.x
  • 运行平台: Windows
  • IDE: PyCharm
  • 参考资料:《机器学习》(西瓜书)《机器学习实战》(王斌)
  • 转载请标明出处
  • 数据下载,提取码:tmu6

    目录

    • 一、前言
    • 二、什么叫K-近邻算法(KNN)
      • 优缺点
    • 三、简单实例
      • 实例中理解理论部分
      • 实例
    • 四、实例---海伦约会
      • 背景介绍
        • 1.收集数据
        • 2.准备数据
        • 3.分析数据
        • 4.编写分类器
        • 5.测试算法
        • 6.使用算法
    • 五、实例---手写数字识别
      • 补充呀!!!
      • 正题开始---手写数字识别(sklearn模块实践)
        • 1.收集数据
        • 2.准备数据
        • 3.sklearn中的KNN学习
        • 4.测试算法
        • 5.使用算法
    • 六、总结

一、前言

这章本来应该更新线性模型部分,但一看笔记,内容太少了,没必要特意的更新一章,而且以后的实战会用到这章的知识,在实战中讲解这些不更好吗?( ̄▽ ̄)-----------------------------------好吧!我承认是我懒了(✿◕‿◕✿)。
K-近邻算法开始吧!开始吧!

二、什么叫K-近邻算法(KNN)

简单地说,k近邻算法采⽤测量不同特征值之间的距离⽅法进⾏分类。⼯作原理是:存在⼀个样本数据集合, 也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每⼀数据与所属分类的对应关系。输⼊没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进⾏⽐较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。⼀般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不⼤于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

优缺点

  • 优点:精度⾼、对异常值不敏感、⽆数据输⼊假定。
  • 缺点:计算复杂度⾼、空间复杂度⾼。
  • 适⽤数据范围:数值型和标称型。

三、简单实例

实例中理解理论部分

众所周知,电影可以按照题材分类,然⽽题材本⾝是如何定义的?由谁来判定某部电影属于哪个题材?也就是说同⼀题材的电影具有哪些公共特征?这些都是在进⾏电影分类时必须要考虑的问题。没有哪个电影⼈会说⾃⼰制作的电影和以前的某部电影类似,但我们确实知道每部电影在风格上的确有可能会和同题材的 电影相近。那么动作⽚具有哪些共有特征,使得动作⽚之间⾮常类似,⽽与爱情⽚存在着明显的差别呢? 动作⽚中也会存在接吻镜头,爱情⽚中也会存在打⽃场景,我们不能单纯依靠是否存在打⽃或者亲吻来判断影⽚的类型。但是爱情⽚中的亲吻镜头更多,动作⽚中的打⽃场景也更频繁,基于此类场景在某部电影中出现的次数可以⽤来进⾏电影分类。本节我们基于电影中出现的亲吻、打⽃出现的次数,使⽤k近邻 算法构造程序,⾃动划分电影的题材类型。
有⼈曾经统计过很多电影的打⽃镜头和接吻镜头,下图显⽰了6部电影的打⽃和接吻镜头数。假如有⼀部未看过的电影,如何确定它是爱情⽚还是动作⽚呢?我们可以使⽤KNN来解决这个问题。

⾸先我们需要知道这个未知电影存在多少个打⽃镜头和接吻镜头。问号位置是该未知电影出现的镜头数图形化展⽰。详看下图。

那么未知电影到它们个点的距离怎么算呢?这个电影分类的例子有2个特征,可以用2维实数向量空间,我们高中学过的两点距离公式计算距离,如下。

计算得到已知电影与未知电影的距离。

如果是多个特征的情况呢?不单单只是接吻数和打斗数。它们的距离该如何计算?这时我们就可以用欧氏距离(也称欧几里德度量),它和二维距离的求法具有相似之处,公式如下。

理论说完了,那么下面开始代码实践一下吧!

实例

这⾥⾸先给出k近邻算法的伪代码,方便我们理解。
对未知类别属性的数据集中的每个点依次执⾏以下操作:

  1. 计算已知类别数据集中的点与当前点之间的距离;
  2. 按照距离递增次序排序;
  3. 选取与当前点距离最⼩的k个点;
  4. 确定前k个点所在类别的出现频率;
  5. 返回前k个点出现频率最⾼的类别作为当前点的预测分类。

第一步,导入相关库

"""
简单的KNN分类,分类器
电影的简单分类
@Author:Yuuuuu、Tian
Tue Feb 11 10:10:21 2020
"""
import numpy as np
import operator   #运算包
import matplotlib.pyplot as plt

第二步,准备数据。

#准备数据
def createDataset():group = np.array([[1,101],[5,89],[7,100],[108,5],[115,8],[102,6]])   #特征labels = ["爱情片","爱情片","爱情片","动作片","动作片","动作片"]  #标签return group,labels
#创建数据集
group,labels = createDataset()
#查看一下数据
print(group)
print(labels)

数据准备完了,看一下自己写的是否正确。(就是自己看着图片写了个电影的大概位置)
结果:

接下来,就到了K邻接算法的主体代码啦。注释写的很详细,我自己测试的东西也没删,认真看吧!

def classify0(inX,dataSet,labels,k):#求出dataSet多少行dataSetSize = dataSet.shape[0]#数据和测试的依次相减diffMat = dataSet - inX    #测试的数据做运算会自动变为与dataSet同样大小的数据#计算距离,平方相加,再开方distances = ((diffMat ** 2).sum(axis = 1)) ** 0.5#从小到大排序,返回索引,argsort()排序,argsort(x)从小到大,argsort(-x)从大到小sortedDistances = distances.argsort()#一个记录类别数量的字典classCount = {}for i in range(k):#取出前k个元素的类别,K近邻votaIlabel = labels[sortedDistances[i]]# dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。# 计算类别次数classCount[votaIlabel] = classCount.get(votaIlabel,0) + 1    #一开始空字典,初始化get(votaIlabel,0)没有返回0# python3中用items()替换python2中的iteritems()# key=operator.itemgetter(1)根据字典的值进行排序# key=operator.itemgetter(0)根据字典的键进行排序# reverse降序排序字典#按分类个数从大到小排列sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse=True)# 返回次数最多的类别,即所要分类的类别#return sortedClassCount[0]return sortedClassCount[0][0]

代码打完了,测试一下。

if __name__ == '__main__':#测试集test = [100,5]   #通过坐标可知这是动作片,看咱们自写的分类器可以分出来吗?#kNN分类test_class = classify0(test, group, labels, 3)#打印分类结果print(test_class)

结果:

这样,咱们的一个简单的分类器就写完了,你可能会问:“分类器何种情况下会出错?”或者“答案是否总是正确的?”答案是否定的,分类器并不会得到百分百正确的结果,我们可以使⽤多种⽅法检测分类器的正确率。此外分类器的性能也会受到多种因素的影响,如分类器设置和数据集等。不同的算法在不同数据集上的表现可能完全不同。
为了测试分类器的效果,我们可以使⽤已知答案的数据,当然答案不能告诉分类器,检验分类器给出的结果是否符合预期结果。通过⼤量的测试数据,我们可以得到分类器的错误率——分类器给出错误结果的次数除以测试执⾏的总数。
上面我们介绍的例⼦已经可以正常运转了,但是并没有太⼤的实际⽤处,下面将在现实世界中使⽤k近邻算法。让我们开始吧!

四、实例—海伦约会

背景介绍

我的朋友海伦⼀直使⽤在线约会⽹站寻找适合⾃⼰的约会对象。尽管约会⽹站会推荐不同的⼈选,但她没有从中找到喜欢的⼈。经过⼀番总结,她发现曾交往 过三种类型的⼈:

  • 不喜欢的⼈
  • 魅⼒⼀般的⼈
  • 极具魅⼒的⼈

尽管发现了上述规律,但海伦依然⽆法将约会⽹站推荐的匹配对象归⼊恰当的类别。她觉得可以在周⼀到周五约会那些魅⼒⼀般的⼈,⽽周末则更喜欢与那些极具魅⼒的⼈为伴。海伦希望我们的分类软件可以更 好地帮助她将匹配对象划分到确切的分类中。此外海伦还收集了⼀些约会⽹站未曾记录的数据信息,她认为这些数据更有助于匹配对象的归类。
这里,我们也要先写出我们的流程。

  • 收集数据:提供⽂本⽂件。
  • 准备数据:使⽤Python解析⽂本⽂件。
  • 分析数据:使⽤Matplotlib画⼆维扩散图。
  • 训练算法:此步骤不适⽤于k近邻算法。
  • 测试算法:使⽤海伦提供的部分数据作为测试样本。 测试样本和⾮测试样本的区别在于:测试样本是 已经完成分类的数据,如果预测分类与实际类别不同,则标记为⼀个错误。
  • 使⽤算法:产⽣简单的命令⾏程序,然后海伦可 以输⼊⼀些特征数据以判断对⽅是否为⾃⼰喜欢的类型。
    流程有了,开始实现吧!

1.收集数据

数据可以在我的置顶处下载。

2.准备数据

海伦收集约会数据已经有了⼀段时间,她把这些数据存放在⽂本⽂件datingTestSet.txt中,每个样本数据占据⼀⾏,总共有1000⾏(约会了1000次可还行,U•ェ•*U分我一次也好啊U•ェ•*U)。海伦的样本主要包含以下3 种特征:

  • 每年获得的飞⾏常客⾥程数
  • 玩视频游戏所耗时间百分⽐
  • 每周消费的冰琪淋公升数

首先导入相关库

"""
简单的KNN分类,分类器
海伦约会网站的配对
@Author:Yuuuuu、Tian
Tue Feb 11 10:10:21 2020
"""
import numpy as np
from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import matplotlib.pyplot as plt
import operator

数据处理,这步需要打开文件,提取文件中的特征放入特征矩阵,标签放入标签矩阵,并对标签中的中文类别进行数据化的操作,为了后续操作方便。

def file2matrix(filename):"""函数说明:打开并解析文件,对数据进行分类:1代表不喜欢,2代表魅力一般,3代表极具魅力Parameters:filename - 文件名Returns:returnMat - 特征矩阵classLabelVector - 分类Label向量"""#打开文件fr = open(filename)#读取文件内容arrayOLines = fr.readlines()#获取行数numberOLines = len(arrayOLines)#返回的Numpy矩阵,解析完成的数据;numberOLines行,3列returnMat = np.zeros((numberOLines,3))#返回的分类的标签向量classLableVector = []#行索引index = 0for line in arrayOLines:#s.strip(rm),当rm空时,默认删除空白符(包括'\n','\r','\t',' '),删除首尾指定元素,中间的无影响#主要为删除回车line = line.strip()#使用s.split(str="",num=string,cout(str))将字符串根据'\t'分隔符进行切片。listFromLine = line.split('\t')#将数据前三列提取出来,存放到returnMat的NumPy矩阵中,也就是特征矩阵。returnMat[index,:] = listFromLine[0:3]    #0-3列,依照行放入特征矩阵if listFromLine[-1] == 'didntLike':       #标签放入标签矩阵classLableVector.append(1)elif listFromLine[-1] == 'smallDoses':classLableVector.append(2)elif listFromLine[-1] == 'largeDoses':classLableVector.append(3)index += 1return returnMat, classLableVector    #返回特征矩阵,标签矩阵
#-----测试----------------------------------------------------------
if __name__ == '__main__':#打开文件名filename = "0402Dataset/datingTestSet.txt"returnMat, classLableVector = file2matrix(filename)  print(returnMat)  #特征矩阵print(classLableVector)  #标签

查看一下我们是否将数据放入该放的矩阵内了。
结果:

3.分析数据

我们要⽤Matplotlib制作原始数据的散点图,因为用图像可以直观的看出各数据的关系。我们有三个特征,二维的坐标轴是无法画出他们的关系的,所以我们两两特征画图。共三个图。

#将数据可视化
def showdatas(datingDataMat,datingLabels):#设置汉字格式font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)#将fig画布分隔成1行1列,不共享x轴和y轴,fig画布的大小为(13,8)#当nrow=2,nclos=2时,代表fig画布被分为四个区域,axs[0][0]表示第一行第一个区域#创建画布和子图fig, axs = plt.subplots(nrows = 2,ncols = 2,sharex = False,figsize = (13, 8))numberOfLabels = len(datingLabels)   #获取样本数LabelsColors = []   #创建颜色标签for i in datingLabels:if i == 1:LabelsColors.append('black')elif i == 2:LabelsColors.append('orange')elif i == 3:LabelsColors.append('red')    #不同分类不同颜色#画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第二列(玩游戏)数据画散点数据,#散点大小为15,透明度为0.5#分成了两行两列,在[0][0]位置作此图。以datingDataMat矩阵的第一(飞行常客例程)、第二列(玩游戏占比)数据画散点数据,axs[0][0].scatter(x = datingDataMat[:,0],y=datingDataMat[:,1],color = LabelsColors,s = 15,alpha= 0.5)#设置标题,行纵坐标axs0_title_text = axs[0][0].set_title('每年获得的飞行常客里程数与玩视频游戏所消耗时间占比', FontProperties=font)axs0_xlabel_text = axs[0][0].set_xlabel('每年获得的飞行常客里程数', FontProperties=font)axs0_ylabel_text = axs[0][0].set_ylabel('玩视频游戏所消耗时间占', FontProperties=font)plt.setp(axs0_title_text, size=10, weight='bold', color='red')plt.setp(axs0_xlabel_text, size=10, weight='bold', color='black')plt.setp(axs0_ylabel_text, size=10, weight='bold', color='black')#画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第三列(消费冰淇凌)数据画散点数据。#散点大小为15,透明度为0.5axs[0][1].scatter(x = datingDataMat[:,0],y=datingDataMat[:,2],color = LabelsColors,s = 15,alpha= 0.5)#设置标题,行纵坐标axs0_title_text = axs[0][1].set_title('每年获得的飞行常客里程数与消费冰淇凌占比', FontProperties=font)axs0_xlabel_text = axs[0][1].set_xlabel('每年获得的飞行常客里程数', FontProperties=font)axs0_ylabel_text = axs[0][1].set_ylabel('消费冰淇凌', FontProperties=font)plt.setp(axs0_title_text, size=10, weight='bold', color='red')plt.setp(axs0_xlabel_text, size=10, weight='bold', color='black')plt.setp(axs0_ylabel_text, size=10, weight='bold', color='black')# 画出散点图,以datingDataMat矩阵的第二列(玩游戏)、第三列(消费冰淇凌)数据画散点数据,# 散点大小为15,透明度为0.5axs[1][0].scatter(x=datingDataMat[:, 1], y=datingDataMat[:, 2], color=LabelsColors, s=15, alpha=0.5)# 设置标题,行纵坐标axs0_title_text = axs[1][0].set_title('每年玩视频游戏所消耗时间占比与消费冰淇凌占比', FontProperties=font)axs0_xlabel_text = axs[1][0].set_xlabel('每年玩视频游戏所消耗时间占比', FontProperties=font)axs0_ylabel_text = axs[1][0].set_ylabel('消费冰淇凌', FontProperties=font)plt.setp(axs0_title_text, size=10, weight='bold', color='red')plt.setp(axs0_xlabel_text, size=10, weight='bold', color='black')plt.setp(axs0_ylabel_text, size=10, weight='bold', color='black')plt.show()
#-----测试------------------------------------------------------------
if __name__ == '__main__':#打开的文件名filename = "0402Dataset/datingTestSet.txt"#打开并处理数据datingDataMat, datingLabels = file2matrix(filename)showdatas(datingDataMat, datingLabels)

结果:

由图可以直观的看出海伦喜欢那类人,但在细细的分析一下数据,有的数据的取值在2以下,有的则已经飙到了100000。举个例子看一下距离公式,随机找两个样本如下。

容易发现,上⾯⽅程中数字差值最⼤的属性对计算结果的影响最⼤,也就是说,每年获取的飞⾏常客⾥程数对于计算结果的影响将远远⼤于其他两个特征——玩视频游戏的和每周消费冰淇淋公升数 ——的影响。⽽产⽣这种现象的唯⼀原因,仅仅是因为飞⾏常客⾥程数远⼤于其他特征值。但海伦认为这三种特征是同等重要的,因此作为三个等权重的特征之⼀,飞⾏常客⾥程数并不应该如此严重地影响到计算结果。我们要怎么解决这个问题呢?处理这种不同取值范围的特征值时,通常采⽤的⽅法就是将数值归⼀化,如将取值范围处理为0到1或 者-1到1之间。下⾯的公式可以将任意取值范围的特征值转化为0到1区间内的值。

原理知道了,那么开始归一化吧!

#数组集体操作进行归一化
def autoNorm(dataSet):#找到每列最小最大值#min(0)返回该矩阵中每一列的最小值#min(1)返回该矩阵中每一行的最小值minVals = dataSet.min(0)maxVals = dataSet.max(0)#范围rangs = maxVals - minVals#创建存放归一化的矩阵normDataSet = np.zeros(np.shape(dataSet))#进行归一化normDataSet = (dataSet - minVals)/ rangs#返回归一化结果,范围,最小值return normDataSet,rangs,minVals
#------测试-----------------------------------------------------------
if __name__ == "__main__":#打开的文件名filename = "0402Dataset/datingTestSet.txt"#打开并处理数据datingDataMat, datingLabels = file2matrix(filename)#showdatas(datingDataMat, datingLabels)normDataSet, rangs, minVals = autoNorm(datingDataMat)print(normDataSet)#print(rangs)#print(minVals)

结果:

可以看出,归一化后结果都在0–1之间。

4.编写分类器

分类器可直接用上个实例的即可,原理一样,我就直接复制过来啦!

#分类器函数
def classify0(inX,dataSet,labels,k):#求出dataSet多少行dataSetSize = dataSet.shape[0]#将要测试的数据变为dataSet同样大小的数据#inXs = np.tile(inX,(dataSetSize,1))#数据和测试的依次相减diffMat = dataSet - inX   #各数据依次和它相减#计算距离,平方相加,再开方distances = ((diffMat ** 2).sum(axis = 1)) ** 0.5#从小到大排序,返回索引sortedDistances = distances.argsort()#一个记录类别数量的字典classCount = {}for i in range(k):#取出前k个元素的类别votaIlabel = labels[sortedDistances[i]]# dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。# 计算类别次数classCount[votaIlabel] = classCount.get(votaIlabel,0) + 1    #一开始空字典,初始化get(votaIlabel,0)没有返回0# python3中用items()替换python2中的iteritems()# key=operator.itemgetter(1)根据字典的值进行排序# key=operator.itemgetter(0)根据字典的键进行排序# reverse降序排序字典#按分类个数从大到小排列sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse=True)# 返回次数最多的类别,即所要分类的类别#return sortedClassCount[0]return sortedClassCount[0][0]

5.测试算法

我们已经将数据按照需求做了处理,本节我们将测试分类器的效果,如果分类器的正确率满⾜要求,海伦就可以使⽤这个软件来处理约会⽹站提供的约会名单了。机器学习算法⼀个很重要的⼯作就是评估算法的正确率,通常我们只提供已有数据的90%作为训练样本来训练分类器,⽽使⽤其余的10%数据去测试分类器,检测分类器的正确率。
最原始的做法。需要注意的是10%的测试数据应该是随机选择的,由于海伦提供的数据并没有按照特定⽬的来排序,所以我们可以随意选择10%数据⽽不影响其随机性。 前⾯我们已经提到可以使⽤错误率来检测分类器的性能。对于分类器来说,错误率就是分类器给出错误结果的次数除以测试数据的总数,完美分类器的错误率为0,⽽错误率为1.0的分类器不会给出任何正确的分类结果。代码⾥我们定义⼀个计数器变量,每次分类 器错误地分类数据,计数器就加1,程序执⾏完成之后计数器的结果除以数据点总数即是错误率。
好,开始!

def datingClassTest():#打开文件filename = "0402Dataset/datingTestSet.txt"#读取文件,处理数据datingDataMat,datingLables = file2matrix(filename)#取10%hoRatio = 0.10#数据归一化,返回归一化后的矩阵,数据范围,数据最小值NormMat,ranges,minVals = autoNorm(datingDataMat)#获取元素个数m = NormMat.shape[0]#取10%numTestVecs = int(m * hoRatio)#初始化错误个数errorCount = 0#前numTestVecsge为测试集,其余为训练集for i in range(numTestVecs):#开始分类classifierResult = classify0(NormMat[i,:], NormMat[numTestVecs:m,:],datingLables[numTestVecs:m], 4)   #返回预测类别#输出测试与真实值print("测试值为:%d\t真实值为:%d"%(classifierResult,datingLables[i]))if classifierResult != datingLables[i]:    #判断第i个预测和第i个的真实标签是否一致errorCount += 1print("错误率:%f%%"%(errorCount/float(numTestVecs) * 100))   #-----测试----------------------------------------------------------------
if __name__ == '__main__':datingClassTest()

通过测试,我们知道了这个分类器的正确率。
结果:

6.使用算法

上⾯我们已经在数据上对分类器进⾏了测试,现在终于可以使⽤这个分类器为海伦来对⼈们分类。我们会给海伦⼀⼩段程序,通过该程序海伦会在约会⽹站上找到某个⼈并输⼊他的信息。程序会给出她对对⽅喜欢程度的预测值。

#构建可用系统
def ClassifyPerson():#输出结果#上面我们为了好操作,将三种态度转变为1,2,3数字代替,现在到了出结果的过程了,我们要还原回显示文字resultList = ['讨厌','有些喜欢','喜欢']      #输入条件percentTats = float(input("玩游戏消耗占比:"))ffmiles= float(input("每年飞行里程数:"))iceCream = float(input("每周冰淇凌消费:"))#打开数据,归一化filename = "0402Dataset/datingTestSet.txt"datingDataMat,datingLabels = file2matrix(filename)   #处理数据normMat,ranges,minVal = autoNorm(datingDataMat)       #归一化#生成测试集,处理我们输入的数据inArr = np.array([ffmiles,percentTats,iceCream])#归一化norminArr = (inArr - minVal) / ranges#返回分类结果classifyResult = classify0(norminArr,normMat,datingLabels,3)print("对此人态度可能为:%s"%(resultList[classifyResult - 1]))
#--------测试----------------------------------------------------------------
if __name__ == '__main__':ClassifyPerson()

我依次输入了一组测试数据,得到结果如下。

我们已经看到如何在数据上构建分类器。 这⾥所有的数据让⼈看起来都很容易,但是如何在⼈不太容易看懂的数据上使⽤分类器呢?下面我们讲解如何在⼆进制存储的图像数据上使⽤kNN。

五、实例—手写数字识别

本节我们⼀步步地构造使⽤k近邻分类器的⼿写识别系统。为了简单起见,这⾥构造的系统只能识别数字0到9,如下图。需要识别的数字已经使⽤图形处理软件,处理成具有相同的⾊彩和⼤⼩ :宽⾼是32像素x32像素的⿊⽩图像。尽管采⽤⽂本格式存储图像不能有效地利⽤内存空间,但是为了⽅便理解,我们还是将图像转换为⽂本格式。
(具体牛X的手写数字识别,会在深度学习tensorflow那讲-------如果我还更的话d=====( ̄▽ ̄*)b) )
先看这个写法吧!

补充呀!!!

再讲之前,先说一下怎么样将手写的数字变为⽂本格式存储呢?
这是补充部分,后面实例的数据集都是⽂本格式存储的。挺简单的,直接上代码,看我注释(☆▽☆)

"""
k-近邻算法实战之sklearn手写数字识别----补充
@Author:Yuuuuu、Tian
Wed Feb 12 11:13:21 2020
自己写了个数字,照下后想看一下是否可以判断正确
将一图片转化为32*32的图片,然后转为文本格式
"""
#图像转为二进制数据
from PIL import Image
import matplotlib.pylab as plt
import numpy as np
def picTo01(filename):"""将图片转化为32*32像素的文件,用0 1表示"""# 打开图片img = Image.open(filename).convert('RGBA')# 得到图片的像素值raw_data = img.load()# 将其降噪并转化为黑白两色,#亮度分界线注意,数值越大越亮   128为分界  <128为暗   >128为亮#对各图层进行处理for y in range(img.size[1]):      #yfor x in range(img.size[0]):  #xif raw_data[x, y][0] < 128:                    raw_data[x, y] = (0, 0, 0, 255)for y in range(img.size[1]):for x in range(img.size[0]):if raw_data[x, y][1] < 128:raw_data[x, y] = (0, 0, 0, 255)     #黑色for y in range(img.size[1]):for x in range(img.size[0]):if raw_data[x, y][2] < 128:raw_data[x, y] = (0, 0, 0, 255)   #黑色# 设置为32*32的大小img = img.resize((32, 32), Image.LANCZOS)# 进行保存,方便查看img.save('test.png')# 得到像素数组,为(32,32,4)array = plt.array(img)# 按照公式将其转为01, 公式: 0.299 * R + 0.587 * G + 0.114 * Bgray_array = np.zeros((32, 32))# 行数for x in range(array.shape[0]):# 列数for y in range(array.shape[1]):# 计算灰度,若为255则白色,数值越小越接近黑色gary = 0.299 * array[x][y][0] + 0.587 * array[x][y][1] + 0.114 * array[x][y][2]# 设置一个阙值,记为0if gary == 255:gray_array[x][y] = 0else:# 否则认为是黑色,记为1gray_array[x][y] = 1# 得到对应名称的txt文件name01 = filename.split('.')[0]name01 = name01 + '.txt'# 保存到文件中np.savetxt(name01, gray_array, fmt='%d', delimiter='')
#-------测试-----------------------------------------------------------
if __name__ == '__main__':picTo01('0403Dataset/zixie.png')

我自己写个了如下的数字,命名为zixie.txt,现在通过函数来看一下处理结果吧!

结果:

这就是补充的部分啦,情况就是这么个情况,事情就是这么个事情。下面开始进入正题。

正题开始—手写数字识别(sklearn模块实践)

流程如下:

  1. 收集数据:提供了⽂本⽂件。
  2. 准备数据:编写函数classify0(),将图像格式转换为分类器使⽤的list格式。
  3. 分析数据:在Python命令提⽰符中检查数据,确 保它符合要求。
  4. 训练算法:此步骤不适⽤于k近邻算法。
  5. 测试算法:编写函数使⽤提供的部分数据集作为 测试样本,测试样本与⾮测试样本的区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为⼀个错误。
  6. 使⽤算法:从图像中提取数字,并完成数字识别。
    根据流程开始编写吧!

1.收集数据

数据已放入置顶链接

2.准备数据

提供的trainingDigits中包含了⼤约2000个例⼦,每个例⼦的内容如下图所⽰,每个数字⼤约有200个样本;testDigits中包含了⼤约900个测试数据。我们使⽤⽬录 trainingDigits中的数据训练分类器,使⽤⽬录testDigits 中的数据测试分类器的效果。

为了使⽤前⾯两个例⼦的分类器,我们必须将图像格式化处理为⼀个向量。我们将把⼀个32x32的⼆进制图像矩阵转换为1x1024的向量,这样前两节使⽤的分类器就可以处理数字图像信息了。 但只讲这种方法多没劲啊,而且代码又复杂,下面咱介绍个简单的,用sklearn的模块来实现(用自写分类器的方法也要自练一下,我就不写了(ง •_•)ง)。
先将32x32的⼆进制图像矩阵转换为1x1024的向量。
导入相关包

"""
手写数字识别---sklearn
@Author:Yuuuuu、Tian
Wed Feb 12 11:13:21 2020
"""
import numpy as np
import operator
from os import listdir
from sklearn.neighbors import KNeighborsClassifier as KNN
from PIL import Image
import matplotlib.pylab as plt

将32*32的二进制图像转变为1 * 1024向量

#将32*32的二进制图像转变为1 * 1024向量
def img2vector(filename):#初始化一个1*1024的向量returnVect = np.zeros((1,1024))fr = open(filename)# lineStr1 = fr.readline()# lineStr2 = fr.readline()#按行读for i in range(32):#读出一行lineStr = fr.readline()   #每执行一次,自动都下行for j in range(32):returnVect[0,32 * i + j] = int(lineStr[j])return returnVect#return  lineStr1,lineStr2
#测试
if __name__ == "__main__":filename = "0403Dataset/trainingDigits/0_0.txt"   #查看一下0.0.txt文件的转变#returnVect,a = img2vector(filename)returnVect  = img2vector(filename)print(returnVect)#print(a)

结果是1*1024的向量,中间省略了。

3.sklearn中的KNN学习

下面就用到sklearn了,在用此模块前,先介绍一下咱们要用的参数
KNN需要调用的是 sklearn.neighbors.KNeighborsClassifier
KNneighborsClassifier参数说明:参考来源

  • n_neighbors:默认为5,就是KNN中k的值,选取最近的k个点。
  • weights:默认是uniform,参数可以是uniform、distance,也可以是用户自己定义的函数。uniform是均等的权重,就说所有的邻近点的权重都是相等的。distance是不均等的权重,距离近的点比距离远的点的影响大。用户自定义的函数,接收距离的数组,返回一组维数相同的权重。
  • algorithm:快速k近邻搜索算法,默认参数为auto,可以理解为算法自己决定合适的搜索算法。除此之外,用户也可以自己指定搜索算法ball_tree、kd_tree、brute方法进行搜索,brute是蛮力搜索,也就是线性扫描,当训练集很大时,计算非常耗时。kd_tree,构造kd树存储数据以便对其进行快速检索的树形数据结构,kd树也就是数据结构中的二叉树。以中值切分构造的树,每个结点是一个超矩形,在维数小于20时效率高。ball tree是为了克服kd树高纬失效而发明的,其构造过程是以质心C和半径r分割样本空间,每个节点是一个超球体。
  • leaf_size:默认是30,这个是构造的kd树和ball树的大小。这个值的设置会影响树构建的速度和搜索速度,同样也影响着存储树所需的内存大小。需要根据问题的性质选择最优的大小。
  • metric:用于距离度量,默认度量是minkowski,也就是p=2的欧氏距离(欧几里德度量)。
  • p:距离度量公式。我们使用欧氏距离公式进行距离度量。除此之外,还有其他的度量方法,例如曼哈顿距离。这个参数默认为2,也就是默认使用欧式距离公式进行距离度量。也可以设置为1,使用曼哈顿距离公式进行距离度量。
  • metric_params:距离公式的其他关键参数,这个可以不管,使用默认的None即可。
  • n_jobs:并行处理设置。默认为1,临近点搜索并行工作数。如果为-1,那么CPU的所有cores都用于并行工作。

分类器我们直接用模块了,那么下面就直接开始测试呗。

4.测试算法

上面我们已经将数据处理成分类器可以识别的格式, 本节我们将这些数据输⼊到分类器,检测分类器的执⾏效果。在写⼊这些代码之前,我们必须确保将 from os import listdir 写⼊⽂件的起始部分,这段代码的主要功能是从os模块中导⼊函数listdir,它可以列出给定⽬录的⽂件名。 因为我们数据集的命名就是真实标签。

#手写数字分类测试
def handwritingClassTest():#测试集的LabelshwLables = []#返回trainingDigits目录下的文件名trainingFileList = listdir('0403Dataset/trainingDigits')#返回文件下的数量m = len(trainingFileList)#初始化训练矩阵trainingMat = np.zeros((m,1024))#从文件名中得到标签for i in range(m):#获取文件名fileNameStr = trainingFileList[i]   #截取标签classNumber = int(fileNameStr.split('_')[0])    #真实标签#将获得的类别添加到hwLablehwLables.append(classNumber)#处理数据,转为1*1024,转入trainingMat矩阵中trainingMat[i,:] = img2vector('0403Dataset/trainingDigits/%s'%(fileNameStr))   #每个文件向量处理#构建KNN分类器neigh = KNN(n_neighbors=3,algorithm='auto')    #选3个近邻的,使用快速k近邻搜索算法,其余默认#用获得的数据拟合模型neigh.fit(trainingMat,hwLables)#===================================#开始测试#对测试集进行处理#返回测试集下的目录testFileList = listdir('0403Dataset/testDigits')#初始化错误个数errorCount = 0m_Test = len(testFileList)for i in range(m_Test):#获取目录下的名字fileNameStr = testFileList[i]#截取真实标签classNumber = int(fileNameStr.split('_')[0])#数据处理,1*1024vectorTest = img2vector('0403Dataset/testDigits/%s'%(fileNameStr))#开始分类器分类,获得结果#classifierResult = classify0(vectorTest, trainingMat, hwLables, 3)classifierResult = neigh.predict(vectorTest)   #通过训练的模型预测测试集结果print("预测结果:%d\t真实结果:%d"%(classifierResult,classNumber))if classifierResult != classNumber:errorCount += 1print("错了%d个,错误率为:%f%%"%(errorCount,(errorCount/m_Test) * 100))
#--------测试---------------------------------------------
if __name__ == '__main__':handwritingClassTest()

结果:

5.使用算法

既然咱们前面的补充内容讲解了怎样将图片转化为文本型,那为何不用自己写的数字来验证一下这个分类器呢?不用白不用。
前面我写了一个6,是否可以将它识别出来呢?稍微修改一下上面的测试算法,测试一下吧。

#手写数字分类测试
def handwritingClassTest():#测试集的LabelshwLables = []#返回trainingDigits目录下的文件名trainingFileList = listdir('0403Dataset/trainingDigits')#返回文件下的数量m = len(trainingFileList)#初始化训练矩阵trainingMat = np.zeros((m,1024))#从文件名中得到标签for i in range(m):#获取文件名fileNameStr = trainingFileList[i]#截取标签classNumber = int(fileNameStr.split('_')[0])#将获得的类别添加到hwLablehwLables.append(classNumber)#处理数据,转为1*1024,转入trainingMat矩阵中trainingMat[i,:] = img2vector('0403Dataset/trainingDigits/%s'%(fileNameStr))#构建KNN分类器neigh = KNN(n_neighbors=3,algorithm='auto')#拟合模型neigh.fit(trainingMat,hwLables)#=======================================#开始测试#对测试集进行处理#返回测试集下的目录#数据处理,1*1024vectorTest = img2vector('0403Dataset/zixie.txt')#开始分类器分类,获得结果classifierResult = neigh.predict(vectorTest)print("预测结果:%d"%(classifierResult))
#-------使用算法------------------------
if __name__ == '__main__':picTo01('0403Dataset/zixie.png')    #先处理我的图像handwritingClassTest()

结果:

可以看到预测的结果是对的( ̄▽ ̄)*。
其实我写了好几个,都识别错了。可测试集的正确率很高呀,难道我字太难看,为难它了???ㄟ( ▔, ▔ )ㄏ
实际使⽤这个算法时,算法的执⾏效率并不⾼。因为算法需要为每个测试向量做2000次距离计算,每个距离计算包括了1024个维度浮点运算,总计要执⾏900次,此外,我们还需要为测试向量准备2MB的存储空间。是否存在⼀种算法减少存储空间和计算时间的开销呢?决策树就是k近邻算法的优化版,可以节省⼤量的计算开销。那么下章我们就讲决策树吧!

六、总结

k近邻算法是分类数据最简单最有效的算法,本章我们通过三个例⼦讲述了如何使⽤k近邻算法构造分类器。k近邻算法是基于实例的学习,使⽤算法时我们必须有接近实际数据的训练样本数据。k近邻算法也必须保存全部数据集,如果训练数据集的很⼤,必须使⽤⼤量的存储空间。此外,由于必须对数据集中的每个数据计算距离值,实际使⽤时可能⾮常耗时。 K近邻算法的另⼀个缺陷是它⽆法给出任何数据的基础结构信息,因此我们也⽆法知晓平均实例样本和典型实例样本具有什么特征。优点也是有的,就是简单好用,容易理解,精度高,理论成熟,既可以用来做分类也可以用来做回归;训练时间复杂度为O(n);无数据输入假定;对异常值不敏感;可用于数值型数据和离散型数据。

  • 本文结合各位大牛所思所想,不胜感激!
  • 如有错误,请不吝指正!

《机器学习》及实战二、K-近邻算法(KNN)理论及实战相关推荐

  1. 机器学习-分类之K近邻算法(KNN)原理及实战

    k近邻算法(KNN) 简介 KNN算法是数据挖掘分类技术中最简单的方法之一.它通过测量不同特征值之间的距离进行分类的.其基本思路为:如果一个样本在特征空间中的k个最近邻样本中的大多数属于某一个类别,则 ...

  2. 基于KD树的K近邻算法(KNN)算法

    文章目录 KNN 简介 KNN 三要素 距离度量 k值的选择 分类决策规则 KNN 实现 1,构造kd树 2,搜索最近邻 3,预测 用kd树完成最近邻搜索 K近邻算法(KNN)算法,是一种基本的分类与 ...

  3. 01 K近邻算法 KNN

    01 K近邻算法 KNN k近邻算法基础 等价于 scikit-learn中的机器学习算法封装 训练数据集,测试数据集 分类准确度 超参数 考虑距离权重 更多关于距离的定义 搜索明可夫斯基距离相应的p ...

  4. k近邻算法(KNN)-分类算法

    k近邻算法(KNN)-分类算法 1 概念 定义:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别. k-近邻算法采用测量不同特征值之间的 ...

  5. k近邻算法 (KNN)

    k近邻算法 k近邻算法(KNN,K-NearestNeighbor)是一种基本分类和回归方法,监督学习算法,本质上是基于一种数据统计的方法: 核心思想:给定一个训练数据集,对新的输入实例,在训练数据集 ...

  6. 【白话机器学习】算法理论+实战之K近邻算法

    作者1. 写在前面 如果想从事数据挖掘或者机器学习的工作,掌握常用的机器学习算法是非常有必要的,在这简单的先捋一捋, 常见的机器学习算法: 监督学习算法:逻辑回归,线性回归,决策树,朴素贝叶斯,K近邻 ...

  7. k近邻算法_【白话机器学习】算法理论+实战之K近邻算法

    1. 写在前面 如果想从事数据挖掘或者机器学习的工作,掌握常用的机器学习算法是非常有必要的,在这简单的先捋一捋, 常见的机器学习算法: 监督学习算法:逻辑回归,线性回归,决策树,朴素贝叶斯,K近邻,支 ...

  8. 白话机器学习算法理论+实战之K近邻算法

    1. 写在前面 如果想从事数据挖掘或者机器学习的工作,掌握常用的机器学习算法是非常有必要的,比如我之前写过的一篇十大机器学习算法的小总结,在这简单的先捋一捋, 常见的机器学习算法: 监督学习算法:逻辑 ...

  9. 2 机器学习 K近邻算法(KNN) 学习曲线 交叉验证 手写数字识别

    机器学习 1 K-近邻算法介绍 1.1 分类问题 分类问题:根据已知样本的某些特征,判断一个未知样本属于哪种样本类别. 与回归问题相比,分类问题的输出结果是离散值,用于指定输入的样本数据属于哪个类别. ...

  10. 【模式识别】实验二:K近邻算法(KNN)

    KNN是模式识别中的经典算法,本次实验就MNIST数据集来做KNN算法的实验,并结合前一次的LDA降维对数据进行进一步处理. 实验报告图片版 pdf版本可以戳这:模式识别实验报告:KNN K近邻算法 ...

最新文章

  1. 临危不乱,.Net+IIS环境经常出现的问题及排障。
  2. 关闭防火墙和selinux
  3. 焦虑的移动互联网开发者如何破局?专题解析
  4. mess系统可以读取opc服务器,C3. Messages
  5. 如何真正做好项目管理?
  6. WPF设计の自定义窗体
  7. vue-cli入门(四)——vue-resource登录注册实例
  8. 深度学习:bert embedding用法详解
  9. [转载] 柯受良-柯受良飞跃黄河
  10. 配置Spring.NET
  11. PHP之Seay工具的安装与使用
  12. 【转载】财务主管的ERP实施之路
  13. 点击电脑桌面图标就点计算机图标老是出现是否删除快捷方式,电脑点击桌面图标提示缺少快捷方式怎么办...
  14. 【装机】关于WINRE/ESP/LRS_ESP/MSR/PBR这些分区
  15. QQ空间人气精灵王 QQ空间人气提升
  16. 超纯水工业水处理工程方案解析
  17. Gartner发布2021年新兴技术成熟度曲线
  18. GB/Gb分不清楚?
  19. linux桌面入口文件(.desktop)规范
  20. 三万块钱6天的区块链培训,我学会了搭建区块链系统框架?

热门文章

  1. 基于遗传算法的人工智能实例之拼图游戏(python实现)
  2. 圆的面积为什么是π r²
  3. 那个外汇交易软件好用一些
  4. 存储型xss实例分享---基于web靶机的织梦内容管理系统
  5. 大学计算机access操作题,计算机二级access操作题.doc
  6. 分享四题网络规划设计师下午考题与答案解析
  7. 凸显强劲实力!智领云入选2022德勤“光谷明日之星”榜单
  8. 4.5 创建透视表与交叉表
  9. 高熵合金FeNiCrCoCu纳米压痕模拟代码
  10. JavaScript 取整