YOLOv5转ONNX转NCNN

基于YOLOv5最新v5.0 release(https://github.com/ultralytics/yolov5/releases/tag/v5.0),和NCNN官方给出example的差别主要有:

项目代码:https://github.com/midasklr/yolov5ncnn

编译运行

动态库用的是官方编译好的ncnn-20210507-ubuntu-1604-shared

mkdir build 
cd build
cmake ..
make -j8
./yolov5 ../bus.jpg

可以看到:

流程

以下为yolov5s.pt转NCNN流程,自己训练的模型一样:

pytorch测试和导出onnx

先测试下yolov5s效果:

python detect.py --weights yolov5s.pt --source data/images

效果不错:

导出 onnx,并用 onnx-simplifer 简化模型,这里稍微不同,如果按照详细记录u版YOLOv5目标检测ncnn实现(https://zhuanlan.zhihu.com/p/275989233?utm_source=qq),那么直接导出来的模型可以看到输出:

python models/export.py --weights yolov5s.pt --img 640 --batch 1

我们用netron查看导出的onnx模型,模型输出那部分节点:

可以看到后处理怎么都出来了???这和nihui大佬转出来的onnx输出不一样。

看看models/yolo.py代码发现:

inference里面不就对应上面onnx模型那部分输出处理后然后torch.cat起来么,这部分处理我们放在代码里面做,所以可以注释这部分:

这样导出来的模型就是三个输出了:

ok,输出和详细记录u版YOLOv5目标检测ncnn实现(https://zhuanlan.zhihu.com/p/275989233?utm_source=qq)对应上了,同时可以看到激活函数silu:

这里silu激活函数被转为了x*sigmoid(x)而不是silu,不过是等价的。

经过onnx-sim简化一下:

python -m onnxsim yolov5s.onnx yolov5s-sim.onnx

转换和实现focus模块等

后续和详细记录u版YOLOv5目标检测ncnn实现(https://zhuanlan.zhihu.com/p/275989233?utm_source=qq)一样,ncnn转化后激活函数转为swish,可swish的实现:

Swish::Swish()
{
    one_blob_only = true;
    support_inplace = true;
}

int Swish::forward_inplace(Mat& bottom_top_blob, const Option& opt) const
{
    int w = bottom_top_blob.w;
    int h = bottom_top_blob.h;
    int channels = bottom_top_blob.c;
    int size = w * h;

    #pragma omp parallel for num_threads(opt.num_threads)
    for (int q = 0; q < channels; q++)
    {
        float* ptr = bottom_top_blob.channel(q);

        for (int i = 0; i < size; i++)
        {
            float x = ptr[i];
            ptr[i] = static_cast<float>(x / (1.f + expf(-x)));
        }
    }

    return 0;
}

} // namespace ncnn

和silu一样,那么就可以正常进行推理了,可能需要注意的就是三个输出节点不要弄错就ok。

安卓

参考https://github.com/nihui/ncnn-android-yolov5,替换模型使用这里转的v5.0分支的ncnn模型。

### 将ONNX文件换为NCNN模型的两种方法 #### 方法一:使用 `onnx-simplify` 和 NCNN 工具链 一种常见的做法是先通过 `onnx-simplify` 对 ONNX 文件进行优化简化处理,然后再利用 NCNN 提供的工具将其换为目标格式。具体过程如下: 1. 安装并配置好 NCNN 的开发环境后,在其构建过程中会生成一系列工具,其中包括用于 ONNX 换的工具 `onnx2ncnn`[^2]。 2. 使用 `onnx-simplify` 来清理和优化原始 ONNX 模型。这一步可以通过 Python 或命令行完成。例如: ```bash python -m onnxsimplifier input_model.onnx output_model_simplified.onnx ``` 3. 利用 NCNN 自带的 `onnx2ncnn` 工具执行最终的换操作: ```bash ./tools/onnx2ncnn output_model_simplified.onnx model.param model.bin ``` 这里生成了两个主要文件:`.param`(描述网络结构)和 `.bin`(存储权重数据)。这两个文件共同构成了 NCNN 可加载的模型。 #### 方法二:直接调用 NCNN 的 API 实现程序化换 除了依赖外部脚本或工具外,还可以编写自定义代码来实现更灵活的换逻辑。这种方法通常适用于需要集成到更大项目中的场景或者当默认工具无法满足特定需求时。 以下是基于 C++ 编写的简单示例框架,展示如何读取 ONNX 并保存至 NCNN 格式: ```cpp #include "ncnn/net.h" using namespace ncnn; int main() { Net net; // 加载 ONNX 模型 if (net.load_param("input_model.onnx") != 0) return -1; // 导出成 NCNN 支持的形式 const char* param_path = "model.param"; const char* bin_path = "model.bin"; FILE* fp_param = fopen(param_path, "wb"); FILE* fp_bin = fopen(bin_path, "wb"); Layer* layer = nullptr; std::vector<Layer*> layers = net.layers(); for(auto& l : layers){ fprintf(fp_param,"%s\n",l->type().c_str()); fwrite(l->weights(), sizeof(float), l->weight_data_size(),fp_bin); } fclose(fp_param); fclose(fp_bin); return 0; } ``` 此代码片段展示了基本流程——即从解析输入 ONNX 开始直到写出对应的参数与权重文件结束。需要注意的是实际应用可能还需要额外考虑更多细节比如层类型兼容性等问题。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值