YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(上)
YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(中)
YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(下)

文章目录

  • 声明
  • 2 工程应用分析
    • 2.1 平台/软件介绍和环境搭建
    • 2.2 网络训练方式选择
    • 2.3 SVP-NNIE前向计算处理过程
      • 2.3.1 例程中对YOLOv3网络模型的初始化操作
      • 2.3.2 图像的输入
      • 2.3.3 NNIE输出数据的内存分布图
      • 2.3.4 网络的后级处理
      • 2.3.5 性能分析和优化思路
  • 3 Caffe框架和网络训练流程
    • 3.1 Caffe平台的搭建
    • 3.2 Caffe计算框架基础(基于mnist示例)
      • 3.2.1 Prototxt文件、网络结构和训练参数
      • 3.2.2 网络的输入层和输出层
        • (1)网络输入层
        • (2)网络输出层
      • 3.2.3 处理操作相关
        • (1)模型的训练 & 结果的保存
        • (2)(在PC上)使用训练好的mnist模型做预测
        • (3)训练日志的可视化
    • 3.3 MobileNet-YOLOv3工程实现分析
      • 3.3.1 工程编译
      • 3.3.2 网络的输入层&检测任务数据集
        • (1)AnnotatedData类型数据输入层
        • (2)VOC数据集转LMDB数据库文件
      • 3.3.3 网络的训练
    • 3.4 标准YOLOv3的适配、预测和训练
      • 3.4.1 (Upsample)网络层的添加
      • 3.4.2 基于Caffe的标准YOLOv3预测
      • 3.4.3 基于Caffe的标准YOLOv3训练
  • 本文资源共享

声明

本文由 凌然 编写。

当前版本R1.0(预发布)。

作者联系方式:E-mail: WindForest@yeah.net

本文仅为个人学习记录,其中难免存在客观事实的错谬或理解上的歪曲,因此望读者切勿“拿来主义”,由本文的错误造成的损失,作者概不负责。

因在发布期间可能对本文即时修改或校对,因此如非必要请勿转载本文,以免错误的内容在转载后无法得到更新从而对其他人造成误导或负面影响。

2 工程应用分析

[说明]

工程拟基于的SDK版本为:用于 Hi3516DV300Hi3516CV500R001C02SPC010 和用于 Hi3519AV100Hi3519AV100R001C02SPC020

[补充]

海思IPC平台中,支持NNIE神经网络推理的型号并不多,截至编写,最高端的芯片型号为Hi3559AV100。由于针对的场景不同,因此需要的算力也不同,开发者需要根据设计目标和网络规模进行选型。

如果最终的工程应用有批量生产的需求,可考虑如A311D、RK3399、S905D3、RV1126、RV1109等带有NPU的SoC作为替代型号(截至2021年)。

2.1 平台/软件介绍和环境搭建

Hi3519AV100是一颗面向监控IP摄像机、运动相机、全景相机、后视镜、航拍无人机等多个产品领域推出的高性能、低功耗的4K Smart Camera SoC。它集成了性能强大的可编程神经网络推理引擎和一个向量DSP,支持多种智能算法应用。——《Hi3519AV100 4K Smart Camera SoC 产品简介(2019-02-21)》

**SVP(Smart Vision Platform)**是海思媒体处理芯片智能视觉异构加速平台。该平台包含了CPU、DSP、NNIE等多个硬件处理单元和运行在这些硬件上SDK开发环境,以及配套的工具链开发环境。

**NNIE(Neural Network Inference Engine)**是海思媒体SoC中专门针对神经网络特别是深度学习卷积神经网络进行加速处理的硬件单元。目前NNIE配套软件及工具链仅支持以Caffe框架,且以Caffe-1.0版本为基础。使用其他框架的网络模型需要转化为Caffe框架下的模型。

nnie_mapper,简称mapper,该工具将用户通过开源深度学习框架训练得到的模型转化成在Hi35xx芯片上或者在仿真库中可以加载的数据指令文件(*.wk)。

RuyiStudio集成windows版的NNIE mapper、Runtime mapper和仿真库,具有生成NNIE wk功能、Runtime wk功能和仿真NNIE功能,同时具有代码编辑、编译、调试、执行功能、网络拓扑显示、目标检测画框、向量相似度对比、调试定位信息获取等功能。——《HiSVP 开发指南(2019-07-10)》(后续简称《指南》)

查看《指南》可知,YOLOv3中的特殊网络层(如Shortcut、Route、Upsample等)都已经被SVP工具支持(或可被类似功能网络层替换),这极大地方便了我们的移植,但如果想获得更高的性能,则必须进行二次开发,详见后文。在Windows平台上仿真网络模型所需的RuyiStudio和Mapper、仿真库之间的关系如下图所示:

SVP-NNIE工具关系

截至文章编写,一些开发包中依赖的资源已经不复存在,因此需要手动下载这些资源。这些缺少的资源为:qt-5.6.2-vc14_1.tar.bz2、libtiff-4.0.9-vc14_0.tar.bz2、jpeg-9b-vc14_2.tar.bz2。

