- 计算机视觉
- 深度学习
【免费下载链接】ccv
C-based/Cached/Core Computer Vision Library, A Modern Computer Vision Library
本篇技术指南围绕 ccv(C-based/Cached/Core Computer Vision Library)中的 ConvNet 模块展开,系统讲解其深度卷积网络的架构来源、ImageNet 图像分类的推理与验证流程、准确率与速度实测、以及从零训练自有分类器的完整步骤。读完本文,你将掌握如何用 ccv 的cnnclassify加载预训练模型做 1000 类 ImageNet 分类、用cnnvldtr.rb评估 top-1/top-5 缺失率、理解 ccv 与 Caffe/AlexNet 在数据预处理与多 patch 平均上的关键差异,并具备复现 ImageNet 训练流程的完整知识。
什么是 ConvNet:ccv 深度卷积网络的由来
卷积网络(Convolutional Network)是一种受生物视觉皮层启发、由 Yann LeCun 于 1990 年代初针对计算机视觉任务设计的人工神经网络拓扑。ccv 中实现的卷积网络并非凭空发明,而是建立在以下几项经典工作之上,参数与结构均有明确出处:
- AlexNet:ccv 的实现基础是 Alex Krizhevsky、Ilya Sutskever、Geoffrey E. Hinton 在 NIPS 2012 发表的《ImageNet Classification with Deep Convolutional Neural Networks》,这是把深度卷积网络带上 ImageNet 舞台的奠基性工作;
- Zeiler 参数:网络参数基于 Matthew D. Zeiler 与 Rob Fergus 在 Arxiv 1311.2901(2013 年 11 月)发表的《Visualizing and Understanding Convolutional Networks》做了修改;
- 多 GPU 并行:多 GPU 实现深受 Alex Krizhevsky 在 ICLR 2014 发表的《One Weird Trick for Parallelizing Convolutional Neural Networks》影响;
- VGG-D 模型:仓库内提供的 VGG-D 预训练模型基于 Karen Simonyan 与 Andrew Zisserman 在 ICLR 2015 发表的《Very Deep Convolutional Networks for Large-Scale Image Recognition》。
在源码层面,这些网络拓扑被直接编码为可读的 C 语言参数数组:VGG-A 与 VGG-D 的逐层定义位于 bin/vgg_models.inc,AlexNet 相关配置位于 bin/alex_model.inc,Matt(Zeiler)配置位于 bin/matt_models.inc。以vgg_d_params为例,它定义了 21 层结构:前五组为"卷积层组 + 最大池化"(64×2、128×2、256×3、512×3、512×3),随后接 3 层全连接(4096、4096、1000),最后一个全连接层relu = 0以输出 1000 类分数。卷积层采用 3×3 滤波器、stride 1,池化采用 3×3 窗口、stride 2,输入尺寸为 225×225×3。这些参数数组通过ccv_convnet_new(1, ccv_size(257, 257), model_params, depth)传入,构建运行时网络实例(见 bin/image-net.c)。
ConvNet 如何工作:从推理命令说起
借助 GPGPU 编程的进步,ccv 可以承载超过 5000 万参数的深度卷积网络并在百万级图像上训练。配合 dropout、pooling 等"技巧包",这类网络能够在图像分类任务上取得良好效果。ccv 的推理使用极其简单,官方文档给出的单图分类命令为:
./cnnclassify ../samples/dex.png ../samples/image-net-2012-vgg-d.sqlite3 | ./cnndraw.rb ../samples/image-net-2012.words ../samples/dex.png output.png这条命令的含义是:cnnclassify读取输入图片dex.png与预训练模型image-net-2012-vgg-d.sqlite3,输出若干个"类别 id + 置信度"对;管道下游的cnndraw.rb脚本接收这些分类结果,结合image-net-2012.words(类别词汇表,每行形如类别名, ...)把置信度绘制成条形图叠加到原图上,生成output.png。打开output.png,左上角图表会展示卷积网络给出的若干候选类别及其置信度排序。
从源码看,cnnclassify的调用链非常清晰(bin/cnnclassify.c):单图模式下依次调用ccv_convnet_read(0, argv[2])读取模型、ccv_convnet_input_formation(convnet->input, image, &input)完成输入成形(缩放、减均值等)、ccv_convnet_classify(convnet, &input, 1, &rank, 5, 1)输出 top-5 分类;批量模式(传入文件列表)则以BATCH_SIZE = 8分批调用ccv_convnet_classify(..., 1000, BATCH_SIZE),对每张图输出全部 1000 类的得分并打印elapsed time计时。cnndraw.rb(bin/cnndraw.rb)解析标准输入中的id confidence序列,把分数归一化为条形图宽度,逐条标注类别名。
性能表现:ImageNet 上的准确率与速度实测
ConvNet 在超大规模问题上并非极速。文档明确指出,这一初步实现"正确性优先于速度",ccv 在速度上与其他实现基本处于同一水平,但并未专门优化。以下所有分析都基于 ImageNet 数据集、网络拓扑严格遵循论文规格的前提,测试采用vgg_d_params。
准确率(Accuracy)
在 ILSVRC 2012 验证集上,训练在约 55 个 epoch 后停止改善:此时验证集中心 patch 的 top-1 缺失率(越低越好)为31.96%,训练集为28.00%。与三篇论文的对照为:
| 实现 | 评测方式 | top-1 缺失率 |
|---|---|---|
| Alex 论文 | 平均 10 patches | 40.5% |
| Matt(Zeiler)论文 | 单网络、Fig.3 配置、平均 10 patches | 38.4% |
| VGG 论文 | 平均密集采样图像 | 27.0% |
| ccv(vgg_d_params) | 中心 patch | 31.96% |
验证集完整评测的复现步骤如下。假设验证集文件列表按序保存在image-net-2012-val.txt:
./cnnclassify image-net-2012-val.txt ../samples/image-net-2012-vgg-d.sqlite3 > image-net-2012-classify.txt完整验证集在单 GPU 上约需6 小时;若无 GPU 支持,纯 CPU 约需两天。得到分类结果后,用官方验证脚本 bin/cnnvldtr.rb 对照 ILSVRC 2012 验证集真值文件LSVRC2012_val_ground_truth.txt:
./cnnvldtr.rb LSVRC2012_val_ground_truth.txt image-net-2012-classify.txt该脚本逐行读取真值,统计 top-1 缺失(第 1 个类名不匹配)与 top-5 缺失(前 5 个候选均不匹配),输出形如29.61% (1), 9.9% (5)的缺失率报告。
官方实测数据:32 位浮点模型image-net-2012-vgg-d-32bit.sqlite3在 GPU 上 top-1 缺失率29.61%(比 ccv 此前单网络结果绝对提升 8.56%,比 VGG 单网络 mode D 差 2.3%),top-5 缺失率9.9%(相对此前提升 6.3%,比 VGG 差 1.1%)。半精度模型image-net-2012-vgg-d.sqlite3(即默认下载到 samples 目录的版本)top-1 缺失率同为 29.61%、top-5 为 9.9%。32 位浮点版本可通过 samples/download-vgg-d-32bit.sh 下载(内部执行wget -c从官方静态资源站点拉取)。此外,基于matt_params的半精度image-net-2012.sqlite3top-1 缺失率为38.17%、top-5 为16.22%。
速度(Speed)
官方速度实验环境:Core i7 5930K CPU、4 路 NVIDIA TITAN 与 TITAN Black 混合显卡(默认频率)、Samsung MZ-7TE500BW 500GiB SSD,配合 clang、libdispatch、libatlas 与 GNU Scientific Library 构建。核心实测数据如下:
- CPU 前向:从 RGB 输入到分类结果约700ms/图,依赖多线程卷积核计算。作为参照,Decaf(Caffe 的 CPU 对应实现)报告约 0.5s/图(硬件未指明,评测方式同为 10 patches 平均);
- AlexNet 12:GPU 上前向 + 反向传播,batch 128 约0.664s,即 100 epoch 训练 ImageNet 约需 186 小时。作为参照,Caffe 在 NVIDIA TITAN 上 batch 256 约 1.3s,Alex 论文为双 GeForce 580 上 90 epoch / 6 天;
- MattNet:单 GPU 前向 + 反向,batch 128 约0.845s;4 GPU 下 batch 512 约0.909s,即3.72 倍加速,与 AlexNet 14(One Weird Trick)报告的 3.74x 基本一致;
- VGG-D:4 GPU 前向 + 反向,batch 192 约5.439s;
- AlexNet 14:单 GPU 前向 + 反向,batch 128 约0.55s,对应 90 epoch 约 137.6 小时。
作为"初步实现",ccv 并未充分优化这些算子。文档以代码量作对比:cuda-convnet 约 10000 行、Caffe 约 14000 行,而该版本 ccv 的实现约4300 行。未来低垂的优化机会包括引入 cuDNN、在密集卷积层使用 FFT 等。
如何训练自己的图像分类器
ccv 支持从零训练卷积网络,官方文档以 ImageNet LSVRC 2012 数据集为例完整演示了整个流程。训练前需备齐三样东西:实际的 ImageNet 数据集(及元数据)、显存不低于 6GiB 的 CUDA GPU、以及足以容纳 ImageNet 数据集的大容量 SSD(否则从机械盘加载数据会慢过实际计算)。
第一步:转换 ImageNet 元数据
ImageNet 2012 挑战赛的元数据以 Matlab 专有格式分发,需要用 Octave 转换。在 Linux 系系统(如 Ubuntu)上安装 Octave 只需一行:
sudo apt-get install octave假设已下载 devkit-1.0 并在压缩包中找到meta.mat,启动 Octave 交互环境执行:
octave> file = fopen('meta.txt', 'w+') octave> for i = 1:1000 octave> fprintf(file, "%d %s %d\n", synsets(i).ILSVRC2012_ID, synsets(i).WNID, synsets(i).num_train_images) octave> endfor octave> fclose(file)生成的meta.txt每行包含三列:类别 id、WordNet id(WNID)、该类的训练图像数量。
第二步:生成训练/测试文件列表
ImageNet 数据集的训练图像按 WordNet id 命名目录存放,可用find生成文件清单:
find <ImageNet dataset>/train/ -name "*.JPEG" > train-file.txt测试集按数字顺序排列,因此:
find <ImageNet dataset>/test/ -name "*.JPEG" > test-file.txt生成的文件列表对应ILSVRC2012_test_ground_truth.txt中的类别 id 顺序。这两份列表还需要借助作者提供的转换脚本(位于 liuliu 的 GitHub gist:8393461 与 8393516)改写为 ccv 能直接理解的行格式(即类别 id 文件名)。从 bin/image-net.c 的解析逻辑可以看出,ccv 逐行fscanf("%d %s")读取类别与文件名,并将 ImageNet 从 1 开始的类别 id 减 1 转为 0 索引的ccv_categorized_t。
第三步:图像预处理与缩放
训练图像需先预处理到正确尺寸。作者将 bin/image-net.c 局部替换为一段生成.resize.png后缀文件的代码(对应 gist 8906523)。编译并运行:
./image-net --train-list ~/Fast/imageNet/train-file.txt --test-list ~/Fast/imageNet/test-file.txt --base-dir ~/Fast/imageNet --working-dir image-net.sqlite3缩放阶段约需3 小时。完成后,train.txt与test.txt会由train-file.txt、test-file.txt通过给每行追加.resize.png后缀自动生成。image-net的完整命令行参数见 bin/image-net.c:必需参数为--train-list、--test-list、--working-dir(保存进度与产出模型的目录);可选参数--base-dir切换图片读取的基准目录、--max-epoch控制 SGD 的 epoch 数(默认 100)、--iterations控制 mini-batch 迭代数(默认 5000)。
第四步:训练与学习率调度
数据就绪后即可开训。以 4 块 TITAN GPU 为例约需一天半。官方模型image-net-2012.sqlite3的学习率调度遵循 Alex 论文的做法、共衰减三次:
| epoch | learn_rate |
|---|---|
| 0 起 | 0.01 |
| 第 30 epoch | 0.001 |
| 第 60 epoch | 0.0001 |
| 第 80 epoch | 0.00001 |
训练超参数的默认值可以从源码中核实:train_params默认max_epoch = 100、mini_batch = 64、sgd_frequency = 1、iterations = 50000、device_count = 4、peer_access = 1、symmetric = 1、image_manipulation = 0.2、color_gain = 0.001、输入min_dim/max_dim = 257(bin/image-net.c)。逐层训练参数中,权重与偏置的学习率均为 0.01、动量 0.9,权重衰减 0.0005,最后两个全连接层(倒数第 3 到倒数第 2 层)设置 dropout 率 0.5(bin/image-net.c)。训练由ccv_convnet_supervised_train(lib/ccv_convnet.c)驱动,网络构建前先用ccv_convnet_verify(convnet, 1000)校验拓扑合法性——最后一层必须是全连接、不可启用 ReLU、输出维度必须等于 1000(lib/ccv_convnet.c)。
第五步:精简模型文件
训练产出的image-net-2012.sqlite3约600MiB,因为其中包含训练与断点续训所需的数据。它本质上是标准的 SQLite 数据库文件,可用 sqlite 命令行工具直接操作并瘦身:
sqlite> drop table conv_vary_params; sqlite> drop table momentum_data; sqlite> drop table function_state; sqlite> vacuum;执行后文件可缩减至约200MiB。若希望进一步减小体积,可用ccv_convnet_write重写为半精度(设置write_param.half_precision = 1)。ccv_convnet_write_param_t.half_precision字段的语义在 lib/ccv.h 中有明确注释:"使用半精度浮点表示网络参数"。这样处理得到的image-net-2012.sqlite3正是 samples 目录中随仓库分发的版本。
预训练模型可以商用吗
可以。ccv 本身以 FreeBSD 3-clause 许可发布,而预训练模型 samples/image-net-2012.sqlite3 与 samples/image-net-2012-vgg-d.sqlite3 以Creative Commons Attribution 4.0 International License发布:只要注明出处,几乎可以在任何场景、以任何方式使用和修改。官方文档强调,这可以说是最早以商用友好许可发布的预训练模型之一——Caffe 本身虽采用 FreeBSD 许可,但其预训练模型仅限研究用途;OverFeat 则使用自定义的"仅限研究"许可。
ccv 与 Caffe、Alex、Matt 实现的差异
ccv 的网络拓扑虽与 Matt(Zeiler)的实现高度接近,但实测结果差异明显,官方文档为此专门记录了三者在实现细节上的区别,这些细节直接影响可复现的精度结果。
网络拓扑
- ccv 的局部响应归一化(LRN)层(如有)紧跟卷积层之后,池化层再跟随 LRN;Alex 论文对此有过简略提及,而 Caffe 的 AlexNet 将 LRN 放在池化层之后;
- ccv 网络的输入尺寸为225×225,Caffe 为 227×227,Alex 与 Matt 论文均提到 224×224。对 225×225 输入而言,相当于在输入图像周围留 1 像素 padding:配合 7×7 滤波器与 stride 2,将生成 111×111 的输出;而 Matt 论文中第一卷积层的输出是 110×110。
数据准备
| 实现 | 缩放策略 | 均值图像 |
|---|---|---|
| Caffe | 直接 resize 到 256×256,不保持宽高比 | — |
| Alex | 最小边缩放到 256、保持宽高比,再中心裁剪为 256×256 | — |
| ccv | 最小边缩放到257、保持宽高比(下采样用CCV_INTER_AREA,上采样用CCV_INTER_CUBIC) | 由中心裁剪的 257×257 图像计算 |
数据增强
- Caffe:从 256×256 随机裁剪 227×227;
- Alex:从 256×256 随机裁剪 224×224,再做颜色增强,高斯随机系数
sigma == 0.1; - ccv:从保持宽高比的图像随机裁剪为 257×257,减均值后再随机裁剪为 225×225;颜色增强的高斯随机系数为
sigma == 0.001;此外还对亮度、对比度、饱和度做 0.8~1.2 倍的拉伸增强; - 三者都使用水平镜像作为数据增强手段。
平均分类(多 patch 评测)
评测阶段对测试图像的多个 patch 分别推理并平均 softmax 输出,是这一代模型的标准做法,但各家取样方式不同:
- Caffe:先将图像 resize 到 256×256(不保持宽高比),从左上、右上、中间、左下、右下取 5 个 227×227 patch,再加 5 个水平镜像,共 10 patches;
- Alex:保持宽高比缩放至最小边 256 后中心裁剪 256×256,再从该裁剪图按 Caffe 的 5 位置方式取 10 个 224×224 patch(含镜像);
- ccv GPU:先将图像保持宽高比缩放至最小边 257,从左上、中间、右下做 3 次裁剪得到 257×257;每张裁剪图减均值后,再按 5 位置取 225×225 patch,得到 15 个 patch,加上各自的水平镜像共30 patches;
- ccv CPU:出于效率考虑,平均 10 patches——先将图像保持宽高比缩放到最小边 257,把均值图像用
CCV_INTER_CUBIC上采样到同样尺寸并做减法,再提取 5 个 225×225 patch 并镜像补齐 10 个。
这些 patch 采样策略与ccv_convnet_classify的symmetric参数(lib/ccv.h)直接相关,cnnclassify中单图模式传1即启用对称(含镜像 patch)平均。
进一步探索
- 官方 ConvNet 文档的 RST 原稿位于 doc/convnet.rst,与本文主体内容同源;
- 核心实现位于 lib/ccv_convnet.c,公开 API 声明与参数结构体定义在 lib/ccv.h(含
ccv_convnet_write_param_t、ccv_convnet_supervised_train、ccv_convnet_input_formation、ccv_convnet_classify、ccv_convnet_read/write等); - 训练与评测工具集中在 bin 目录:
cnnclassify.c、cnndraw.rb、cnnvldtr.rb、image-net.c,网络拓扑定义见alex_model.inc、matt_models.inc、vgg_models.inc; - 单元测试覆盖了卷积、全连接、池化、LRN 的前向/反向与数值梯度校验,见 test/unit/convnet.tests.c(例如"convolutional network of 11x11 on 225x225 with uniform weights""numerical gradient versus analytical gradient for convolutional network"等用例),可作为理解算子和验证实现的参考;
- 模型文件(半精度 VGG-D 与 32 位浮点版)的下载脚本分别见 samples/download-vgg-d-model.sh 与 samples/download-vgg-d-32bit.sh。
- 计算机视觉
- 深度学习
【免费下载链接】ccv
C-based/Cached/Core Computer Vision Library, A Modern Computer Vision Library
相关推荐
用 CNTK 训练 VGG 深度卷积网络:ImageNet 图像分类的 Python 与 BrainScript 实战指南
用 CNTK 训练 VGG 深度卷积网络:ImageNet 图像分类的 Python 与 BrainScript 实战指南 导读 本文围绕 Microsoft
深度学习机器学习人工智能CNTK 图像分类实战:用 Python 从零构建卷积神经网络(ConvNet)
CNTK 图像分类实战:用 Python 从零构建卷积神经网络(ConvNet) 本指南以 CNTK 仓库 Examples/Image/Classificat
深度学习机器学习人工智能CNTK 图像分类实战:在 CIFAR-10 与 ImageNet 上训练 ResNet 深度残差网络(Python 版)
CNTK 图像分类实战:在 CIFAR 10 与 ImageNet 上训练 ResNet 深度残差网络(Python 版) 本篇技术指南以 CNTK 仓库 Ex
深度学习机器学习人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考