构建一个相对较小的图像识别卷积神经网络

今天的文章是有关 “高级卷积神经” 的教程。我们希望您能够以本文为起点,在 tensorflow 上构建更大的 cnn 来处理视觉任务。
概述
cifar-10 分类问题是机器学习领域一种常见的基准问题,其任务是将 rgb 32x32 像素的图像分为以下 10 类:
airplane, automobile, bird, cat, deer, dog, frog, horse, ship, and truck.
有关详情,请参阅cifar-10 页面 (https://www.cs.toronto.edu/~kriz/cifar.html)及 alex krizhevsky 发表的一篇 技术报告 (https://tensorflow.google.cn/tutorials/images/deep_cnn?hl=zh-cn)。
目标
本文的目标是构建一个相对较小的图像识别卷积神经网络(cnn)。在此过程中,本文将:
重点介绍网络架构、训练和评估的规范结构
提供一个用于构建更大、更为复杂的模型的模板
选择 cifar-10 的原因是它足够复杂,可以用来练习 tensorflow 的大部分功能,进而扩展到大型模型。同时,该模型足够小,可以快速训练,是尝试新想法以及实验新技术的理想之选。
本文的要点
cifar-10 教程介绍了几个用于在 tensorflow 中设计更大、更为复杂的模型的重要结构:
核心数学组件,包括卷积(维基百科页面)、修正线性激活函数(维基百科页面)、最大池化(维基百科页面)和局部响应归一化(alexnet 论文的第 3.3 节)
训练期间网络活动(包括输入图像、损失以及激活函数和梯度的分布)的可视化
例行程序,用于计算已学参数的移动平均值,并在评估期间使用这些平均值提升预测性能
实施学习速率计划(随时间的推移系统性地降低)
输入数据的预取队列,使模型避开磁盘延迟和代价高的图像预处理过程
此外,我们还提供了模型的多 gpu 版本,它会展示:
如何配置模型以跨多个 gpu 卡并行训练
如何在多个 gpu 间共享和更新变量
模型架构
本 cifar-10 教程中的模型是一个多层架构,由卷积层和非线性层交替排列后构成。这些层后面是全连接层,然后通向 softmax 分类器。该模型除了最顶部的几层外,基本跟alex krizhevsky描述的模型架构一致。
在 gpu 上经过几个小时的训练后,该模型的准确率达到峰值(约 86%)。详情请参阅下文和相应代码。模型中包含 1068298 个可学习参数,对一张图像进行推理计算大约需要 1950 万个乘加操作。
代码结构
本教程使用的代码位于models/tutorials/image/cifar10/中。
cifar-10 模型
cifar-10 网络主要包含在cifar10.py中。完整的训练图大约包含 765 个操作。我们发现,使用以下模块构建训练图可最大限度地提高代码的重复使用率:
模型输入:inputs()和distorted_inputs()分别可添加读取和预处理 cifar 图像以用于评估和训练的操作
模型预测:inference()可添加对提供的图像进行推理(即分类)的操作
模型训练:loss()和train()可添加计算损失和梯度、更新变量和呈现可视化汇总的操作
模型输入
模型的输入部分由inputs()和distorted_inputs()函数构建,这两种函数会从 cifar-10 二进制数据文件中读取图像。这些文件包含字节长度固定的记录,因此我们可以使用tf.fixedlengthrecordreader。如需详细了解reader类的工作原理,请参阅 读取数据(https://tensorflow.google.cn/api_guides/python/reading_data?hl=zh-cn#reading-from-files)。
图像按以下方式处理:
从中心(用于评估)或随机(用于训练)剪裁成 24 x 24 像素
进行近似白化处理,使模型对图像的动态范围变化不敏感
对于训练,我们还会额外向图像应用一系列随机失真,以人为增加数据集的大小:
从左到右随机翻转图像
随机对图像亮度进行失真处理
随机对图像对比度进行失真处理
要查看可采用的失真列表,请访问图像页面(https://tensorflow.google.cn/api_guides/python/image?hl=zh-cn)。此外,我们还向图像附加了tf.summary.image,以便在tensorboard中可视化它们。这对验证输入的构建是否正确十分有用。
从磁盘读取图像并进行失真处理需要不少时间。为了防止这些操作影响训练速度,我们在 16 个独立的线程中执行这些操作,而这些线程会不断填充一个 tensorflow队列。
模型预测
模型的预测部分由inference()函数构建,该函数可添加计算预测对数的操作。模型这一部分的结构如下:
下图是从 tensorboard 生成的图表,描述了推理操作的过程:
练习:inference的输出为非归一化对数。请尝试使用tf.nn.softmax修改网络架构以返回归一化预测结果。
inputs()和inference()函数提供了评估模型所需的所有组件。我们现在将重点转向构建训练模型所需的操作。
练习:inference()中的模型架构与cuda-convnet中指定的 cifar-10 模型的架构略有不同。具体而言,alex 的初始模型的顶层是局部连接层,而非全连接层。请尝试修改架构以在顶层中完全重现局部连接层。
模型训练
训练网络执行 n 元分类的常用方法是多项逻辑回归(又称 softmax 回归)。softmax 回归向网络输出应用softmax非线性函数,并计算归一化预测与标签索引之间的交叉熵。在正则化过程中,我们还会对所有已学变量应用常见的权重衰减损失。模型的目标函数是求交叉熵损失和所有权重衰减项的和并由loss()函数返回。
我们通过tf.summary.scalar在 tensorboard 中对其进行可视化:
我们使用标准的梯度下降法训练模型(有关其他方法,请参阅 训练https://github.com/tensorflow/docs/tree/master/site/en/api_guides/python),其中学习速率随时间的推移呈指数级衰减。
train()函数会添加一些最小化目标所需的操作,包括计算梯度、更新学习变量(详情请参阅tf.train.gradientdescentoptimizerhttps://tensorflow.google.cn/api_docs/python/tf/train/gradientdescentoptimizer?hl=zh-cn)。它会返回一项用以对一批图像执行所有计算的操作,以便训练并更新模型。
启动并训练模型
我们已构建了模型,现在使用脚本cifar10_train.py启动该模型并执行训练操作。
python cifar10_train.py
注意:首次运行 cifar-10 教程中的任何目标时,系统都会自动下载 cifar-10 数据集。该数据集大约为 160mb,因此首次运行时您可以喝杯咖啡小栖一会。
您应该会看到以下输出:
filling queue with 20000 cifar images before starting to train. this will take a few minutes.
2015-11-04 11:45:45.927302: step 0, loss = 4.68 (2.0 examples/sec; 64.221 sec/batch)2015-11-04 11:45:49.133065: step 10, loss = 4.66 (533.8 examples/sec; 0.240 sec/batch)2015-11-04 11:45:51.397710: step 20, loss = 4.64 (597.4 examples/sec; 0.214 sec/batch)2015-11-04 11:45:54.446850: step 30, loss = 4.62 (391.0 examples/sec; 0.327 sec/batch)2015-11-04 11:45:57.152676: step 40, loss = 4.61 (430.2 examples/sec; 0.298 sec/batch)2015-11-04 11:46:00.437717: step 50, loss = 4.59 (406.4 examples/sec; 0.315 sec/batch)...
该脚本每隔 10 步报告一次总损失值及最后一批数据的处理速度。需要注意以下几点:
第一批数据的处理速度可能会非常慢(例如,需要几分钟),因为预处理线程需要将 20000 张处理过的 cifar 图像填充到随机化处理队列中
报告的损失是最近一批数据的平均损失。请注意,该损失是交叉熵和所有权重衰减项的和
请留意一批数据的处理速度。上述数字是在 tesla k40c 上得出的结果。如果您是在 cpu 上运行,速度可能会慢些
练习:进行实验时,有时候第一个训练步持续时间比较长。请尝试减少最初填充队列的图像数量。在cifar10_input.py中搜索min_fraction_of_examples_in_queue。
cifar10_train.py会定期将所有模型参数保存在检查点文件中,但不会对模型进行评估。cifar10_eval.py将使用检查点文件衡量预测性能(请参阅下文中的评估模型部分)。
如果您按照上述步骤进行操作,那么现在已开始训练 cifar-10 模型了。恭喜!
cifar10_train.py返回的终端文本几乎不提供任何有关模型训练情况的信息。我们希望在训练期间更深入地了解模型的以下信息:
损失是真的在减小,还是只是噪点?
为模型提供的图像是否合适?
梯度、激活函数和权重的值是否合理?
当前的学习速率是多少?
tensorboard可提供此功能,它会通过tf.summary.filewriter显示定期从cifar10_train.py导出的数据。
例如,我们可以观看local3特征中激活函数的分步及稀疏程度在训练过程中的变化情况:
跟踪各个损失函数以及总损失在不同时间段的情况尤为有用。不过,由于训练所用的批次较小,因此损失中夹杂的噪点相当多。在实践中,我们发现除了原始值之外,可视化损失的移动平均值也非常有用。了解脚本如何将tf.train.exponentialmovingaverage用于此用途。
评估模型
现在,我们来评估一下经过训练的模型在保留数据集上的表现如何。该模型由脚本cifar10_eval.py进行评估。它通过inference()函数构建模型,并使用 cifar-10 评估数据集中的全部 10000 张图像。它会计算 precision @ 1,表示得分最高的一项预测与图像的真实标签一致的频率。
为了监控模型在训练过程中的改进情况,评估脚本会定期在cifar10_train.py创建的最新检查点文件上运行。
python cifar10_eval.py
注意不要在同一 gpu 上同时运行评估和训练二进制文件,否则可能会耗尽内存。您可以考虑在其他 gpu(如可用)上单独运行评估二进制文件,或在同一 gpu 上运行评估二进制文件时暂停训练二进制文件的运行。
您应该会看到以下输出:
2015-11-06 08:30:44.391206: precision @ 1 = 0.860...
该脚本只是定期返回 precision @ 1,在本例中,返回的准确率为 86%。cifar10_eval.py还会导出可以在 tensorboard 中可视化的汇总。在评估期间,您可通过这些汇总进一步了解模型。
训练脚本会计算所有已学变量的移动平均值。评估脚本会将所有已学模型参数替换为移动平均值。这种替换可以在评估时提升模型的性能。
练习:根据 precision @ 1,采用平均参数可以使预测性能提升 3% 左右。修改cifar10_eval.py,使模型不采用平均参数,然后验证预测性能是否会下降。
使用多个 gpu 卡训练模型
现代工作站可能会包含多个用于科学计算的 gpu。tensorflow 可利用此环境在多个卡上同时运行训练操作。
如果要以并行的分布式方式训练模型,则需要协调训练过程。在接下来的内容中,术语 “模型副本” 指在数据子集上训练的模型副本。
简单地采用模型参数异步更新方法会导致训练性能无法达到最佳,因为单个模型副本在训练时使用的可能是过时的模型参数。反之,如果采用完全同步的更新后参数,其速度堪比最慢的模型副本。
在具有多个 gpu 卡的工作站中,每个 gpu 的速度大致相当,且具有足够的内存来运行整个 cifar-10 模型。因此,我们选择按照以下方式设计训练系统:
在每个 gpu 上放一个模型副本
等待所有 gpu 完成一批数据的处理工作,然后同步更新模型参数
模型示意图如下所示:
请注意,每个 gpu 都会针对一批唯一的数据计算推理和梯度。这种设置可以有效地将一大批数据划分到各个 gpu 上。
这种设置要求所有 gpu 都共享模型参数。众所周知,将数据传输到 gpu 或从中向外传输数据的速度非常慢。因此,我们决定在 cpu 上存储和更新所有模型参数(如绿色方框所示)。当所有 gpu 均处理完一批新数据时,系统会将一组全新的模型参数传输给相应 gpu。
gpu 会同步运行。gpu 的所有梯度将累积并求平均值(如绿色方框所示)。模型参数会更新为所有模型副本的梯度平均值。
将变量和操作放到多个设备上
将操作和变量放到多个设备上需要一些特殊的抽象操作。
第一个抽象操作是计算单个模型副本的推理和梯度的函数。在代码中,我们将此抽象操作称为 “tower”。我们必须为每个 tower 设置两个属性:
tower 中所有操作的唯一名称。tf.name_scope通过添加作用域前缀提供唯一的名称。例如,第一个 tower 中的所有操作都会附带tower_0前缀,例如tower_0/conv1/conv2d
运行 tower 中操作的首选硬件设备。tf.device会指定该属性。例如,第一个 tower 中的所有操作都位于device('/device:gpu:0')作用域内,表示它们应在第一个 gpu 上运行
为了在多 gpu 版本中共享变量,所有变量都固定到 cpu 上且通过tf.get_variable访问。了解如何共享变量。
在多个 gpu 卡上启动并训练模型
如果计算机上安装了多个 gpu 卡,您可以使用cifar10_multi_gpu_train.py脚本借助它们加快模型的训练过程。此版训练脚本可在多个 gpu 卡上并行训练模型。
python cifar10_multi_gpu_train.py --num_gpus=2
请注意,使用的 gpu 卡数量默认为 1。此外,如果计算机上仅有一个 gpu,则所有计算都会在该 gpu 上运行,即使您设置的是多个 gpu。
练习:cifar10_train.py的默认设置是在大小为 128 的批次数据上运行。请尝试在 2 个 gpu 上运行cifar10_multi_gpu_train.py,批次大小为 64,然后比较这两种方式的训练速度。
后续学习计划
如果您有兴趣开发并训练您自己的图像分类系统,我们建议您分叉本教程的代码,并替换组件以解决您的图像分类问题。
练习:下载street view house numbers (svhn)数据集(http://ufldl.stanford.edu/housenumbers/)。分叉 cifar-10 教程的代码并将输入数据替换为 svhn。尝试调整网络架构以提高预测性能。

具 ±24V 电源及过流保护能力的 5V 系统
腾讯起诉离职员工获赔1940万 创行业最高记录
伪区块链骗局你有中招吗
2GHZ宽频带放大器
Semtech“LoRa生态发展及创新应用论坛”举办 4月11日与你相约深圳
构建一个相对较小的图像识别卷积神经网络
Power Integrations的LED驱动器IC现以超薄封装供货
关于发电机组的选购方法的详细说明
浅述LED的制作工艺流程
西门子S7-1500的应用案例分析
手机指纹传感器已经“烂大街”?各大厂商的竞争非常激烈
云计算领域在2020年是怎样的展望
奔驰新少帅康林松:中国市场愈发重要,电动车战略是重头戏
华为荣耀8/华为P9/华为mate9/华为P10再度降价,你打算入手谁?
人工智能平台公司思必驰如何奋进“十四五”?
车载音速喷嘴气体流量检定系统
物联网测试如何才能攻破
华为鸿蒙OS将天然无root 可从源头提升系统安全性
接地电阻柜的电阻值选多大合适
安捷伦与NTS携手推进超高速USB 3.0测试