第一个机器学习算法:K-近邻算法实现手写数字识别系统

一、前提知识点:

1、参考Linux命令大全:http://man.linuxde.net/wget

命令wget:

用来从指定的URL下载文件;wget非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性,如果是由于网络的原因下载失败,wget会不断的尝试,直到整个文件下载完毕。如果是服务器打断下载过程,它会再次联到服务器上从停止的地方继续下载。这对从那些限定了链接时间的服务器上下载大文件非常有用。

使用wget下载单个文件

wget http://www.linuxde.net/testfile.zip

以下的例子是从网络下载一个文件并保存在当前目录,在下载的过程中会显示进度条,包含(下载完成百分比,已经下载的字节,当前下载速度,剩余下载时间)。

命令!:

2、Numpy教程:https://www.yiibai.com/numpy/numpy_matrix_library.html

官网:http://www.numpy.org/

https://docs.scipy.org/doc/numpy/reference/generated/numpy.tile.html

ndarray.shape:这一数组属性返回一个包含数组维度的元组,它也可以用于调整数组大小。



numpy.ndim:


下面的是摘自:https://blog.csdn.net/xiahei_d/article/details/52749395

在讲tile方法之前,先要讲一下Numpy数组中的几个概念。

在NumPy中,数组这一类又被称为ndarray

2.1、ndarray.ndim

指数组的维度,即数组轴(axes)的个数,其数量等于秩(rank)。通俗地讲,我们平时印象中的数组就是一维数组,维度为1、轴的个数为1、秩也等于1;最常见的矩阵就是二维数组,维度为2、轴的个数为2(可以理解为由x轴、y轴组成)、秩等于2;我们所知的空间就相当于三维数组,维度为3、轴的个数为3(x、y、z轴)、秩等于3;以此类推。

2.2、ndarray.shape

按教程的话翻译过来是数组的维度,这样就很容易和ndim的概念混淆。所以可以这样理解,shape的返回值是一个元组,元组的长度就是数组的维数,即ndim。而元组中每个整数分别代表数组在其相应维度(/轴)上的大小。以最常见的矩阵为例,print shape后返回(2,3),说明这是一个2行3列的矩阵。


2.3、下面说一下tile函数,其原型如下。

原型:numpy.tile(A,reps)

tile共有2个参数,A指待输入数组,reps则决定A重复的次数。整个函数用于重复数组A来构建新的数组。

假设reps的维度为d,那么新数组的维度为max(d,A.ndim)。下面分三种情况进行讨论:

(1)A.dim < d

则向A中添加新轴扩充A的维度。维度大小可以从shape中看出,一般通过向shape对应的元组中添加1完成对A维度的扩充。扩充完成后,则可根据reps的值对A中相应维度的值进行重复。

例如,一维数组shape为(3,),扩充至2维则shape值为(1,3),扩充至3维则shape值为(1,1,3)

(2)A.dim > d

将reps扩充至与A相同的维度。扩充方法同上,也是向shape对应元组中添1,然后再进行重复。

例如,4维数组A的shape为(2,3,4,5),而reps为(2,2)只有2维,那么就要对reps添维进行扩充,得到(1,1,2,2)

(3)A.dim = d

 不需要扩充,直接按reps的值对相应维度的值进行重复。

[python]  view plain  copy
  1. >>>from numpy import *  
  2. >>> a = array([1,2,3])  
  3. >>>print a.shape  
  4. (3.)  
  5. >>>print a.ndim  
  6. 1  
  7. >>>b = tile(a,2)  
  8. >>>print b  
  9. [1 2 3 1 2 3]  
  10. >>>print b.shape  
  11. (6,)  
  12. >>>print b.ndim  
  13. 1  
  14. >>>c = tile(a,(2,3))  
  15. >>>print c  
  16. [[1 2 3 1 2 3 1 2 3]  
  17.  [1 2 3 1 2 3 1 2 3]]  
  18. >>>print c.shape  
  19. (2,9)  
  20. >>>print c.ndim  
  21. 2  
