基于 Caffe 框架的 AI 图像识别自动化

自动化实现过程,UI框架的自动化往往不能满足所有场景的需求,比如:动态效果图片内容一致性检查;在全民AI的浪潮中,基于Caffe框架的AI图像识别结合QT4A自动化测试尝试,在企鹅电竞弹幕识别,以及表情业务自动化中动态图像识别有了落地,填补了自动化对动态图片内容精准检测的不足。

Caffe是一个清晰而高效的深度学习框架,也是一个被广泛使用的开源深度学习框架,在Tensorflow出现之前一直是深度学习领域Github star最多的项目。

Caffe的主要优势为:容易上手,网络结构都是以配置文件形式定义,不需要用代码设计网络。训练速度快,组件模块化,可以方便的拓展到新的模型和学习任务上。但是Caffe最开始设计时的目标只针对于图像,没有考虑文本、语音或者时间序列的数据,因此Caffe对卷积神经网络的支持非常好,但是对于时间序列RNN,LSTM等支持的不是特别充分。

一、自动化检测结合AI图像识别效果图

效果:caffe训练的模型,企鹅电竞APP中对当前整个手机屏幕中的某一个特征弹幕识别率可达95%以上,其中表情的动态内容识别可达到100%。

企鹅电竞弹幕识别:   

表情识别:

二、AI识别结合自动化整体方案

整体的流程:在Caffer框架环境下,我们对训练好的模型生成服务,结合QT4A自动化框架,在用例中调用AI识别接口,回调给自动化检查结果,整个过程简单可分两部分:模型训练和自动化识别调用检查

1、设计特征

尝试方案一:在企鹅电竞弹幕中,最初担心对整个屏幕任意区域去识别特征,识别率不高,所以尝试是对弹幕显示的固定位置作为采样训练,也就是对整个屏幕中缩小范围到固定的区域识别特征,这样的识别效果准确率可达95%,但对训练图片采样比较耗时,工作量有所增加,该方案也不灵活。

尝试方案二:我们尝试对整个手机屏幕中去识别某个特征,这样对采样图片没有任何要求,只要图片中有所需特征,清晰就可以作为训练素材,无需二次介入加工处理,意想不到,通过多次的模型训练调整,整体的识别率其实并不低,也在95%以上;

2、准备训练集和测试集

Caffer的模型训练需要预先准备好训练集合测试集,同类型的数据最好要1000张以上,并且覆盖尽可能多场景,增加泛化能力,目录结构对应文件夹名为0/1/2/3,其中文件夹0表示无特征图片,非0按顺序对应每个文件夹对应一个特征,比如1/2/3分别对应666/单身狗/坑三个特征表情的多个特征素材;

 

备注小技巧:准备素材可使用视频分帧或者自动化脚本截图方式。

采用ffmpeg视频分帧,如下命令:

利用python脚本截图:

3、生成训练集及测试集

调用genTrainset.py脚本,对采集的训练和测试素材重新生成:

4、处理数据

将图片转换为Caffe识别的LMDB或LEVELDB,他们是Google开发的一种Key-Value存储管理器。

5、选择模型

模型选择上,caffe工程的models文件夹中有这些网络模型,查看models文件夹,常用的网络模型比较多,比如Lenet、AlexNet、ZFNet、VGGNet、GoogleNet、ResNet根据实验对比,如果纯图像识别,用GoogleNet已经远可满足使用:

deploy.prototxt为部署网络结构:

里面定义各种网络层,如:LRN层、cancat层、全链接层、全链接输出层、损失层等等,一般无需修改次配置,具体每一层的含义:

solver.prototxt中定义了训练所用到的参数:

具体每个参数的意义如下:

train_val.prototxt定义网络结构

里面定义包含字段:网络名称、定义一个层、层的名称、层的数据类型、层的输出、每次读取图片数目、图片裁剪尺寸、图片举止RGB、层的参数等等,具体每层意义详见本章节三

6、训练模型

调用caffe train -solver solver.prototxt 开始训练参数。添加-snapshot iter_100.caffemodel接着训练,训练过程需要持续观察收敛loss的值,一般越小越成熟,但也不能过度,防止训练出来的模型过拟合。

模型训练经验:

GoogleNet本身是比较大的网络,我们可以根据自己的需求裁剪网络。

GoogleNet中的LRN层影响不大可以去掉,或者删除一些卷积层,降低网络的层数。

当我们的数据量比较小的时候,训练完整的网络参数比较容易过拟合。这时我们可以通过只训练网络的某几层来解决这个问题。

