深度学习---AlexNet

html

Alex2012提出的alexnet网络结构模型引爆了神经网络的应用热潮并赢得了2012届图像识别大赛的冠军,使得CNN成为在图像分类上的核心算法模型python

接下来本文对该网络配置结构中各个层进行详细的解读(训练阶段):git

注:下述关于卷积核的尺寸来自于Alex2012年发表的经典论文。github


 

1. conv1阶段DFDdata flow diagram):算法

 

第一层输入数据为原始的227*227*3的图像,这个图像被11*11*3的卷积核进行卷积运算,卷积核对原始图像的每次卷积都生成一个新的像素。卷积核沿原始图像的x轴方向和y轴方向两个方向移动,移动的步长是4个像素。所以,卷积核在移动的过程当中会生成(227-11)/4+1=55个像素(227个像素减去11,正好是54,即生成54个像素,再加上被减去的11也对应生成一个像素),行和列的55*55个像素造成对原始图像卷积以后的像素层。共有96个卷积核,会生成55*55*96个卷积后的像素层。96个卷积核分红2组,每组48个卷积核。对应生成255*55*48的卷积后的像素层数据。这些像素层通过relu1单元的处理,生成激活像素层,尺寸仍为255*55*48的像素层数据。数组

这些像素层通过pool运算(池化运算)的处理,池化运算的尺度为3*3,运算的步长为2,则池化后图像的尺寸为(55-3)/2+1=27。 即池化后像素的规模为27*27*96;而后通过归一化处理,归一化运算的尺度为5*5;第一卷积层运算结束后造成的像素层的规模为27*27*96。分别对应96个卷积核所运算造成。这96层像素层分为2组,每组48个像素层,每组在一个独立的GPU上进行运算。markdown

反向传播时,每一个卷积核对应一个误差值。即第一层的96个卷积核对应上层输入的96个误差值。网络

2. conv2阶段DFDdata flow diagram):session

 

第二层输入数据为第一层输出的27*27*96的像素层,为便于后续处理,每幅像素层的左右两边和上下两边都要填充2个像素;27*27*96的像素数据分红27*27*48的两组像素数据,两组数据分别再两个不一样的GPU中进行运算。每组像素数据被5*5*48的卷积核进行卷积运算,卷积核对每组数据的每次卷积都生成一个新的像素。卷积核沿原始图像的x轴方向和y轴方向两个方向移动,移动的步长是1个像素。所以,卷积核在移动的过程当中会生成(27-5+2*2)/1+1=27个像素。(27个像素减去5,正好是22,在加上上下、左右各填充的2个像素,即生成26个像素,再加上被减去的5也对应生成一个像素),行和列的27*27个像素造成对原始图像卷积以后的像素层。共有2565*5*48卷积核;这256个卷积核分红两组,每组针对一个GPU中的27*27*48的像素进行卷积运算。会生成两组27*27*128个卷积后的像素层。这些像素层通过relu2单元的处理,生成激活像素层,尺寸仍为两组27*27*128的像素层。多线程

这些像素层通过pool运算(池化运算)的处理,池化运算的尺度为3*3,运算的步长为2,则池化后图像的尺寸为(57-3)/2+1=13。 即池化后像素的规模为213*13*128的像素层;而后通过归一化处理,归一化运算的尺度为5*5;第二卷积层运算结束后造成的像素层的规模为213*13*128的像素层。分别对应2128个卷积核所运算造成。每组在一个GPU上进行运算。即共256个卷积核,共2GPU进行运算。

反向传播时,每一个卷积核对应一个误差值。即第一层的96个卷积核对应上层输入的256个误差值。

 

3. conv3阶段DFDdata flow diagram):

 

第三层输入数据为第二层输出的213*13*128的像素层;为便于后续处理,每幅像素层的左右两边和上下两边都要填充1个像素;2组像素层数据都被送至2个不一样的GPU中进行运算。每一个GPU中都有192个卷积核,每一个卷积核的尺寸是3*3*256。所以,每一个GPU中的卷积核都能对213*13*128的像素层的全部数据进行卷积运算。卷积核对每组数据的每次卷积都生成一个新的像素。卷积核沿像素层数据的x轴方向和y轴方向两个方向移动,移动的步长是1个像素。所以,运算后的卷积核的尺寸为(13-3+1*2)/1+1=1313个像素减去3,正好是10,在加上上下、左右各填充的1个像素,即生成12个像素,再加上被减去的3也对应生成一个像素),每一个GPU中共13*13*192个卷积核。2GPU中共13*13*384个卷积后的像素层。这些像素层通过relu3单元的处理,生成激活像素层,尺寸仍为213*13*192像素层,共13*13*384个像素层。

 

4. conv4阶段DFDdata flow diagram):

 

第四层输入数据为第三层输出的213*13*192的像素层;为便于后续处理,每幅像素层的左右两边和上下两边都要填充1个像素;2组像素层数据都被送至2个不一样的GPU中进行运算。每一个GPU中都有192个卷积核,每一个卷积核的尺寸是3*3*192。所以,每一个GPU中的卷积核能对113*13*192的像素层的数据进行卷积运算。卷积核对每组数据的每次卷积都生成一个新的像素。卷积核沿像素层数据的x轴方向和y轴方向两个方向移动,移动的步长是1个像素。所以,运算后的卷积核的尺寸为(13-3+1*2)/1+1=1313个像素减去3,正好是10,在加上上下、左右各填充的1个像素,即生成12个像素,再加上被减去的3也对应生成一个像素),每一个GPU中共13*13*192个卷积核。2GPU中共13*13*384个卷积后的像素层。这些像素层通过relu4单元的处理,生成激活像素层,尺寸仍为213*13*192像素层,共13*13*384个像素层。

 

5. conv5阶段DFDdata flow diagram):

 

第五层输入数据为第四层输出的213*13*192的像素层;为便于后续处理,每幅像素层的左右两边和上下两边都要填充1个像素;2组像素层数据都被送至2个不一样的GPU中进行运算。每一个GPU中都有128个卷积核,每一个卷积核的尺寸是3*3*192。所以,每一个GPU中的卷积核能对113*13*192的像素层的数据进行卷积运算。卷积核对每组数据的每次卷积都生成一个新的像素。卷积核沿像素层数据的x轴方向和y轴方向两个方向移动,移动的步长是1个像素。所以,运算后的卷积核的尺寸为(13-3+1*2)/1+1=1313个像素减去3,正好是10,在加上上下、左右各填充的1个像素,即生成12个像素,再加上被减去的3也对应生成一个像素),每一个GPU中共13*13*128个卷积核。2GPU中共13*13*256个卷积后的像素层。这些像素层通过relu5单元的处理,生成激活像素层,尺寸仍为213*13*128像素层,共13*13*256个像素层。

