利用Python对MNIST手写数据集进行数字识别(初学者入门级)

利用Python对MNIST手写数据集进行数字识别

一、编程环境Jupyter Notebook

Jupyter Notebook,之前被称为IPython notebook,是一个交互式的Web应用程序,可以用Python进行编程。它能实时反馈结果,与MATLABd的交互式应用相似。

对于Jupyter Notebook如何进行下载与安装并建立程序在这里就不做过多叙述,由于外网限制,可以通过清华镜像网站下载Anaconda,安装后该软件中就可以使用Jupyter Notebook,下载网址放在下面:

Anaconda下载(https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/)

另外,在看本文章之前,需要对Python有基础的了解,会进行简单的编程与基本的函数库导入操作,对于神经网络算法有基础的认识。

二、MNIST数据集

  1. MNIST介绍
    MNIST 数据集来自美国国家标准与技术研究所, National Institute of Standards and Technology (NIST). 训练集 (training set) 由来自 250 个不同人手写的数字构成, 其中 50% 是高中学生, 50% 来自人口普查局 (the Census Bureau) 的工作人员. 测试集(test set) 也是同样比例的手写数字数据。

人工智能领域大佬Yann LeCun在利用MNIST做过测试,他首次提出了CNN神经网络的概念,并且用神经网络实现了MNIST数字识别的算法,有兴趣的同学可以去查一查他的个人经历,他被誉为CNN之父,他的关于CNN的论文的网盘链接我放在下面,可以看一看。

Jann LeCun的论文链接:https://pan.baidu.com/s/1eDy4aTDlMmQo9UEhmZiUXQ 提取码:igs5

MNIST数据集下载地址http://yann.lecun.com/exdb/mnist/

数据集分为训练集和测试集,训练集有60000条数据,测试集有10000条数据,每一条数据都是由785个数字组成,数值大小在0~255之间,第一个数字代表该条数据所表示的数字,后面的784个数字可以形成28×28的矩阵,每一个数值都对应该位置的像素点的像素值大小,由此形成了一幅像素为28×28的图片。

2、MNIST的预处理

首先下载的数据集是train-images.idx3-ubyte与t10k-images.idx3-ubyte文件,由于本人不会使用该类型的文件进行相应的操作,所以本文对文件进行预处理,使其转换成.csv文件类型,该类型的文件可以用WPS或者office的EXCEL表格直接查看,由于这部分不是文章重点,本文将转换需要的Python代码直接放到下面,读者可直接进行复制并且将下载并解压好的数据集文件与该程序放到同一文件夹下,直接在Jupyter Notebook上运行该程序就可以生成对应的.csv文件。

def convert(imgf, labelf, outf, n):
    f = open(imgf, "rb")
    o = open(outf, "w")
    l = open(labelf, "rb")

    f.read(16)
    l.read(8)
    images = []

    for i in range(n):
        image = [ord(l.read(1))]
        for j in range(28*28):
            image.append(ord(f.read(1)))
        images.append(image)

    for image in images:
        o.write(",".join(str(pix) for pix in image)+"\n")
    f.close()
    o.close()
    l.close()

convert("train-images.idx3-ubyte", "train-labels.idx1-ubyte",
        "mnist_train.csv", 60000)
convert("t10k-images.idx3-ubyte", "t10k-labels.idx1-ubyte",
        "mnist_test.csv", 10000)

同时,本文将转换之后的.csv文件放到下面,供读者下载
链接: https://pan.baidu.com/s/14blvBCnwR6Bs1tYCFk7EgA 提取码: wzht

图一  下载后的压缩包解压的MNIST数据集的.csv文件

                 图一  下载后的压缩包解压的MNIST数据集的.csv文件

如上图所示该文件夹中有四个文件,mnist_test.csv是转换后的测试集,mnist_train.csv是转换后的训练集,mnist_test_10.csv是从测试集中选取的十条测试数据,也就是测试集的十个数字,mnist_train_100.csv是训练集的100条数据,也就是训练集的前100个数字。因为我们一开始学习神经网络只需要其中的一小部分训练集与测试集即可,因为如果用的数据过多,每次运行程序都需要耗费很长时间,所以一开始我们是用的是mnist_train_100.csv和mnist_test_10.csv两个文件即可,最后完成神经网络算法的编写之后就可以将程序中的文件换成mnist_train.csv和mnist_test.csv。

