日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

开源项目|基于darknet实现量化感知训练,已实现yolov3-tiny所有算子

發(fā)布時(shí)間:2025/3/8 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 开源项目|基于darknet实现量化感知训练,已实现yolov3-tiny所有算子 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

◎本文為極市開發(fā)者「ArtyZe」原創(chuàng)投稿,轉(zhuǎn)載請注明來源。

◎極市「項(xiàng)目推薦」專欄,幫助開發(fā)者們推廣分享自己的最新工作,歡迎大家投稿。聯(lián)系極市小編(fengcall19)即可投稿~

量化簡介

在實(shí)際神經(jīng)網(wǎng)絡(luò)在例如端側(cè)的部署時(shí),由于內(nèi)存,帶寬或者最重要計(jì)算資源的限制,通常會(huì)采用量化等手段來加速神經(jīng)網(wǎng)絡(luò)的表現(xiàn)。量化的意思即是將原來浮點(diǎn)運(yùn)算轉(zhuǎn)化為定點(diǎn)運(yùn)算,例如最常見的8bit量化,無論是int8還是uint8,都是將浮點(diǎn)的區(qū)間參數(shù)映射到256個(gè)離散區(qū)間上。這樣原來32位的運(yùn)算就變成了8位的運(yùn)算

r=S(q?Z)r=S(q-Z)r=S(q?Z)

這里我們以非對(duì)稱量化到uint8舉例,其中S代表量化因子(scale factor), Z代表zero point.

量化的優(yōu)點(diǎn)非常明顯,即使除去后處理,反量化或者非對(duì)稱量化帶來額外運(yùn)算,單張圖片的推理速度通常都能獲得2-3倍的提升(這里不討論針對(duì)硬件進(jìn)行特殊優(yōu)化帶來的加速),但是隨之而來的就是量化造成的精度下降問題。

簡單來說,量化造成精度損失主要來自兩個(gè)方面:

  • 取整損失,例如r = [6.8, 7.2, -0.6], scale = (7.2+0.6)/127 = 0.061417, q1 = 7.2/scale = 117.23,那么他的量化值就是117,有了0.23的損失

  • 截?cái)鄵p失?,因?yàn)閟cale是取最優(yōu)區(qū)間,那么邊界的點(diǎn)勢必會(huì)有超過最大量化值的情況,這些離群點(diǎn)就會(huì)被忽略掉,量化的最大最小值區(qū)間相比于原數(shù)據(jù)分布就有了截?cái)鄵p失

為了能夠減少量化過程中的精度損失,我們參考google的論文

《Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference》,這種方法屬于aware training quantization,與之對(duì)應(yīng)的是post training quantization,后面一種方法是tensorRT使用的量化方法,后面有機(jī)會(huì)可以把實(shí)現(xiàn)的代碼上傳到github上。

事實(shí)上,學(xué)術(shù)界認(rèn)為8bit的量化已經(jīng)飽和了,已經(jīng)開始做4bit的量化研究了,但是在實(shí)際的工作過程中,發(fā)現(xiàn)對(duì)于較小的識(shí)別網(wǎng)絡(luò),8bit的量化效果依然不是令人非常滿意。

量化實(shí)現(xiàn)

為了方便的部署到嵌入式端,我最初選擇實(shí)現(xiàn)框架定在實(shí)現(xiàn)語言為C或者C++,最終選定的框架為darknet,一方面darknet在工業(yè)界有著不錯(cuò)的應(yīng)用群體,二來框架簡單直接,實(shí)現(xiàn)起來非常方便,同時(shí)還可以驗(yàn)證反向過程是否正確。在復(fù)現(xiàn)過程中,為了能夠?qū)⑺惴ǔ晒Φ募蛇M(jìn)去,對(duì)darknet做了許多小的修改,正好這里也記錄一下。

代碼鏈接:
https://github.com/ArtyZe/yolo_quantization

偽量化

相信對(duì)量化了解的同學(xué)都讀過這篇文章,tf-lite都是用的這種量化方式。區(qū)別于訓(xùn)練后量化的方式,google采用的是在訓(xùn)練過程中加入偽量化來模擬量化過程中由于取整造成的精度損失。