213*13*128像素层分别在2个不一样GPU中进行池化(pool)运算处理。池化运算的尺度为3*3,运算的步长为2,则池化后图像的尺寸为(13-3)/2+1=6。 即池化后像素的规模为两组6*6*128的像素层数据,共6*6*256规模的像素层数据。

 

6. fc6阶段DFDdata flow diagram):

 

第六层输入数据的尺寸是6*6*256,采用6*6*256尺寸的滤波器对第层的输入数据进行卷积运算;每一个6*6*256尺寸的滤波器对第层的输入数据进行卷积运算生成一个运算结果,经过一个神经元输出这个运算结果;共有40966*6*256尺寸的滤波器输入数据进行卷积运算,经过4096个神经元输出运算结果;这4096运算结果经过relu激活函数生成4096并经过drop运算后输出4096本层的输出结果值。

     因为第六层的运算过程当中,采用的滤波器的尺寸(6*6*256)待处理的feature map的尺寸(6*6*256)相同即滤波器中的每一个系数只与feature map中的一个像素相乘而其它卷积层,每一个滤波器的系数都会与多个feature map像素值相乘;所以第六层称为全链接层。

第五层输出的6*6*256规模的像素层数据与第六层的4096个神经元进行全链接,而后经由relu6进行处理后生成4096个数据,再通过dropout6处理后输出4096个数据。

 

7. fc7阶段DFDdata flow diagram):

 

第六层输出的4096个数据与第七层的4096个神经元进行全链接,而后经由relu7进行处理后生成4096个数据,再通过dropout7处理后输出4096个数据。

 

8. fc8阶段DFDdata flow diagram):

 

第七层输出的4096个数据与第八层的1000个神经元进行全链接,通过训练后输出被训练的数值。

 

Alexnet网络中各个层发挥的做用以下表所述:

 

在学习过程当中咱们使用随机梯度降低法和一批大小为128、动力为0.9、权重衰减为0.0005的样例来训练咱们的网络。咱们发现,这少许的权重衰减对于模型学习是重要的。换句话说,这里的权重衰减不只仅是一个正则化矩阵:它减小了模型的训练偏差。对于权重w的更新规则为:


其中i是迭代指数,v是动力变量,ε是学习率,是目标关于w、对求值的导数在第i批样例上的平均值。咱们用一个均值为0、标准差为0.01的高斯分布初始化了每一层的权重。咱们用常数1初始化了第2、第四和第五个卷积层以及全链接隐层的神经元误差。该初始化经过提供带正输入的ReLU来加速学习的初级阶段。咱们在其他层用常数0初始化神经元误差。
    对于全部层都使用了相等的学习率,这是在整个训练过程当中手动调整的。咱们遵循的启发式是,当验证偏差率在当前学习率下再也不提升时,就将学习率除以10。学习率初始化为0.01,在终止前下降三次。做者训练该网络时大体将这120万张图像的训练集循环了90次,在两个NVIDIA GTX 580 3GB GPU上花了五到六天。

 

各类layeroperation更多解释能够参考http://caffe.berkeleyvision.org/tutorial/layers.html

从计算该模型的数据流过程当中,该模型参数大概5kw+

1. 图片数据处理

一张图片是由一个个像素组成,每一个像素的颜色经常用RGB、HSB、CYMK、RGBA等颜色值来表示,每一个颜色值的取值范围不同,但都表明了一个像素点数据信息。对图片的数据处理过程当中,RGB使用得最多,RGB表示红绿蓝三通道色,取值范围为0~255,因此一个像素点能够把它看做是一个三维数组,即:array([[[0, 255, 255]]]),三个数值分布表示R、G、B(红、绿、蓝)的颜色值。好比下图一张3*3大小的jpg格式的图片:

2017-10-08-13-48-58

它的图片通过Tensorflow解码后,数据值输出为

image_path = 'images/image.jpg' filename_queue = tf.train.string_input_producer(tf.train.match_filenames_once(image_path)) image_reader = tf.WholeFileReader() _,image_file = image_reader.read(filename_queue) image = tf.image.decode_jpeg(image_file) # 若是是png格式的图片,使用tf.image.decode_png() sess.run(image) --result array([[[0, 0, 0], [255, 255, 255], [254, 0, 0]], [[0, 191, 0], [3, 108, 233], [0, 191, 0]], [[254, 0, 0], [255, 255, 255], [0, 0, 0]])image_path = 'images/image.jpg' filename_queue = tf.train.string_input_producer(tf.train.match_filenames_once(image_path)) image_reader = tf.WholeFileReader() _,image_file = image_reader.read(filename_queue) image = tf.image.decode_jpeg(image_file) # 若是是png格式的图片,使用tf.image.decode_png() sess.run(image) --result array([[[0, 0, 0], [255, 255, 255], [254, 0, 0]], [[0, 191, 0], [3, 108, 233], [0, 191, 0]], [[254, 0, 0], [255, 255, 255], [0, 0, 0]])

图片的数据处理不只仅就是把RGB值转换成运算须要的值,还包括调整图片大小、图片截取、图片翻转、图片色彩调整,标注框、多线程处理图片等等,在这里就不一一介绍了,可是对于图片的处理是进行卷积网络的首要任务,你须要了解,并学会对图片的相关操做。这里只介绍RGB值的转换,为下一节的卷积提供数据支持。

2. 卷积神经网络

卷积神经网络(CNN)的基本架构一般包括卷积层,池化层,全链层三大层次,其中不一样的层中可能还会包括一些非线性变化(RELU函数)、数据归一化处理、dropoout等。咱们常据说的LeNet-五、AlexNet、VGG、ResNet等都是卷积神经网络,并且都是由这些层组成,只是每一个网络的层数不同,所达到的分类效果也不同。

2.1. 卷积层

卷积层是整个神经网络中最重要的一层,该层最核心的部分为过滤器,或者称为卷积核,卷积核有大小和深度两个属性,大小经常使用的有3X三、5X5,也有11X11的卷积核,而深度通俗一点理解就是卷积核的个数。卷积核的大小和深度均由人工指定,而权重参数则在初始化的时候由程序随机生成,并在后期训练过程当中不断优化这些权重值,以达到最好的分类效果。卷积的过程就是用这些权重值不断的去乘这些图片的RGB值,以提取图片数据信息。下面的动图完美地诠释了卷积是怎么发生的:
2017-10-08-15-05-31
上面黄色3X3大小不停移动的就是卷积核,绿色部分是5X5的输入矩阵,粉色部分是卷积后的结果,称做特征值。从上面动图看出,卷积不只提取了图片信息,也能够达到降维效果。若是但愿卷积后的特征值维度和原图片一致,须要设置padding值(全零填充)为SAME(若是为VALID表示不填充),其中i为输入图片,k为卷积核大小,strides为移动步长(移动步长>1也能够达到降维的效果)。

