方案名
1. 摘要
-
身份编号:fwb0042
-
类型与序号:VGG16 方案1
-
进展:
item status detail 方案选择与硬件资源划分 1 模型导出 1 ~导出模型精度一致,提供python验证脚本 1 cpu处理部分c++工程移植 1 ~c++工程精度一致,提供验证脚本 1 ~c++工程性能优化 0 icraft编译通过 1 ~存在量化精度调优工作 0 ~完成基本pyrtcrt搭建与结果验证 0 ~完成pyrtcrt精度测试与对齐 0 完成上板耗时测试 0 ~使用运行时提供的api进行基本性能优化 0 ~存在整体方案各环节优化或优化方案的设计 0 提出bug数 0
2. 方案简介
2.1 算法流程介绍
模型或算法背景:
- VGGNet (Visual Geometry Group Network) 是一系列深度卷积神经网络,由牛津大学的视觉几何组提出。VGG16是其中最著名和最广泛使用的版本。
- 源仓库链接:VGGNet没有唯一的官方开源仓库,但其实现在各大深度学习框架(Caffe, TensorFlow, PyTorch等)中均已成为标准。
- 开发团队:牛津大学视觉几何组 (Karen Simonyan & Andrew Zisserman)
- 发布时间:2014年 (发表于ICLR 2015 Workshop)
- 性能水平:VGGNet在2014年的ImageNet大规模视觉识别挑战赛(ILSVRC)中取得了定位任务第一名和分类任务第二名的优异成绩。其核心贡献在于证明了通过简单、一致地堆叠更小的卷积核(3x3)来增加网络深度,是提升模型性能的有效途径。这一“深度为王”的设计哲学,深刻影响了后续所有深度学习网络的设计,如ResNet、Inception等。
VGG系列模型在ImageNet验证集上的性能:
| Model | Depth | Parameters (M) | Top-1 Accuracy | Top-5 Accuracy |
|---|---|---|---|---|
| VGG11 | 11 | 133 | 69.02% | 88.63% |
| VGG16 | 16 | 138 | 71.59% | 90.38% |
| VGG19 | 19 | 144 | 72.38% | 90.87% |
| GoogLeNet | 22 | 6.8 | 68.7% (Single-crop) | 89.9% (Single-crop) |
注:VGG的精度是在多尺度、多裁剪测试下取得的,这里为了对比,也列出了单裁剪下的精度。VGG的参数量主要集中在全连接层。
算法流程梳理
VGG16的结构极其规整、优雅和统一,完全由重复的、简单的构建块组成。其名称中的“16”代表网络中包含16个带权重的层(13个卷积层和3个全连接层)。
数据输入 ——> Conv Block 1 ——> Pool ——> Conv Block 2 ——> Pool ——> Conv Block 3 ——> Pool ——> Conv Block 4 ——> Pool ——> Conv Block 5 ——> Pool ——> FC Layers ——> Softmax
VGG16的架构可以清晰地分为两个部分:
1. 卷积基 (Convolutional Base):
- 统一的卷积核:整个网络只使用3x3大小的卷积核。这是VGG最核心的设计思想。作者们论证了两个堆叠的3x3卷积层拥有与一个5x5卷积层相同的感受野,而三个堆叠的3x3卷积层则等效于一个7x7卷积层的感受野。使用更小的卷积核堆叠,有两大好处:
- 引入更多非线性:更多的层数意味着更多的ReLU激活函数,这能增强网络的表达能力。
- 减少参数量:例如,三个3x3卷积层的参数量(3 * (33CC) = 27C^2)远小于一个7x7卷积层的参数量(77CC = 49C^2),其中C是通道数。
- 规整的模块化设计:卷积基由5个卷积块(Conv Block)组成。每个块包含2到3个连续的3x3卷积层,其后紧跟一个2x2的最大池化层(Max Pooling)用于下采样。
- 通道数变化:随着网络的加深,特征图的尺寸(H, W)通过池化层减半,而通道数(C)则逐块翻倍(64 -> 128 -> 256 -> 512 -> 512),这是一种非常经典的设计模式。
2. 分类器 (Classifier):
- 全连接层 (Fully Connected Layers):在卷积基之后,特征图被展平(Flatten)为一个长向量,然后送入三个全连接层。前两个全连接层各有4096个神经元,最后一个全连接层的神经元数量为1000,对应ImageNet的1000个类别。
- 巨大的参数量:VGG16的绝大部分参数(约1.2亿,占总参数量1.38亿的90%)都集中在这三个庞大的全连接层中,这也是其后续被优化的主要方向。
基本组件: 3x3卷积层:网络中唯一的卷积操作单元,证明了“小而深”的有效性。 ReLU激活函数:在每个卷积层后使用,引入非线性。 2x2最大池化层:用于下采样,缩小特征图尺寸。 全连接层:用于最终的分类决策。
训练技巧
数据集
- 模型在 ImageNet (ILSVRC 2012) 数据集上进行训练,该数据集是评估大规模图像分类模型性能的基准。
训练设置
- 优化器与学习率:采用带动量的随机梯度下降(SGD)进行优化。学习率初始值较小,并在验证集准确率不再提升时手动进行衰减。
- 权重初始化:采用了精心设计的随机初始化方法(后来被称为“Xavier/Glorot初始化”),这对于成功训练深层网络至关重要。
- 分阶段训练:作者们首先训练了一个较浅的网络(如VGG11),然后用其权重来初始化更深网络的对应层,再继续训练。这种方法有助于深层网络的收敛。
前后处理
前处理:
- 图像缩放:
- 将输入图像的短边等比例缩放到一个固定尺寸S(称为训练尺度)。
- 随机裁剪:
- 在训练时,从缩放后的图像中随机裁剪出224x224大小的区域作为模型输入。这是一种有效的数据增强。
- 随机水平翻转:
- 以50%的概率对裁剪出的图像进行水平翻转。
- 均值减法:
- 从每个像素的RGB值中减去在整个训练集上计算出的RGB均值。
后处理 (测试时):
- 多尺度/多裁剪测试 (Multi-scale/Multi-crop Testing):
- 为了获得更高的精度,VGG在测试时采用了非常耗时的评估策略。它会将图像缩放到多个不同的尺度S,并在每个尺度上,从图像的四个角和中心,以及它们的水平翻转版本中,裁剪出多个224x224的图像块。
- 结果平均:
- 将所有这些裁剪块送入网络得到各自的预测概率,然后将这些概率向量进行平均,得到最终的分类结果。
2.2 硬件资源分配
plin模式:摄像头输入数据直接传给pl部分
前处理:fpga,做在数据流上
网络主体:npu
后处理等:cpu
可视化:cpu
4. 模型导出说明
直接运行1_save.py导出整个模型即可
5. cpu处理部分c++工程移植说明
CPU部分主要实现了图片的读取,前处理尺寸变换和后处理的Softmax激活和获取预测结果功能
// 统计图片数量
int index = 0;
auto namevector = toVector(imgList);
int totalnum = namevector.size();
for (auto name : namevector) {
progress(index, totalnum);
index++;
//-------------PRE PROCESS-----------------------//
std::string img_path = imgRoot + '/' + name;
PicPre img(img_path, cv::IMREAD_COLOR);
img.Resize(resize_hw, PicPre::SHORT_SIDE).rCenterCrop(crop_hw);
Tensor img_tensor = CvMat2Tensor(img.dst_img, network);
dmaInit(run_sim, netinfo.ImageMake_on, img_tensor, device);
auto output_tensors = session.forward({ img_tensor});
if (!run_sim) device.reset(1);
// 计时
#ifdef __linux__
device.reset(1);
calctime_detail(session);
#endif
auto host_tensor_class = output_tensors[0].to(HostDevice::MemRegion());
auto outputs_class = (float*)host_tensor_class.data().cptr();
auto softmax_class = softmax(outputs_class, 1, 0, N_CLASS);
// auto softmax_class = softmax(outputs_class, 0, output_tensors[0].dtype()->shape);
auto topk_res = topK(softmax_class, 5);//save top-5 res
for (int i = 0; i < 5; i++) {
auto c = topk_res[i].first;
auto prob = topk_res[i].second;
std::ostringstream text;
text << LABELS[c] << " : " << std::fixed << std::setprecision(6) << prob;
cv::putText(img.ori_img, text.str(), cv::Point2f(10, 20 + i * 15), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(128, 0, 0), 2);
}
#ifdef _WIN32
if (show) {
cv::imshow("results", img.ori_img);
cv::waitKey(0);
}
if (save) {
//save top-5 label in txt
auto res_path = resRoot + "//" + name;
std::regex reg(R"(\.(\w*)$)");
res_path = std::regex_replace(res_path, reg, ".txt");
std::ofstream outputFile(res_path);
if (!outputFile.is_open()) {
std::cout << "Create txt file fail." << std::endl;
}
for (auto&& res : topk_res) {
outputFile << res.first << std::endl;
}
outputFile.close();
}
#elif __linux__
if (save) {
//saveRes
std::regex rgx("\\.(?!.*\\.)"); // 匹配最后一个点号(.)之前的位置,且该点号后面没有其他点号
std::string RES_NAME = std::regex_replace(name, rgx, "_result.");
cv::imwrite(resRoot + RES_NAME, img.ori_img);
}
#endif
}
//关闭设备
if (!run_sim) Device::Close(device);
return 0;
6. ICRAFT编译
编译通过
-
16bit下推理测试结果:
框架动态图推理 导出静态模型推理 解析组件仿真 量化组件仿真 指令组件仿真 




-
8bit,16bit,混合精度的精度测试结果:
-
8bit测试结果:
top1: 65.74% top5: 90.02% -
16bit测试结果:
top1: 66.32000000000001% top5: 90.08%
7. 上板测试与优化(尚未进行上版测试)
-
优化前各环节耗时统计
-
优化后耗时统计(表格)
-
优化项与效果
分条描述做了哪些性能优化,提升了多少
-
运行时工程的整体介绍(最好带流程图)