首先修改train_val.prototxt中的全连接层的名称,即loss1/classifier,loss2/classifier,loss3/classifier这三层的名称,以及他们的输出分类个数,默认是1000,需要改成我们自己的种类总数。这样我们再加载训练好的model时,这三层的参数才会重新初始化。然后将所有其他层的lr_mult该为0,这样其他层的参数不会改变,使用预先训练好的参数。

下载bvlc_googlenet.caffemodel,这是谷歌在ImageNet上训练出来的参数。

然后调用caffe train -solver solver.prototxt -weights bvlc_googlenet.caffemodel即可训练。

当收敛loss值达不到要求时,可以调整train_val.prototxt文件中的test_iter和test_interval的值以及average_loss的值来训练。

7、测试模型

训练好的模型,还需验证模型的识别率,这里需要用到测试集素材(有特性,但跟训练素材不是同一份),如下图,我们对表情训练的模型,用测试图片集的188图片验证整体识别率为100%,同时,可取非特征图片验证(文件夹0的图片)不可识别。

8、部署模型

训练好的模型,可作为服务部署,供自动化调用:

9、自动化调用

提供post方法供自动化用例传递识别校验图片:

三、Caffe入门介绍

易上手

模型与相应优化都是以文本形式而非代码形式给出。Caffe 给出了模型的定义、最优化设置以及预训练的权重,方便立即上手。

速度快

能够运行最棒的模型与海量的数据。Caffe 与 cuDNN 结合使用,测试 AlexNet 模型,在K40 上处理每张图片只需要1.17ms。

模块化

方便扩展到新的任务和设置上。可以使用 Caffe 提供的各层类型来定义自己的模型。

开放性

公开的代码、可参考的模型和可再现性。

Caffe的安装:

Caffe 需要预先安装比较多的依赖项,CUDA,snappy,leveldb,gflags,glog,szip,lmdb,OpenCV,hdf5,BLAS,boost等等

Caffe官网:http://caffe.berkeleyvision.org/

Caffe Github : https://github.com/BVLC/caffe

Caffe 安装教程:

http://caffe.berkeleyvision.org/installation.html

http://blog.csdn.net/yhaolpz/article/details/71375762

Caffe 安装分为CPU和GPU版本,GPU版本需要显卡支持以及安装CUDA。

Caffe依赖 ProtoBuffer Boost GFLAGS GLOG BLAS HDF5 OpenCV LMDB LEVELDB Snappy

Layer

Layer 是 Caffe 模型的本质内容和执行计算的基本单元。Layer 可以进行很多运算,如: convolve(卷积)、pool(池化)、inner product(内积),rectified-linear 和 sigmoid 等非线性运算,元素级的数据变换,normalize(归一化)、load data(数据加载)、softmax 和 hinge 等losses(损失计算)。可在 Caffe 官方文档的 layer catalogue 中查看所有操作,其囊括了绝大部分目前最前沿的深度学习任务所需要的层类型。

一个 layer 通过 bottom(底部)连接层接收数据,通过 top(顶部)连接层输出数据。每一个 layer 都定义了 3 种重要的运算:setup (初始化设置),forward (前向传播),backward (反向传播)。

·         Setup: 在模型初始化时重置 layers 及其相互之间的连接 ;

·         Forward: 从 bottom 层中接收数据,进行计算后将输出送入到 top 层中;

·         Backward: 给定相对于 top 层输出的梯度,计算其相对于输入的梯度,并传递到 bottom 层。一个有参数的 layer 需要计算相对于各个参数的梯度值并存储在内部。

特别地,Forward 和 Backward 函数分别有 CPU 和 GPU 两种实现方式。如果没有实现 GPU 版本,那么 layer 将转向作为备用选项的CPU 方式。尽管这样会增加额外的数据传送成本(输入数据由 GPU 上复制到 CPU,之后输出数据从 CPU 又复制回到 GPU),但是对于做一些快速实验这样操作还是很方便的。

总的来说,layer 承担了网络的两个核心操作:forward pass (前向传播) —— 接收输入并计算输出;backward pass (反向传播) —— 接收关于输出的梯度,计算相对于参数和输入的梯度并反向传播给在它前面的层。由此组成了每个 layer 的前向和反向通道。

由于 Caffe 网络的组合性和其代码的模块化,自定义 layer 是很容易的。只要定义好 layer 的 setup (初始化设置)、forward (前向通道)和backward (反向通道),就可将 layer 纳入到网络中。

Net

通过合成和自动微分,网络同时定义了一个函数和其对应的梯度。通过合成各层的输出来计算这个函数,来执行给定的任务,并通过合成各层的后向传播过程来计算来自损失函数的梯度,从而学习任务。Caffe 模型是端到端的机器学习引擎。

