当前位置: 首页 > 编程日记 > 正文

AlexNet 网络详解及Tensorflow实现源码

版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 1. 图片数据处理
  • 2. 卷积神经网络
    • 2.1. 卷积层
    • 2.2. 池化层
    • 2.3. 全链层
  • 3. AlexNet
  • 4. 用Tensorflow搭建完整的AlexNet
  • 5. 用AlexNet识别猫狗图片
    • 5.1. 定义分类
    • 5.2. 训练网络
    • 5.3. 验证

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)--resultarray([[[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-5、AlexNet、VGG、ResNet等都是卷积神经网络,而且都是由这些层组成,只是每个网络的层数不一样,所达到的分类效果也不一样。

2.1. 卷积层

卷积层是整个神经网络中最重要的一层,该层最核心的部分为过滤器,或者称为卷积核,卷积核有大小和深度两个属性,大小常用的有3X3、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')

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

对于卷积后的矩阵大小,有一个计算公式,如果使用了全0填充,则卷积后的矩阵大小为:
\[out_{length}=\left \lceil in_{length}/stride_{length} \right \rceil\]

\[out_{width}=\left \lceil in_{width}/stride_{width} \right \rceil\]
即输出矩阵的长等于输入矩阵长度除以长度方向上的步长,并向上取整数值;输出矩阵的宽度等于输入矩阵的宽度除以宽度方向上的步长,并向上取整数值。
如果不使用全0填充,则输出矩阵的大小为:
\[out_{length}=\left \lceil (in_{length}-filter_{length}+1)/stride_{length} \right \rceil\]

\[out_{width}=\left \lceil (in_{width}-filter_{width}+1)/stride_{width} \right \rceil\]
卷积计算完成后,往往会加入一个修正线性单元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填充

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 * 11964 * 4
池化层过滤器步长
3 * 32 * 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')

第二层

卷积核深度步长
5 * 52561 * 1
池化层过滤器步长
3 * 32 * 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')

第三层

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

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

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

第四层

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

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

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

第五层

卷积核深度步长
3 * 32561 * 1
池化层过滤器步长
3 * 32 * 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')

第六层

第六层是全链层,卷积层输出的数据一共有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)

第七层

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

# 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')

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 memoryweights_dict = np.load(self.WEIGHTS_PATH, encoding='bytes').item()# Loop over all layer names stored in the weights dictfor op_name in weights_dict:# Check if layer should be trained from scratchif op_name not in self.SKIP_LAYER:with tf.variable_scope(op_name, reuse=True):# Assign weights/biases to their corresponding tf variablefor data in weights_dict[op_name]:# Biasesif len(data.shape) == 1:var = tf.get_variable('biases', trainable=False)session.run(var.assign(data))# Weightselse: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 channelsinput_channels = int(x.get_shape()[-1])# Create lambda function for the convolutionconvolve = 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 layerweights = 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 andelse:# Split input and weights and convolve them separatelyinput_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 againconv = tf.concat(axis=3, values=output_groups)# Add biasesbias = tf.reshape(tf.nn.bias_add(conv, biases), tf.shape(conv))# Apply relu functionrelu = 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 = imagesself.labels = labelsself.num_classes = num_classesself.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_pathslabels = self.labelspermutation = 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 Iteratorlearning_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)

训练数据准备好以后,让数据通过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.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()

最后,训练数据:


# 定义一代的迭代次数
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())# 把模型图加入Tensorboardwriter.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 = 0for _ 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 += acctest_count += 1test_acc /= test_countprint("{} 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))

到此为止,一个完整的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 pltclass_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])# 图片通过AlexNetmodel = 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.fc8print(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

2017-10-18-10-16-50

2017-10-18-10-18-37

2017-10-18-10-19-57

2017-10-18-10-21-22

2017-10-18-10-23-09

2017-10-18-10-27-53

2017-10-18-10-26-36

2017-10-18-10-29-58

2017-10-18-10-33-15
2017-10-18-10-38-02