tf.nn.conv2d(i, k,strides,padding='VALID')tf.nn.conv2d(i, k,strides,padding='VALID')

在卷积层中,过滤器中的参数是共享的,即一个过滤器中的参数值在对全部图片数据进行卷积过程当中保持不变,这样卷积层的参数个数就和图片大小无关,它只和过滤器的尺寸,深度,以及当前层节点的矩阵深度有关。好比,以手写图片为例,输入矩阵的维度是28X28X1,假设第一层卷积层使用的过滤器大小为5X5,深度为16,则该卷积层的参数个数为5X5X1X16+16=416个,而若是使用500个隐藏节点的全链层会有1.5百万个参数,相比之下,卷积层的参数个数远远小于全链层,这就是为何卷积网络普遍用于图片识别上的缘由。

对于卷积后的矩阵大小,有一个计算公式,若是使用了全0填充,则卷积后的矩阵大小为:

outlength=inlength/stridelength outlength=⌈inlength/stridelength⌉

outwidth=inwidth/stridewidth outwidth=⌈inwidth/stridewidth⌉

即输出矩阵的长等于输入矩阵长度除以长度方向上的步长,并向上取整数值;输出矩阵的宽度等于输入矩阵的宽度除以宽度方向上的步长,并向上取整数值。
若是不使用全0填充,则输出矩阵的大小为:
outlength=(inlengthfilterlength+1)/stridelength outlength=⌈(inlength−filterlength+1)/stridelength⌉

outwidth=(inwidthfilterwidth+1)/stridewidth outwidth=⌈(inwidth−filterwidth+1)/stridewidth⌉

卷积计算完成后,每每会加入一个修正线性单元ReLU函数,也就是把数据非线性化。为何要把数据进行非线性化呢,这是由于非线性表明了输入和输出的关系是一条曲线而不是直线,曲线可以刻画输入中更为复杂的变化。好比一个输入值大部分时间都很稳定,但有可能会在某个时间点出现极值,可是经过ReLU函数之后,数据变得平滑,这样以便对复杂的数据进行训练。
ReLU是分段线性的,当输入为非负时,输出将与输入相同;而当输入为负时,输出均为0。它的优势在于不受“梯度消失”的影响,且取值范围为[0,+∞];其缺点在于当使用了较大的学习速率时,易受达到饱和的神经元的影响。

2017-10-09-15-39-32

2.2. 池化层

卷积层后通常会加入池化层,池化层能够很是有效地缩小矩阵的尺寸,从而减小最后全链层中的参数,使用池化层既能够加快计算速度也有防止过拟合问题的做用。
池化层也存在一个过滤器,可是过滤器对于输入的数据的处理并非像卷积核对输入数据进行节点的加权和,而只是简单的计算最大值或者平均值。过滤器的大小、是否全0填充、步长等也是由人工指定,而深度跟卷积核深度不同,卷积层使用过滤器是横跨整个深度的,而池化层使用的过滤器只影响一个深度上的节点,在计算过程当中,池化层过滤器不只要在长和宽两个维度移动,还要在深度这个维度移动。使用最大值操做的池化层被称之为最大池化层,这种池化层使用得最多,使用平均值操做的池化层被称之为平均池化层,这种池化层的使用相对要少一点。
如下动图能够看到最大值池化层的计算过程:

2017-10-09-15-58-41

Tensorflow程序很容易就能够实现最大值池化层的操做:

 pool = tf.nn.max_pool(i, ksize=[1,3,3,1], stride=[1,2,2,1], padding='SAME') # i为输入矩阵 # ksize为过滤器尺寸,其中第一个和第四个值必须为1,表示过滤器不能够垮不一样的输入样列和节点矩阵深度。中间的两个值为尺寸,常使用2*2或3*3。 # stride为步长,第一个值和第四个值与ksize同样 # padding为全0填充,‘SAME’表示使用全0填充,‘VALID’表示不使用全0填充pool = tf.nn.max_pool(i, ksize=[1,3,3,1], stride=[1,2,2,1], padding='SAME') # i为输入矩阵 # ksize为过滤器尺寸,其中第一个和第四个值必须为1,表示过滤器不能够垮不一样的输入样列和节点矩阵深度。中间的两个值为尺寸,常使用2*2或3*3。 # stride为步长,第一个值和第四个值与ksize同样 # padding为全0填充,‘SAME’表示使用全0填充,‘VALID’表示不使用全0填充

2.3. 全链层

在KNN或线性分类中有对数据进行归一化处理,而在神经网络中,也会作数据归一化的处理,缘由和以前的同样,避免数据值大的节点对分类形成影响。归一化的目标在于将输入保持在一个可接受的范围内。例如,将输入归一化到[0.0,1.0]区间内。在卷积神经网络中,对数据归一化的处理咱们有可能放在数据正式输入到全链层以前或以后,或其余地方,每一个网络均可能不同。

全链层的做用就是进行正确的图片分类,不一样神经网络的全链层层数不一样,但做用确是相同的。输入到全链层的神经元个数经过卷积层和池化层的处理后大大的减小了,好比以AlexNet为例,一张227*227大小,颜色通道数为3的图片通过处理后,输入到全链层的神经元个数有4096个,最后softmax的输出,则能够根据实际分类标签数来定。

在全链层中,会使用dropout以随机的去掉一些神经元,这样可以比较有效地防止神经网络的过拟合。相对于通常如线性模型使用正则的方法来防止模型过拟合,而在神经网络中Dropout经过修改神经网络自己结构来实现。对于某一层神经元,经过定义的几率来随机删除一些神经元,同时保持输入层与输出层神经元的我的不变,而后按照神经网络的学习方法进行参数更新,下一次迭代中,从新随机删除一些神经元,直至训练结束。
2017-10-09-16-57-39

3. AlexNet

AlexNet是2012年ILSVRC比赛的冠军,它的出现直接打破了沉寂多年的图片识别领域(在1998年出现LeNet-5网络一直占据图片识别的领头地位),给该领域带来了新的契机,并一步步发展至今,甚至战胜了人类的识别精确度,惋惜的是2017年的ILSVRC举办方宣布从2018年起将取消该比赛,由于目前的神经网络精确度已经达到跟高的程度了。但深度学习的步伐不会中止,人们将在其余方面进行深刻的研究。