3、MNIST数据集的图像输出
为了让读者更直观的感受MNIST数据集,本文对数据集进行了处理,并以数组和图片的形式输出。下面是对应的Python代码与相应的输出结果。

data_file = open("mnist_train_100.csv") # open("文件路径") 该函数用于打开.csv文件,并分配给data_file变量方便使用
data_list = data_file.readlines() # readlines()函数用于读取.csv文件并将其读入到data_list变量中
data_file.close() # 关闭.csv文件,为了防止之后的处理中不小心对原始.csv文件进行修改
len(data_list) # len(变量名)用于检测读取的文件长度
print(data_list[0]# 运行该语句会直接显示.csv文件中第一个数组的内容

输出结果:


其中第二个输出结果的第一个数字 “5” 代表跟在后面这一串784个数字组成的28×28的像素图片是数字5的手写体。下面我们来输出图片,看看mnist_train_100.csv文件中的第一条数据所形成的数字长什么样。以下是Python代码与相应的输出结果。

import numpy as np  # numpy是矩阵和数组进行运算或其他处理时所需用到的函数库
import matplotlib.pyplot as plt  # matplotlib是图像处理所需用到的函数库
%matplotlib inline  

all_values = data_list[50].split(',') # split()函数将第49条数据进行拆分,以‘,’为分界点进行拆分
image_array = np.asfarray(all_values[1:]).reshape((28,28)) # asfarray()函数将all_values中的后784个数字进行重新排列
# reshape()函数可以对数组进行整型,使其成为28×28的二维数组,asfarry()函数可以使其成为矩阵。
plt.imshow(image_array, interpolation = 'nearest')  # imshow()函数可以将28×28的矩阵中的数值当做像素值,使其形成图片
print(data_list[50])  # 打印data_list中的第49条数据,可以看到第一个数字为3,也就是说该条数据表示的手写体为数字3

输出结果:

三、利用神经网络进行MNIST数字识别程序编写

如果有想了解更深的内容与推导过程的读者可以看一本书叫做《Python神经网络编程》,该书是由英国人工智能领域的硕士Tariq Rashid先生编写,本文的大部分内容与感悟都来自于这本书,给予了我很大帮助,另外吴恩达老师的视频课对于我的前期学习也帮助很大。

程序编写与结果输出如下:(以下代码需要有对神经网络的基本认识,并且对Python有基前期的学习)

# code for a 3-layer neural network,and code for learning the MNIST daeaset
# 一个用于MNIST数字识别的三层神经网络程序(输入层,隐藏层,输出层)

import numpy as np  # 用于进行数组和矩阵的运算操作
import scipy.special as ssp  # 里面有激活函数sigmoid函数,可以直接调用
import matplotlib.pyplot as plt  # 用于进行数字矩阵的图像输出,该函数库时进行图像处理常用到的函数库
%matplotlib inline

# neural network class definition
# 制作一个神经网络算法的类,其名为神经网络,相当于函数库,直接进行调用里面的函数即可。
class neuralNetwork:
    # initialise the neural network
    # 对神经网络的参数进行初始化
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate): # 用于类的初始值的设定
        # set number of nodes in each input, hidden, output layer
        # 设置节输入层、隐藏层、输出层的节点数  (self表示类所自带的不能改变的内容)
        self.inodes = inputnodes # 输入层节点数
        self.hnodes = hiddennodes # 隐藏层节点数
        self.onodes = outputnodes # 输出层节点数
        
        # link weight matrices, wih and who
        # 设置输入层与隐藏层直接的权重关系矩阵以及隐藏层与输出层之间的权重关系矩阵
        # (一开始随机生成权重矩阵的数值,利用正态分布,均值为0,方差为隐藏层节点数的-0.5次方,)
        self.wih = np.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes)) #矩阵大小为隐藏层节点数×输入层节点数
        self.who = np.random.normal(0.0, pow(self.hnodes, -0.5), (self.onodes, self.hnodes)) #矩阵大小为输出层节点数×隐藏层节点数
        
        # learning rate
        # 设置学习率α
        self.lr = learningrate
        
        # activation function is the sigmoid function
        # 将激活函数sigmoid定义为self.activation_function
        self.activation_function = lambda x: ssp.expit(x) 
        # lambda x:表示快速生成函数f(x) 并将其命名为self.activation_function
        
        pass
    
    # train the neural network
    # 训练数据集所要用到的函数定义为:train()
    def train(self, inputs_list, targets_list):
        # convert inputs list to 2d array
        # 将导入的输入列表数据和正确的输出结果转换成二维矩阵
        inputs = np.array(inputs_list, ndmin = 2).T # array函数是矩阵生成函数,将输入的inputs_list转换成二维矩阵,ndmin=2表示二维矩阵
        targets = np.array(targets_list, ndmin = 2).T # .T表示矩阵的转置,生成后的矩阵的转置矩阵送入变量targets
        
        # 进行前向传播
        # calculate signals into hidden layer
        # 利用导入的数据计算进入隐藏层的数据
        hidden_inputs = np.dot(self.wih, inputs) # dot()函数是指两个矩阵做点乘
        # calculate the signals emerging from hidden layer
        # 利用激活函数sigmoid计算隐藏层输出的数据
        hidden_outputs = self.activation_function(hidden_inputs)
        
        # calculate signal into final output layer
        # 利用隐藏层输出的数据计算导入输出层的数据
        final_inputs = np.dot(self.who, hidden_outputs) # dot()函数是指两个矩阵做点乘
        # calculate the signals emerging from final output layer
        # 利用激活函数sigmoid计算输出层的输出结果
        final_outputs = self.activation_function(final_inputs)
        # 前向传播结束
        
        # 进行反向传播
        # output layer error is the (target - actual)
        # 计算前向传播得到的输出结果与正确值之间的误差
        output_errors = targets - final_outputs
        # hidden layer error is the output_errors, split by weights, recombined at hidden nodes
        # 隐藏层的误差是由输出层的误差通过两个层之间的权重矩阵进行分配的,在隐藏层重新结合
        hidden_errors = np.dot(self.who.T, output_errors) # 隐藏层与输出层之间的权重矩阵的转置与前向传播的误差矩阵的点乘
        
        # update the weights for the links betwwen the hidden and output layers
        # 对隐藏层与输出层之间的权重矩阵进行更新迭代
        self.who += self.lr * np.dot((output_errors * final_outputs * (1.0 - final_outputs)),np.transpose(hidden_outputs))
        # update the weights for the links between the input and hidden layers
        # 对输入层与隐藏层之间的权重矩阵进行更新迭代
        self.wih += self.lr * np.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), np.transpose(inputs))
        
        pass
    
    # query the nerual network
    # 查询函数,用于在训练算法完成训练之后检验训练所得的权重矩阵是否准确
    def query(self, inputs_list):
        # convert inputs lst to 2d array
        # 将输入的测试集数据转换成二维矩阵
        inputs = np.array(inputs_list, ndmin = 2).T

        # 以下程序为计算输出结果的程序,与上面前向传播算法一致(到 return final_outputs结束)
        # calculate signals into hidden layer
        hidden_inputs = np.dot(self.wih, inputs)
        # calculate the signals emerging from hidden layer
        hidden_outputs = self.activation_function(hidden_inputs)

        # calculate signal into final output layer
        final_inputs = np.dot(self.who, hidden_outputs)
        # calculate the signals emerging from final output layer
        final_outputs = self.activation_function(final_inputs)

        return final_outputs