那么偽量化是個(gè)什么操作呢?

q=?x?as?x+aq=\left\lfloor\frac{x-a}{s}\right\rfloor x+aq=?sx?a??x+a

其中,類似中括號(hào)那里就是取整的意思。可以看到,如果說沒有取整這個(gè)操作,完全就是減一個(gè)數(shù),除一個(gè)數(shù),再乘回來,再加回來,完全就沒有任何變化。但是因?yàn)橛辛诉@個(gè)取整,所以這中間就有了變化。

想象一下,如果在訓(xùn)練過程中,采取了這么一個(gè)操作,那不就相當(dāng)于提前就把量化的損失考慮進(jìn)去了嗎?這樣等到inference的時(shí)候,精度下降就少的多了呀。

那么要把這個(gè)偽量化放在哪里呢?

那當(dāng)然是放在inference的時(shí)候需要進(jìn)行量化的位置,以論文中給出的圖來解析,

卷積的操作用公式來描述無非就是:

y=f(wx)y=f(w x)y=f(wx)

所以要量化的就是weights以及feature x。

這時(shí)候就有人提出疑問了,可是你看啊,人家給出的圖中是weights和激活值的偽量化啊,你怎么說是input的feature呢,可是如果你這樣想呢,除了第一層真正的輸入之外,剩下的層,上一層的activ輸出值不就是下一層的input值嗎,而且使用activ值有一個(gè)什么最大的好處呢?在最后一層將定點(diǎn)值反量化回到浮點(diǎn)值需要用到激活值的scale和zero_point(如果是非對(duì)稱量化的話)

在訓(xùn)練中融合BN到CONV

我們平時(shí)見到的最多的融合BN+CONV就是在inference的時(shí)候?yàn)榱思铀僮龅?#xff0c;但是你細(xì)想一下,你BN的參數(shù)在inference的時(shí)候怎么辦呢?如果inference的時(shí)候不融合,那么BN的參數(shù)你要怎么量化,如果融合了,那么weights的量化參數(shù)是根據(jù)融合前生成的啊,那你怎么能用呢?

所以解決方案就是,把BN融合在訓(xùn)練階段就加進(jìn)去,如下圖:

具體怎么做呢?

- 首先就y=wxy=w xy=wx的前向跑一遍,計(jì)算得到均值,方差等一系列BN的參數(shù)

- 然后,利用這些BN的參數(shù),通過融合公式加到input和weights中去,將卷積公式變成真正的

y′=w′x+b′y^{\prime}=w^{\prime} x+b^{\prime}y=wx+b

其中

w′=pwσ2+εb′=b?γμσ2+εw^{\prime}=\frac{p w}{\sqrt{\sigma^{2}+\varepsilon}} \quad b^{\prime}=b-\frac{\gamma \mu}{\sqrt{\sigma^{2}+\varepsilon}}w=σ2+ε?pw?b=b?σ2+ε?γμ?

為了后續(xù)能夠更新原生wwwb,b,b, 該過程中不僅需要保存 w′w^{\prime}wb′,b^{\prime},b, 還需要保存 wwwbbb,至于反向更新過程中,需要使用Straight Through Estimator(STE)來跳過偽量化過程中的round使得梯度可以正常回傳

- 之后根據(jù)不同層的type添加input, weights和activation量化即可。目前我采用的方式是第一層卷積input, weights和activation量化都要有,其他層如route后面的卷積層同樣需要input量化,因?yàn)閞oute的activation量化參數(shù)直接使用他的輸入層的activation量化參數(shù)即可;maxpool或者upsample都是添加activation量化即可

需要注意的

Uint8推理實(shí)現(xiàn)

下面開始介紹定點(diǎn)推理,公式如下

y=wx+by=w x+by=wx+b

由前面可知

r=S(q?Z)r=S(q-Z)r=S(q?Z)