作者:帅虫哥 出处: http://www.cnblogs.com/vipyoumay/p/7686230.html

转载于:https://www.cnblogs.com/vipyoumay/p/7686230.html

相关文章:

.net反射详解(转)

摘自&#xff1a;http://www.cnblogs.com/knowledgesea/archive/2013/03/02/2935920.html 概述反射 通过反射可以提供类型信息&#xff0c;从而使得我们开发人员在运行时能够利用这些信息构造和使用对象。 反射机制允许程序在执行过程中动态地添加各种功能。 运行时类型标识 …

C# 多网卡 Server Listen

VC和BCB中做一个Server的监听程序,只需要指定端口,然后监听(Listen)就行了.在C#找不到这个函数了,慢慢看MSDN,怎么需要指定IP和Port才能监听,那么多网卡的机器应该怎么写程序呢?下面的程序可以解释怎么去做. TcpListener 类别会提供简易的方法&#xff0c;用以在封锁的同步模式…

赠书 | 一文了解预训练语言模型

来源 | 博文视点头图 | 下载于视觉中国近年来&#xff0c;在深度学习和大数据的支撑下&#xff0c;自然语言处理技术迅猛发展。而预训练语言模型把自然语言处理带入了一个新的阶段&#xff0c;也得到了工业界的广泛关注。通过大数据预训练加小数据微调&#xff0c;自然语言处理…

写了六个相同功能的函数之后,我学到了什么

本文讲的是写了六个相同功能的函数之后&#xff0c;我学到了什么&#xff0c;几周之前&#xff0c;一个社区在 Free Code Camp’s Forum 上发起了非官方的算法大赛。 这个题目看似很简单&#xff1a;返回小于数字 N 的所有 3 或者 5 的倍数的和&#xff0c;N 是函数的参数。 但…

libevent介绍

libevent是一款事件驱动的网络开发包 由于采用 c 语言开发 体积小巧&#xff0c;跨平台&#xff0c;速度极快。 通常我们在建立服务器的处理模型的时候,主要是下面集中模型;(1) a new Connection 进来&#xff0c;用 fork() 产生一个 Process 处理。 (2) a new Connecti…

蓝色起源载人火箭7月首飞,贝索斯即将实现儿时愿望

整理 | 寇雪芹出品 | AI 科技大本营&#xff08;ID:rgznai100&#xff09;头图 | 下载于ICphoto美国当地时间6月7日早&#xff0c;亚马逊创始人、世界首富贝索斯&#xff08;Jeff Bezos&#xff09;在社交媒体上发帖表示&#xff0c;自己将在7月20日乘坐蓝色起源&#xff08;Bl…

使用jquery.more.js来实现点击底部更多后, 底部加载出新的数据

<div class"bus-nav-bar ft12"><div class"navt bor-r-c pos-rel {if $int 0}fwbold{/if}"><a href"portal.php?modmerchant&actionvoucherlist&int0">全部订单</a><em class"pos-abs"></…

ios开发学习-手势交互(Gesture)效果源码分享

qianqianlianmengios开发学习-手势交互&#xff08;Gesture&#xff09;效果源码分享 All Around Pull View 介绍&#xff1a;实现视图四个方向&#xff08;上下左右&#xff09;都能够拖动更新&#xff08;pull to refresh&#xff09;。 编译测试&#xff0c;测试环境…

通过C#实现集合类纵览.NET Collections及相关技术

概述&#xff1a;在真正的对象化开发项目中&#xff0c;我们通常会将常用的业务实体抽象为特定的类&#xff0c;如Employee、Customer、Contact等&#xff0c;而多数的类之间会存在着相应的关联或依存关系&#xff0c;如Employee和Customer通过Contact而产生关联、Contact是依赖…

TIOBE 6 月榜单: Python 有望超越 C 语言成为第一名

