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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

【玩转yolov5】使用TensorRT C++ API搭建yolov5s-v4.0网络结构(1)

發(fā)布時(shí)間:2024/3/24 c/c++ 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【玩转yolov5】使用TensorRT C++ API搭建yolov5s-v4.0网络结构(1) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?

注意:對于yolov5s-v4.0網(wǎng)絡(luò)結(jié)構(gòu),上圖僅作參考,實(shí)際結(jié)構(gòu)以代碼為準(zhǔn),存在少量差異!

#需要一個(gè)全局的ILogger對象,用于記錄日志信息 static Logger gLogger; #創(chuàng)建一個(gè)網(wǎng)絡(luò)生成器 IBuilder* builder = createInferBuilder(gLogger); #使用IBuilder類方法創(chuàng)建一個(gè)空的網(wǎng)絡(luò) INetworkDefinition* network = builder->createNetworkV2(0U);

? ? ? ? builder是構(gòu)建器,他會自動(dòng)搜索cuda內(nèi)核目錄以獲得最快的可用實(shí)現(xiàn),構(gòu)建和運(yùn)行時(shí)的GPU需要保持一致。由builder構(gòu)建的引擎(engine)不能跨平臺和TensorRT版本移植。上面由builder創(chuàng)建了一個(gè)空的網(wǎng)絡(luò)結(jié)構(gòu),后面就需要通過tensorrt c++ api來逐填充該網(wǎng)絡(luò)結(jié)構(gòu),直至完整構(gòu)建yolov5s-v4.0網(wǎng)絡(luò)。

? ? ? ???首先構(gòu)造focus結(jié)構(gòu),在yolov3和yolov4中并沒有這個(gè)結(jié)構(gòu),其中比較關(guān)鍵的是切片操作。以我訓(xùn)練的輸入為640*640*3的yolov5s的結(jié)構(gòu)為例,原始640*640*3的圖像輸入focus結(jié)構(gòu),采用切片操作,生成320*320*12的特征圖,再經(jīng)過一個(gè)輸出通道為32的卷積操作,生成320*320*32的特征圖。focus結(jié)構(gòu)的意義在于可以最大程度的減少信息損失而進(jìn)行下采樣操作。focus結(jié)構(gòu)中需要用到的一個(gè)重要的tensorrt api就是addSlice接口,它用于創(chuàng)建一個(gè)slice層。

virtual ISliceLayer* nvinfer1::INetworkDefinition::addSlice(?? ?ITenso& ?? ?input,Dims ?? ?start,Dims ?? ?size,Dims ?? ?stride? )?

https://docs.nvidia.com/deeplearning/tensorrt/api/c_api/classnvinfer1_1_1_i_network_definition.html#ab3d64683b10afdbc944075da818fb086

ILayer* focus(INetworkDefinition *network, std::map<std::string, Weights>& weightMap, ITensor& input, int inch, int outch, int ksize, std::string lname) {ISliceLayer *s1 = network->addSlice(input, Dims3{ 0, 0, 0 }, Dims3{ inch, Yolo::INPUT_H / 2, Yolo::INPUT_W / 2 }, Dims3{ 1, 2, 2 });ISliceLayer *s2 = network->addSlice(input, Dims3{ 0, 1, 0 }, Dims3{ inch, Yolo::INPUT_H / 2, Yolo::INPUT_W / 2 }, Dims3{ 1, 2, 2 });ISliceLayer *s3 = network->addSlice(input, Dims3{ 0, 0, 1 }, Dims3{ inch, Yolo::INPUT_H / 2, Yolo::INPUT_W / 2 }, Dims3{ 1, 2, 2 });ISliceLayer *s4 = network->addSlice(input, Dims3{ 0, 1, 1 }, Dims3{ inch, Yolo::INPUT_H / 2, Yolo::INPUT_W / 2 }, Dims3{ 1, 2, 2 });ITensor* inputTensors[] = { s1->getOutput(0), s2->getOutput(0), s3->getOutput(0), s4->getOutput(0) };auto cat = network->addConcatenation(inputTensors, 4); #通道維度上的拼接auto conv = convBlock(network, weightMap, *cat->getOutput(0), outch, ksize, 1, 1, lname + ".conv");return conv; }

接下來是一個(gè)CBL結(jié)構(gòu),這個(gè)比較好理解,拆開來看就是:Conv + BN + Silu。注意,雖然上面的全局網(wǎng)絡(luò)結(jié)構(gòu)圖中展示的CBL中的激活函數(shù)是LeakyRelu,但是在v4.0中激活函數(shù)是Silu(Sigmoid Weighted Linear Unit),是一種較為平滑的激活函數(shù)。

ILayer* convBlock(INetworkDefinition *network, std::map<std::string, Weights>& weightMap, ITensor& input, int outch, int ksize, int s, int g, std::string lname) {Weights emptywts{ DataType::kFLOAT, nullptr, 0 };int p = ksize / 2;IConvolutionLayer* conv1 = network->addConvolutionNd(input, outch, DimsHW{ ksize, ksize }, weightMap[lname + ".conv.weight"], emptywts);assert(conv1);conv1->setStrideNd(DimsHW{ s, s });conv1->setPaddingNd(DimsHW{ p, p });conv1->setNbGroups(g);IScaleLayer* bn1 = addBatchNorm2d(network, weightMap, *conv1->getOutput(0), lname + ".bn", 1e-3);// silu = x * sigmoidauto sig = network->addActivation(*bn1->getOutput(0), ActivationType::kSIGMOID);assert(sig);auto ew = network->addElementWise(*bn1->getOutput(0), *sig->getOutput(0), ElementWiseOperation::kPROD);assert(ew);return ew; }

因?yàn)楹竺嬉l繁用到該結(jié)構(gòu),這里拆開來詳細(xì)講解一下。首先是卷積,調(diào)用addConvolutionNd來創(chuàng)建一個(gè)新的卷積層。?因?yàn)闆]有bias一項(xiàng),定義的bias的Weights結(jié)構(gòu)中values為nullptr。stride,padding,group等參數(shù)通過IConvolutionLayer的內(nèi)部成員函數(shù)來設(shè)置。