S3(q3?Z3)=S2(q2?Z2)S1(q1?Z1)+Sb(qb?Zb)S_{3}\left(q_{3}-Z_{3}\right)=S_{2}\left(q_{2}-Z_{2}\right) S_{1}\left(q_{1}-Z_{1}\right)+S_{b}\left(q_{b}-Z_{b}\right)S3?(q3??Z3?)=S2?(q2??Z2?)S1?(q1??Z1?)+Sb?(qb??Zb?)

為了保持量綱一致,令,Sb=S1S2,Zb=0S_{b}=S_{1} S_{2}, \quad Z_{b}=0Sb?=S1?S2?,Zb?=0

對(duì)上式進(jìn)行簡單的變換
q3=Z3+M(NZ1Z2?Z1∑q2?Z2∑q1+∑q1q2+qb)q_{3}=Z_{3}+M\left(N Z_{1} Z_{2}-Z_{1} \sum q_{2}-Z_{2} \sum q_{1}+\sum q_{1} q_{2}+q_{b}\right)q3?=Z3?+M(NZ1?Z2??Z1?q2??Z2?q1?+q1?q2?+qb?)

其中, M=S1S2/S3M=S_{1} S_{2} / S_{3}M=S1?S2?/S3? 是唯一的浮點(diǎn)數(shù), 因此采用 M=M0×2?shiftM=M_{0} \times 2^{-s h i f t}M=M0?×2?shift 來代表, M0M_{0}M0? 和 shift 都是定點(diǎn)值,具體多大需要看精度需要,一般采用32位的值來表示。

  • 在進(jìn)入到正式的推理之前,首先看上式哪些值是常量可以提前計(jì)算出來,例如Z3,Z1Z2,Z1∑q2,qbZ_{3}, Z_{1} Z_{2}, Z_{1} \sum q_{2}, q_{b}Z3?,Z1?Z2?,Z1?q2?,qb?都是常量,其中1代表ft,2代表weights

  • 進(jìn)入到正式推理后,需要注意的問題就是溢出的問題,一般情況下為了防止這種情 況有兩種方式,一種就是使用一個(gè)shift來統(tǒng)計(jì)溢出的情況,另一種就是直接把輸出范圍擴(kuò)大,例如8bit的乘加輸出到32bit。下面我們開始計(jì)算Z2∑q1Z_{2} \sum q_{1}Z2?q1?∑q1q2\sum q_{1} q_{2}q1?q2?,為了能夠盡可能的探索優(yōu)化速度的極限,gemm函數(shù)我們使用的是mkl中的cblas庫函數(shù)。

  • 得到q3q_{3}q3?之后的最后一步操作就是激活,這部分在實(shí)際使用過程中也是關(guān)乎到量化精度的一個(gè)關(guān)鍵點(diǎn)。如果激活函數(shù)是類似softmax,tanh,swish等非線性函數(shù)的話,都要通過lookup table查表的方式,為了能夠盡快的實(shí)現(xiàn),我這里選用的是tiny-yolov3,里面的激活函數(shù)都是leaky relu的線性激活函數(shù)

  • 其他層例如maxpool,route由于并不涉及到計(jì)算操作,因此直接將代碼轉(zhuǎn)成uint8的即可

  • 在最后一層yolo層的前面需要將uint8反量化回到float類型,方式如下

后續(xù)改進(jìn)

目前已經(jīng)實(shí)現(xiàn)了yolov3-tiny的所有算子的實(shí)現(xiàn),為了方便,目前使用relu6替代了原來的

leakyrelu,包括conv, pooling, route, upsample,這些除了conv全部都是線性的算子,后續(xù)會(huì)

繼續(xù)支持leaky relu, softmax, shortcut, elementwise add, concat等非線性算子。

量化performance

為了盡可能的不影響精度,我選擇在yolo層的上面一層conv層不進(jìn)行量化。測試結(jié)果如下,可以看到前向時(shí)間相比于原來的darknet壓縮明顯,同時(shí)精度下降非常低。

傳送門

Github鏈接:
https://github.com/ArtyZe/yolo_quantization

總結(jié)

以上是生活随笔為你收集整理的开源项目|基于darknet实现量化感知训练,已实现yolov3-tiny所有算子的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。