wishgale / ffcnn

ffcnn is a cnn neural network inference framework, written in 600 lines C language.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

+----------------------------+
 ffcnn 卷积神经网络前向推理库
+----------------------------+

ffcnn 是一个 c 语言编写的卷积神经网络前向推理库
只用了 500 多行代码就实现了完整的 yolov3、yolo-fastest 网络的前向推理
不依赖于任何第三方库,在标准 c 环境下就可以编译通过,在 VC、msys2+gcc、ubuntu+gcc
等多个平台上都可以正确的编译运行

这个代码相对于 darknet、ncnn 来说,没做特殊指令集优化,但代码更加简洁易懂,可以
作为大家学习卷积神经网络的一个参考


darknet 与 yolov3 的一些总结
----------------------------
yolov3 的网络结构里面,只有卷积层、dropout 层、shortcut 层、route 层、maxpool 层、
upsample 层和 yolo 层这几种类型。因此要实现起来还是比较容易的

卷积层:
1. 要搞明白卷积的含义和计算方法
2. 卷积运算的 pad、stride 的含义
3. 每个卷积核还有一个 bias 参数,计算完每个点后需要加上这个 bias
   没有归一化的情况(batch_normalize),其计算方法:
   x += bias;
   x  = activate(x, type);
4. 要搞明白什么是分组卷积
5. 卷积运算每个输出的点,都要经过激活函数
6. 卷积层如果有归一化操作(batch_normalize),其计算方法:
   x  = (x + rolling_mean) / sqrt(rolling_variance + 0.00001f)
   x *= scale;
   x += bias;
   x  = activate(x, type);
   其中 rolling_mean、rolling_variance、scale、bias 在 darknet 的 weights 文件中可以读取到

dropout 层:
前向推理时,这一层可以当做不存在,输入数据不做任何处理,直接传给下一层即可

shortcut 层:
把指定层的数据和当前层的数据相加,然后结果输出到下一层

route 层:
把指定的层(最多可以有 4 个)做拼接,宽高不变,channel 个数增加,然后结果输出到下一层

maxpool 层:
max 池化层,将 filter 覆盖的数据取最大值作为结果

upsample 层:
上采样层,可以理解为把图像放大,stride 指定了放大倍数,一般用最近邻法就可以了

yolo 层:
这一层主要是根据输入的 feature map 计算出 bbox
以 yolo-fastest 为例,总共有两个 yolo 层,其输入分别是 10x10x255 和 20x20x255
其中 255 表示有 255 个通道,其每个数据的含义如下:
255 = 3 * (4 + 1 + 80)
3 表示这个 grid 里面有 3 个 bbox 结果数据
每个 bbox 结果数据里面,4 个 x, y, w, h 坐标数据,1 个 object score 评分,然后是 80 个分类的评分
每个 bbox 里面在 80 个分类中找出评分最高的,作为这个 bbox 的分类,评分如果小于阈值(ignore_thresh)则丢弃
将符合要求的全部 bbox 放入一个列表保存,然后再做一个 nms 操作,就得到最终结果了

每个 bbox 的评分和 (x, y, w, h) 计算方法:
设 tx, ty, tw, th, bs 分别对应channel 0, 1, 2, 3, 4 的值(后面还有 80 个分类的评分)

评分的计算方法:score = sigmoid(bs); (80 个分类评分计算方法是一样的)
坐标计算方法:
float bbox_cx = (j + sigmod(tx)) * grid_width;  (grid_width 就是网络输入层即 0 层的宽度除以格子数目,即每个格子的像素宽度)
float bbox_cy = (i + sigmod(ty)) * grid_height; (方法与 bbox_cx 一致)
float bbox_w  = (float)exp(tw) * anchor_box_w;  (如果有缩放系数还要乘以这个系数)
float bbox_h  = (float)exp(th) * anchor_box_h;  (方法与 bbox_w  一致)

bbox_cx、bbox_cy 是中心点坐标,bbox_w、bbox_h 是宽高,转换一下得到:
x1 = bbox_cx - bbox_w * 0.5f;
y1 = bbox_cy - bbox_h * 0.5f;
x2 = bbox_cx + bbox_w * 0.5f;
y2 = bbox_cy + bbox_h * 0.5f;


darknet 的 weights 文件
-----------------------

文件最前面有一个文件头:
#pragma pack(1)
typedef struct {
    int32_t  ver_major, ver_minor, ver_revision;
    uint64_t net_seen;
} WEIGHTS_FILE_HEADER;
#pragma pack()

然后就是全部的权重数据,yolov3、yolo-fastest 的模块基本上就只有卷积层的权重,其它层是没有权重数据的。
图像和卷积核(filter)的数据都是 NCHW 格式,卷积层的权重数据存放顺序为:

n 个 bias
if (batchnorm) {
    n 个 scale
    n 个 rolling_mean
    n 个 rolling_variance
}
n * c * h * w 个权重数据


ffcnn 的特点
------------
1. 极为简洁易懂的 c 语言代码实现
2. 核心算法仅仅 600 行
3. 不依赖于任何第三方库
4. 可以很方便的移植到各种平台
5. 推理时会自动释放不需要的 layer 减小内存占用
6. 现阶段是 make it work first 后面有时间再优化性能
7. 直接使用 darknet 的 .cfg 和 .weights 文件(不需要再转换)


ffcnn vs ncnn 性能评测
----------------------

测试环境:
1. Intel(R) Core(TM) i5-4250U CPU @ 1.30GHz 1.90GHZ, 8GB RAM
2. win7 64bit 操作系统 + msys2 + mingw32 + gcc version 10.3.0
3. ffcnn + yolo-fastest 代码:https://github.com/rockcarry/ffcnn
4. ncnn  + yolo-fastest 代码:https://github.com/rockcarry/ffyolodet
5. 测试图片 test.bmp 100 次推理

测试结果:
+----------+--------------+-------------------+------------------+
| 测试项目 | ffcnn-v1.2.0 | ncnn with avx off | ncnn with avx on |
+----------+--------------+-------------------+------------------+
| 耗    时 | 14555ms      | 14649 ms          | 8424 ms          |
+----------+--------------+-------------------+------------------+
| 内存占用 | 5MB          | 41MB              | 41MB             |
+----------+--------------+-------------------+------------------+
| 程序体积 | 68KB         | 1.2MB             | 1.2MB            |
+----------+--------------+-------------------+------------------+

可以看到 ffcnn 已经逼近 ncnn(不开启 avx 指令优化)的性能


rockcarry@163.com
20:22 2021/8/7









About

ffcnn is a cnn neural network inference framework, written in 600 lines C language.

License:GNU Lesser General Public License v3.0


Languages

Language:C 99.8%Language:Shell 0.2%