keras框架下的深度学习(一)手写体数字识别

  • Post author:
  • Post category:其他



文章目录


前言


一、keras的介绍及其操作使用


二、手写题数字识别


1.介绍


2.对数据的预处理


3.搭建网络框架


4.编译


5.循环训练


6.测试训练的网络模


7.总代码


三、附:梯度下降算法


1.导数的梯度下降算法


2.偏导数的梯度下降算法


总结


前言


这个系列文章主要记录使用keras


框架来搭建深度学习模型的学习过程,其中有一些自己的想法和体会,主要学习的书籍是:




Deep Learning with Python











使用的


IDE





pycharm,需要安装keras以及相应的tensorflow的库


。这篇文章主要是


keras


的介绍以及在


keras


框架下的


mnist


手写体数字识别程序的讲解。



一、


keras


的介绍及其操作使用



Keras 是一个


Python


深度学习框架,可以方便地定义和训练几乎所有类型的深度学习模型。


Keras


最开始是为研究人员开发的,其目的在于快速实验。我们可以进入网站:


主页 – Keras 中文文档


查看keras内的配置,如图所示:


Keras


的使用很简单上手,但是运行


keras


需要一个后端引擎,这里推荐使用


tensorflow


后端。


当我们对


keras


代码中有什么疑惑时,可以选择查看网页上对应的内容,然后对源代码进行查看。比如说我们选择查看常用数据集(


Datasets):


接下来我们在本地文件路径下:


C:\Users\Quiming\AppData\Roaming\Python\Python37\site-packages\keras\datasets


,可以看到每个数据集的调用文件的源代码:


我们随便选择一个数据集文件打开,比如说mnist.py文件:


我们可以看到


mnist


里面的


load_data


函数具体是做什么,不一定要看懂,但是至少要知道在函数中输入的是什么,输出的是什么,这样更能方便于理解。


当我们不理解


layers


层时,我们可以查看


layers文件下的编程文件:


所以当遇到keras


中遇到语句不明白的或者错误但是找不到原因的,可以尝试去


keras


网页上或者本地文件中查看每个部分的信息;若只关注代码的使用,则需要明白函数的输入输出,以及这个函数大概做了什么事情。(三大利器:


keras


文档网页、源码以及


baidu


搜索)


二、


手写题数字识别



1.


介绍



在深度学习中的深度指的是数据模型中包含着的多个



次,而深度学习是对一堆数值做数学运算,但是这种数学运算是高纬度的,是大量的;在这些数学运算中,深度学习中的



通过反馈(比如后向传播)来对参数进行调整,然后再进行计算。如此反复数次,从而越来越接近我们所给出的正确结果。而在这个过程中,深度学习中的每个层所学习到的就是参数的具体数值。模型训练好以后,只需要一个输入,就能得到相应的输出。


对于深度学习的编程,以下是具体步骤(针对于


keras


框架):


1.


对数据的预处理,如把图片数据变成数值矩阵,把离散的数据变成向量,把数据归一化等等,其目的是为了之后的学习;


2.


搭建网络框架,即输入层,隐藏层和输出层,深度学习所需要学习的参数在这些层里面;


3.


编译,即定义所需要用的优化器、损失函数等;


4.


循环训练,让网络一次又一次的运行,最后得到学习后的参数;


5.


测试训练的网络模型,在模型训练结束后,进行测试来验证其泛化能力。


本文中我们使用


mnist


数据集,这个数据集包含


60000


张训练图像和


10000


张测试图像,全部都是尺寸


28*28像素的手写数字图片,如下图所示:


2.


对数据的预处理


from keras.datasets import mnist
(train_image,train_labels),(test_images,test_labels)=mnist.load_data()


第一句是从keras.datasets


中导入


mnist


,具体来说,我们在


pychram


中下载了


keras


这个库。它是一个文件夹,里面由很多的文件,而其中一个文件是


datasets


,里面主要用来调用各种数据集;比如


mnist





keras


文件下


datasets


文件中的一个编程文件(


.py


),和我们自己写的编程文件没有区别,而这个文件的具体功能是取爬取到


mnist