AlexNet是神经网络之父Hinton的学生Alex Krizhevsky开发完成,它总共有8层,其中有5个卷积层,3个全链层,附上最经典的AlexNet网络架构图,以下。Alex在他的论文中写到,他在处理图片的时候使用了两个GPU进行计算,所以,从图中看出,在卷积过程当中他作了分组的处理,可是因为硬件资源问题,咱们作的Alex网络是使用一个CPU进行计算的,但原理和他的同样,只是计算速度慢一点而已,对于大多数没有性能优良的GPU的人来讲,用咱们搭建好的网络,彻底可使用家用台式机进行训练。
2017-10-09-17-25-04

Alex在论文中写到他使用的输入图片大小为224 X 224 X 3,但咱们使用的图片尺寸为227 X 227 X 3,这个没有太大影响。AlexNet网络分为8层结构,前5层其实不彻底是卷积层,有些层还加入了池化层,并对数据进行标准化处理。下面简要介绍一下每一层:

第一层

卷积核 深度 步长
11 * 11 96 4 * 4
池化层过滤器 步长  
3 * 3 2 * 2  

第一层包含了卷积层、标准化操做和池化层,其中卷积层和池化层的参数在上表已给出。在Tensorflow中,搭建的部分代码程序为:

# 1st Layer: Conv (w ReLu) -> Lrn -> Pool conv1 = conv(X, 11, 11, 96, 4, 4, padding='VALID', name='conv1') norm1 = lrn(conv1, 2, 2e-05, 0.75, name='norm1') pool1 = max_pool(norm1, 3, 3, 2, 2, padding='VALID', name='pool1')# 1st Layer: Conv (w ReLu) -> Lrn -> Pool conv1 = conv(X, 11, 11, 96, 4, 4, padding='VALID', name='conv1') norm1 = lrn(conv1, 2, 2e-05, 0.75, name='norm1') pool1 = max_pool(norm1, 3, 3, 2, 2, padding='VALID', name='pool1')

第二层

卷积核 深度 步长
5 * 5 256 1 * 1
池化层过滤器 步长  
3 * 3 2 * 2  

第二层实际也包含了卷积层、标准化操做和池化层,其中卷积层和池化层的参数在上表已给出。在Tensorflow中,搭建的部分代码程序为:

# 2nd Layer: Conv (w ReLu) -> Lrn -> Pool with 2 groups conv2 = conv(pool1, 5, 5, 256, 1, 1, groups=2, name='conv2') norm2 = lrn(conv2, 2, 2e-05, 0.75, name='norm2') pool2 = max_pool(norm2, 3, 3, 2, 2, padding='VALID', name='pool2')# 2nd Layer: Conv (w ReLu) -> Lrn -> Pool with 2 groups conv2 = conv(pool1, 5, 5, 256, 1, 1, groups=2, name='conv2') norm2 = lrn(conv2, 2, 2e-05, 0.75, name='norm2') pool2 = max_pool(norm2, 3, 3, 2, 2, padding='VALID', name='pool2')

第三层

卷积核 深度 步长
3 * 3 384 1 * 1

第三层仅有一个卷积层,卷积核的相关信息如上表,在Tensorflow中的部分代码为:

# 3rd Layer: Conv (w ReLu) conv3 = conv(pool2, 3, 3, 384, 1, 1, name='conv3')# 3rd Layer: Conv (w ReLu) conv3 = conv(pool2, 3, 3, 384, 1, 1, name='conv3')

第四层

卷积核 深度 步长
3 * 3 384 1 * 1

第四层仅有一个卷积层,卷积核的相关信息如上表,该层与第三层很类似,只是把数据分红了2组进行处理,在Tensorflow中的部分代码为:

# 4th Layer: Conv (w ReLu) splitted into two groups conv4 = conv(conv3, 3, 3, 384, 1, 1, groups=2, name='conv4')# 4th Layer: Conv (w ReLu) splitted into two groups conv4 = conv(conv3, 3, 3, 384, 1, 1, groups=2, name='conv4')

第五层

卷积核 深度 步长
3 * 3 256 1 * 1
池化层过滤器 步长  
3 * 3 2 * 2  

第五层是最后一层卷积层,包含一个卷积层和一个池化层,卷积核和池化层过滤器的相关信息如上表,该层仍然把数据分红了2组进行处理,在Tensorflow中的部分代码为:

# 5th Layer: Conv (w ReLu) -> Pool splitted into two groups conv5 = conv(conv4, 3, 3, 256, 1, 1, groups=2, name='conv5') pool5 = max_pool(conv5, 3, 3, 2, 2, padding='VALID', name='pool5')# 5th Layer: Conv (w ReLu) -> Pool splitted into two groups conv5 = conv(conv4, 3, 3, 256, 1, 1, groups=2, name='conv5') pool5 = max_pool(conv5, 3, 3, 2, 2, padding='VALID', name='pool5')

第六层

第六层是全链层,卷积层输出的数据一共有4096个神经元,在进入第六层全链层后,首先作了数据的平滑处理,并随机删除了一些神经元,在Tensorflow中的部分代码为:

# 6th Layer: Flatten -> FC (w ReLu) -> Dropout flattened = tf.reshape(pool5, [-1, 6*6*256]) fc6 = fc(flattened, 6*6*256, 4096, name='fc6') dropout6 = dropout(fc6, self.KEEP_PROB)# 6th Layer: Flatten -> FC (w ReLu) -> Dropout flattened = tf.reshape(pool5, [-1, 6*6*256]) fc6 = fc(flattened, 6*6*256, 4096, name='fc6') dropout6 = dropout(fc6, self.KEEP_PROB)

第七层

第七层是全链层,也会作dropout处理,在Tensorflow中的部分代码为:

# 7th Layer: FC (w ReLu) -> Dropout fc7 = fc(dropout6, 4096, 4096, name='fc7') dropout7 = dropout(fc7, self.KEEP_PROB)# 7th Layer: FC (w ReLu) -> Dropout fc7 = fc(dropout6, 4096, 4096, name='fc7') dropout7 = dropout(fc7, self.KEEP_PROB)

第八层

第八层是全链层,在最后softmax函数输出的分类标签是根据实际分类状况来定义的,可能有2种,可能10种,可能120种等等,在Tensorflow中的部分代码为:

# 8th Layer: FC and return unscaled activations self.fc8 = fc(dropout7, 4096, self.NUM_CLASSES, relu=False, name='fc8')# 8th Layer: FC and return unscaled activations self.fc8 = fc(dropout7, 4096, self.NUM_CLASSES, relu=False, name='fc8')

4. 用Tensorflow搭建完整的AlexNet