准确的说,Net 是由一系列层组成的有向无环 (DAG) 计算图,Caffe 保留了计算图中所有的中间值以确保前向和反向迭代的准确性。一个典型的 Net 开始于 data layer ——从磁盘中加载数据,终止于 loss layer —— 计算如分类和重构这些任务的目标函数。

Net 由一系列层和它们之间的相互连接构成,用的是一种文本建模语言。一个简单的逻辑回归分类器的定义如下:

Net::Init()进行模型的初始化。初始化主要实现两个操作:创建 blobs 和 layers 以搭建整个网络 DAG 图,以及调用 layers 的SetUp()函数。初始化时也会做另一些记录,例如确认整个网络结构的正确与否等。另外,初始化期间,Net 会打印其初始化日志到INFO 信息中。

Caffe 中网络的构建与设备无关,可回忆下我们之前的解释,blobs 和 layers 在模型定义时是隐藏了实现细节的。网络构建完之后,通过设置 Caffe::mode() 函数中的 Caffe::set_mode(), 即可实现在 CPU 或 GPU 上的运行。采用 CPU 或 GPU 计算得到的结果相同,CPU 与 GPU 无缝切换并且独立于模型定义。对于研究和调用来说,将模型定义和实现分离开来是最好不过了。

Model

模型是利用文本 protocol buffer (prototxt) 语言定义的,学习好的模型会被序列化地存储在二进制 protocol buffer (binaryproto).caffemodel 文件中。

模型格式用 protobuf 语言定义在 caffe.proto 文件中。大部分源文件中都带有解释。

Caffe 使用 Google Protocol Buffer 有以下优势:按序排列时二进制字符串尺寸最小,高效序列化,易读的文本格式与二进制版本兼容,可用多种语言实现高效的接口,尤其是 C++ 和Python。这些优势造就了 Caffe 模型的灵活性与扩展性。

Forward

forward 过程为给定的待推断的输入计算输出。在forward 过程中,Caffe 组合每一层的计算以得到整个模型的计算“函数”。本过程自底向上进行。数据 x 通过一个内积层得到 g(x),然后通过 softmax 层得到 h(g(x)),通过 softmax loss 得到 fw(x)。

Backward

backward 过程根据损失来计算梯度从而进行学习。在backward 过程中,Caffe 通过自动求导并反向组合每一层的梯度来计算整个网络的梯度。这就是反传过程的本质。本过程自顶向下进行。

Forward 和 Backward的实现

只要定义好了模型,这些计算就可以立即进行:Caffe 已经准备好了 forward 和 backward 的实现方法。

·         Net::Forward() 和 Net::Backward() 方法实现网络的 forward 和 backward,而 Layer::Forward() 和Layer::Backward() 计算每一层的 forward 和 backward;

·         每一层都有 forward_{cpu, gpu}() 和 backward_{cpu, gpu} 方法来适应不同的计算模式。由于条件限制或者为了使用便利,一个层可能仅实现了 CPU 或者 GPU 模式。

Loss

与大多数的机器学习模型一样,在 Caffe 中,学习是由一个损失函数驱动的(通常也被称为误差、代价或者目标函数)。一个损失函数通过将参数集(即当前的网络权值)映射到一个可以标识这些参数 “不良程度” 的标量值来学习目标。因此,学习的目的是找到一个网络权重的集合,使得损失函数最小。

在 Caffe 中,损失是通过网络的前向计算得到的。每一层由一系列的输入 blobs (bottom),然后产生一系列的输出 blobs (top)。这些层的某些输出可以用来作为损失函数。典型的一对多分类任务的损失函数是 softMaxWithLoss 函数,使用以下的网络定义,例如:

在 softMaxWithLoss 函数中,top blob 是一个标量数值,该数值是整个 batch 的损失平均值(由预测值 pred 和真实值 label 计算得到)。

Loss weights

对于含有多个损失层的网络 (例如,一个网络使用一个 softMaxWithLoss 输入分类并使用 EuclideanLoss 层进行重构),损失权值可以被用来指定它们之间的相对重要性。

按照惯例,有着 Loss 后缀的 Caffe 层对损失函数有贡献,其他层被假定仅仅用于中间计算。然而,通过在层定义中添加一个loss_weight: 字段到由该层的 top blob,任何层都可以作为一个 loss。对于带后缀 Loss 的层来说,其对于该层的第一个top blob 含有一个隐式的 loss_weight:1;其他层对应于所有 top blob 有一个隐式的 loss_weight:0。因此,上面的softMaxWithLoss 层等价于:

然而,任何可以 backward 的层,可允许给予一个非 0 的 loss_weight,例如,如果需要,对网络的某些中间层所产生的激活进行正则化。对于具有相关非 0 损失的非单输出,损失函数可以通过对所有 blob 求和来进行简单的计算。