网站中的训练集和测试集。在


mnist


中有一个


load_data


的函数,在该函数中把训练集和测试集都分类好了,所以我们用


(train_image,train_labels),(test_images,test_labels)


来调用该函数中的训练集和测试集。训练集是为了训练模型识别手写体,而测试集是为了检测模型训练好之后对其他非训练集图片识别的能力,也就是泛化能力,而模型的优秀泛化能力是我们所需要的。


其中


train_image


的类型是一个三维张量:(


60000





28





28


),即是拥有


60000





28*28


的照片。而


train_labels


是一个一维的张量:(


60000


,),表示有


60000


个标签。测试集


test


也和训练集合类似,只是照片总数只有


10000


张,所以标签也只有


10000


个。


在导入数据集后,我们首先对数据进行处理,首先我们得确定深度学习网络是用什么层来搭建,我们假设使用全连接层(后面会讲到用卷积网络),则需要把图片数据处理成一个矩阵输入,该矩阵的多少行表示是有多少张照片,列是图像


28*28


个像素值(把图片拉成一维的)因为矩阵内是灰色图片的像素点,又需要把矩阵里面的数值压缩到


0





1


之间变成


float32


类型,所以我们需要把矩阵内的数值变成浮点型变量后除以


255


,最后还需要对标签(是什么数字)处理成为一个一维的向量(


one-hot


),比如,如果标签是


3


(对应的图片是手写体数字


3


),则生成一个只在索引


3


写入


1


,其他地方写入


0


的一维向量:


[0,0,0,1,0,0,0,0,0,0,]


。代码如下:

train_image=train_image.reshape((60000,28*28)) #定义训练集的数据类型,是一个(60000,784)的矩阵
train_image=train_image.astype('float32')/255 #把以上定义的矩阵内数值变成一个在(0,1)之间的数

test_images=test_images.reshape((10000,28*28))  #同上
test_images=test_images.astype('float32')/255

train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)



3.




搭建网络框架


from keras import models
from keras import layers
network=models.Sequential()
network.add(layers.Dense(512,activation='relu',input_shape=(28*28,)))
network.add(layers.Dense(10,activation='softmax'))


首先需要导入


models





layers


,即导入


keras


中编写好了的层和模型,第一行和第二行是要放在最前面的(所有的导入都需要放在最前面,这里为了方便讲解,所以和相应的代码放在一起)


我们可以想象该程序在


keras


这个文件中,导入


models


编程文件和


layers


文件,然后使得


network





models.Sequential


(),


sequential





models


中的一个函数,表示以下的网络结构都是线性排列下去。


然后我们添加了两个全连接层(


Dense


),该网络一共就只有两个层次。第一层,神经元是


512


个,激活函数是


relu


函数,而且定义了输入的形状,其参数一共有:


28*28*512+512=401920


(图像像素尺寸


*


神经元个数


+


权值


b


)。第二层是最后一层也是输出层,因为是判断手写数字,所以是


10


个神经元,表示输出


10


个概率值(总和为


1


)的列表,其中激活函数是


softmax


,其参数一共有:


512*10+10=5120


(上一层神经元个数


*


该层神经元个数


+


权值


b


)。



4.




编译


network.compile(
optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy']
)


这一步是为第二步定义的网络结构做训练的准备,其中有三个参数:1.


损失函数(


loss


):告诉网络学习的情况,如何让网络朝着争取的放向进行。(告诉网络,你和正确值差了多少,然后用优化器来反馈信号(后向传播)进行参数更新)


2.


优化器(


optimizer


):用于训练数据和损失函数来更新网络的机制。


3


训练过程中需要监视的指标(


metrics


):这里只监视训练的正确率如何(把每一次运行的


accuracy


的数值打印在页面)


对于损失函数,我们可以调用


optimizer


文件对其优化器进行选择,以及其参数的修改,具体操作在下一篇文章会进行讨论。



5.




循环训练


network.fit(train_image,train_labels,epochs=100,batch_size=34)


用之前定义的


network


调用


fit


函数执行循环训练,在


fit


中,


train_image





train_labels


是训练要使用的图片数据和标签,


epochs