Weights emptywts{ DataType::kFLOAT, nullptr, 0 };int p = ksize / 2;IConvolutionLayer* conv1 = network->addConvolutionNd(input, outch, DimsHW{ ksize, ksize }, weightMap[lname + ".conv.weight"], emptywts);assert(conv1);conv1->setStrideNd(DimsHW{ s, s });conv1->setPaddingNd(DimsHW{ p, p });conv1->setNbGroups(g);

然后是BN層,回顧一下BN層的定義:

E [ x ] 是batch的均值,V a r [ x ] 是batch的方差,?為了防止除0,γ 對應(yīng)batch學(xué)習(xí)得到的權(quán)重,β 就是偏置。

TensorRT中并沒有直接的BatchNorm層,該層實(shí)際上是通過轉(zhuǎn)換系數(shù)依靠Scale層來完成。

好了,萬事具備,可以手撕代碼了。

IScaleLayer* addBatchNorm2d(INetworkDefinition *network, std::map<std::string, Weights>& weightMap, ITensor& input, std::string lname, float eps) {float *gamma = (float*)weightMap[lname + ".weight"].values;float *beta = (float*)weightMap[lname + ".bias"].values;float *mean = (float*)weightMap[lname + ".running_mean"].values; //均值float *var = (float*)weightMap[lname + ".running_var"].values; //方差int len = weightMap[lname + ".running_var"].count;//scalefloat *scval = reinterpret_cast<float*>(malloc(sizeof(float) * len));for (int i = 0; i < len; i++) {scval[i] = gamma[i] / sqrt(var[i] + eps);}Weights scale{ DataType::kFLOAT, scval, len };//shiftfloat *shval = reinterpret_cast<float*>(malloc(sizeof(float) * len));for (int i = 0; i < len; i++) {shval[i] = beta[i] - mean[i] * gamma[i] / sqrt(var[i] + eps);}Weights shift{ DataType::kFLOAT, shval, len };//powerfloat *pval = reinterpret_cast<float*>(malloc(sizeof(float) * len));for (int i = 0; i < len; i++) {pval[i] = 1.0;}Weights power{ DataType::kFLOAT, pval, len };weightMap[lname + ".scale"] = scale;weightMap[lname + ".shift"] = shift;weightMap[lname + ".power"] = power;//BatchNorm是channel維度的操作IScaleLayer* scale_1 = network->addScale(input, ScaleMode::kCHANNEL, shift, scale, power);assert(scale_1);return scale_1; }

然后就是激活函數(shù)Silu,從下面的公式可以看出來其實(shí)就是給sigmoid激活函數(shù)加了一個(gè)權(quán)重,這個(gè)權(quán)重恰恰就是輸入。

f(x)=x?σ(x)? ??

f′(x)=f(x)+σ(x)(1?f(x))

同樣,TensorRT中也沒有直接提供Silu的api,通過addActivation配合addElementWise中的乘操作可以輕松構(gòu)建Silu。

// silu = x * sigmoidauto sig = network->addActivation(*bn1->getOutput(0), ActivationType::kSIGMOID);assert(sig);auto ew = network->addElementWise(*bn1->getOutput(0), *sig->getOutput(0), ElementWiseOperation::kPROD);assert(ew);

【參考文獻(xiàn)】

https://zhuanlan.zhihu.com/p/172121380

?

?

總結(jié)

以上是生活随笔為你收集整理的【玩转yolov5】使用TensorRT C++ API搭建yolov5s-v4.0网络结构(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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