在搭建完整的AlexNet以前,须要作一些准备工做,以方便后期作训练的时候观测网络的运行状况。首先就是配置Tensorboard,Tensorboard是一款可视化工具,能够用它来展示你的TensorFlow图像,绘制图像生成的定量指标图,观察loss函数的收敛状况,网络的精确度,以及附加数据等等,具体如何配置,网上也有不少讲解,这里就不详细讲述了;另外就是准备数据,imageNet官网上有不少图片数据能够供你们无偿使用,官网地址:http://image-net.org/download-images 。网上还有不少无偿使用的爬虫能够去爬取数据,总之,数据是训练的根本,在网络搭建好以前最好准备充分。准备好的数据放入当前训练项目的根目录下。

为了让各类需求的人可以复用AlexNet,咱们在Python类中定义了AlexNet,并把接口暴露出来,须要使用的人根据本身的状况调用网络,并输入数据以及分类标签个数等信息就能够开始训练数据了。要使用搭建好的网络进行训练,不只仅要利用网络,更是须要网络中的各项权重参数和偏置来达到更好的分类效果,目前,咱们使用的是别人已经训练好的参数,全部的参数数据存放在bvlc_alexnet.npy这个文件中,下载地址为:http://www.cs.toronto.edu/~guerzhoy/tf_alexnet/ ,下载后放入当前训练项目的根目录下便可。若是你有充分的时间和优越的硬件资源,你也能够本身训练参数,并把这些参数存储起来供之后使用,可是该bvlc_alexnet.npy文件中的参数是imageNet训练好了的,使用这些参数训练的模型精确度比咱们以前训练的要高。
在Tensorflow中,定义加载参数的程序代码以下,默认的参数就是bvlc_alexnet.npy中存储的权重和偏置值。

def load_initial_weights(self, session): """Load weights from file into network.""" # Load the weights into memory weights_dict = np.load(self.WEIGHTS_PATH, encoding='bytes').item() # Loop over all layer names stored in the weights dict for op_name in weights_dict: # Check if layer should be trained from scratch if op_name not in self.SKIP_LAYER: with tf.variable_scope(op_name, reuse=True): # Assign weights/biases to their corresponding tf variable for data in weights_dict[op_name]: # Biases if len(data.shape) == 1: var = tf.get_variable('biases', trainable=False) session.run(var.assign(data)) # Weights else: var = tf.get_variable('weights', trainable=False) session.run(var.assign(data))def load_initial_weights(self, session): """Load weights from file into network.""" # Load the weights into memory weights_dict = np.load(self.WEIGHTS_PATH, encoding='bytes').item() # Loop over all layer names stored in the weights dict for op_name in weights_dict: # Check if layer should be trained from scratch if op_name not in self.SKIP_LAYER: with tf.variable_scope(op_name, reuse=True): # Assign weights/biases to their corresponding tf variable for data in weights_dict[op_name]: # Biases if len(data.shape) == 1: var = tf.get_variable('biases', trainable=False) session.run(var.assign(data)) # Weights else: var = tf.get_variable('weights', trainable=False) session.run(var.assign(data))

在上一节讲述AlexNet的架构的时,曾出现过数据分组处理,这里用程序来描述一下在一个CPU状况下,如何把数据进行分组处理。数据的分组处理都在卷积层中发生,所以首先一个卷积函数,因为在第一层卷积没有分组,因此在函数中须要作分组的判断,若是没有分组,输入数据和权重直接作卷积运算;若是有分组,则把输入数据和权重先划分后作卷积运算,卷积结束后再用concat()合并起来,这就是分组的具体操做。

def conv(x, filter_height, filter_width, num_filters, stride_y, stride_x, name,padding='SAME', groups=1): """Create a convolution layer.""" # Get number of input channels input_channels = int(x.get_shape()[-1]) # Create lambda function for the convolution convolve = lambda i, k: tf.nn.conv2d(i, k, strides=[1, stride_y, stride_x, 1], padding=padding) with tf.variable_scope(name) as scope: # Create tf variables for the weights and biases of the conv layer weights = tf.get_variable('weights', shape=[filter_height, filter_width, input_channels/groups, num_filters]) biases = tf.get_variable('biases', shape=[num_filters]) if groups == 1: conv = convolve(x, weights) # In the cases of multiple groups, split inputs & weights and else: # Split input and weights and convolve them separately input_groups = tf.split(axis=3, num_or_size_splits=groups, value=x) weight_groups = tf.split(axis=3, num_or_size_splits=groups, value=weights) output_groups = [convolve(i, k) for i, k in zip(input_groups, weight_groups)] # Concat the convolved output together again conv = tf.concat(axis=3, values=output_groups) # Add biases bias = tf.reshape(tf.nn.bias_add(conv, biases), tf.shape(conv)) # Apply relu function relu = tf.nn.relu(bias, name=scope.name) return reludef conv(x, filter_height, filter_width, num_filters, stride_y, stride_x, name,padding='SAME', groups=1): """Create a convolution layer.""" # Get number of input channels input_channels = int(x.get_shape()[-1]) # Create lambda function for the convolution convolve = lambda i, k: tf.nn.conv2d(i, k, strides=[1, stride_y, stride_x, 1], padding=padding) with tf.variable_scope(name) as scope: # Create tf variables for the weights and biases of the conv layer weights = tf.get_variable('weights', shape=[filter_height, filter_width, input_channels/groups, num_filters]) biases = tf.get_variable('biases', shape=[num_filters]) if groups == 1: conv = convolve(x, weights) # In the cases of multiple groups, split inputs & weights and else: # Split input and weights and convolve them separately input_groups = tf.split(axis=3, num_or_size_splits=groups, value=x) weight_groups = tf.split(axis=3, num_or_size_splits=groups, value=weights) output_groups = [convolve(i, k) for i, k in zip(input_groups, weight_groups)] # Concat the convolved output together again conv = tf.concat(axis=3, values=output_groups) # Add biases bias = tf.reshape(tf.nn.bias_add(conv, biases), tf.shape(conv)) # Apply relu function relu = tf.nn.relu(bias, name=scope.name) return relu

对于AlexNet中池化层,全链层的代码在alexnet.py已经所有定义好了,这里就不一一列出来了。接着开始如何在Tensorflow中导入图片,在图片数据量大的状况下,Tensorflow会建议把数据转换成tfrecords文件,而后在导入到网络中运算,这样的好处是能够加快计算速度,节约内存空间。但咱们没有这样作,由于在训练网络的时候咱们没有发现转换成tfrecords文件就明显提升了计算速度,因此这里直接把原生的图片直接转化成三维数据输入到网络中。这样作代码还要简短一点,而图片也是预先存储在硬盘中,须要训练的那一部分再从硬盘中读取到内存中,并无浪费内存资源。