# 神经网络算法的类定义完毕

# number of input, hidden and output nodes
# 设置输入节点数、隐藏层节点数、输出节点数
input_nodes = 784 # 因为有784个像素值(28×28),所以相当于输入有784个
hidden_nodes = 200  # 隐藏层节点数设置为200,可以随意设置,理论上节点数越多,得到的算法准确率越高
# 实际上达到一定值后将会基本不变,而且节点数越多,训练算法所需花费时间越长,因此节点数不宜设置的过多
output_nodes = 10 # 因为从0到9一共十个数,所以输出节点数为10

# learning rate
learning_rate = 0.1 # 学习率设置为0.1,可以随意设置,但是经过测试,当为0.1时,得到的算法准确率最高

# create instance of neural network
# 建立神经网络的类,取名为"n",方便后面使用
n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)


# load the mnist training data CSV file into a list
# 将mnist_train.csv文件导入
training_data_file = open("mnist_train.csv", 'r') # ‘r’表示只读
training_data_list = training_data_file.readlines()
training_data_file.close()

# train the neural network
# 利用导入的数据训练神经网络算法

epochs = 5
# epochs为世代,让算法循环5次

for e in range(epochs):
    # go through all records in the training data set
    # 遍历所有输入的数据
    for record in training_data_list:
        # split the record by the ',' commas
        # 将所有数据通过逗号分隔开
        all_values = record.split(',')
        # scale and shift the inputs
        # 对输入的数据进行处理,取后784个数据除以255,再乘以0.99,最后加上0。01,是所有的数据都在0.01到1.00之间
        inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
        # create the target output values (all 0.01, except the desired label which is 0.99)
        # 建立准确输出结果矩阵,对应的位置标签数值为0.99,其他位置为0.01
        targets = np.zeros(output_nodes) + 0.01
        # all_values[0] is the target label for this record
        targets[int(all_values[0])] = 0.99
        n.train(inputs, targets) # 利用训练函数训练神经网络
        pass
    
    pass