是指训练多少次,


batch_size


是指一次训练多少个数据(比如每一次


epoch


中,一共要训练


60000


组数据,但是一次性只训练


34


次)。在这里的代码,我们可以令其等于一个变量,然后在最后画出准确率和损失值的图形(详细方法在下篇文章讨论)



6.




测试训练的网络模


test=network.evaluate(test_images,test_labels)
print(test)


在network


中调用


evaluate


函数对最后的测试集进行评估,然后把数赋值给


test


,最后把


test


打印出来,得到最后的


acc


(准确度)和


loss


(损失值)。一般来说,最后的测试是在训练好了模型之后,去测试模型的泛化能力。



7.




总代码


from keras.datasets import mnist
from keras import models
from keras import layers
from keras.utils import to_categorical
#导入数据集
(train_image,train_labels),(test_images,test_labels) = mnist.load_data()
#数据预处理
train_image=train_image.reshape((60000,28*28)) 
train_image=train_image.astype('float32')/255 

test_images=test_images.reshape((10000,28*28)) 
test_images=test_images.astype('float32')/255

train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
#定义网络架构
network=models.Sequential()
network.add(layers.Dense(512,activation='relu',input_shape=(28*28,)))
network.add(layers.Dense(10,activation='softmax'))
#编译
network.compile(
    optimizer='rmsprop',
    loss='categorical_crossentropy',
    metrics=['accuracy']
)
#开始循环训练网络
network.fit(train_image,train_labels,epochs=100,batch_size=34)
#对网络进行评估
test=network.evaluate(test_images,test_labels)
print(test)



三、附:梯度下降算法




1.




导数的梯度下降算法



梯度下降算法其内容不是很难,主要是在某个点找到其梯度的方向,然后选择梯度的反方向移动,最后慢慢的接近函数的极值点。


举个简单的例子,令


f(x)=x**2


(函数是


x


的平方)


,


然后取一个点:


x=2


,下面我们编写其梯度下降的算法。


在编写之前,我们首先要明白,梯度下降算法是怎么慢慢的接近极值点的。在上面例子中,我们选定点


x=2


后,我们在


x=2


这一点求其导数,然后用


x


减去该导数乘以学习率,得到一个新的


x


坐标,计算出函数的值,再重复以上步骤,让其慢慢接近极值点(导数为零,或者每一次移动都足够小,小到满足需求)。





python


程序如下:


首先我们定义


f(x)


函数,以及其一阶导函数


fn(x):

def f(x):
    f=x**2
    return f
def fn(x):  
    h=0.0001
    fn=(f(x+h)-f(x-h))/(2*h)
    return fn


注:


fn=(f(x+h)-f(x-h))/(2*h)


是取了


x


两边的变化(中心差分),这会使得导数误差更小。


然后定义学习率,循环的次数以及


x


的初始值,代码如下:

eta=0.01         #学习率
mun_times=1000   #循环次数
x_value=[]       #保存x的值
f_value=[]       #保存f(x)的值
x=2              #让x从2开始取值


之后就是通过循环,不断的接近函数的极值:

for i in range(mun_times):
    x-=eta*fn(x)            #更新x的取值
    x_value.append(x)       
    f_value.append(f(x))


最后可以把x


的每次取值和


f(x)


的值画出来,看看梯度下降的轨迹图:

import matplotlib.pyplot as plt
plt.plot(x_value,f_value)
plt.show


图形如下图所示:


总的代码为:

import matplotlib.pyplot as plt
def f(x):
    f=x**2
    return f
def fn(x):  #输入的是x输出的是derivate
    h=0.0001
    fn=(f(x+h)-f(x-h))/(2*h)
    return fn

eta=0.01         #学习率
mun_times=1000   #循环次数
x_value=[]       #保存x的值
f_value=[]       #保存f(x)的值
x=2              #让x从2开始取值

for i in range(mun_times):
    x-=eta*fn(x)            #更新x的取值
    x_value.append(x)
    f_value.append(f(x))

plt.plot(x_value,f_value)
plt.show()



2.




偏导数的梯度下降算法



下面讨论一下偏导数的梯度下降,核心思想是求出


