<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          詳細(xì)記錄u版YOLOv5目標(biāo)檢測(cè)ncnn實(shí)現(xiàn)

          共 3405字,需瀏覽 7分鐘

           ·

          2020-11-11 16:05

          ↑ 點(diǎn)擊藍(lán)字?關(guān)注極市平臺(tái)

          作者丨nihui@知乎
          來(lái)源丨h(huán)ttps://zhuanlan.zhihu.com/p/275989233
          編輯丨極市平臺(tái)

          極市導(dǎo)讀

          ?

          本文作者用 yolov5 作為例子,介紹了如何用 ncnn 實(shí)現(xiàn)出完整形態(tài)的 yolov5。完整闡述了如何用自定義層以及動(dòng)態(tài)輸入的注意事項(xiàng),給大家作為一個(gè)參考。?>>加入極市CV技術(shù)交流群,走在計(jì)算機(jī)視覺(jué)的最前沿

          0x0 u版YOLOv5

          眾所周知,原版YOLO系列是 darknet 框架訓(xùn)練的,而廣泛使用的是 YOLOv4 作者 AlexeyAB 的版本

          AlexeyAB 首字母是a,于是也被叫做 a版,darknet模型可以用 ncnn 自帶的 darknet2ncnn 無(wú)痛轉(zhuǎn)換,步驟比較簡(jiǎn)單,因此本文不提

          https://github.com/AlexeyAB/darknet

          Ultralytics LLC 再次改進(jìn)出更快更好的 YOLOv5,并且之前也有獨(dú)立實(shí)現(xiàn)的 pytorch yolov3

          Ultralytics 首字母是u,于是也被叫做 u版。pytorch 大法好!(曾經(jīng)我以為u版的意思是能放在u盤里跑的yolo(((

          https://github.com/ultralytics/yolov5

          0x1 緣由

          pytorch yolov5 轉(zhuǎn) ncnn 推理,搜索下 github 便能找到好幾個(gè),zhihu 也有文章

          ncnn example 里沒(méi)有 yolov5.cpp,本打算借鑒下社區(qū)成果,結(jié)果仔細(xì)看了代碼發(fā)現(xiàn)這些實(shí)現(xiàn)都缺少了 yolov5 Focus 模塊和動(dòng)態(tài)尺寸輸入,前者導(dǎo)致檢測(cè)精度差一截,后者導(dǎo)致推理速度差一截,這樣子放進(jìn)官方repo當(dāng)成參考代碼是不行的

          這里就用 yolov5 作為例子,介紹下如何用 ncnn 實(shí)現(xiàn)出完整形態(tài)的 yolov5

          0x2 pytorch測(cè)試和導(dǎo)出onnx

          按照 yolov5 README 指引,下載 yolov5s.pt,調(diào)用 detect.py 看看檢測(cè)效果

          $ python detect.py --source inference/images --weights yolov5s.pt --conf 0.25

          效果沒(méi)有問(wèn)題,繼續(xù)按照 README 指引,導(dǎo)出 onnx,并用 onnx-simplifer 簡(jiǎn)化模型,到此都很順利

          https://github.com/ultralytics/yolov5/issues/251github.com

          $ python models/export.py --weights yolov5s.pt --img 640 --batch 1
          $ python -m onnxsim yolov5s.onnx yolov5s-sim.onnx


          0x3 轉(zhuǎn)換和實(shí)現(xiàn)focus模塊

          $ onnx2ncnn yolov5s-sim.onnx yolov5s.param yolov5s.bin

          轉(zhuǎn)換為 ncnn 模型,會(huì)輸出很多 Unsupported slice step,這是focus模塊轉(zhuǎn)換的報(bào)錯(cuò)

          Unsupported slice step !
          Unsupported slice step !
          Unsupported slice step !
          Unsupported slice step !
          Unsupported slice step !
          Unsupported slice step !
          Unsupported slice step !
          Unsupported slice step !


          好多人遇到這種情況,便不知所措,這些警告表明focus模塊這里要手工修復(fù)下

          打開(kāi) yolov5/models/common.py 看看focus在做些什么

          class Focus(nn.Module):
          # Focus wh information into c-space
          def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
          super(Focus, self).__init__()
          self.conv = Conv(c1 * 4, c2, k, s, p, g, act)

          def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
          return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))


          這其實(shí)是一次 col-major space2depth 操作,pytorch 似乎并沒(méi)有對(duì)應(yīng)上層api實(shí)現(xiàn)(反向的 depth2space 可以用 nn.PixelShuffle),yolov5 用 stride slice 再 concat 方式實(shí)現(xiàn),實(shí)乃不得已而為之的騷操作

          用netron工具打開(kāi)param,找到對(duì)應(yīng)focus的部分

          把這堆騷操作用個(gè)自定義op YoloV5Focus代替掉,修改param

          • 找準(zhǔn)輸入輸出 blob 名字,用一個(gè)自定義層 YoloV5Focus 連接
          • param 開(kāi)頭第二行,layer_count 要對(duì)應(yīng)修改,但 blob_count 只需確保大于等于實(shí)際數(shù)量即可
          • 修改后使用 ncnnoptimize 工具,自動(dòng)修正為實(shí)際 blob_count

          替換后用 ncnnoptimize 過(guò)一遍模型,順便轉(zhuǎn)為 fp16 存儲(chǔ)減小模型體積

          $ ncnnoptimize yolov5s.param yolov5s.bin yolov5s-opt.param yolov5s-opt.bin 65536

          接下來(lái)要實(shí)現(xiàn)這個(gè)自定義op YoloV5Focus,wiki上的步驟比較繁多

          https://github.com/Tencent/ncnn/wiki/how-to-implement-custom-layer-step-by-stepgithub.com

          針對(duì) focus 這樣,沒(méi)有權(quán)重,也無(wú)所謂參數(shù)加載的 op,繼承 ncnn::Layer 實(shí)現(xiàn) forward 就可以用,注意要用 DEFINE_LAYER_CREATOR 宏定義 YoloV5Focus_layer_creator

          #include "layer.h"
          class YoloV5Focus : public ncnn::Layer
          {
          public:
          YoloV5Focus()
          {
          one_blob_only = true;
          }

          virtual int forward(const ncnn::Mat& bottom_blob, ncnn::Mat& top_blob, const ncnn::Option& opt) const
          {
          int w = bottom_blob.w;
          int h = bottom_blob.h;
          int channels = bottom_blob.c;

          int outw = w / 2;
          int outh = h / 2;
          int outc = channels * 4;

          top_blob.create(outw, outh, outc, 4u, 1, opt.blob_allocator);
          if (top_blob.empty())
          return -100;

          #pragma omp parallel for num_threads(opt.num_threads)
          for (int p = 0; p < outc; p++)
          {
          const float* ptr = bottom_blob.channel(p % channels).row((p / channels) % 2) + ((p / channels) / 2);
          float* outptr = top_blob.channel(p);

          for (int i = 0; i < outh; i++)
          {
          for (int j = 0; j < outw; j++)
          {
          *outptr = *ptr;

          outptr += 1;
          ptr += 2;
          }

          ptr += w;
          }
          }

          return 0;
          }
          };

          DEFINE_LAYER_CREATOR(YoloV5Focus)

          加載模型前先注冊(cè) YoloV5Focus,否則會(huì)報(bào)錯(cuò)找不到 YoloV5Focus

          ncnn::Net yolov5;

          yolov5.opt.use_vulkan_compute = true;
          // yolov5.opt.use_bf16_storage = true;

          yolov5.register_custom_layer("YoloV5Focus", YoloV5Focus_layer_creator);

          yolov5.load_param("yolov5s-opt.param");
          yolov5.load_model("yolov5s-opt.bin");

          0x4 u版YOLOv5后處理

          其實(shí)工程量最大的倒是后處理的實(shí)現(xiàn),u版的后處理和a版本是不一樣的,ncnn內(nèi)置的YoloV3DetectionOuptut是對(duì)著a版實(shí)現(xiàn)的,不能直接拿來(lái)接住,需要自己實(shí)現(xiàn)

          anchor信息是在 yolov5/models/yolov5s.yaml

          pytorch的后處理在 yolov5/models/yolo.py Detect類 forward函數(shù),對(duì)著改寫(xiě)成 cpp

          netron里找到模型的3個(gè)輸出blob,分別對(duì)應(yīng)于 stride 8/16/32 的輸出

          輸出shape可知

          • w=85,對(duì)應(yīng)于bbox的dx,dy,dw,dh,bbox置信度,80種分類的置信度
          • h=6400,對(duì)應(yīng)于整個(gè)圖片里全部anchor的xy,這個(gè)1600是stride=8的情況,輸入640的圖片,寬高劃分為640/8=80塊,80x80即6400
          • c=3,對(duì)應(yīng)于三種anchor

          sort nms 可以借鑒 YoloV3DetectionOuptut

          0x5 動(dòng)態(tài)尺寸推理

          u版yolov5 是支持動(dòng)態(tài)尺寸推理的

          • 靜態(tài)尺寸:按長(zhǎng)邊縮放到 640xH 或 Wx640,padding 到 640x640 再檢測(cè),如果 H/W 比較小,會(huì)在 padding 上浪費(fèi)大量運(yùn)算
          • 動(dòng)態(tài)尺寸:按長(zhǎng)邊縮放到 640xH 或 Wx640,padding 到 640xH2 或 W2x640 再檢測(cè),其中 H2/W2 是 H/W 向上取32倍數(shù),計(jì)算量少,速度更快

          ncnn天然支持動(dòng)態(tài)尺寸輸入,無(wú)需reshape或重新初始化,給多少就算多少

          如果直接跑小圖,會(huì)發(fā)現(xiàn)檢測(cè)框密密麻麻布滿整個(gè)畫(huà)面,或者根本檢測(cè)不到東西,就像這樣

          問(wèn)題出在最后 Reshape 層把輸出grid數(shù)寫(xiě)死了,根據(jù) ncnn Reshape 參數(shù)含義,把寫(xiě)死的數(shù)量改為 -1 便可以自適應(yīng)

          后處理部分也不可寫(xiě)死 sqrt(num_grid),要根據(jù)圖片寬高和 stride 自適應(yīng)

          const int num_grid = feat_blob.h;

          int num_grid_x;
          int num_grid_y;
          if (in_pad.w > in_pad.h)
          {
          num_grid_x = in_pad.w / stride;
          num_grid_y = num_grid / num_grid_x;
          }
          else
          {
          num_grid_y = in_pad.h / stride;
          num_grid_x = num_grid / num_grid_y;
          }

          ncnn實(shí)現(xiàn)代碼和轉(zhuǎn)好的模型已上傳到github

          0x6 android例子

          https://github.com/nihui/ncnn-android-yolov5github.com

          根據(jù) README 步驟就能編譯,yolov5 小目標(biāo)檢測(cè)挺厲害的

          0x7 總結(jié)

          沒(méi)啥好總結(jié)的,寫(xiě)個(gè)文章,實(shí)踐下如何用自定義層,講講動(dòng)態(tài)輸入的注意事項(xiàng),將來(lái)有需要可以參考著來(lái)

          雖然沒(méi)有這教程,也能把 example 的 yolov5 跑起來(lái),但里頭的過(guò)程和細(xì)節(jié)就看不到了,授人魚(yú)不如授人漁

          ncnn就要1w star啦(小聲

          https://github.com/Tencent/ncnngithub.com


          推薦閱讀


          添加極市小助手微信(ID : cvmart2),備注:姓名-學(xué)校/公司-研究方向-城市(如:小極-北大-目標(biāo)檢測(cè)-深圳),即可申請(qǐng)加入極市目標(biāo)檢測(cè)/圖像分割/工業(yè)檢測(cè)/人臉/醫(yī)學(xué)影像/3D/SLAM/自動(dòng)駕駛/超分辨率/姿態(tài)估計(jì)/ReID/GAN/圖像增強(qiáng)/OCR/視頻理解等技術(shù)交流群:月大咖直播分享、真實(shí)項(xiàng)目需求對(duì)接、求職內(nèi)推、算法競(jìng)賽、干貨資訊匯總、與?10000+來(lái)自港科大、北大、清華、中科院、CMU、騰訊、百度等名校名企視覺(jué)開(kāi)發(fā)者互動(dòng)交流~

          △長(zhǎng)按添加極市小助手

          △長(zhǎng)按關(guān)注極市平臺(tái),獲取最新CV干貨

          覺(jué)得有用麻煩給個(gè)在看啦~??
          瀏覽 112
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91精品在线播放 | 淫荡留学生激情 | 超碰在线公开91 | 久久精品一二三 | 国产字幕在线观看 |