# load the mnist test data CSV file into a list
# 导入测试集数据
test_data_file = open("mnist_test.csv", 'r')
test_data_list = test_data_file.readlines()
test_data_file.close()

# test the neural network
# 用query函数对测试集进行检测
# go through all the records in the test data set for record in the test_data_list:
scorecard = 0 # 得分卡,检测对一个加一分

for record in test_data_list:
    # split the record by the ',' comas
    # 将所有测试数据通过逗号分隔开
    all_values = record.split(',')
    # correct answer is first value
    # 正确值为每一条测试数据的第一个数值
    correct_lebal = int(all_values[0])
    print("correct lebal", correct_lebal) # 将正确的数值在屏幕上打印出来
    # scale and shift the inputs
    # 对输入数据进行处理,取后784个数据除以255,再乘以0.99,最后加上0。01,是所有的数据都在0.01到1.00之间
    inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
    # query the network
    # 用query函数对测试集进行检测
    outputs = n.query(inputs)
    # the index of the highest value corresponds to out label
    # 得到的数字就是输出结果的最大的数值所对应的标签
    lebal = np.argmax(outputs) # argmax()函数用于找出数值最大的值所对应的标签
    print("Output is ", lebal) # 在屏幕上打出最终输出的结果
    
    # output image of every digit
    # 输出每一个数字的图片
    image_correct = np.asfarray(all_values[1:]).reshape((28, 28))
    plt.imshow(image_correct, cmap = 'Greys', interpolation = 'None')
    plt.show()
    # append correct or incorrect to list
    if (lebal == correct_lebal):
        # network's answer matchs correct answer, add 1 to scorecard
        scorecard += 1
    else:
        # network's answer doesn't match correct answer, add 0 to scorecard
        scorecard += 0
        pass
    pass

# calculate the performance score, the fraction
# 计算准确率 得分卡最后的数值/10000(测试集总个数)
print("performance = ", scorecard / 10000)

[1] 《Python神经网络编程》 [英]塔里克·拉希德(Tariq Rashid) 人民邮电出版社
[2] https://www.bilibili.com/video/BV164411b7dx
[3]https://blog.csdn.net/simple_the_best/article/details/75267863

  • 73
    点赞
  • 532
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 27
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仲子_real

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值