x





x


在这里是一个列表,有多个数值)的偏导数,然后把梯度存储在一个列表中,最后总的进行数值更新:


首先我们定义其求偏导的过程,即分别求


x


列表中的每个


x


的偏导数:

def gradient(f,x):
    h=0.0001
    grad = np.zeros_like(x) #生成一个与x大小一致的全零矩阵

    for i in range(x.size):
        tem = x[i]
        x[i] = tem + h    #求第i个x的前向差分
        fh1 = f(x)

        x[i] = tem - h     #求第i个x的后向差分
        fh2 = f(x)

        grad[i] = (fh1-fh2)/(2*h)    #求第i个x的中心差分
        x[i] = tem           #把第i个x的值还给它自己  
    return grad


定义函数,这里为了方便,我们只定义一个包含两个变量的函数:

def function(x):
    return x[0]**2+x[1]**2


定义初始参数:

x_list1=[]                  #存储x[0]的值
x_list2=[]                  #存储x[1]的值
function_list=[]            #存储函数的值
x = np.array([-2.0,4.0])    #选定初始的点
lr=0.01                     #学习率是0.01
ste_num=500                 #循环500次


梯度下降:

for i in range(ste_num):
    x_list1.append(x[0])    
    x_list2.append(x[1])
    function_list.append(function(x))
    grad=gradient(function,x)   #计算x的梯度
    x -= lr * grad              #更新x的值


最后画图查看:

import matplotlib.pyplot as plt
plt.plot(x_list1,function_list,'r',label='x[0]')
plt.plot(x_list2,function_list,'b',label='x[1]')
plt.show()


图像如下图所示:


总代码:

import numpy as np
import matplotlib.pyplot as plt
def gradient(f,x):
    h=0.0001
    grad = np.zeros_like(x)

    for i in range(x.size):
        tem = x[i]
        x[i] = tem + h
        fh1 = f(x)

        x[i] = tem - h
        fh2 = f(x)

        grad[i] = (fh1-fh2)/(2*h)
        x[i] = tem
    return grad

def function(x):
    return x[0]**2+x[1]**2
x_list1=[]                  #存储x[0]的值
x_list2=[]                  #存储x[1]的值
function_list=[]            #存储函数的值
x = np.array([-2.0,4.0])    #选定初始的点
lr=0.01                     #学习率是0.01
ste_num=500                 #循环500次
for i in range(ste_num):
    x_list1.append(x[0])
    x_list2.append(x[1])
    function_list.append(function(x))
    grad=gradient(function,x)   #计算x的梯度
    x -= lr * grad              #更新x的值

plt.plot(x_list1,function_list,'r',label='x[0]')
plt.plot(x_list2,function_list,'b',label='x[1]')
plt.show()


总结


第一部分是对


keras


的简单介绍,在遇到不懂的语句时,可以借助


keras


中文文档以及


keras


文件下的源码进行了解。


第二部分是编写一个简单的深度学习网络来识别手写数字。难度不是很大,主要是对


keras


框架中语句的调用,以及参数的改写(


keras


已经把深度学习中的一系列操作打包成了函数或者编程文件,所以我们只需要调用即可)。该深度学习模型的主要步骤是:首先对数据进行预处理,变成归一化的浮点型数值矩阵输入神经网络的输入层,然后第一层的权重与输入的数据做运算后加上权重


b


,通过一个非线性函数输入到下一层,其他层类似;最后在输出层设有


10


个神经元,表示最后的输出是一个一行十列的数组,用来存放估计的数字的概率。之后由损失函数(


categorical_crossentropy


)进行后向传播来更新网络中的权重,其中所用到的优化器是


rmsprop




第三部是简单梯度下降算法,类似于自己编写简单的梯度下降算法,只要清楚的了解深度学习的每个步骤,就可以自己尝试编写代码完成,从简单的开始,一步步去优化。这样会更加有利于去理解


keras


中的每个步骤是怎么来的,对深度学习的每个步骤理解更深。在之后的文章中,可能会慢慢的把深度学习中的步骤一一的尝试去实现。



版权声明:本文为openabox原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。