那么,在 Caffe 中最终的损失函数可以通过对整个网络中所有的权值损失进行求和计算获得,正如以下的伪代码:

Solver

Solver 通过协调 Net 的前向推断计算和反向梯度计算 (forward inference and backward gradients),来对参数进行更新,从而达到减小loss 的目的。Caffe 模型的学习被分为两个部分:由 Solver 进行优化、更新参数,由 Net 计算出 loss 和 gradient。

Caffe 支持的 Solver 包括有:

·         Stochastic Gradient Descent (type: “SGD”),随机梯度下降

·         AdaDelta (type: “AdaDelta”)

·         Adaptive Gradient (type: “AdaGrad”),自适应梯度

·         Adam (type: “Adam”)

·         Nesterov’s Accelerated Gradient (type: “Nesterov”) and

·         RMSprop (type: “RMSProp”)

各项 Solver 的具体说明可以参见这里。

Layer分类

为了创建一个 caffe 模型,我们需要在一个 protocol buffer(prototxt) 文件中定义模型的结 构。在 caffe 中,层和相应的参数都定义在caffe.proto 文件里。

视觉层 Vision Layers

头文件:

./include/caffe/vision_layers.hpp

视觉层的输入与输出均为图像。一个典型的图像通常为单通道的灰度图或三通道的 RBG 彩色图。但本文所指图像是一个广义的概念,明显特性来自于空间结构:高和宽通常均大于 1 而通道数不限,类似结构的数据均可理解为图像。这种结构可以帮助 caffe 的层决定如何处理输入数据,具体来说,大多数视觉层通常是在输入数据的某块区域执行特定操作来产生对应的输出。相反的,其它类型的层通常会忽略空间结构而把输入图像看作是一个维度为 chw 的 “单个大向量”。

损失层 Loss Layers

Loss 设置了一个损失函数用来比较网络的输出和目标值,通过最小化损失来驱动网络的训练。网络的损失通过前向操作计算,网络参数相对于损失函数的梯度则通过反向操作计算。

激活层 Activation / Neuron Layers

一般来说,激活层执行逐个元素的操作,输入一个底层 blob,输出一个尺寸相同的 顶层 blob。 在以下列出的这些层中,我们将忽略输入和输出 blob 的尺寸,因为它们是相同的。

数据层 DataLayers

数据能过数据层进入 caffe 网络:数据层处于网络的最底层,数据可以从高效率的数据库中读取 (如LevelDB 或 LMDB),可以直接从内存中读取,若对读写效率要求不高也可以从硬盘上的 HDFT 文件或者普通的图片文件读取。常见的数据预处理操作 (减均值,尺度变换,随机裁剪或者镜像) 可以通过设定参数 TransformationParameter 来实现。

普通层 Common Layers

普通层主要负责做一些诸如:内积/全连接、分裂(Splitting)、平摊 (Flattening)、变形 (Reshape)、连结(Concatenation)、切片 (Slicing)、计算均值等操作。

Caffe的接口

Caffe 有命令行、Python 和 MATLAB 三种接口,来实现日常使用、研究代码的交互以及实现快速原型。Caffe 以 C++ 库为核心,其在开发中使用模块化接口,而不是每次都调用其定义的编译。cmdcaffe,pycaffe 与 matcaffe 接口都可供用户使用。

命令行

命令行接口 cmdcaffe 是 caffe 中用来训练模型,计算得分以及方法判断的工具。

训练模型

caffe train 命令可以从零开始学习模型,也可以从已保存的快照继续学习,或将已经训练好的模型应用在新的数据与任务上进行微调即fine-tuning 学习:

·         所有的训练都需要添加 -solver solver.prototxt 参数完成 solver 的配置

·         继续训练需要添加 -snapshot model_iter_1000.solverstate 参数来加载 solver 快照

·         fine-tuning 需要添加 -weights model.caffemodel 参数完成模 型初始化

例如,可以运行如下代码:

对于 fine-tuning 的完整例子,可以参考 examples/finetuningonflickr_style,但是只调用训练命令如下:

测试

caffe test 命令通过在 test phase 中运行模型得到分数,并且用这分数表示网络输出的最终结果。网络结构必须被适当定义,生成accuracy 或 loss 作为其结果。测试过程中,终端会显示每个 batch 的得分,最后输出全部 batch 得分的平均值。

后期我们会根据每个维度陆续写相关的测试文章,如果你有兴趣,请关注我们哦。

长按指纹识别图中的二维码,获取更多测试干货分享!

 将我们公众号置顶   不会漏掉我们的原创干货哦!