整理 | 苏宓出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;头图 | 下载于ICphotoTIOBE 官方最新发布了 6 月的编程语言榜单&#xff0c;这个月榜单中又有怎样的发展趋势&#xff1f;Python 有望成为第一名在本月榜单中&#xff0c;位居第二名的 Python 与第一名 C…

使用dom4j解析XML例子

包括三个文件&#xff1a;studentInfo.xml(待解析的xml文件), Dom4jReadExmple.java(解析的主要类), TestDom4jReadExmple.java(测试解析的结果) 代码运行前需先导入dom4j架包。 studentInfo.xml文件&#xff08;该文件放在本项目目录下&#xff09;内容如下&#xff1a; <?…

mkdir、rmdir命令、head、tail命令

mkdir-p 递归创建目录11里面都是空目录rmdir删除空目录 -p 当子目录被删除后使它也成为空目录的话&#xff0c;则一并删除步骤&#xff1a;先删除11/22/33 发现22目录空了&#xff0c;因为33删了&#xff0c;于是再删22&#xff0c;空了再删11head默认是前10行 –n指定几行tai…

Linux Find 命令精通指南

作者&#xff1a;Sheryl Calish Linux find 命令是所有 Linux 命令中最有用的一个&#xff0c;同时也是最混乱的一个。它很难&#xff0c;因为它的语法与其他 Linux 命令的标准语法不同。但是&#xff0c;它很强大&#xff0c;因为它允许您按文件名、文件类型、用户甚至是时间戳…

【安全运维】 linux 系统账户,网络,简易安全加固方案(第一部分),经测试可行...

前言讲到linux系统账户的管理以及安全&#xff0c;就必须涉及 /etc/passwd /etc/shadow 这2个文件这里以截图中文字说明的方式&#xff0c;来分析这2个文件的内容&#xff0c;并且给出一些实用的安全加固方案注意&#xff0c;本文会持续更新&#xff0c;后续加入的内容都以直…

不用深度学习,怎么提取图像特征?

来源 | 小白学视觉头图 | 下载于ICphoto图像分类是数据科学中最热门的领域之一&#xff0c;在本文中&#xff0c;我们将分享一些将图像转换为特征向量的技术&#xff0c;可以在每个分类模型中使用。VATboxVATbox&#xff0c;作为n一个我们所暗示的&#xff0c;涉及增值税问题&a…

课程第五天内容《基础交换 五》

2019独角兽企业重金招聘Python工程师标准>>> 以太网/LAN(local area network )的相关概念&#xff1a; 问题&#xff1a; 信号传输距离有限&#xff1b; 解决方案&#xff1a; 中继器 - 放大电信号&#xff0c;延长信息的传输距离&#xff1…

C#综合揭秘——Entity Framework 并发处理详解

引言 在软件开发过程中&#xff0c;并发控制是确保及时纠正由并发操作导致的错误的一种机制。从 ADO.NET 到 LINQ to SQL 再到如今的 ADO.NET Entity Framework&#xff0c;.NET 都为并发控制提供好良好的支持方案。 并发处理方式一般分为乐观必并发与悲观必并发两种&#xff0…

@2021高考生,用 Python 分析专业“钱景”

来源 | 关于数据分析于可视化头图 | 下载于ICphoto2021年的高考在昨日拉开帷幕&#xff0c;十年的寒窗苦读&#xff0c;终于到了最后见分晓的时候了。在这么一场关键的考试当中&#xff0c;除了考试前努力奋斗&#xff0c;考场上认真答题&#xff0c;考后的志愿填报也是极其的重…

Linux下C语言的fgets与fputs

使用的是 CentOS gcc编译下面程序 显示warning: the gets function is dangerous and should not be used.问题出在程序中使用了 gets Linux 下gcc编译器不支持这个函数&#xff0c;解决办法是使用 fgets fgets()函数的基本用法为&#xff1a; fgets(char * s,int size,FILE * …

linux发行版的用户交互