如果你使用的开发包版本为HiSVP_PC_V1.2.1.0ruyi_env_setup-2.0.31RuyiStudio-2.0.31,则直接使用随文附带的资源即可。按照下面的步骤或《指南》中的描述配置RuyiStudio所需运行环境:

  1. 安装wget-1.11.4-1-setup.exe,在命令提示符中输入

    C:\>wget
    wget: missing URL
    Usage: wget [OPTION]... [URL]...Try `wget --help' for more options.
    

    以验证安装。该工具将用于安装脚本下载网络资源。

  2. 解压MinGW-w64-x86_64-7.3.0-release-posix-seh-rt_v5-rev0.7zC:\mingw64目录。

  3. 解压msys+7za+wget+svn+git+mercurial+cvs-rev13.7zC:\msys目录。此时该目录文件夹组织如下:

    C:\mingw64>dir驱动器 C 中的卷是 OS卷的序列号是 6009-7164C:\mingw64 的目录2021-03-31  17:43    <DIR>          .
    2021-03-31  17:43    <DIR>          ..
    2021-03-31  17:46    <DIR>          bin
    2018-03-19  23:59            49,517 build-info.txt
    2018-03-19  23:39    <DIR>          etc
    2018-03-19  23:59    <DIR>          include
    2018-03-19  23:59    <DIR>          lib
    2018-03-19  23:13    <DIR>          libexec
    2018-03-19  23:59    <DIR>          licenses
    2013-04-24  17:22    <DIR>          msys
    2018-03-19  23:29    <DIR>          opt
    2018-03-19  23:59    <DIR>          share
    2018-03-19  22:08    <DIR>          x86_64-w64-mingw32
    

    将以下目录添加到PATH环境变量(如果目录设置不同,对应地进行修改即可):

    C:\mingw64\bin;C:\mingw64\msys\bin;
    

    C:\mingw64\bin\x86_64-w64-mingw32-gcc.exe在所在目录下建立副本并重命名为mingw32-gcc.exe

  4. HiSVP_PC_V1.2.1.0\tools\nnie\windows\ruyi_env_setup-2.0.31目录拷贝到C:\ruyi_env_setup目录下,该目录将作为最终SVP所需Python3.5+Caffe目录使用。

  5. 拷贝libraries_v140_x64_py35_1.1.0.tar.bz2文件到C:\ruyi_env_setup目录下。

  6. 在当前目录下建立python35目录,拷贝随文资源中Ruyi Python35 Lib Needed目录下所有压缩包到此目录,并执行C:\ruyi_env_setup\setup_python.bat

    [说明]

    如果系统中安装有其它版本的Python,有可能出现调用到错误pip版本的情况,此时需手动指定pip安装压缩包中的whl文件:

    cd C:\ruyi_env_setup\python35
    .\Scripts\pip.exe install Cython-0.28.5-cp35-cp35m-win_amd64.whl
    .\Scripts\pip.exe install opencv_python-3.4.0.12-cp35-cp35m-win_amd64.whl
    .\Scripts\pip.exe install PyYAML-3.13-cp35-cp35m-win_amd64.whl
    
  7. 执行C:\ruyi_env_setup\setup_roi_caffe.bat以安装RuyiSdutio在进行网络模型分析时所需的rpn包。

    [说明]

    即使网络中未使用RPN层也需要进行此步骤,因为工具脚本中有导入rpn包(import rpn)的语句。

这里有几点需要说明:

  1. *如果不需要网络层分析工具,仅使用网络模型转换功能,可不安装Python3.5+Caffe环境。
  2. 根据SDK中提供的脚本进行安装后的Pycaffe仅为CPU版本,因此不适合用于训练。
  3. 如果系统中有多个版本的Python共存,则可能造成冲突。RuyiStudio环境配置脚本中配置了PYTHONPATH环境变量,这可能导致其它版本的Python无法正常找到库位置,此时可尝试使用Anaconda进行隔离。

更具体的操作方法参见《指南》,此处不再赘述。

2.2 网络训练方式选择

最终的网络模型移植将在RuyiStudio工具中完成。因此我们应选取Caffe1.0神经网络计算框架或将最终模型文件转化为Caffe1.0的格式。整体的训练和开发流程如下图所示:

网络训练和开发流程

由上图可知,网络训练的重点在于如何得到网络的Caffe模型(*.caffemodel + *.prototxt)。在海思SVP开发SDK中,提供了可用于学习的YOLOv1~YOLOv3预训练模型的caffemodel文件和prototxt文件,它是从YOLO的Darknet版本生成的网络模型转化而来得到的。由此不难想到几种网络训练方式(叙述以YOLO为例):

  1. 使用Darknet或其它框架(如Pytorch或Keras)训练网络,最后使用如Darknet2Caffe等工具对模型进行转化;
  2. 查找已有的可训练版本的Caffe实现,适配编译得到的Caffe进行训练得到最终的模型;
  3. 基于Caffe源码中的examples,编写YOLOv3的训练描述文件,适配编译得到的Caffe进行训练得到最终的模型。

Python的版本(Python2.7 v.s. Python3.5)、CUDA版本和操作系统(Windows v.s. Linux)多种多样,故上述训练方式有众多的可组合路径。由于网络最终需要转换为Caffe模型,因此无论采用上述哪种方式,均需Caffe环境并为其添加Upsample等必要的网络层。更为详细的训练细节请参阅网络训练相关章节。

2.3 SVP-NNIE前向计算处理过程

本节以SVP-NNIE例程中对YOLOv3的处理过程为例,简要记录整个处理流程及其中关键之处,并为后面使用NNIE加速网络预测打好基础。由于文档的形式并不适合解读源码,因此这里仅叙述一些程序理解上的关键之处。你在阅读源码的时候应该至少关注以下问题:

  1. 使用NNIE加速神经网络模型的处理流程是怎样的?
  2. 待检测图像是如何输入到网络模型中的?
  3. 从NNIE中输出的预测结果数据的内存分布图是怎样的,与Darknet框架下的网络输出结果是否一致?
  4. 从网络输出到最终结果需要做怎样的后级处理?
  5. 示例程序的运行速度如何、主要性能瓶颈在哪里,以及实时性优化可以从哪些方面入手?

2.3.1 例程中对YOLOv3网络模型的初始化操作

阅读NNIE例程的时候,需要明白一些重要概念:

  • 网络分段

    网络分段的描述可参见《HiSVP API参考05版(2019-06-25)》P29。YOLOv3网络属于端到端的One-Stage网络模型,且在例程中由应用程序处理网络的最终输出,因此仅有一个网络分段。一个网络分段中可以包含众多的网络层。

  • Blob(源Blob、输出Blob)和YOLOv3输入输出的关系

    YOLOv3模型具有一个输入层和3个输出层(YOLO层),因此具有一个源blob和3个输出blobs。Blob是Caffe框架中的基本类,用于网络层之间的数据传递。因此获取网络段的输出blob即为获取网络的最终输出数据。

[说明]

在MPP例程中,使用YOLOv3做图像预测的过程可通过以下代码路径查看:

sample/svp/nnie/sample_nnie_main.c main -> SAMPLE_SVP_NNIE_Yolov3

和大部分程序一样,在平台上运行网络模型同样遵循“初始化-使用-去初始化”的流程,在例程中,初始化完成的工作主要有:

  • 复制网络模型到MMZ内存并加载;

  • 为NNIE硬件计算申请所需内存,该片内存分为三个部分:

    • 模型计算所需要的辅助内存(u32TmpBufSize),具体大小由加载模型时得到的模型描述结构体提供;
    • 网络模型中各分段任务缓冲区(u32TotalTaskBufSize),具体大小通过调用 HI_MPI_SVP_NNIE_GetTskBufSize API获得;
    • 在各网络段之间传输的源Blob和输出Blob所占空间(astBlobSize),如原始图像和网络段输出等。
  • 为软件计算初始化必要的参数,并为用于处理网络输出数据的辅助空间申请所需内存。辅助空间存放从网络段输出Blobs得到的原始数据,这些数据经过后级计算、排序和NMS等操作,得到最终的输出结果(s32DstRoi、st32DstScore、u32ClassRoiNum)。辅助空间由两部分组成:

    • 用于存放从网络输出Blob获取的和计算过程中的临时数据的空间(u32TmpBufTotalSize),具体大小在例程库中通过 SAMPLE_SVP_NNIE_Yolov3_GetResultTmpBuf 函数计算。该部分空间又由三部分组成:

      • 用于完整获取各输出Blob的内存空间,空间大小为各网络段输出Blob的最大值;
      • 用于存储所有原始输出Blobs(即网络的3个YOLO层)的所有网格(即13×13、26×26、52×52)的所有输出目标(对应3个锚框)的信息(即4bbox+1objectness+80classes)的内存空间;
      • 其它临时变量(例程中为快排操作所需的SAMPLE_SVP_NNIE_STACK_S类型的结构体空间)。
    • 用于存放最终过滤后的目标结果的空间(u32DstRoiSize+u32DstScoreSize+u32ClassRoiNumSize)。

2.3.2 图像的输入

在程序初始化网络模型的硬件参数时,将申请的源blob内存空间地址记录在了 s_stYolov3NnieParam.astSegData[0].astSrc[0] 中,因此按照网络训练时的输入图像格式将待检测图像拷贝到源blob地址空间即可完成图像输入,而后需刷新MMZ缓存以同步数据。

2.3.3 NNIE输出数据的内存分布图

由于例程中使用的YOLOv3网络模型是从原作者Darknet网络预训练权重直接转化而来,因此网络输出数据格式也遵循了原网络输出。所不同的是:海思NNIE为了更好地进行加速,对每行数据进行了对齐操作(参见《HiSVP API参考05版(2019-06-25)》P30)。因此获取网络输出时需要略过这些用于对齐的无效字节。

和输入一样,申请的输出blob内存空间地址记录在了astSegData[0].astDst[0:2]中,对应着YOLOv3网络的3个YOLO层。以13×13的YOLO网络层举例,其输出张量(13×13×[3×(4+1+80)]13×13×[3×(4+1+80)]13×13×[3×(4+1+80)])在内存中的存储布局如下图所示:

SVP-NNIE-YOLOv3网络输出内存分布图-1

将数据按照特征图每行排列可以更直观地看到输出内存空间的全貌,如下图所示:

SVP-NNIE-YOLOv3网络输出内存分布图-2

2.3.4 网络的后级处理

知道了输出数据在内存中的存储格式,那么例程又是如何对数据进行处理的呢?

SVP-NNIE-YOLOv3网络后级处理

如上图所示,处理函数遍历了3个YOLO网络层输出,每一次遍历将blob暂存在 pstSoftwareParam->stGetResultTmpBuf (在处理函数 SVP_NNIE_Yolov3_GetResult 中为 pf32Permute 指针)指向的空间内,而后对该blob数据进行Sigmoid计算和映射关系计算,得到每个网格针对每个锚框预测的bbox信息。遍历结束后,对这些bbox进行快排和NMS等操作即得到最终的输出结果。

通过阅读转换函数可知,存储在 pf32Permute 指向的空间中的数据组织形式为:

SVP-NNIE-YOLOv3网络结果转换blob临时空间内存分布图

2.3.5 性能分析和优化思路

某次在Hi3516DV300平台上执行YOLOv3示例程序时主要部分耗时如下:

前向计算过程:

处理动作 时间消耗(us) 在本过程中的占比
待检测图像输入 (未统计) -
缓存同步(任务段内存&输出blob) 530 0.522%
调用API进行前向推理直到完成 100421 98.93%
缓存同步(输出blob) 559 0.55%
Total 101510 -

结果获取和处理过程:

处理动作 时间消耗(us) 在本过程中的占比
变量所有输出blobs,剔除对齐字节并转换输出 933677 99.975%
结果快排 104 0.011%
NMS去重 67 0.0072%
结果过滤和统计 66 0.0071%
Total 933914 -

注1:以上时间数据包括时间打印语句本身带来的处理开销,因此数值会稍微偏大。

注2:在Hi3519AV100平台上,同样例程的计算耗时为:前向67ms,后续461ms。为了让矛盾更清晰,这里仅基于Hi3516DV300的数据做分析和优化。

例程使用的测试图像包含的目标较少,因此快排和NMS没有明显耗时,但从上述结果来看,网络的前项推理和后续对各输出blob的遍历的确花费了大量的时间。阅读代码可知,例程中遍历了所有网格(13×13、26×26、52×52)的所有预测(3个),并对坐标、置信度和类概率执行了Sigmoid计算,对宽高做了exp计算。仅浮点计算次数就以百万计,这其中不乏大量的空预测和无效预测,不加区分地处理每个输出严重浪费了处理器性能。

[补充]

对网络输出使用Sigmoid计算等操作的处理依据来自Darknet-YOLOv3的源码。不同于YOLOv1,YOLOv3在损失函数进行计算前,对YOLO网络层输出中的相关量做了同样的激活计算。

对于例程中采用的结果处理方法,有以下几种改进策略:

  • 先进行过滤处理,再对有效预测结果做激活和映射等浮点运算

    每个网格会为每个尺度的anchorbox预测结果输出80个分类的类概率,而最终处理又仅会选取其中得分最大的作为最终结果类别,因此可先进行数值比较再对单一分类的结果做浮点计算。

  • 重新训练网络,减少网络预测的分类数

    减少网络分类数也就减少了网络的输出张量,进而在加快前向计算的同时降低了后级处理的开销。

网络前向计算速度则快很多,但100ms+的耗时依然没有达到实时性要求,好在对于目标任务,我们并不需要像YOLOv3这样“完美”的网络,因此在网络深度和输出上有不少可裁剪甚至重新设计的空间。例如考虑到监控摄像头所处角度较高,因此拍摄到的目标较小,甚至不会占满半个屏幕,因此可以裁剪特征图较小的输出层;视频监控不需要严格定位目标的边界,而更倾向于对目标的检测和类别的识别,因此可以减少骨干网络后用于特征合并和坐标预测的网络层;另外,诸如“使用深度可分离卷积替换常规卷积核”和“对网络进行剪枝”等方法可能会对减小网络模型体积、提升网络预测速度有帮助。

[补充]

事实上,YOLO系列网络发展至此,从工程应用出发,基于YOLOv3进行改进已经没有太大的必要性了,在后文中我们将一起看一个更轻、更快、更好的YOLO-Fastest模型,它将比YOLOv3更适合追求实时性的工程应用。

3 Caffe框架和网络训练流程

计算框架的选择是和模型本身以及平台支持相关的,使用Caffe不一定合适,其它框架也不一定不适合。这部分内容请参见 2.2 网络训练方式选择 章节。

[说明]

本文保留本章的目的是为未来的读者们提供必要的参考,事实上Caffe框架已经停止更新很久了(BVLC版本),但由于Caffe具有简洁清晰的架构,至今仍被很多的开发者和公司青睐。使用Caffe开发除了需要良好的算法开发能力外还需要C++编程经验,因此对于学生而言,在C++编程能力欠缺的情况下,不建议使用Caffe框架编写具有结构创新的新网络。

一些新的支持NPU或神经网络加速模块的芯片的开发工具包中已经具备了直接对Tensorflow或Keras等模型进行转换的能力,一些新的工程也支持了onnx格式的模型输出支持。Android/IOS端则可以使用诸如ncnn等框架进行部署。因此除非你已经熟悉Caffe或目标任务严重依赖Caffe,建议优先考虑使用其它框架。

本章的内容为后文的Darknet转Caffe提供了必要的基础,例如对Prototxt文件和Caffemodel相关的描述。

3.1 Caffe平台的搭建

在Windows上搭建PyCaffe环境可参考《Windows版PyCaffe-GPU的编译》(MS-Python2版本)和《Windows版PyCaffe-GPU的编译(续)》(BVLC-Python3版本)。

在Linux上搭建OpenCV+Caffe+Python3环境可参考:《Install OpenCV 4.4.0 and Caffe on Ubuntu 20.04 for Python 3》及系列指南,本人未做尝试不予评价。

3.2 Caffe计算框架基础(基于mnist示例)

3.2.1 Prototxt文件、网络结构和训练参数

从深度卷积神经网络的所需出发,一个计算框架应该实现的基本功能包括:对网络结构进行定义的方法、各网络层的前向计算和反向传播的实现、提供不同的权重更新方式、定义在各网络层之间传递的数据的格式、提供数据输入输出接口以及对网络进行保存和恢复的机制等。

Caffe使用三种(或两种)不同的prototxt文件定义训练网络结构、验证网络结构和训练参数,它们根据作用可被命名为 NetName_train.prototxtNetName_test.prototxtNetName_solver.prototxt或类似的名字。本章后续进行描述时可能使用solver文件train文件test文件对上述三类文件进行指代。实际上,由于设计的不同,一些网络可将train文件和test文件实现在一个文件中,这样的网络描述文件一般命名为NetName_train_test.prototxt

接下来以Caffe目录中的lenet_solver.prototxt 文件和对应的 examples/mnist/lenet_train_test.prototxt 文件为例,对Caffe训练所需的Prototxt文件进行简单的说明。这些文件可以被以普通的文本文件形式打开和编辑,当需要以图形化的方式查看网络结构的时候,可使用Netscope(网页版)或者是Netron(网页版或单机版)进行查看。

mnist示例网络图形化(Netron生成) 左侧为训练(TRAIN)时网络,右侧为测试(TEST)时网络

由上图可看出,mnist示例使用的Lenet是一个one-stage网络。

solver文件(lenet_solver.prototxt)

solver文件指定了网络训练时的参数,以该文件为例,各参数的作用和介绍如下:

  1. net

    该参数用于指示train_test共用网络模型描述文件的路径。如:

    net: "PATH_TO_FILE/NetName_train_test.prototxt"
    

    当train文件和test文件是各自独立的时候,对其分别指定:

    train_net: "PATH_TO_FILE/NetName_train.prototxt"
    test_net: "PATH_TO_FILE/NetName_test.prototxt"
    
  2. test_iter(test iteration,测试迭代) & max_iter(max iteration,最大迭代)

    迭代表示网络完成一次前向计算-损失函数计算-反向传播的过程。

    batch为单次送入网络进行一次迭代的一批数据,batchsize为该批数据的数量。

    epoch表示将所有训练数据集中的图片在网络中训练一次所需要的迭代次数,即当网络完成一个epoch训练时,训练数据集中的每张图片都被网络”看到“了一次。

    若希望每张图片被训练x次,则需要增加总的迭代次数,即max_iteration=x×epochmax\_iteration=x×epochmax_iteration=x×epoch。

batch、epoch、max_iteration和训练数据集的关系

```
# 每迭代100次进行一次测试
test_iter: 100
# 设置最大迭代10000次
max_iter: 10000
```
  1. test_interval(测试间隔)

    该参数用于设置测试网络当前效果的间隔,单位为迭代次数。如:

    # 设置每500次迭代进行一次测试
    test_interval: 500
    
  2. base_lr(base learning rate,基础学习率)& momentum(动量)& weight_decay(权重衰减)

    学习率可以理解为每次损失函数的梯度对网络权重的影响力度,一般网络训练初期会选取一个较大的学习率以便网络权重的快速初始化,训练后期则采用相对较小的学习率使网络权重逐步趋近最优值。

    动量算法是对梯度下降算法的一种优化,动量决定了前一次梯度更新在本次更新的权重。动量的引入使梯度更新不仅依赖于本次的损失函数计算结果,还依赖于历史梯度数据,若本次梯度更新方向与历史梯度更新方向相似,则动量算法会加强这次更新,反之减弱。

    权重衰减实际就是L2正则化,其作用为防止网络过拟合。

    # 设置学习率为0.01,动量0.9,权重衰减0.0005
    base_lr: 0.01
    momentum: 0.9
    weight_decay: 0.0005
    
  3. lr_policy(learning rate policy,学习率策略)

    Caffe提供的学习率策略一共有7种,它们分别依赖其它不同的辅助参数,具体如下:

    学习率调整策略 依赖的其它参数 调整公式
    fixed - base_lrbase\_lrbase_lr
    step gamma、stepsize $base_lr*gamma^{floor(\frac{iter}{stepsize} )} $
    exp gamma $base_lr*gamma^{iter} $
    inv gamma、power $base_lr*(1+gamma*iter)^{-power} $
    multistep stepvalue 与step类似,根据stepvalue变化
    poly power $base_lr*(1-iter/max_iter)^{power} $
    sigmoid gamma base_lr∗(1/(1+ℓ−gamma∗(iter−stepsize)))base\_lr * \left ( 1 / ( 1 + \ell ^{ -gamma * ( iter - stepsize ) } ) \right )base_lr∗(1/(1+ℓ−gamma∗(iter−stepsize)))

    注:iter为当前迭代次数,floor为向下取整,ceil为向上取整。

  4. display(显示间隔)

    该参数用于设置屏幕打印间隔,单位为迭代次数。如:

    # 设置每100次迭代打印一次结果
    display: 100
    
  5. snapshot(快照)

    该参数用于设置快照保存间隔,单位为迭代次数。如:

    # 设置每5000次迭代保存一次快照
    snapshot: 5000
    

    快照文件包括当前网络权重文件(*.caffemodel)和solver状态文件(*.solverstate)。

  6. snapshot_prefix(快照文件前缀)

    该参数用于指定快照文件存放的位置和文件名前缀,编号和后缀名将由Caffe自动生成。如:

    snapshot_prefix: "PATH_TO_FILE/NetName_PrefixName"
    
  7. solver_mode(解析器模式)

    该参数用于指示训练过程运行在CPU或是GPU上。如:

    solver_mode: GPU
    

然而在众多的网络模型参数配置文件中,我们经常可以看见上述参数之外的其它配置项,这些Caffe提供的配置项共有几十个之多,网上也有很详细的讲解帖子,此处不再赘述,后面如果有需要会单独说明。

train & test文件(lenet_train_test.prototxt)

train文件是网络进行训练时的结构描述文件,test文件是网络进行测试和验证时的结构描述文件。通常来说,train文件中包含网络的损失函数层实现,网络最后的输出层会被输入到损失函数层进行计算,进而通过前向传播更新各层权重,而test文件中网络的后级输出会被连到结果输出层从而获取分类或预测信息。在mnist示例中,train和test文件共用一个。

Caffe的网络描述文件还是非常通俗易懂的,每网络层以这样的形式进行抽象:

name: "NetName" # 该参数在整个网络描述文件中只配置一次
layer{# 网络层参数描述name: "网络层名称"type: "网络层类型"bottom: "网络层的输入来源"top: "网络层的输出名称"param{# 所有层共用参数在本网络层的具体配置lr_mult: 1 # 学习率相乘系数}LayerType_param{# 该类型网络层特有参数在本网络层的具体配置}
}

其中,根据网络层类型的不同,参数项不是必须的,但必须至少具有名称、类型、输入或输出。

本节内容参考了《深度学习实践 基于Caffe的解析》一书。

3.2.2 网络的输入层和输出层

网络层可以有多个输入或多个输出,则对应地增加bottom字段和top字段即可。对于较为常见的卷积层(Convolution)、池化层(Pooling)、BN层(BatchNorm)等,它们的层参数还是比较好理解的。对于某些网络特有层或者是不常见的网络层,我们也可以通过网络层类型、参数命名或者阅读在 src/caffe/proto/caffe.proto 文件中的定义和注释了解对应的含义。然而无论是怎样的网络,都无一例外需要考虑到两种层类型:一是网络的数据输入层;二是网络的输出处理层(包括对应train文件的损失函数层和对应test文件的最终输出处理层)。

(1)网络输入层

mnist示例网络为一个经典的多分类网络,输入数据包括图片和对应的标签,网络输出为一个Softmax分类器。Caffe根据当前网络运行状态(训练或验证)分别使用以下两种网络输入层:

# 训练模式下网络输入层
layer {name: "mnist"type: "Data"top: "data"top: "label"include {phase: TRAIN}transform_param {scale: 0.00390625 # 即1/256}data_param {source: "examples/mnist/mnist_train_lmdb" # 选择训练数据集batch_size: 64backend: LMDB}
}# 测试/验证模式下网络输入层
layer {name: "mnist"type: "Data"top: "data"top: "label"include {phase: TEST}transform_param {scale: 0.00390625}data_param {source: "examples/mnist/mnist_test_lmdb" # 选择测试数据集batch_size: 100backend: LMDB}
}

根据include项中包含的内容可以猜测出网络层的使用场景,“Data”的网络层类型表示输入图像格式为LevelDB或LMDB,由backend字段具体选择。Data层的transform_param参数包括四个选项:scale、mean_file_size、mirror和crop_size。它们的具体含义在链接给出的博客中很详细地进行了说明。但是Caffe不仅提供了这一种数据输入方式,输入数据还可以来自内存(MemoryData类型)、HDF5文件(HDF5Data类型)、已有的图片(ImageData类型)、带注释数据(AnnotatedData类型)等等,或者使用自定义的网络输入层。这些支持的网络层及其选项都可以在 caffe.proto 文件中找到。

数据输入层的存在为不同的网络结构提供了统一的数据输入接口,通过该层的top输出,其它网络层可从原始的训练集中获取到图像、标签或标注信息。当网络目标任务发生变化时,例如从训练变为部署,可通过替换数据输入层达到兼容不同应用场景的目的。

(2)网络输出层

在mnist示例中,网络末端为两层全连接层。Inner Product意为内积,这是因为全连接层的前向计算的实质为矩阵乘法。

和输入不同,Caffe无法为网络的输出层提供通用的示例或接口,输出一般由设计者定义。以第2章所示转换后的YOLOv3网络结构示例进行说明:它仅获取了YOLOv3网络YOLO层的卷积结果,而后在软件中进行激活等处理,因此网络结构本身并没有包含输出处理层。

对于通常的深度学习,在训练阶段,最后一层网络的输出将连接到损失函数层,通过损失函数校正网络训练方向;在验证或部署阶段则连接到输出数据处理层,以便直接提供网络最终的预测结果。为了提升速度,在训练阶段使用到的网络层一般均需提供CUDA代码实现以便Caffe调用GPU进行加速,而在终端部署的时候则需提供针对目标平台的加速代码。

由于嵌入式平台对一些新型算法、算子、激活函数等支持存在时间差,因此并非总是能完全移植最新的网络结构,这种情况下工程师们一般使用相似的功能对不支持的模块进行替换,并对网络模型进行量化和精简,即所谓工程实践和理论研究的不同之处。

3.2.3 处理操作相关

(1)模型的训练 & 结果的保存

注:本节内容基于BVLC版本Caffe进行,环境搭建参见 3.1 Caffe平台的搭建 章节。

说明:为了简单起见,本节不使用Caffe的Python接口。

说明:以下操作均在 ./mnist_BVLC 目录中进行。

使用随文资源中已经下载好的mnist-LMDB数据集或者按照网上众多博文给出的步骤下载并生成mnist数据集。在 ./mnist_BVLC 目录下准备好训练集和验证集的 mnist_train_lmdb 目录和 mnist_test_lmdb 目录。拷贝Caffe工程中 examples/mnist/lenet_train_test.prototxtexamples/mnist/lenet_solver.prototxt 文件到 ./mnist_BVLC

[补充]

通过在solver文件中添加快照相关配置,可使网络在训练过程中定期生成快照和模型文件。通过在训练时指定 snapshot 参数或者weights 参数可选择基于快照或模型文件进行再训练。

solver文件内容如下:

net: "./lenet_train_test.prototxt"
test_iter: 100
test_interval: 500
base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005
lr_policy: "inv"
gamma: 0.0001
power: 0.75
display: 100
max_iter: 10000
snapshot: 5000
snapshot_prefix: "./lenet_mnist"
solver_mode: GPU

修改train & test网络结构描述文件内容中的数据集位置字段:

name: "LeNet"
layer {name: "mnist"type: "Data"top: "data"top: "label"include {phase: TRAIN}transform_param {scale: 0.00390625}data_param {source: "./mnist_train_lmdb" # 指向训练数据集目录batch_size: 64backend: LMDB}
}
layer {name: "mnist"type: "Data"top: "data"top: "label"include {phase: TEST}transform_param {scale: 0.00390625}data_param {source: "./mnist_test_lmdb" # 指向验证数据集目录batch_size: 100backend: LMDB}
}...# 后续网络结构

而后使用命令提示符或Windows PowerShell进入 ./mnist_BVLC 并执行:

cd /d PATH_TO_DEST_DIRECTORY/
PATH_TO_BVLC/scripts/build/tools/Release/caffe.exe train --solver=lenet_solver.prototxt

最终的网络精度达到99.1%:

...
I0504 16:07:09.694408 21636 solver.cpp:447] Snapshotting to binary proto file ./lenet_mnist_iter_10000.caffemodel
I0504 16:07:09.722406 21636 sgd_solver.cpp:273] Snapshotting solver state to binary proto file ./lenet_mnist_iter_10000.solverstate
I0504 16:07:09.737406 21636 solver.cpp:310] Iteration 10000, loss = 0.00320604
I0504 16:07:09.737406 21636 solver.cpp:330] Iteration 10000, Testing net (#0)
I0504 16:07:10.240324 17376 data_layer.cpp:73] Restarting data prefetching from start.
I0504 16:07:10.261320 21636 solver.cpp:397]     Test net output #0: accuracy = 0.9912
I0504 16:07:10.261320 21636 solver.cpp:397]     Test net output #1: loss = 0.0270723 (* 1 = 0.0270723 loss)
I0504 16:07:10.261320 21636 solver.cpp:315] Optimization Done.
I0504 16:07:10.262322 21636 caffe.cpp:260] Optimization Done.

网络的验证过程也可以手动进行:

cd /d PATH_TO_DEST_DIRECTORY/
PATH_TO_BVLC/scripts/build/tools/Release/caffe.exe test --model=lenet_train_test.prototxt --weights=lenet_mnist_iter_10000.caffemodel -iterations 100

使用该命令重复测试100轮。其中model参数和weights参数是必须的。

[补充]

caffe.exe命令的更多参数和可选项可以参考文档或者其它博文。

(2)(在PC上)使用训练好的mnist模型做预测

首先从mnist-LMDB数据集生成均值文件:

cd /d PATH_TO_DEST_DIRECTORY/
PATH_TO_BVLC/scripts/build/tools/Release/compute_image_mean.exe ./mnist_train_lmdb mnist_mean.binaryproto --backend=lmdb

设法获取28*28的手写数字灰度图像,例如本节使用到的 2.png ,以及标签文件 mnist_labels.txt

0
1
2
3
4
5
6
7
8
9

将网络结构描述文件拷贝一个副本重命名为 lenet_deploy.prototxt ,并将两网络输入层替换为:

layer {name: "data"type: "Input"top: "data"input_param {shape: {dim: 1dim: 1dim: 28dim: 28}}
}

将网络输出层的 accuracy 层删除,将 SoftmaxWithLoss 层改为:

layer {name: "Predict"type: "Softmax"bottom: "ip2"top: "Predict"
}

使用以下语句进行模型预测:

cd /d PATH_TO_DEST_DIRECTORY/
PATH_TO_BVLC/scripts/build/examples/cpp_classification/Release/classification.exe ./lenet_train_test.prototxt ./lenet_mnist_iter_10000.caffemodel ./mnist_mean.binaryproto ./mnist_labels.txt ./2.png
---------- Prediction for ./2.png ----------
1.0000 - "2"
0.0000 - "0"
0.0000 - "3"
0.0000 - "1"
0.0000 - "4"

有可能出现预测结果不正确的情况,这是正常的。

(3)训练日志的可视化

[引用]

本节内容参考:《Caffe - 训练日志 log 可视化分析_CSDN》

Caffe的训练日志即为训练过程中的打印信息,信息输出频次在solver文件中设置,上文已经说明过了。打印的信息包含网络训练过程中的损失、精度等信息,可使用以下语句将训练过程中的输出保存到文件:

cd /d PATH_TO_DEST_DIRECTORY/
PATH_TO_BVLC/scripts/build/tools/Release/caffe.exe train --solver=lenet_solver.prototxt 2>&1 | tee mnist_train.log

使用Caffe自带的日志分析工具进行数据提取:

cd /d PATH_TO_DEST_DIRECTORY/
python PATH_TO_BVLC/tools/extra/parse_log.py mnist_train.log ./

即可在当前目录中得到 mnist_train.log.trainmnist_train.log.test 两文件。

[注意]

该工具脚本基于Python2语法,可在Anaconda中新建Python2.7的最小环境,而后在该虚拟环境中运行脚本。无需特殊依赖包。

使用以下代码可将损失和精度变化绘制出来。如果需要其它的信息或美化打印图,可自行修改代码。

import pandas as pd
import matplotlib.pyplot as plttrain_log = pd.read_csv("./mnist_train.log.train")
test_log = pd.read_csv("./mnist_train.log.test")_, ax1 = plt.subplots()
ax1.set_title("mnist log")
ax1.plot(train_log["NumIters"], train_log["loss"], alpha=0.5)
ax1.plot(test_log["NumIters"], test_log["loss"], 'g')
ax1.set_xlabel('iteration')
ax1.set_ylabel('train loss')
plt.legend(loc='upper left')ax2 = ax1.twinx()
ax2.plot(test_log["NumIters"], test_log["accuracy"], 'r')
ax2.set_ylabel('test accuracy')
plt.legend(loc='upper right')plt.show()

[说明]

该代码为随文draw_log.py文件,运行环境兼容Python2和Python3。

mnist示例日志数据可视化

由打印图可见模型在约1000次迭代之后即达到了饱和。

3.3 MobileNet-YOLOv3工程实现分析

Caffe的文档即使在几大框架中也是最为人诟病的一点,更何况我们是从零开始,如果没有示例确实寸步难行,因此我尝试寻找Caffe版本的YOLOv3代码实现,但并没有找到完全契合的源码,也许是我的搜索技术不到家或者没有人将其开源的缘故吧。在查找过程中发现,被克隆最多的工程是Caffe-YOLOv3-Windows-eric612,该工程将MobileNet和YOLOv3损失函数组合在一起,因此被作者称为“MobileNet-YOLOv3”。

寻找和学习现有的工程实现主要是为了解决以下几方面的问题:

  1. 网上大部分Caffe示例是基于分类网络的,针对目标检测任务的描述较少。Caffe是如何处理针对检测任务的数据输入的?
  2. 接上一问,如何制作可用于训练的LMDB目标检测数据集?
  3. 由于Caffe的网络层编写是基于C++的,而我现在C++上完全是菜鸟,因此如果有可借鉴的代码会减少工作量。
  4. 基于一份示例,尝试网络的训练流程,有助于整体工程的把控和时间节点规划。

3.3.1 工程编译

该工程的编译过程和 3.1 Caffe平台的搭建 章节描述的基本一致。这里仍然使用VS2015进行编译。

[注意]

实践中若开启NCCL选项则会出现 class “boost::common_type<int_least64_t, int_least64_t>” has no member “type” 的错误,因此应予以禁用。

[注意]

若禁用Python接口,编译将使用默认的Python2.7环境,若 libraries_v140_x64_py27_1.1.0.tar.bz2 下载过慢,可手动下载后拷贝到:

C:/Users/USERNAME/.caffe/dependencies/download/

[注意]

编译中可能出现 LINK : fatal error LNK1181: 无法打开输入文件“\openmp.obj” 的错误。

重新解压工程目录,在build_win.cmd脚本运行配置刚刚完成的时候终止脚本,修改 build/CMakeCache.txt 文件中关于OpenMP的配置为:

...
//C++ compiler flags for OpenMP parallization
- OpenMP_CXX_FLAGS:STRING=/openmp
+ OpenMP_CXX_FLAGS:STRING=-openmp//C compiler flags for OpenMP parallization
- OpenMP_C_FLAGS:STRING=/openmp
+ OpenMP_C_FLAGS:STRING=-openmp
...

而后重新运行脚本,脚本将根据该配置缓存生成VS工程,用VS打开 build/caffe.sln 解决方案并在Release x64下生成全部即可。

3.3.2 网络的输入层&检测任务数据集

(1)AnnotatedData类型数据输入层

[说明]

这里以 models/yolov3/mobilenet_yolov3_train.prototxt 中描述的结构为例。

数据输入层及其注释如下,关于其中一些参数的定义可在 caffe.proto 文件中找到,这一点前文已经说过了。

layer {name: "data"type: "AnnotatedData"top: "data"top: "label"include {phase: TRAIN             # 表示该层仅用于训练过程}transform_param {scale: 0.007843          # 即1/127.5mirror: true             # 开启镜像mean_value: 127.5mean_value: 127.5mean_value: 127.5        # 用于图像归一化:(x-mean_value)*scaleresize_param {prob: 0.1              # 使用该缩放策略的可能性resize_mode: WARP      # OpenCV中通过仿射变换完成图像缩放height: 320width: 320interp_mode: LINEAR    # 双线性插值interp_mode: AREA      # 区域插值interp_mode: LANCZOS4  # 兰索斯插值}...                      # 此处省略了一些缩放参数层emit_constraint {emit_type: CENTER}distort_param {          # 图像变换brightness_prob: 0.5brightness_delta: 32.0 # 亮度相关contrast_prob: 0.5contrast_lower: 0.5contrast_upper: 1.5    # 对比度相关hue_prob: 0.5hue_delta: 18.0        # 色调相关saturation_prob: 0.5saturation_lower: 0.5saturation_upper: 1.5  # 饱和度相关random_order_prob: 0.0}expand_param {prob: 0.66max_expand_ratio: 2.0}}data_param {source: "examples/VOC0712/VOC0712_trainval_lmdb"  # 训练数据集目录batch_size: 6                                     # 训练时的batch大小backend: LMDB                                     # 数据集格式}annotated_data_param {yolo_data_type : 1yolo_data_jitter : 0.3label_map_file:  "data/VOC0712/labelmap_voc.prototxt"  # 数据集分类信息}
}

该AnnotatedData类型的网络层来自SSD实现。作者在当前工程中的README中给出了MobileNet-SSD-Windows的工程链接。查看SSD网络的train文件可发现其数据输入层也是使用AnnotatedData类型。该类型网络层允许在图像数据之外增加标注数据,属于用户扩展网络层,因此不在BVLC版本中提供。

(2)VOC数据集转LMDB数据库文件

[引用]

本节内容参考:

  1. caffe-ssd 训练自己的VOC数据集(一):转换VOC xml数据为lmdb格式_CSDN
  2. caffe-ssd训练自己的数据集_博客园

观察已有的配置文件可知,AnnotatedData层输入数据的格式为LMDB(或者LevelDB),因此如果我们想使用这个框架进行训练,首先需要获取到LMDB格式的检测数据集。将VOC(格式的)数据集转为LMDB格式的步骤网上有很多,这里为了防止链接失效再重复记录一遍,应该不算抄袭吧(笑)。

[补充]

VOC数据集下载链接:

  1. VOC2007-TrainVal
  2. VOC2007-Test
  3. VOC2012

下载好的数据集原件已经放在随文资源中。

将数据集解压,即内容存放在 VOCdevkit/VOC2007VOCdevkit/VOC2012 目录中。将以下脚本保存为sh文件(e.g. create_list.sh)存放在 **VOCdevkit/**目录下,并在该目录下通过Windows PowerShell执行:

#!/bin/bash# --------------------------------------------------
# 1. 注意修改root_dir变量指向的数据集路径!
# 2. 注意修改get_image_size.exe的执行路径!
# 3. 注意将路径中的斜杠换为Linux风格的“/”!
# --------------------------------------------------root_dir=PATH_TO_DIRECTORY/VOCdevkit/
sub_dir=ImageSets/Main
bash_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
for dataset in trainval test
dodst_file=$bash_dir/$dataset.txtif [ -f $dst_file ]thenrm -f $dst_filefifor name in VOC2007 VOC2012doif [[ $dataset == "test" && $name == "VOC2012" ]]thencontinuefiecho "Create list for $name $dataset..."dataset_file=$root_dir/$name/$sub_dir/$dataset.txtimg_file=$bash_dir/$dataset"_img.txt"cp $dataset_file $img_filesed -i "s/^/$name\/JPEGImages\//g" $img_filesed -i "s/$/.jpg/g" $img_filelabel_file=$bash_dir/$dataset"_label.txt"cp $dataset_file $label_filesed -i "s/^/$name\/Annotations\//g" $label_filesed -i "s/$/.xml/g" $label_filepaste -d' ' $img_file $label_file >> $dst_filerm -f $label_filerm -f $img_filedone# Generate image name and size infomation.if [ $dataset == "test" ]then../../build/tools/Release/get_image_size.exe $root_dir $dst_file $bash_dir/$dataset"_name_size.txt"fi# Shuffle trainval file.if [ $dataset == "trainval" ]thenrand_file=$dst_file.randomcat $dst_file | perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' > $rand_filemv $rand_file $dst_filefi
done

即可在目录下生成 test.txttrainval.txt 文件,该过程因处理器或磁盘性能不同可能需要几分钟到十几分钟不等。查看文件可知,其内容涵盖了两大数据集。

[补充]

实际上,该文件内容参考自Caffe_SSD工程的 data/VOC0712目录下的同名文件。

而后我们使用MobileNet-YOLOv3工程提供的 convert_annoset 工具将数据集转为LMDB格式。该工具源码在tools目录下。我们使用的转换命令为:

# 转换测试数据集(test)
PATH_TO_FILE/convert_annoset.exe \--anno_type=detection \--encode_type=jpg \--encoded=true \--shuffle=true \--label_map_file=PATH_TO_DIRECTORY/VOCdevkit/labelmap_voc.prototxt \PATH_TO_DIRECTORY/VOCdevkit/ \PATH_TO_DIRECTORY/VOCdevkit/test.txt \PATH_TO_DIRECTORY/VOCdevkit/VOCdevkit_test_lmdb # 转换训练数据集(trainval)
PATH_TO_FILE/convert_annoset.exe \--anno_type=detection \--encode_type=jpg \--encoded=true \--shuffle=true \--label_map_file=PATH_TO_DIRECTORY/VOCdevkit/labelmap_voc.prototxt \PATH_TO_DIRECTORY/VOCdevkit/ \PATH_TO_DIRECTORY/VOCdevkit/trainval.txt \PATH_TO_DIRECTORY/VOCdevkit/VOCdevkit_trainval_lmdb

这些步骤完成后,即可在VOCdevkit目录下得到LMDB数据集文件夹 VOCdevkit_test_lmdbVOCdevkit_trainval_lmdb

3.3.3 网络的训练

修改已有训练文件中相关路径,而后仿照前面的运行示例执行训练命令即可。

[注意]

如果数据集路径不正确,将出现训练进程执行到数据输入层解析时闪退的现象,train文件和test文件中的相关路径都必须被正确设置才能执行训练。

[注意]

该版本工程中,若运行前文中的mnist示例,需要特别指定solver文件中的type字段为“sgd”而非“SGD”,或在相应源码中进行修改亦可。

使用 models/yolov3/mobilenet_yolov3_XXX.prototxt 在作者本机上训练时发现,batch_size设置为1时的显存占用约1.4G。

3.4 标准YOLOv3的适配、预测和训练

Caffe中已经包含了众多常用的网络层,对于YOLOv3使用的一些特殊层,也有可替换的选项,例如shortcut对应elwise、route对应concat。因此如果只是最基本地跑一下YOLOv3,网络层这部分基本没有什么需要额外做的工作。

本节将基于上节的Caffe-YOLOv3-Windows-eric612工程,尝试使用标准YOLOv3网络执行目标检测任务。

3.4.1 (Upsample)网络层的添加

添加网络层的一般步骤简述为:

  1. 实现网络层的C++代码、头文件和CUDA代码,将头文件放在 /include/caffe/layers 目录下,将源文件放在 /src/caffe/layers 目录下;

  2. 修改Caffe原型文件 src/caffe/proto/caffe.proto ,在 message LayerParameter 结构中增加新网络层定义,并分配不同于现有的标号;

    message LayerParameter{# 早先的定义...optional NewLayerParameter newlayer_param = 1111; # 分配号不可重复,一般按顺序分配,此处仅为示例
    }
    
  3. 接上一步,在文件中(如最末尾处)为新网络层增加 message NewLayerParameter 描述,以告知网络层所需参数;

    message UpsampleParameter{optional 变量类型 变量名 = 变量值 [default = 默认值];...
    }
    
  4. 在VS工程中增加对新网络层的编译,或重新生成VS工程;

  5. 重新编译Caffe。

按照上述步骤添加现有的Upsample实现(例如来自Darknet2Caffe工程中的实现)以使Caffe支持Upsample网络层解析。如果操作遇到困难,可参考网上相关经验文章。

3.4.2 基于Caffe的标准YOLOv3预测

[说明]

本节使用的原始YOLOv3的Prototxt文件和Caffemodel文件来自SVP示例中提供的版本,它们都是从Darknet预训练网络转换而来的。

对于网络预测任务,只需要在该Prototxt文件末尾增加Yolov3DetectionOutput网络层以获取输出即可。使用示例可参考MobileNet-YOLO网络的Prototxt文件。下面是修改后的检测输出层参数。将修改后的文件命名为 yolov3_deploy.prototxt 保存到 models/yolov3/ 目录,Caffemodel文件同样复制到该目录下并重命名为 yolov3_deploy.caffemodel

...layer {name: "detection_out"type: "Yolov3DetectionOutput"bottom: "layer82-conv"bottom: "layer94-conv"bottom: "layer106-conv"top: "detection_out"yolov3_detection_output_param {confidence_threshold: 0.01nms_threshold: 0.3num_classes: 80biases: 10biases: 13biases: 16biases: 30biases: 33biases: 23biases: 30biases: 61biases: 62biases: 45biases: 59biases: 119biases: 116biases: 90biases: 156biases: 198biases: 373biases: 326mask:6mask:7mask:8mask:3mask:4mask:5mask:0mask:1mask:2anchors_scale:32anchors_scale:16anchors_scale:8mask_group_num:3}
}

按照下面的示意修改现有的检测脚本 examples/demo_yolo_lite.cmd

    ...-   set in_dir=data\
+   set in_dir=data\VOC0712\set wait_time=1000
-   build\examples\ssd\Release\ssd_detect models\yolov3\mobilenet_yolov3_lite_deploy.prototxt ^
+   build\examples\ssd\Release\ssd_detect models\yolov3\yolov3_deploy.prototxt ^
-   models\yolov3\mobilenet_yolov3_lite_deploy.caffemodel ^
+   models\yolov3\yolov3_deploy.caffemodel ^%in_dir% ^...

YOLOv3预训练权重是基于80分类数据集的,因此需要修改 examples/ssd/ssd_detect.cpp 工具源码,删除对custom_class宏定义的注释以启用80分类检测,而后重新编译该工具。

[注意]

若不对ssd_detect.cpp进行修改,由于检测器默认配置为基于VOC-20分类标签,会导致检测结果分类错误。

执行脚本即可看到网络模型的输出演示。

使用预训练权重的标准YOLOv3预测结果

3.4.3 基于Caffe的标准YOLOv3训练

MobileNet-YOLO工程使用的YOLOv3网络输出层可以正常处理Darknet转换出的Caffemodel权重佐证了一点:该工程的YOLOv3损失函数层对YOLO网络层的每项输出的定义和Darknet是相同的,因此可以直接复用这些已有的实现进行网络训练,训练出的模型可以直接进行(第2章所示的)移植和验证,而不用大规模修改原例程的处理逻辑。

[补充]

其实在尝试使用现有的标准YOLOv3模型进行预测之前,应当先对网络层实现进行分析,目的是为了查看MobileNet-YOLOv3实现中网络输出张量的内存分布是否和Darknet-YOLOv3的一致。

损失函数通过对不同地址的输出张量数据进行读取并计算损失值,间接规定了每个网络输出的含义。这并不很难理解,结合指针和偏移的概念,如果损失函数假设数据是以4+1+804+1+804+1+80的形式存放在内存中的,并在 *(BaseAddr+4) 的位置读取目标置信度,那么最终的网络输出中,*(BaseAddr+4)位置输出的张量即表示目标的置信度信息——损失函数规定了它的意义。

因此,只需查找代码中对网络输出层数据的使用是否符合 1.3.4-(4) yolo网络层结果输出 章节中示意的内存分布即可做出判断。各网络层源码在 PATH_TO_CAFFE/src/caffe/layers/ 目录下。只需查看 yolov3_layer.cppyolov3_detection_output_layer.cpp 文件中 Forward_cpu 函数对 bottom 参数的使用情况即可。

基于前一节用于预测的网络模型描述文件,参照MobileNet-YOLO对应的描述文件修改YOLOv3训练和验证模型结构描述文件的输入和损失函数层/输出层,修改过程中需要注意以下几点:

  1. 无论是train文件还是test文件,数据输入层所有resize_param参数中的prob字段数值的加和应为1;
  2. 根据使用的数据集的不同,每YOLO网络层的输出张量需要进行修改。在使用3个anchors的情况下,当基于COCO数据集训练时,由于类别数为80,因此YOLO层输出张量为3×(4+1+80)=2553×(4+1+80)=2553×(4+1+80)=255个;当基于VOC数据集训练时,由于类别数为20,因此YOLO层输出张量为3×(4+1+20)=753×(4+1+20)=753×(4+1+20)=75个;其它或自定义数据集的计算与此类似。涉及修改的YOLO网络层分别为: layer82-convlayer94-convlayer106-conv
  3. 基于上一点,网络的损失函数层和准确度计算层的分类数量同样需要做修改。

修改后的train文件网络输入和损失函数层配置如下:

# 以下内容为网络层输入,使用前文生成的VOC-LMDB数据集
name: "Caffe-YOLOv3"
layer {name: "data"type: "AnnotatedData"top: "data"top: "label"include {phase: TRAIN         # 该字段表示此配置用于训练过程}transform_param {scale: 0.007843mirror: truemean_value: 127.5mean_value: 127.5mean_value: 127.5force_color: trueresize_param {prob: 1.0          # 训练仅适配单一分辨率,因此prob应为1resize_mode: WARPheight: 416width: 416interp_mode: LINEARinterp_mode: AREAinterp_mode: LANCZOS4}emit_constraint {emit_type: CENTER}distort_param {brightness_prob: 0.5brightness_delta: 32.0contrast_prob: 0.5contrast_lower: 0.5contrast_upper: 1.5hue_prob: 0.5hue_delta: 18.0saturation_prob: 0.5saturation_lower: 0.5saturation_upper: 1.5random_order_prob: 0.0}expand_param {prob: 0.66max_expand_ratio: 2.0}}data_param {source: "PATH_TO_FILE/VOCdevkit/VOCdevkit_trainval_lmdb" # 即使在Windows下,目录也要使用'/'分割而非'\'batch_size: 1                                            # batch_size的大小根据数据集和显存大小做适配backend: LMDB}annotated_data_param {yolo_data_type : 1yolo_data_jitter : 0.3label_map_file:  "PATH_TO_FILE/VOCdevkit/labelmap_voc.prototxt"}
}# 这里省略掉原Prototxt文件的内容,其中YOLO层输出数量记得要适配数据集
...# 以下为损失函数层,可以直接复制到文件末尾
layer {name: "Yolov3Loss1"type: "Yolov3"bottom: "layer82-conv" # 这里连接到对应mask的YOLO网络层bottom: "label"        # 两个bottom的前后顺序不能调换,这是由该网络层实现决定的:网络层会从bottom[0]中获取图像数据;从bottom[1]中获取标签信息top: "det_loss1"loss_weight: 1yolov3_param {side: 13             # 这里设置网络层输出特征图大小num_class: 20        # 这里设置网络层输出分类数num: 3               # 这里设置每网格预测的bbox数量,亦即该网格分配的anchors数量object_scale: 5.0noobject_scale: 1.0class_scale: 1.0coord_scale: 1.0thresh: 0.6anchors_scale : 32   # 这里设置anchor box缩放倍数,参考网络结构进行计算,该层YOLO输出为输入的32倍下采样biases: 10biases: 13biases: 16biases: 30biases: 33biases: 23biases: 30biases: 61biases: 62biases: 45biases: 59biases: 119biases: 116biases: 90biases: 156biases: 198biases: 373biases: 326           # 这里设置总共使用到的anchor boxesmask:6mask:7mask:8                # 这里的掩码用于分配anchor boxes}
}layer {name: "Yolov3Loss2"type: "Yolov3"bottom: "layer94-conv"bottom: "label"top: "det_loss2"loss_weight: 1yolov3_param {side: 26num_class: 20num: 3  object_scale: 5.0noobject_scale: 1.0class_scale: 1.0coord_scale: 1.0thresh: 0.6anchors_scale : 16biases: 10biases: 13biases: 16biases: 30biases: 33biases: 23biases: 30biases: 61biases: 62biases: 45biases: 59biases: 119biases: 116biases: 90biases: 156biases: 198biases: 373biases: 326mask:3mask:4mask:5}
}layer {name: "Yolov3Loss3"type: "Yolov3"bottom: "layer106-conv"bottom: "label"top: "det_loss3"loss_weight: 1yolov3_param {side: 52num_class: 20num: 3  object_scale: 5.0noobject_scale: 1.0class_scale: 1.0coord_scale: 1.0thresh: 0.6anchors_scale : 8biases: 10biases: 13biases: 16biases: 30biases: 33biases: 23biases: 30biases: 61biases: 62biases: 45biases: 59biases: 119biases: 116biases: 90biases: 156biases: 198biases: 373biases: 326mask:0mask:1mask:2}
}

修改后的test文件网络输入和输出层配置如下:

# 以下内容为网络层输入,使用前文生成的VOC-LMDB数据集
name: "Caffe-YOLOv3"
layer {name: "data"type: "AnnotatedData"top: "data"top: "label"include {phase: TEST          # 该字段表示此配置用于验证过程}transform_param {scale: 0.007843mean_value: 127.5mean_value: 127.5mean_value: 127.5resize_param {prob: 1.0          # 训练仅适配单一分辨率,因此prob应为1resize_mode: WARPheight: 416width: 416interp_mode: LINEAR}}data_param {source: "PATH_TO_FILE/VOCdevkit/VOCdevkit_test_lmdb"batch_size: 1        # 一般进行单张验证,因此这里的batch_size为1backend: LMDB}annotated_data_param {batch_sampler {}label_map_file: "PATH_TO_FILE/VOCdevkit/labelmap_voc.prototxt"}
}# 这里省略掉原Prototxt文件的内容,其中YOLO层输出数量记得要适配数据集
...# 以下为验证层,可以直接复制到文件末尾
layer {name: "detection_out"type: "Yolov3DetectionOutput"bottom: "layer82-conv"bottom: "layer94-conv"bottom: "layer106-conv" # 连接到全部的YOLO输出层top: "detection_out"yolov3_detection_output_param {confidence_threshold: 0.01nms_threshold: 0.45num_classes: 20       # 这里根据数据集分类数做修改biases: 10biases: 13biases: 16biases: 30biases: 33biases: 23biases: 30biases: 61biases: 62biases: 45biases: 59biases: 119biases: 116biases: 90biases: 156biases: 198biases: 373biases: 326           # 这里设置总共使用到的anchor boxesmask:6mask:7mask:8mask:3mask:4mask:5mask:0mask:1mask:2                # 这里的掩码用于分配anchor boxesanchors_scale:32anchors_scale:16anchors_scale:8       # 这里设置anchor box缩放倍数,参考网络结构进行计算mask_group_num:3      # 这里设置的数量即每个网格分配的anchors数量}
}layer {name: "detection_eval"type: "DetectionEvaluate"bottom: "detection_out"bottom: "label"top: "detection_eval"detection_evaluate_param {num_classes: 21       # 这里根据数据集分类数做修改background_label_id: 0overlap_threshold: 0.5evaluate_difficult_gt: false}
}

最后,修改训练所需的solver文件。由于本节的尝试仅仅是为了使网络可以正常训练,因此可直接拷贝MobileNet-YOLO使用的solver文件重命名为 yolov3_solver_voc.prototxt ,仅修改其中的配置文件路径部分。

使用以下命令开始网络的训练:

PATH_TO_CAFFE\build\tools\Release\caffe.exe train --solver=models\yolov3\yolov3_solver_voc.prototxt

网络结构本身需要约1.4GB内存;batch_size设置为1时,训练需要约3.2GB内存;batch_size设置为2时,训练需要约4.2GB内存。

本文资源共享

百度网盘链接: https://pan.baidu.com/s/1_7TRD9rDUsxgnIGjKYF-UQ

提取码: mhn9

YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(上)
YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(中)
YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(下)

———— END 2021@凌然 ————

YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(中)相关推荐

  1. YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(上)

    YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(上) YOLO系列(v1~v3)的学习及YOLO-Fastest在海思平台的部署(中) YOLO系列(v1~v3)的学习及Y ...

  2. 海思平台ISP系列从零开始:一、PQTools工具的使用总结

    海思平台 ISP 系列: 一. PQTools 工具使用方法 正所谓工欲善其事必先利其器,磨刀不误砍柴工, 想要在海思平台开始IQ, 就得利用海思SDK中提供的PQTools工具,这个工具作的很好的, ...

  3. 海思平台HI35XX系列内存设置

    海思平台的内存分为两部分,一部分给系统使用,另外的一部分给多媒体使用.可以通过cat /proc/meminfo查看系统内存和cat /proc/media-mem 查看多媒体内存使用情况. /pro ...

  4. 【YOLO】深度学习-物体检测-YOLO系列(网易云课程笔记)

    第一章 YOLO系列概述 1.深度学习经典检测方法 (1) tow-stage(两阶段):Faster-rcnn Mask-rcnn系列:增加了区域建议网络(RPN),即预选框 特点 速度通常较慢(5 ...

  5. YOLO系列:YOLOv1,YOLOv2,YOLOv3,YOLOv4,YOLOv5简介

    原文链接: https://zhuanlan.zhihu.com/p/136382095 YOLO系列:YOLOv1,YOLOv2,YOLOv3,YOLOv4,YOLOv5简介 YOLO系列是基于深度 ...

  6. YOLO系列知识点整理

    之前系列博客中,各类深度学习的框架,一些新东西的接触都是从官网去接触的,好的东西必然有详尽的资料去阐述,很多都是外文网站,老外讲一些东西不是那么抽象,逻辑也很清楚,能理解的更为透彻,也可以去看看一些帖 ...

  7. 目标检测 YOLO系列算法

    文章目录 1. YOLO算法 1.1 Yolo算法思想 1.2 Yolo的网络结构 1.2.1 网络输入 1.2.2 网络输出 1.7X7网格 2x30维向量 1.3 Yolo模型的训练 1.3.1训 ...

  8. 目标检测——YOLO系列(一网打尽)

    YOLO系列算法 1. 基本概念 2. 目标检测算法分类及流程 2.1 算法分类 2.2 基本流程 2.3 Two-stage与One-stage基本流程比较 3. YOLO系列目标检测模型 3.1 ...

  9. 死磕YOLO系列,YOLOv1 的大脑、躯干和手脚

    系列文章: 死磕YOLO系列,YOLOv2的自我修养 YOLO 是我非常喜欢的目标检测算法,堪称工业级的目标检测,能够达到实时的要求,它帮我解决了许多实际问题. 这就是 YOLO 的目标检测效果.它定 ...

最新文章

  1. Datawhale组队学习周报(第017周)
  2. SAP RETAIL 特征参数文件(Characteristic Profile) I
  3. 文本处理利器Linux Awk这样入门☛常见变量的使用
  4. sql 2008 使用output避免数据修改后的二次查询
  5. C++手动开启O2优化(以及-O -O1 -O2 -O3优化的知识点)(竞赛可用)
  6. Linux和Windows下计算文件的Hash值
  7. SSH连接慢与反向解析(转)
  8. ISO50001认证辅导,ISO50001能源管理体系的框架审核通过系统的提高能源效率和消耗
  9. 阿里巴巴代码规范考试
  10. 【滤波器】归一化LMS自适应滤波器
  11. 如何利用Python分离文件中的英文和中文?
  12. 如何快速实现人脸识别通道?一文了解具体技巧
  13. 语音增强算法研究系列笔记 - 语音噪声分类及特点
  14. oracle清除temp表空间,Temp表空间占用长时间不释放,是谁惹的祸
  15. 【python机器学习】普通最小二乘法多元线性回归
  16. 【随笔】记录一次简易的液位报警器的拆机修理
  17. 搜遍全网,终于找到一个适合新手入门的物联网教程
  18. 大暴雷,“山寨版拼多多”宣告破产!曾一年收割 1.3 亿用户,如今自救失败负债 16 亿...
  19. 如何提高自己的专注度
  20. C# 集合-并发处理

热门文章

  1. 五年,他们从应届生成为了滴滴的「技术扛把子」
  2. 空气质量指数日历史数据
  3. 2022就业环境以及市场分析,职业前景。
  4. macu盘数据恢复|易我数据恢复Mac版免费下载
  5. 搜索引擎优化系统知名乐云seo_站群搜索引擎优化排名
  6. 杰理之MIC 免电容方案需要设置【篇】
  7. ETH节点调用出现 tx fee (1.30 ether) exceeds the configured cap (1.00 ether)
  8. 更改电脑桌面小图标——更好的摸鱼小技巧
  9. 杂题记录及简要题解(一)
  10. macbook matlab 键盘没作用,mac键盘(苹果电脑键盘功能介绍)