【神经⽹络篇】--基于数据集cifa10的经典模型实例⼀、前述
本⽂分享⼀篇基于数据集cifa10的经典模型架构和代码。
⼆、代码
import tensorflow as tf
import numpy as np
import math
import time
truncated模型用什么软件
from tutorials.image.cifar10 import cifar10
from tutorials.image.cifar10 import cifar10_input
# 本节使⽤的数据集是CIFAR-10,这是⼀个经典的数据集,包含60000张32*32的彩⾊图像,其中训练集50000张,测试集10000张
# ⼀共标注为10类,每⼀类图⽚6000张。10类分别是 airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck
# 我们载⼊⼀些常⽤库,⽐如NumPy和time,并载⼊TensorFlow Models中⾃动下载、读取CIFAR-10数据的类
max_steps = 3000#迭代3000次
batch_size = 128#每次128张图⽚
# 下载cifar10数据集的默认路径
data_dir = 'D:/cifar10_data/cifar-10-batches-bin'
def variable_with_weight_losses(shape, stddev, wl):#w1是L1正则中的系数
# 定义初始化weights的函数,和之前⼀样依然使⽤tf.truncated_normal截断的正太分布来初始化权值
var = tf.uncated_normal(shape, stddev=stddev))
if wl is not None:
# 给weight加⼀个L2的loss,相当于做了⼀个L2的正则化处理
# 在机器学习中,不管是分类还是回归任务,都可能因为特征过多⽽导致过拟合,⼀般可以通过减少特征或者惩罚不重要特征的权重来缓解这个问题# 但是通常我们并不知道该惩罚哪些特征的权重,⽽正则化就是帮助我们惩罚特征权重的,即特征的权重也会成为模型的损失函数的⼀部分
# 我们使⽤w1来控制L2 loss的⼤⼩
weight_loss = tf.l2_loss(var), wl, name='weight_loss')
# 我们使⽤tf.add_to_collection把weight loss统⼀存到⼀个collection,这个collection名为"losses",它会在后⾯计算神经⽹络
# 总体loss时被⽤上
tf.add_to_collection("losses", weight_loss)
return var
# 下载cifar10类下载数据集,并解压,展开到其默认位置
cifar10.maybe_download_and_extract()
# 使⽤cifar10_input类中的distorted_inputs函数产⽣训练需要使⽤的数据,包括特征及其对应的label,这⾥是封装好的tensor,
# 每次执⾏都会⽣成⼀个batch_size的数量的样本。需要注意的是这⾥对数据进⾏了Data Augmentation数据增强
# 具体实现细节查看函数,其中数据增强操作包括随机⽔平翻转tf.image.random_flip_left_right()
# 随机剪切⼀块24*24⼤⼩的图⽚tf.random_crop,随机设置亮度和对⽐度,tf.image.random_brightness、tf.image.random_contrast
# 以及对数据进⾏标准化,⽩化 tf.image.per_image_standardization() 减去均值、除以⽅差,保证数据零均值,⽅差为1
images_train, labels_train = cifar10_input.distorted_inputs(#可以之解读数据,然后按每⼀批次的来读取
data_dir=data_dir, batch_size=batch_size
)#对应计算图中的⼀个字图 cifar10_input封装的是⼀个tensor 输⼊进来数据,再把处理逻辑输出出去
# ⽣成测试数据,不过这⾥不需要进⾏太多处理,不需要对图⽚进⾏翻转或修改亮度、对⽐度,
# 不过需要裁剪图⽚正中间的24*24⼤⼩的区块。(因为训练的数据是24*24的,通过函数cifar10_input.distorted_inputs读进来时处理了)
# 并进⾏数据标准化操作
# 测试的是⼀批数据
images_test, labels_test = cifar10_input.inputs(eval_data=True, data_dir=data_dir, batch_size=batch_size)
# 因为batch_size在之后定义⽹络结构时被⽤到了,所以数据尺⼨中的第⼀个值即样本条数需要被预先设定,⽽不能像以前那样设置为None
# ⽽数据尺⼨中的图⽚尺⼨为24*24即是剪裁后的⼤⼩,颜⾊通道数则设为3
# 这⾥写batch_size⽽不是None 因为后⾯代码中get_shape会拿到这⾥⾯的batch_size
image_holder = tf.placeholder(tf.float32, [batch_size, 24, 24, 3])#训练集多少⾏
label_holder = tf.placeholder(tf.int32, [batch_size])#训练集多少⾏,就有多少个Label
# 初始设置第⼀个卷积层,64个卷积核,卷积核⼤⼩是5*5,3通道
weight1 = variable_with_weight_losses(shape=[5, 5, 3, 64], stddev=5e-2, wl=0.0)#在卷积这块不做正则化
kernel1 = v2d(image_holder, filter=weight1, strides=[1, 1, 1, 1], padding='SAME')#做真正的卷积
bias1 = tf.stant(0.0, shape=[64]))#0.1也可
conv1 = bias_add(kernel1, bias1))#⽤激活函数
# 使⽤尺⼨3*3步长2*2的最⼤池化层处理数据,这⾥最⼤池化的尺⼨和步长不⼀样,可以增加数据的丰富性
pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME')
# 使⽤LRN对结果进⾏处理
# LRN最早见于Alex那篇⽤CNN参加ImageNet⽐赛的论⽂,Alex在论⽂中解释LRN层模仿了⽣物神经系统的"侧抑制(单边抑制)"机制,
# 对局部神经元的活动创建竞争环境,使得其中响应⽐较⼤的值变得相对更⼤,并抑制其他反馈较⼩的神经元,增强了模型的泛化能⼒
# Alex在ImageNet(上百万张图⽚)数据集上的实验表明,使⽤LRN后CNN在Top1的错误率可以降低1.4%,因此其在经典AlexNet中使⽤了LRN层
# LRN对ReLU这种没有上限边界的激活函数会⽐较有⽤,因为它会从附近的多个卷积核的响应中挑选⽐较⼤的反馈
# 但不适合Sigmoid这种有固定边界并且能抑制过⼤值得激活函数
# LRN对Relu配合较好,适合Alex架构
norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
# 创建第⼆个卷积层
# 上⾯64个卷积核,即输出64个通道,所以本层卷积核尺⼨的第三个维度即输⼊的通道数也需要调整为64
weight2 = variable_with_weight_losses(shape=[5, 5, 64, 64], stddev=5e-2, wl=0.0)
kernel2 = v2d(norm1, weight2, [1, 1, 1, 1], padding='SAME')
# 还有这⾥的bias值全部初始化为0.1,⽽不是0.最后,调换了最⼤池化层和LRN层的顺序,先进⾏LRN层处理,再使⽤最⼤池化层
bias2 = tf.stant(0.1, shape=[64]))
conv2 = bias_add(kernel2, bias2))
norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
pool2 = tf.nn.max_pool(norm2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME')
# 两个卷积层之后,是全连接层
# 先把第⼆个卷积层之后的输出结果flatten,使⽤tf.reshape函数将每个样本都变成⼀维向量,使⽤get_s
hape函数获取数据扁平化之后的长度
reshape = tf.reshape(pool2, [batch_size, -1])
dim = _shape()[1].value
# 接着初始化权值,隐含节点384个,正太分布的标准差设为0.04,bias的值也初始化为0.1
# 注意这⾥我们希望这个全连接层不要过拟合,因此设了⼀个⾮零的weight loss值0.04,让这⼀层具有L2正则所约束。
weight3 = variable_with_weight_losses(shape=[dim, 384], stddev=0.04, wl=0.004)
bias3 = tf.stant(0.1, shape=[384]))#写0.1是为了Relu⼩于0时全为0,所以给0.1不⾄于成为死亡神经元
# 最后我们依然使⽤ReLU激活函数进⾏⾮线性化
local3 = lu(tf.matmul(reshape, weight3) + bias3)
# 接下来还是全连接层,只是隐含节点只有⼀半,其他⼀样
weight4 = variable_with_weight_losses(shape=[384, 192], stddev=0.04, wl=0.004)#全连接的神经元384---192 是不断减少的,成倍减少因为在不断的总结卷积是不断地变宽,也是成倍的bias4 = tf.stant(0.1, shape=[192]))
local4 = lu(tf.matmul(local3, weight4) + bias4)
# 最后⼀层输出层,依然先创建⼀层weight,其正太分布标准差设为⼀个隐含层节点数的倒数,并且不⽤L2正则
# 这⾥没有⽤之前的softmax输出最后结果,这⾥把softmax操作放在了计算loss部分,其实我们不需要对inference的输出进⾏softmax
# 处理就可以获得最终分类结果(直接⽐较inference输出的各类的数值⼤⼩即可),计算softmax主要是为了计算loss,因此softmax操作整合到后⾯合理
weight5 = variable_with_weight_losses(shape=[192, 10], stddev=1/192.0, wl=0.0)
bias5 = tf.stant(0.0, shape=[10]))
logits = tf.add(tf.matmul(local4, weight5), bias5)
# 到这⾥就完成了整个⽹络inference(构建)的部分,梳理整个⽹络结构,设计性能良好的CNN是有⼀定规律可循的,但是想要针对某个问题设计最合适的
# ⽹络结构,是需要⼤量实际摸索的
# 完成模型inference的构建,接下来是计算CNN的loss,这⾥依然是⽤cross_entropy,这⾥我们把softmax的计算和cross_entropy的计算
# 合在了⼀起,即 tf.nn.sparse_softmax_cross_entropy_with_logits()
# 这⾥使⽤ tf.reduce_mean() 对 cross entropy计算均值,再使⽤ tf.add_to_collection()把cross entropy的loss添加到整体
# losses的collection中,最后,使⽤tf.add_n将整体losses的collection集合中的全部loss求和,得到最终的loss,其中包括
# cross entropy loss, 还有后两个全连接层中weight的L2 loss
def loss(logits, labels):
labels = tf.cast(labels, tf.int64)#真实标签0-9
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
logits=logits, labels=labels, name='cross_entropy_per_example'
)#交叉熵
cross_entropy_mean = tf.reduce_mean(cross_entropy,
name='cross_entropy')
tf.add_to_collection('losses', cross_entropy_mean)#加到最开始定义的集合这⾥最后⼜两部分交叉熵损失函数+L2损失函数
return tf.add__collection('losses'), name='total_loss')#拿到两部分损失
loss = loss(logits=logits, labels=label_holder)#logits输出的结果
# 优化器依然选择Adam Optimizer, 学习速率0.001
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss)#在训练过程中不断调整w和b
# 使⽤ tf.nn.in_top_k()函数求输出结果中 top k的准确率,默认使⽤top 1,也就是输出分数最⾼的那⼀类的准确率
top_k_op = tf.nn.in_top_k(logits, label_holder, 1)#这⾥1是top1 相同为1 进⾏累加得出准确率
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()
# 前⾯对图像进⾏数据增强(各种扭曲缩⼩)的操作需要耗费⼤量CPU时间,因此distorted_inputs使⽤了16个独⽴的线程来加速任务,函数内部会产⽣线程池,
# 在需要使⽤时会通过TensorFlow queue进⾏调度
# 启动图⽚数据增强的线程队列,这⾥⼀共使⽤了16个线程来进⾏加速,如果不启动线程,那么后续inference以及训练的操作都是⽆法开始的
# 进⾏训练
for step in range(max_steps):
start_time = time.time()
image_batch, label_batch = sess.run([images_train, labels_train])#真正执⾏tensor逻辑返回的是⼀批次的数据
_, loss_value = sess.run([train_op, loss],
feed_dict={image_holder: image_batch, label_holder: label_batch})
duration = time.time() - start_time
if step % 10 == 0:
examples_per_sec = batch_size / duration
sec_per_batch = float(duration)
format_str = 'step %d, loss = %.2f (%.1f examples/sec; %.3f sec/batch)'
print(format_str % (step, loss_value, examples_per_sec, sec_per_batch))
# 评测模型在测试集上的准确率
# 我们依然像训练时那样使⽤固定的batch_size,然后⼀个batch⼀个batch输⼊测试数据
num_examples = 10000
# 先计算⼀共要多少个batch才能将全部样本评测完
num_iter = il(num_examples / batch_size))
true_count = 0
total_sample_count = num_iter * batch_size
step = 0
while step < num_iter:
image_batch, label_batch = sess.run([images_test, labels_test])#测试取测试集中的数据
predictions = sess.run([top_k_op], feed_dict={image_holder: image_batch,#有多少个相同的结果预测出的和真实的⽐较
label_holder: label_batch})
true_count += np.sum(predictions)
step += 1
precision = true_count / total_sample_count #预测对的和总的样本数
print('precision @ 1 = %.3f' % precision)
三、总结
最终,在cifar-10数据集上,通过⼀个短时间⼩迭代的训练,可以达到⼤致73%的准确率,持续增加max_steps,可以期望准确率逐渐增加
如果max_steps⽐较⼤,则推荐使⽤学习速率衰减decay的SGD进⾏训练,这样训练过程中能达到的准确率峰值会⽐较⾼,⼤致有86%
其中L2正则以及LRN层的使⽤都对模型准确率有提升作⽤,它们都可以提升模型的泛化能⼒
数据增强Data Augmentation在我们的训练中作⽤很⼤,它可以给单幅图增加多个副本,提⾼图⽚的利
⽤率,防⽌对某⼀张图⽚结构的学习过拟合
这刚好是利⽤了图⽚数据本⾝的性质,图⽚的冗余信息量⽐较⼤,因此可以制造不同的噪声并让图⽚依然可以被识别出来。如果神经⽹络可以克服这些噪声并准确识别,那么他的泛化能⼒必然很好。数据增强⼤⼤增加了样本量,⽽数据量的⼤⼩恰恰是深度学习最看重的,深度学习可以在图像识别上领先
其他算法的⼀⼤因素就是它对海量数据的利⽤效率⾮常⾼。其他算法,可能在数据量⼤到⼀定程度时,准确率就不再上升了,⽽深度学习只要提供⾜够多的样本,准确率基本持续提升,所以说它是最适合⼤数据的算法

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。