1 cli&#xff0c;即command line interface 纯命令行的交互方式&#xff0c;该命令行界面是由shell提供的。 linux内核本身也自带了一个console&#xff0c;即linux console&#xff0c;它是基于frame buffer的。 cli的界面都是基于ncurses库开发的。 2 GUI&#xff0c;graphi…

C#中在应用程序和DLL使用消息

在C#中采用的是事件驱动方式&#xff0c;但在我们使用的过程中&#xff0c;有时候通过调用系统原有的消息&#xff0c;处理起来会比较简单一些&#xff0c;特别是在处理与DLL文件的交互时&#xff0c;的确是非常的方便。 在C#中使用自定义消息 在C#中使用自定义消息非常简单&…

倪光南院士:openEuler与全球开发者共同推动计算产业发展

今日&#xff0c;以“创造最好的OS&#xff0c;成就更好的我们”为主题的 openEuler Developer Day 2021在北京成功举行。本次大会由openEuler社区发起&#xff0c;产业组织专家、学者、企业领袖和开发者们围绕多样性计算、云原生全栈、全场景协同等技术方向共同探讨和创新。大…

jhello框架-ajax

2019独角兽企业重金招聘Python工程师标准>>> 现在的web开发没有ajax都没法活&#xff0c;所以在jhello中实现了一个简单的ajax交互&#xff0c;使用json作为载体。 在上篇《交互》中讲到一种交互的方式是把数据放入Model中&#xff0c;通过ModelAndView类返回到视图…

发微信模版消息换行用\n

发微信模版消息换行用\n发微信模版消息换行用\n发微信模版消息换行用\n发微信模版消息换行用\n发微信模版消息换行用\n转载于:https://blog.51cto.com/xuqin/1974131

二次元会让人脸识别失效吗?

来源 | PyTorch 开发者社区责编 | 寇雪芹头图 | 下载于ICphoto人脸识别也遇到坑了&#xff0c;识别得了三次元&#xff0c;却对二次元无效。迪士尼的技术团队&#xff0c;正在开发这一算法&#xff0c;以帮助动画制作者进行后期搜索。团队利用 PyTorch&#xff0c;效率得到很大…

基于第四层交换技术的负载均衡

摘 要 本文介绍了第四层交换技术的概念&#xff0c;技术原理以及如何使用第四层交换技术实现远程教育系统中的应用服务器负载均衡。 作者&#xff1a; 凌仲权&#xff0c;现就读于西安电子科技大学&#xff0c;2001级硕士研究生。主要的科研方向为计算机网络技术研究以及远程教…

開始Unity3D的学习之旅

前言&#xff1a;这个系列的文章纯属对自己学习的整理&#xff0c;非高手之作。但确实的记载了我作为一个没接触过3D游戏编程的大学生的心路历程。争取每周整理一次吧。之所以会開始学Unity3D&#xff0c;最基本的原因是由于在快放暑假的时候&#xff0c;我找了一家做iPhone游戏…

linux 定时任务crond

1.定时任务crond 1.1 crond是什么 crond是linux系统中用来定期执行命令或指定程序任务的一种服务或软件。特殊需求&#xff1a;&#xff08;秒级别&#xff09;crond服务就无法搞定了&#xff0c;一般工作中写脚本守护进程执行。 为什么要使用crond定时任务&#xff1a; linux系…

用C#去除代码的SourceSafe管理

经常看一些的程序&#xff0c;有些一个解决方案带有多个项目&#xff0c;由于代码比较多&#xff0c;多人开发&#xff0c;所以好多vs.net下的工程是用source safe进行版本控制的。而用source safe进行版本控制需要局域网路径共享&#xff0c;因此好多项目换一台机器打开会出现…

用jarsigner对android apk进行签名

以前对apk重新打包签名都是用的apktool里面的Auto-sign 工具&#xff0c;后来发现有时候利用该工具对一些apk签名会失败&#xff0c;所以后来就使用 jarsigner这个工具&#xff0c;现记录一下这个工具的使用方法1 首先要生成一个自己的keyD:\>keytool -genkey -alias myKey…