美文网首页
mxnet_convert_to_ncnn

mxnet_convert_to_ncnn

作者: LC07 | 来源:发表于2018-02-08 12:02 被阅读0次
    1. 下载ncnn
    • 下载ncnn:推荐使用git工具,不建议直接download zip,后续可能会出现.h与.cpp文件缺失问题。
    1. 编译ncnn
    • 下载protobuf
      使用git工具下载,用cmake工具生成protobuf工程项目,分别在debug与release模式下编译libprotobuf与protoc子工程项目,确认执行该步骤后是否生成protoc.exe、libprotobuf.lib与libprotobufd.lib这三个文件;

    • cmake编译ncnn工程项目的注意点:
      cmake设置:选中Advanced选项,方便查看所有选项;
      ncnn 顶层CMakeLists.txt文件:去掉 # add_subdirectory(examples) 注释,生成的squeezenet子工程方便代码参考;

    • 第一次Configure时会出现如图1所示的错误:
      添加libprotobufd.lib、libprotobuf.lib、protoc.exe文件与src文件夹的路径,再次Configure后不再报错,点击Generate便可生成ncnn.sln文件;


      图1.png
    • debug或release模式下编译ncnn、squeezenet与mxnet2ncnn项目:
      设置子项目工程的Properties/(C/C++)/All Options/Runtime Library选项与protobuf一致(MTD);删除Properties/(C/C++)/All Options/Additional Options的内容,如图2所示:


      图2.png
    • 编译ncnn,出现C3005错误:
      “collapse”: unexpected token encountered on OpenMP “parallel for”directive,点击该处,进入convolutiondepthwise.cpp文件,将175行的collapse注释便可编译成功,得到ncnn.lib文件,编译mxnet2ncnn可得到mxnet2ncnn.exe文件。

    1. 模型转换
    • 在mxnet2ncnn.exe文件的当前目录新建两个文件夹,orig_model用来放mxnet框架的模型文件,converted_model用来放转换为支持ncnn框架的模型文件;

    • 在mxnet2ncnn.exe文件的当前目录新建.bat脚本文件,复制下面的内容;

    set MXNET_MODEL_DIR=orig_model
    set NCNN_MODEL_DIR=converted_model
    mxnet2ncnn.exe  %MXNET_MODEL_DIR%/multitask2-symbol.json  %MXNET_MODEL_DIR%/multitask2-0004.params    %NCNN_MODEL_DIR%/multitask2.param   %NCNN_MODEL_DIR%/multitask2.bin 
    pause
    
    1. 代码移植
    • mxnet对输入图片执行的操作顺序:
      色彩转换:BGR2RGB;
      图片尺寸调整为96×96;
      数据类型转换为float32;
      减均值:RGB的3个通道对应值为(123, 117, 104)

    • 以squeezenet.cpp为框架模板,将mxnet执行的操作按顺序移植到ncnn:
      加载模型:添加转换后的模型路径;
      色彩转换与尺寸调整;
      数据类型转换;
      减均值;
      数据输入与输出(模型的第一层与最后一层);

    1. debug
    • “std::greater< std::pair<float, int> >());” 提示Greater不是std的成员:
      在文件开头添加 #include <functional>; 这一句;

    • cls_scores的尺寸调整:
      设置squeezenet子工程项目为启动项,调试运行出现如图3所示的错误。Debug发现out的维度为26×1×1,执行cls_scores.resize(out.c)后cls_scores的size=1,与out尺寸不匹配,因此在cls_scores.resize(out.c)之前通过out = out.reshape(1,1,26)对out的维度进行调整,再次调试运行不再报错,如图4所示:


      图3.png
      图4.png
    • ncnn框架默认转浮点数:
      在static int print_topk函数与main函数的return 0这两行设置断点,比对两种框架最后一层输出数据发现其大小差距在50倍以上。网上有提到ncnn默认转半精度浮点数,因此将数据类型转换这句代码注释后(代码如下)重新debug,再次比对输出结果,差距缩小,部分数据整数部分一致,小数部分不同;

    static int detect_squeezenet(const cv::Mat& bgr, std::vector<float>& cls_scores)
    {
    ncnn::Net squeezenet;
    squeezenet.load_param("../tools/mxnet/debug/converted_model/multitask2.param");
    squeezenet.load_model("../tools/mxnet/debug/converted_model/multitask2.bin");
    //cv::Mat dst;
    //bgr.convertTo(dst, CV_32FC3, 1.0);
    ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR2RGB, bgr.cols, bgr.rows, 96, 96);
    
    const float mean_vals[3] = {123.f, 117.f, 104.f};
    in.substract_mean_normalize(mean_vals, 0);
    
    ncnn::Extractor ex = squeezenet.create_extractor();
    ex.set_light_mode(true);
    
    ex.input("data", in);
    
    ncnn::Mat out;
    ex.extract("softmax2", out);
    out = out.reshape(1, 1, 26);
    cls_scores.resize(out.c);
    for (int j=0; j<out.c; j++)
    {
        const float* prob = out.channel(j);
        cls_scores[j] = prob[0];
    }
    
    return 0;
    }
    
    • elu函数的指前因子默认设置差异:
      对mxnet与ncnn框架的输出结果进行逐层比对:发现在58层-convolution(共83层)时两者小数部分第2位与第3位之后基本不一致,之后差距随层数的增加逐渐增大,未找出原因。

      重新训练mxnet框架的模型,从只有一层开始,每训练一次增加一层,比对发现两者在elu操作后负数部分不一致,将负数部分取出分析,发现mxnet与ncnn框架的elu服从的函数分别为f(x)=0.25(e^x-1) 与f(x)=0.1(e^x-1),查找mxnet的API,其elu函数的指前因子默认设置为0.25,而ncnn的elu.cpp文件中该参数的设置值为0.1,将其更改为0.25后重新编译ncnn,加载自己训练的一个十几层的模型,输出结果基本一致,再次加载一个二十多层的模型,输出结果也基本一致;

    • pooling层pooling_convention参数的默认设置差异:
      当加载之前的83层的模型时,仍然在第58层时输出结果的小数部分第三位开始不一致,未找到原因;

      重新训练了一个四十多层的模型,发现输出结果维度不匹配,如mxnet的out为64×24×24,ncnn的为64×25×25,在nihui大佬指点下,将mxnet框架模型文件的pooling层pooling_convention参数设置为full(因为该参数mxnet默认设置为valid,而ncnn默认设置为full),重新训练83层的那个模型,比对最终层的输出结果,整数部分一致,小数部分仍有差异;

    • batchnorm(bn)层eps参数的默认设置差异:
      mxnet模型bn层的参数eps设置值为2e-5,查看ncnn中mxnet2ncnn.cpp文件的bn层eps的设置值为1e-3,因此,将其修改一致后重新编译得到mxnet2ncnn.exe文件;

      为避免多gpu训练的模型其bn层的gamma出现问题,nihui建议看看vsooda的帖子,建议训练mxnet的模型时使用单gpu;

      用单gpu重新训练83层的那个模型,用修改eps后重新生成的mxnet2ncnn.exe转换模型,再次加载后比对mxnet与ncnn框架的最终输出结果,整数部分及小数部分的前五位已基本一致。(但我后来下载最新的ncnn编译后,mxnet模型训练时使用双gpu,输出结果也能基本一致)。

    PS:本人为初学者,有经验的前辈们对涉及的基础部分请飘过~~~

    相关文章

      网友评论

          本文标题:mxnet_convert_to_ncnn

          本文链接:https://www.haomeiwen.com/subject/emaazxtx.html