在Python类中定义图片生成器,须要的参数有图片URL,实际的标签向量和标签个数,batch_size等。首先打乱整个训练集图片的顺序,由于图片名多是按照某种规律来定义的,打乱图片顺序能够帮助咱们更好的训练网络。完成这一步后就能够把图片从RGB色转换成BRG三维数组。

 class ImageDataGenerator(object): def __init__(self, images, labels, batch_size, num_classes, shuffle=True, buffer_size=1000): self.img_paths = images self.labels = labels self.num_classes = num_classes self.data_size = len(self.labels) self.pointer = 0 # 打乱图片顺序 if shuffle: self._shuffle_lists() self.img_paths = convert_to_tensor(self.img_paths, dtype=dtypes.string) self.labels = convert_to_tensor(self.labels, dtype=dtypes.int32) data = Dataset.from_tensor_slices((self.img_paths, self.labels)) data = data.map(self._parse_function_train, num_threads=8, output_buffer_size=100 * batch_size) data = data.batch(batch_size) self.data = data """打乱图片顺序""" def _shuffle_lists(self): path = self.img_paths labels = self.labels permutation = np.random.permutation(self.data_size) self.img_paths = [] self.labels = [] for i in permutation: self.img_paths.append(path[i]) self.labels.append(labels[i]) """把图片生成三维数组,以及把标签转化为向量""" def _parse_function_train(self, filename, label): one_hot = tf.one_hot(label, self.num_classes) img_string = tf.read_file(filename) img_decoded = tf.image.decode_png(img_string, channels=3) img_resized = tf.image.resize_images(img_decoded, [227, 227]) img_centered = tf.subtract(img_resized, VGG_MEAN) img_bgr = img_centered[:, :, ::-1] return img_bgr, one_hotclass ImageDataGenerator(object): def __init__(self, images, labels, batch_size, num_classes, shuffle=True, buffer_size=1000): self.img_paths = images self.labels = labels self.num_classes = num_classes self.data_size = len(self.labels) self.pointer = 0 # 打乱图片顺序 if shuffle: self._shuffle_lists() self.img_paths = convert_to_tensor(self.img_paths, dtype=dtypes.string) self.labels = convert_to_tensor(self.labels, dtype=dtypes.int32) data = Dataset.from_tensor_slices((self.img_paths, self.labels)) data = data.map(self._parse_function_train, num_threads=8, output_buffer_size=100 * batch_size) data = data.batch(batch_size) self.data = data """打乱图片顺序""" def _shuffle_lists(self): path = self.img_paths labels = self.labels permutation = np.random.permutation(self.data_size) self.img_paths = [] self.labels = [] for i in permutation: self.img_paths.append(path[i]) self.labels.append(labels[i]) """把图片生成三维数组,以及把标签转化为向量""" def _parse_function_train(self, filename, label): one_hot = tf.one_hot(label, self.num_classes) img_string = tf.read_file(filename) img_decoded = tf.image.decode_png(img_string, channels=3) img_resized = tf.image.resize_images(img_decoded, [227, 227]) img_centered = tf.subtract(img_resized, VGG_MEAN) img_bgr = img_centered[:, :, ::-1] return img_bgr, one_hot

网络搭建完成,数据准备就绪,最后就是开始训练了。因为网络和图片生成器是能够复用的,在训练图片的时候须要用户根据本身的实际状况编写代码调用网络和图片生成器模块,同时定义好损失函数和优化器,以及须要在Tensorboard中观测的各项指标等等操做。下面一节咱们将开始进行网络训练。

5. 用AlexNet识别猫狗图片

5.1. 定义分类

如上一节讲的,datagenerator.py(图片转换模块)和alexnet.py(AlexNet网络模块)已经搭建好了,你在使用的时候无需作修改。如今你只须要根据本身的分类需求编写精调代码,如finetune.py中所示。
假设有3万张猫狗图片训练集和3000张测试集,它们大小不一。咱们的目的是使用AlexNet正确的分类猫和狗两种动物,所以,类别标签个数只有2个,并用0表明猫,1表明狗。若是你须要分类其余的动物或者物品,或者anything,你须要标注好图片的实际标签,定义好图片Tensorboard存放的目录,以及训练好的模型和参数的存放目录等等。就像这样:

import os import numpy as np import tensorflow as tf from alexnet import AlexNet from datagenerator import ImageDataGenerator from datetime import datetime import glob from tensorflow.contrib.data import Iterator learning_rate = 1e-4 # 学习率 num_epochs = 100 # 代的个数 batch_size = 1024 # 一次性处理的图片张数 dropout_rate = 0.5 # dropout的几率 num_classes = 2 # 类别标签 train_layers = ['fc8', 'fc7', 'fc6'] # 训练层,即三个全链层 display_step = 20 # 显示间隔次数 filewriter_path = "./tmp/tensorboard" # 存储tensorboard文件 checkpoint_path = "./tmp/checkpoints" # 训练好的模型和参数存放目录 if not os.path.isdir(checkpoint_path): #若是没有存放模型的目录,程序自动生成 os.mkdir(checkpoint_path)import os import numpy as np import tensorflow as tf from alexnet import AlexNet from datagenerator import ImageDataGenerator from datetime import datetime import glob from tensorflow.contrib.data import Iterator learning_rate = 1e-4 # 学习率 num_epochs = 100 # 代的个数 batch_size = 1024 # 一次性处理的图片张数 dropout_rate = 0.5 # dropout的几率 num_classes = 2 # 类别标签 train_layers = ['fc8', 'fc7', 'fc6'] # 训练层,即三个全链层 display_step = 20 # 显示间隔次数 filewriter_path = "./tmp/tensorboard" # 存储tensorboard文件 checkpoint_path = "./tmp/checkpoints" # 训练好的模型和参数存放目录 if not os.path.isdir(checkpoint_path): #若是没有存放模型的目录,程序自动生成 os.mkdir(checkpoint_path)