由此可以看出,得到的新数组的维度由d和A.ndim的大小决定,shape值由扩充后的A和reps相应维度值的乘积得到。

2.4、Numpy的tile函数(摘自:http://blog.sina.com.cn/s/blog_6bd0612b0101cr3u.html

1.函数的定义与说明
tile函数是模板numpy.lib.shape_base中的函数。
函数的形式是tile(A,reps)
函数参数说明中提到A和reps都是array_like的,什么是array_like的parameter呢?在网上查了一下,始终搞不明白,便把熟悉的python数据类型都试了一下,得出以下结论。
A的类型众多,几乎所有类型都可以:array, list, tuple, dict, matrix以及基本数据类型int, string, float以及bool类型。
reps的类型也很多,可以是tuple,list, dict, array, int,bool.但不可以是float, string, matrix类型。
假定A的维度为d,reps的长度为len
当d>=len时,将reps长度补足为d,即在reps前面加上d-len个1。
这里的意思是,假设A为k维数组,每一维都有一定长度,构成的向量为D。
Numpy的tile函数
而长度为len的reps有len个数,进行tile函数运算时补足d位,前面加d-len个1,如下图所示:
Numpy的tile函数
经过tile运算,生成新的A,A的各维维度为:Numpy的tile函数
其中相乘的意思为,将原来A中每一维度的元素进行copy,生成的A中此元素出现次数为新的reps对应维度的数目。操作从低维度向高维进行。
当d
2.函数操作示例
首先给几个示例:
>>> tile(1.3,2)
array([ 1.3,  1.3])
array([1, 2, 1, 2, 1, 2])
>>> tile((1,2,3),2)
array([1, 2, 3, 1, 2, 3])
>>> a=[[1,2,3],[4,5,5]]
>>> tile(a,2)
array([[1, 2, 3, 1, 2, 3],
       [4, 5, 5, 4, 5, 5]])
>>> tile([1,2,3],[2,2,2,2])
array([[[[1, 2, 3, 1, 2, 3],
         [1, 2, 3, 1, 2, 3]],


        [[1, 2, 3, 1, 2, 3],
         [1, 2, 3, 1, 2, 3]]],




       [[[1, 2, 3, 1, 2, 3],
         [1, 2, 3, 1, 2, 3]],


        [[1, 2, 3, 1, 2, 3],
         [1, 2, 3, 1, 2, 3]]]])
拿其中一个例子进行说明:
>>> a=[[1,2],[2,3]]
>>> tile(a,2)
array([[1, 2, 1, 2],
       [2, 3, 2, 3]])
这里a的维度为2,reps长度为1(仅仅是1个int类型数据)
则将reps长度补足为2,结果为reps = [1,2](这里也可以写成reps=(1,2),都无妨的)
进行copy操作,从低维进行.数组a为a[2][2]
一维copy操作:copy两次。a[0]变为[1,2,1,2],a[1]变为[2,3,2,3]
二维copy操作,copy1次。a变为[[1,2,1,2],[2,3,2,3]]
a数组为a[2][4]
如此则不难理解下面的结果:
>>> tile(a,[1,2])
array([[1, 2, 1, 2],
       [2, 3, 2, 3]])
>>> tile(a,[2,2])
array([[1, 2, 1, 2],
       [2, 3, 2, 3],
       [1, 2, 1, 2],
       [2, 3, 2, 3]])
tile(a,[2,2])中是将上述第二步的对a的第二维的copy次数变成了两次,a[0]copy两次,a[1]copy两次:[a[0],a[0],a[1],a[1]]结果如上所示。
3.函数其他注意事项
①当reps为bool类型或者是bool list类型的时候,与int类型相对应,即True对应为1,False对应为0.如:
>>> tile([1,2],[True,False])
array([], shape=(1, 0), dtype=int32)
>>> tile([1,2],[True,True])
array([[1, 2]])
>>> tile([1,2],[True,True,True])
array([[[1, 2]]])
>>> tile([1,2],True)
array([1, 2])
②当reps为dict类型时,实则取的是key值列表,且key值列表为升序排列如下所示:
>>> tile([1,2,3],{1:2,3:4})
array([[1, 2, 3, 1, 2, 3, 1, 2, 3]])
>>> tile([1,2,3],{3:4,1:2})
array([[1, 2, 3, 1, 2, 3, 1, 2, 3]])
>>> a={1:2,3:4}
>>> tile([1,2,3],a.keys())
array([[1, 2, 3, 1, 2, 3, 1, 2, 3]])
③当A为int,string,float,bool,dict等类型的时候,操作大体相似,都是讲A视为一个整体,生成一个与reps的长度相同维度的数组。如下所示:
>>> tile({1:2,3:4,5:6},3)
array([{1: 2, 3: 4, 5: 6}, {1: 2, 3: 4, 5: 6}, {1: 2, 3: 4, 5: 6}], dtype=object)
>>> tile({1:2,3:4,5:6},[2,2])
array([[{1: 2, 3: 4, 5: 6}, {1: 2, 3: 4, 5: 6}],
       [{1: 2, 3: 4, 5: 6}, {1: 2, 3: 4, 5: 6}]], dtype=object)
>>> tile('abc',3)
array(['abc', 'abc', 'abc'], 
      dtype='|S3')
>>> tile('abc',[3,3])
array([['abc', 'abc', 'abc'],
       ['abc', 'abc', 'abc'],
       ['abc', 'abc', 'abc']], 
      dtype='|S3')
>>> tile(2,3)
array([2, 2, 2])
>>> tile(2,[3,3])
array([[2, 2, 2],
       [2, 2, 2],
       [2, 2, 2]])
不过有所不同的是,当A为string类型以及dict类型的时候,array数组最后多了一个信息,即dtype,原因为何,即便看了一些源码,也不晓得是怎么回事,好像由array到ndarray,一大堆C的东西,搞不明白,索性作罢。另外,当对list类型进行mat操作然后作为参数A传入tile时也可以,不过结果类型不是array类型,而是matrix类型了,原因几何,我也无法解答。
看了tile的源码以后以上内容差不多都可以理解。例如之所以reps的类型有限制,在于代码一开始对reps进行了以下操作:
tuple(reps),无法进行该操作的reps参数就会报错。而且dict类型的reps进行tuple化以后,key值以升序出现,这也是注意事项中2的原因,而list和tuple类型的数值顺序不变。
大概就是以上了。我对numpy这里了解的不是很多,还希望向内行多多讨教呢,所以有问题不妨留言~


2.5、operator.itemgetter(1)

(摘自:https://blog.csdn.net/u012005313/article/details/49154683

参考:http://www.cnblogs.com/100thMountain/p/4719503.html

http://blog.163.com/zhuandi_h/blog/static/1802702882012111284632184/

operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号(即需要获取的数据在对象中的序号)

注意:operator.itemgetter函数获取的不是值,而是定义了一个函数,通过该函数作用到对象上才能获取值。

sorted函数

sorted函数是内建函数

[python]  view plain  copy
  1. help(sorted)  


参数解释:

iterable:指定为要排序的list或iterable对象

cmp:接受一个函数(有两个参数),指定排序时进行比较的函数,可以指定一个函数或lambda表达式,如:

[python]  view plain  copy
  1. stu=[('jhon''a'15), ('jane''b'12), ('save''b'10)]  
  2. def f(a,b):  
  3.     return a-b  
  4.  sorted(stu, cmp=f)  


key:接受一个函数(只有一个参数),指定待排序元素的哪一项进行排序:

[python]  view plain  copy
  1. sorted(stu, key=lambda student:student[2])  
reverse:是一个bool变量,表示升序还是降序排列,默认为false(升序排列),定义为True时表示降序排列

#####################################################################################

sorted函数和operator.itemgetter函数的使用

[python]  view plain  copy
  1. stu=[('jhon''a'15), ('jane''b'12), ('save''b'10)]  
  2. sorted(students, key=operator.itemgetter(2))  
通过stu的第三个域进行排序

[python]  view plain  copy
  1. sorted(students, key=operator.itemgetter(1,2))  
进行多级排序,即先跟第三个域进行排序,再根据第二个域排序(反过来了)



二、K-近邻算法:

思路:

首先:将每个文本文件中的数据转成个向量,准备个类别list、初始化(训练)样本数据矩阵、初始化错误率变量;

然后:将所有训练样本数据存入矩阵,同时将每行训练样本数据(每个文件的)对应的标签放入下标与其训练样本数据相同的类

别list中,针对每个文件的测试数据进行分类;

其中:分类器的实现--->计算测试数据与每个样本数据对应数据项的差值,然后按照距离从低到高排序(得索引数组),依次取出最近的样本数据,记录该测试样本数据所属的类别;对类别出现的频次进行排序,从高到底;最后返回出现频次最高的类别。

inX:用于分类的输入向量,(测试)数据向量:一次调用赋一个文件的。

dataSet:输入的训练样本集,(训练数据矩阵):所有文件的训练数据。
labels:样本数据的类标签向量

k:用于选择最近邻居的数目

最后:# 判断KNN算法结果是否准确,拿返回出现频次最高的类别跟已知结果的测试样本类别比较;对每个测试样本的分类错误进行累计计数,错误率=错误个数/所有的测试数据个数

代码:

def img2vector(filename):
    # 创建向量
    returnVect = np.zeros((1,1024))#[[ 0.  0.  0. ...,  0.  0.  0.]]
    
    # 打开数据文件,读取每行内容
    fr = open(filename)

    for i in range(32):
        # 读取每一行
        lineStr = fr.readline()
        
        # 将每行前 32 字符转成 int 存入向量
        for j in range(32):
            returnVect[0,32*i+j] = int(lineStr[j])
        
    return returnVect

import operator
'''
inX:用于分类的输入向量,(测试)数据向量:一次调用赋一个文件的。
dataSet:输入的训练样本集,(训练数据矩阵):所有文件的训练数据。
labels:样本数据的类标签向量
k:用于选择最近邻居的数目
'''
def classify0(inX, dataSet, labels, k):
    # 获取(训练)样本数据数量(数组的行,每一行是一个数据点,每一行的每一列是该数据的特征。数组名.shape:数组的维数)
    dataSetSize = dataSet.shape[0]

    # 矩阵运算,计算测试数据与每个样本数据对应数据项的差值
    #(tile共有2个参数,A指待输入数组,reps则决定A重复的次数。整个函数用于重复数组A来构建新的数组。)
    diffMat = np.tile(inX, (dataSetSize,1)) - dataSet

    # sqDistances 上一步骤结果平方和
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)

    # 取平方根,得到距离向量
    distances = sqDistances**0.5

    # 按照距离从低到高排序
    #Returns:index_array(索引数组) : ndarray, int
    #Array of indices that sort a along the specified axis. 
    #If a is one-dimensional, a[index_array] yields a sorted a.
    sortedDistIndicies = distances.argsort()     
    classCount={}          

    # 依次取出最近的样本数据
    for i in range(k):
        # 记录该样本数据所属的类别
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1#类别出现的频次

    # 对类别出现的频次进行排序,从高到低
    #Python 字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)

    # 返回出现频次最高的类别
    return sortedClassCount[0][0]


from os import listdir

def handwritingClassTest():
    # 样本数据的类标签列表
    hwLabels = []
    
    # (训练)样本数据文件列表,列表元素都是文件名.后缀
    trainingFileList = listdir('digits/trainingDigits')
    m = len(trainingFileList)
    
    # 初始化(训练)样本数据矩阵(M*1024),每行代表每个文件,每列代表每个文件中的数据
    trainingMat = np.zeros((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('digits/trainingDigits/%s' % fileNameStr)
    
    # 循环读取测试数据
    testFileList = listdir('digits/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('digits/testDigits/%s' % fileNameStr)
        
        # 对数据文件进行分类,返回值:分类器返回的类别
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
        
        # 打印KNN算法分类结果和真实的分类
        print ("the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr))
        
        # 判断KNN算法结果是否准确
        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)))

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值