接着调用图片生成器,来生成图片数据,并初始化数据:

 train_image_path = 'train/' # 指定训练集数据路径(根据实际状况指定训练数据集的路径) test_image_cat_path = 'test/cat/' # 指定测试集数据路径(根据实际状况指定测试数据集的路径) test_image_dog_path = 'test/dog/' label_path = [] test_label = [] # 打开训练数据集目录,读取所有图片,生成图片路径列表 image_path = np.array(glob.glob(train_image_path + 'cat.*.jpg')).tolist() image_path_dog = np.array(glob.glob(train_image_path + 'dog.*.jpg')).tolist() image_path[len(image_path):len(image_path)] = image_path_dog for i in range(len(image_path)): if 'dog' in image_path[i]: label_path.append(1) else: label_path.append(0) # 打开测试数据集目录,读取所有图片,生成图片路径列表 test_image = np.array(glob.glob(test_image_cat_path + '*.jpg')).tolist() test_image_path_dog = np.array(glob.glob(test_image_dog_path + '*.jpg')).tolist() test_image[len(test_image):len(test_image)] = test_image_path_dog for i in range(len(test_image)): if i < 1500: test_label.append(0) else: test_label.append(1) # 调用图片生成器,把训练集图片转换成三维数组 tr_data = ImageDataGenerator( images=image_path, labels=label_path, batch_size=batch_size, num_classes=num_classes) # 调用图片生成器,把测试集图片转换成三维数组 test_data = ImageDataGenerator( images=test_image, labels=test_label, batch_size=batch_size, num_classes=num_classes, shuffle=False) # 定义迭代器 iterator = Iterator.from_structure(tr_data.data.output_types, tr_data.data.output_shapes) # 定义每次迭代的数据 next_batch = iterator.get_next() # 初始化数据 training_initalize = iterator.make_initializer(tr_data.data) testing_initalize = iterator.make_initializer(test_data.data)train_image_path = 'train/' # 指定训练集数据路径(根据实际状况指定训练数据集的路径) test_image_cat_path = 'test/cat/' # 指定测试集数据路径(根据实际状况指定测试数据集的路径) test_image_dog_path = 'test/dog/' label_path = [] test_label = [] # 打开训练数据集目录,读取所有图片,生成图片路径列表 image_path = np.array(glob.glob(train_image_path + 'cat.*.jpg')).tolist() image_path_dog = np.array(glob.glob(train_image_path + 'dog.*.jpg')).tolist() image_path[len(image_path):len(image_path)] = image_path_dog for i in range(len(image_path)): if 'dog' in image_path[i]: label_path.append(1) else: label_path.append(0) # 打开测试数据集目录,读取所有图片,生成图片路径列表 test_image = np.array(glob.glob(test_image_cat_path + '*.jpg')).tolist() test_image_path_dog = np.array(glob.glob(test_image_dog_path + '*.jpg')).tolist() test_image[len(test_image):len(test_image)] = test_image_path_dog for i in range(len(test_image)): if i < 1500: test_label.append(0) else: test_label.append(1) # 调用图片生成器,把训练集图片转换成三维数组 tr_data = ImageDataGenerator( images=image_path, labels=label_path, batch_size=batch_size, num_classes=num_classes) # 调用图片生成器,把测试集图片转换成三维数组 test_data = ImageDataGenerator( images=test_image, labels=test_label, batch_size=batch_size, num_classes=num_classes, shuffle=False) # 定义迭代器 iterator = Iterator.from_structure(tr_data.data.output_types, tr_data.data.output_shapes) # 定义每次迭代的数据 next_batch = iterator.get_next() # 初始化数据 training_initalize = iterator.make_initializer(tr_data.data) testing_initalize = iterator.make_initializer(test_data.data)

训练数据准备好之后,让数据经过AlexNet。

 x = tf.placeholder(tf.float32, [batch_size, 227, 227, 3]) y = tf.placeholder(tf.float32, [batch_size, num_classes]) keep_prob = tf.placeholder(tf.float32) # dropout几率 # 图片数据经过AlexNet网络处理 model = AlexNet(x, keep_prob, num_classes, train_layers) # 定义咱们须要训练的全连层的变量列表 var_list = [v for v in tf.trainable_variables() if v.name.split('/')[0] in train_layers] # 执行整个网络图 score = model.fc8x = tf.placeholder(tf.float32, [batch_size, 227, 227, 3]) y = tf.placeholder(tf.float32, [batch_size, num_classes]) keep_prob = tf.placeholder(tf.float32) # dropout几率 # 图片数据经过AlexNet网络处理 model = AlexNet(x, keep_prob, num_classes, train_layers) # 定义咱们须要训练的全连层的变量列表 var_list = [v for v in tf.trainable_variables() if v.name.split('/')[0] in train_layers] # 执行整个网络图 score = model.fc8

接着固然就是定义损失函数,优化器。整个网络须要优化三层全链层的参数,同时在优化参数过程当中,使用的是梯度降低算法,而不是反向传播算法。

# 损失函数 loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=score, labels=y)) # 定义须要精调的每一层的梯度 gradients = tf.gradients(loss, var_list) gradients = list(zip(gradients, var_list)) # 优化器,采用梯度降低算法进行优化 optimizer = tf.train.GradientDescentOptimizer(learning_rate) # 须要精调的每一层都采用梯度降低算法优化参数 train_op = optimizer.apply_gradients(grads_and_vars=gradients) # 定义网络精确度 with tf.name_scope("accuracy"): correct_pred = tf.equal(tf.argmax(score, 1), tf.argmax(y, 1)) accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 如下几步是须要在Tensorboard中观测loss的收敛状况和网络的精确度而定义的 tf.summary.scalar('cross_entropy', loss) tf.summary.scalar('accuracy', accuracy) merged_summary = tf.summary.merge_all() writer = tf.summary.FileWriter(filewriter_path) saver = tf.train.Saver()# 损失函数 loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=score, labels=y)) # 定义须要精调的每一层的梯度 gradients = tf.gradients(loss, var_list) gradients = list(zip(gradients, var_list)) # 优化器,采用梯度降低算法进行优化 optimizer = tf.train.GradientDescentOptimizer(learning_rate) # 须要精调的每一层都采用梯度降低算法优化参数 train_op = optimizer.apply_gradients(grads_and_vars=gradients) # 定义网络精确度 with tf.name_scope("accuracy"): correct_pred = tf.equal(tf.argmax(score, 1), tf.argmax(y, 1)) accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 如下几步是须要在Tensorboard中观测loss的收敛状况和网络的精确度而定义的 tf.summary.scalar('cross_entropy', loss) tf.summary.scalar('accuracy', accuracy) merged_summary = tf.summary.merge_all() writer = tf.summary.FileWriter(filewriter_path) saver = tf.train.Saver()

最后,训练数据:

 # 定义一代的迭代次数 train_batches_per_epoch = int(np.floor(tr_data.data_size / batch_size)) test_batches_per_epoch = int(np.floor(test_data.data_size / batch_size)) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) # 把模型图加入Tensorboard writer.add_graph(sess.graph) # 把训练好的权重加入未训练的网络中 model.load_initial_weights(sess) print("{} Start training...".format(datetime.now())) print("{} Open Tensorboard at --logdir {}".format(datetime.now(), filewriter_path)) # 总共训练100代 for epoch in range(num_epochs): sess.run(iterator.make_initializer(tr_data.data)) print("{} Epoch number: {} start".format(datetime.now(), epoch + 1)) # 开始训练每一代,一代的次数为train_batches_per_epoch的值 for step in range(train_batches_per_epoch): img_batch, label_batch = sess.run(next_batch) sess.run(optimizer, feed_dict={x: img_batch, y: label_batch, keep_prob: dropout_rate}) if step % display_step == 0: s = sess.run(merged_summary, feed_dict={x: img_batch, y: label_batch, keep_prob: 1.}) writer.add_summary(s, epoch * train_batches_per_epoch + step)# 定义一代的迭代次数 train_batches_per_epoch = int(np.floor(tr_data.data_size / batch_size)) test_batches_per_epoch = int(np.floor(test_data.data_size / batch_size)) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) # 把模型图加入Tensorboard writer.add_graph(sess.graph) # 把训练好的权重加入未训练的网络中 model.load_initial_weights(sess) print("{} Start training...".format(datetime.now())) print("{} Open Tensorboard at --logdir {}".format(datetime.now(), filewriter_path)) # 总共训练100代 for epoch in range(num_epochs): sess.run(iterator.make_initializer(tr_data.data)) print("{} Epoch number: {} start".format(datetime.now(), epoch + 1)) # 开始训练每一代,一代的次数为train_batches_per_epoch的值 for step in range(train_batches_per_epoch): img_batch, label_batch = sess.run(next_batch) sess.run(optimizer, feed_dict={x: img_batch, y: label_batch, keep_prob: dropout_rate}) if step % display_step == 0: s = sess.run(merged_summary, feed_dict={x: img_batch, y: label_batch, keep_prob: 1.}) writer.add_summary(s, epoch * train_batches_per_epoch + step)

训练完成后须要验证模型的精确度,这个时候就得用上测试数据集了。

 # 测试模型精确度 print("{} Start validation".format(datetime.now())) sess.run(testing_initalize) test_acc = 0. test_count = 0 for _ in range(test_batches_per_epoch): img_batch, label_batch = sess.run(next_batch) acc = sess.run(accuracy, feed_dict={x: img_batch, y: label_batch, keep_prob: 1.0}) test_acc += acc test_count += 1 test_acc /= test_count print("{} Validation Accuracy = {:.4f}".format(datetime.now(), test_acc))# 测试模型精确度 print("{} Start validation".format(datetime.now())) sess.run(testing_initalize) test_acc = 0. test_count = 0 for _ in range(test_batches_per_epoch): img_batch, label_batch = sess.run(next_batch) acc = sess.run(accuracy, feed_dict={x: img_batch, y: label_batch, keep_prob: 1.0}) test_acc += acc test_count += 1 test_acc /= test_count print("{} Validation Accuracy = {:.4f}".format(datetime.now(), test_acc))

最后把训练好的模型持久化。

# 把训练好的模型存储起来 print("{} Saving checkpoint of model...".format(datetime.now())) checkpoint_name = os.path.join(checkpoint_path,'model_epoch' + str(epoch + 1) + '.ckpt') save_path = saver.save(sess, checkpoint_name) print("{} Epoch number: {} end".format(datetime.now(), epoch + 1))# 把训练好的模型存储起来 print("{} Saving checkpoint of model...".format(datetime.now())) checkpoint_name = os.path.join(checkpoint_path,'model_epoch' + str(epoch + 1) + '.ckpt') save_path = saver.save(sess, checkpoint_name) print("{} Epoch number: {} end".format(datetime.now(), epoch + 1))

到此为止,一个完整的AlexNet就搭建完成了。在准备好训练集和测试集数据后,下面咱们开始训练网络。

5.2. 训练网络

咱们总共训练了100代,使用CPU计算进行计算,在台式机上跑了一天左右,完成了3万张图片的训练和3000张图片的测试,网络的识别精确度为71.25%,这个结果不是很好,可能与数据量少有关系。若是你有上十万张的数据集,再增长训练次数,相信你网络的精度应该比咱们训练的还要好。下面看看网络的计算图,这是Tensorboard中记录下的,经过该图,你能够对整个网络的架构及运行一目了然。

2017-10-16-13-54-41

5.3. 验证

网络训练好了之后,固然咱们想火烧眉毛的试试咱们网络。首先咱们仍是得编写本身的验证代码:

import tensorflow as tf from alexnet import AlexNet # import训练好的网络 import matplotlib.pyplot as plt class_name = ['cat', 'dog'] # 自定义猫狗标签 def test_image(path_image, num_class, weights_path='Default'): # 把新图片进行转换 img_string = tf.read_file(path_image) img_decoded = tf.image.decode_png(img_string, channels=3) img_resized = tf.image.resize_images(img_decoded, [227, 227]) img_resized = tf.reshape(img_resized, shape=[1, 227, 227, 3]) # 图片经过AlexNet model = AlexNet(img_resized, 0.5, 2, skip_layer='', weights_path=weights_path) score = tf.nn.softmax(model.fc8) max = tf.arg_max(score, 1) saver = tf.train.Saver() with tf.Session() as sess: sess.run(tf.global_variables_initializer()) saver.restore(sess, "./tmp/checkpoints/model_epoch10.ckpt") # 导入训练好的参数 # score = model.fc8 print(sess.run(model.fc8)) prob = sess.run(max)[0] # 在matplotlib中观测分类结果 plt.imshow(img_decoded.eval()) plt.title("Class:" + class_name[prob]) plt.show() test_image('./test/20.jpg', num_class=2) # 输入一张新图片import tensorflow as tf from alexnet import AlexNet # import训练好的网络 import matplotlib.pyplot as plt class_name = ['cat', 'dog'] # 自定义猫狗标签 def test_image(path_image, num_class, weights_path='Default'): # 把新图片进行转换 img_string = tf.read_file(path_image) img_decoded = tf.image.decode_png(img_string, channels=3) img_resized = tf.image.resize_images(img_decoded, [227, 227]) img_resized = tf.reshape(img_resized, shape=[1, 227, 227, 3]) # 图片经过AlexNet model = AlexNet(img_resized, 0.5, 2, skip_layer='', weights_path=weights_path) score = tf.nn.softmax(model.fc8) max = tf.arg_max(score, 1) saver = tf.train.Saver() with tf.Session() as sess: sess.run(tf.global_variables_initializer()) saver.restore(sess, "./tmp/checkpoints/model_epoch10.ckpt") # 导入训练好的参数 # score = model.fc8 print(sess.run(model.fc8)) prob = sess.run(max)[0] # 在matplotlib中观测分类结果 plt.imshow(img_decoded.eval()) plt.title("Class:" + class_name[prob]) plt.show() test_image('./test/20.jpg', num_class=2) # 输入一张新图片

在网上任意下载10张猫狗图片来进行验证,有三张图片识别错误(以下图),验证的精确度70%,效果不是很理想。可是若是你感兴趣,你能够下载咱们的代码,用本身的训练集来试试,

源码地址为:https://github.com/stephen-v/tensorflow_alexnet_classify