主頁 >  其他 > 基于ShuffleNetv2-YOLOv4模型的目標檢測

基于ShuffleNetv2-YOLOv4模型的目標檢測

2022-01-17 21:25:26 其他

目錄

1、引言(摘要)

1.1 說明

1.2替換完成的工程請參考gitee

2、網路結構基礎

2.1YOLOv3

2.1 YOLOv4演算法

2.3 ShuffleNetv2

2.4 替換后的網路結構

3 實驗結果

3.1實驗環境配置及資料集介紹

3.1.1資料集介紹

3.1.2實驗驗環境配置

3.2實驗方案

3.3實驗結果對比

3.3.1ShuffleNetv2-YOLOv4的訓練結果

3.3.2部分訓練結果展示

3.3.3各個演算法模型對比(很多引數是錯的 看看就好 一些資料 比如map從大佬的github直接拷貝)

4、部分代碼演示

4.1 shufflenetv2(來自torchvrsion)

4.2 yolo本體


1、引言(摘要)

目標檢測是機器視覺領域內最具挑戰性的任務之一,近年來,深度學習理論及技術的快速發展,使得基于深度學習的目標檢測演算法取得了巨大進展,目標檢測實時性、準確度得到了很大的提高,但是除了準確度外,計算復雜度也是目標檢測要考慮的重要指標,過復雜的網路可能速度很慢,另外移動端設備也需要既準確又快的小模型,研究輕量化的網路模型是很有必要的,結合前人的成果、及現有的工程,我通過將輕量化網路ShuffleNetv2 代替YoloV4的CSPDarknet-53 作為主干網路,結合一定調參方法,在VOC2007與2012進行訓練,實驗證明,該輕量級YOLOv4模型效果還可以,但相比其他型別的輕量級網路仍然有差距,誠然如此,但我認為這是一次有意義的探索,

1.1 說明

該工程是借鑒了Bubbliiiing的學習小課堂_Bubbliiiing_CSDN博客-神經網路學習小記錄,睿智的目標檢測,有趣的資料結構演算法領域博主Bubbliiiing擅長神經網路學習小記錄,睿智的目標檢測,有趣的資料結構演算法,等方面的知識https://blog.csdn.net/weixin_44791964?spm=1001.2014.3001.5509

的專案

GitHub - bubbliiiing/mobilenet-yolov4-pytorch: 這是一個mobilenet-yolov4的庫,把yolov4主干網路修改成了mobilenet,修改了Panet的卷積組成,使引數量大幅度縮小,這是一個mobilenet-yolov4的庫,把yolov4主干網路修改成了mobilenet,修改了Panet的卷積組成,使引數量大幅度縮小, - GitHub - bubbliiiing/mobilenet-yolov4-pytorch: 這是一個mobilenet-yolov4的庫,把yolov4主干網路修改成了mobilenet,修改了Panet的卷積組成,使引數量大幅度縮小,https://github.com/bubbliiiing/mobilenet-yolov4-pytorch由于我并非計算機科班出身,所以難免有錯誤,請借鑒的同學擦亮眼睛,取其精華,去其糟粕(不用私信我,因為我可能也沒時間看,看了也不一定答得上來),

1.2替換完成的工程請參考gitee

yolov4-lite-pytorch: yolov4-lite-pytorch

2、網路結構基礎

YOLOv4 演算法是在原有的YOLO 目標檢測架構的基礎上,分別在加強特征提取、增強網路模型的非線性、防止過度擬合等方面進行了優化,可謂是集CNN 百家之長,談到YOLOv4,要從YOLOv3入手,

2.1YOLOv3

YOLOv3 是 YOLO 系列演算法的第三代改進演算法,YOLOv3 采用 Darknet-53作為特征提取的主干網路(Backbone),Darknet-53 主干網路由五層殘差網路(Resnet, Res)構成,每個殘差網路由若干個殘差塊(Res Unit)、影像填充(Padding)與 CBL 組件級聯組成,CBL 殘差網路、殘差塊、CBL 組件的組成如圖 所示,每一層殘差網路中的殘差塊個數不同,隨著網路逐層加深,殘差塊數逐漸增多,特征送入 Res Unit 后,Res Unit 將輸入特征分別送入兩個通道:首先,特征經由采樣通道,分別經過卷積核大小為1 × 1的卷積操作調整維度,再經3 × 3的卷積提取高維特征;同時,輸入特征經由另一通道直接輸出,保留了輸入的原始特征;最后,將兩通道的特征相加,得到輸出特征,CBL 是 YOLO 系列演算法中提取特征的基本組件,用于代替原始的卷積程序,CBL由卷積、批正則化(Batch Normalization[47])以及 Leaky Relu激活函式構成,相較于普通的卷積操作,CBL具有更好的特征擬合能力,

詳情請看Bubbliiiing的學習小課堂_Bubbliiiing_CSDN博客-神經網路學習小記錄,睿智的目標檢測,有趣的資料結構演算法領域博主大佬文章睿智的目標檢測26——Pytorch搭建yolo3目標檢測平臺_Bubbliiiing的學習小課堂-CSDN博客_目標檢測26

2.1 YOLOv4演算法

YOLOv4 演算法的網路結構如圖1 所示,其主干特征提取網路CSPDarkNet53 結合了CSPNet和 DarkNet53 的優點,在提高網路推理速度和準確性的同時,又能降低主干網路的計算量,相較于之前的YOLOv3,YOLOv4 的頸部網路(NECK)采用SPP(空間金字塔池化)+PANET(路徑聚合網路)的網路結構,SPP(Spatial Pyramid Pooling),可以使得圖片資料矩陣以固定大小的格式輸出到YOLOv4 的預測網路模塊(Head),避免直接對圖片進行剪裁縮放造成資料資訊丟失,進而導致預測網路可信度降低的問題,同時,在提取圖片各維度特征方面,不再使用上一代演算法中的特征金字塔網路(Feature Pyramid Networks ,FPN),而是采用一種金字塔和倒金字塔并行的網路結構, 即路徑聚合網路( Path AggregationNetwork,PANET),

具體詳細分析請看Bubbliiiing的學習小課堂_Bubbliiiing_CSDN博客-神經網路學習小記錄,睿智的目標檢測,有趣的資料結構演算法領域博主大佬的文章

睿智的目標檢測30——Pytorch搭建YoloV4目標檢測平臺_Bubbliiiing的學習小課堂-CSDN博客_yolov4目標檢測

2.3 ShuffleNetv2

ShuffleNetv2 是在 ShuffleNetv1 基礎上提出的升級版本,并且在同等復雜度下比ShuffleNetv1 和 MobileNetv2 地準確度更高,該模型的研究者提出僅使用每秒計算浮點數(FLOPs)并不能準確衡量模型的復雜度和速度,根據實驗結果提出了 4 個適用原則:(1)channel 大小相同時使用最小的記憶體;(2)組卷積使用過多會增多記憶體使用量;(3)模型碎片化會降低并行度;(4)元素級的運算非常重要,ShuffleNetv2 模型借鑒了 DenseNet 結構,使用 Concat 方操作代替了 DenseNet 結構中的 Add 操作,可以實作特征的重用,與 DenseNet 的不同之處在于:ShuffleNetv2 不是密集的 Concat,并且在 Concat 操作后通過 Channel Shuffle 層來混洗特征,這也是 ShuffleNetv2的速度和精度都優于 ShuffleNetv1 的一個重要原因,

ShuffleNet V2 網路模型的基本組成單元大致可分為兩種,第一種如圖2 中a 部分所示,在特征圖輸入后有一個通道分支(channel split)操作,該操作將輸入通道數為c 的特征圖分為c?c′和c′,左邊的分支不做任何操作,右邊的分支包含了3 個卷積操作,并且兩個1*1 卷積已經由ShuffleNetv1 中的分組卷積更換為普通卷積,最后再將這兩個分支通道中的資料用Concat+Channel Shuffle操作進行合并,這樣不僅可以使得該基礎模塊的輸入輸出通道數一樣,而且避免Add操作,加快模型了的推理速度,最后進行通道重組(channelshuffle)操作,值得注意的是,b中沒有了channel split操作,因此該基本模塊的輸出通道數是輸入通道數的兩倍,其左右分支的操作程序和a 基本一致,此處不在贅述,

詳情請看Bubbliiiing的學習小課堂_Bubbliiiing_CSDN博客-神經網路學習小記錄,睿智的目標檢測,有趣的資料結構演算法領域博主大佬文章

神經網路學習小記錄47——ShuffleNetV2模型的復現詳解_Bubbliiiing的學習小課堂-CSDN博客_shufflenetv2模型

2.4 替換后的網路結構

ShufflenetV2系列網路可用于進行分類,其主干部分的作用是進行特征提取,我們可以使用ShufflenetV2系列網路代替YOLOv4當中的CSPdarknet53進行特征提取,將三個初步的有效特征層相同shape的特征層進行加強特征提取,便可以將ShufflenetV2系列替換進YOLOv4當中了,

對于YOLOv4來講,我們需要取出它的最后三個shape的有效特征層進行加強特征提取,在代碼中,我們取出了out1、out2、out3,

替換教程請看Bubbliiiing的學習小課堂_Bubbliiiing_CSDN博客-神經網路學習小記錄,睿智的目標檢測,有趣的資料結構演算法領域博主大佬的文章

睿智的目標檢測49——Pytorch 利用mobilenet系列(v1,v2,v3)搭建yolov4目標檢測平臺_Bubbliiiing的學習小課堂-CSDN博客

3 實驗結果

3.1實驗環境配置及資料集介紹

3.1.1資料集介紹

PASCAL VOC[82](The PASCAL Visual Object Classes Challenge)曾經是計算機視覺領域中一個世界級的挑戰賽,促進了計算機視覺中目標檢測和語意分割等任務的發展,催生了大量杰出的演算法模型,

PASCAL VOC資料集共有20個類,包含了生活中常見的物體,其中鳥、瓶子、盆栽植物等屬于尺寸較小的物體,目前較為常用的是 PASCAL VOC 2007 和 PASCAL VOC 2012 兩種資料集,其中 PASCAL VOC 2007 包含訓練集 5011 張,驗證集 4952張,PASCAL VOC 2012 包含訓練集 5717 張,測驗集 5823 張,

這里將VOC2007與VOC2012的資料集進行了合并訓練,所以資料集的容量為21503張圖片,在每輪訓練時都會取90%的照片用于訓練,另外10%的照片實時檢測訓練效果,

即是:訓練集:測驗集=9:1;訓練集中(訓練集:驗證集9:1),

3.1.2實驗驗環境配置

實驗平臺

由于條件受限,訓練所用的是筆記本平臺:RTX3060(6G)+16G記憶體 windows10系統,與 CPU 相比,GPU 有并行處理架構,因此可以進行更高效、速度更快的運算,適合處理影像這種引數量大,計算復雜的資料,

演算法框架

運用Anconda3(Python3.8)+Pytorch1.8+Cuda11.2軟體進行搭建,

Pytorch 是基于 Torch 框架開發,使用 Python 語言作為底層代碼,且支持動態網路調整,相比于 Tensorflow,Pytorch結構更加簡潔清晰,易開發除錯,綜合上手難度,代碼繁雜程度以及開發靈活性考慮,本文選用Pytorch 作為本文實驗環境的框架,

3.2實驗方案

本實驗共分為三部分包含1組實驗,本實驗包含五個模型:ShuffleNetv2-YOLOv4、MobileNetv1-YOLOv4、MobileNetv2-YOLOv4、MobileNetv3-YOLOv4、YOLOv4-Tiny和在資料集VOC2007+VOC2012進行訓練與驗證,接著進行橫向向對比,對比不同模型在同一資料集上的表現,

訓練程序

訓練階段的引數設定如下:

使用Pytorch的官方預訓練權重加載ShuffleNetv2,接著凍結ShuffleNetv2的主干網路,對PANET以及YOLO- Head進行訓練,練輪次為50,初始學習率為1e-3,標簽平滑值0.005,batch size 設為16,每迭代一次同時輸入16張圖片進行訓練,

解凍ShuffleNetv2的主干網路,對整個網路進行訓練,練輪次為100,初始學習率為1e-4,標簽平滑值0.005,batch size 設為 8,每迭代一次同時輸入8張圖片進行訓練,

評價指標

MAP

本文方法的目的主要是在保證模型精度和速度的同時,減少模型的訓練時間和模型記憶體占比.通過mAP(mean average precision)和FPS(frameper second)對模型的測驗性能進行評價,具體的運算式如下:

式中:P 為準確率,R 為召回率,TP 為真陽性樣本數, FP 為假陽性樣本數, FN 為假陰性數樣本.AP 表示P-R 曲線下的面積,綜合考慮精確率和召回率的影響,反映了模型對不同種類識別的好壞程度. mAP 全稱為 Mean Average Precision,指平均準確率,對多個資料集的 AP 求平均值,mAP 通常為目標檢測中度量檢測準確率的指標,

FPS

目標檢測模型中另外一個重要的指標為速度,只有保證檢測速度快,才能達到實時檢測的目的,這在自動駕駛等場景中非常重要,我們通常使用每秒幀率(Frame Per Second,FPS目標檢測模型的速度,即每秒鐘處理的影像數,我們在測驗訓練好的模型時可以計算出處理一張圖片需要的時間為 t,則一秒鐘該目標檢測模型可以處理 1/t 張圖片,

3.3實驗結果對比

3.3.1ShuffleNetv2-YOLOv4的訓練結果

圖7.ShuffleNetv2-YOLOv4的訓練結果map

圖7.ShuffleNetv2-YOLOv4的訓練結果log-avg miss rate

表1 ShuffleNetv2-YOLOv4的訓練結果

演算法

MAP

模型大小

FPS

模型引數量

FLOPS

ShuffleNetv2-YOLOv4

75.04

44789K

51.59

10.64M

3.71GFlops

(以上結果可能不太準確哈,主要是資料劃分跟大佬的不太一樣,還有一些引數計算有一點問題)

3.3.2部分訓練結果展示

圖8.ShuffleNetv2-YOLOv4的預測結果展示

3.3.3各個演算法模型對比(很多引數是錯的 看看就好 一些資料 比如map從大佬的github直接拷貝

詳細見大佬工程

GitHub - bubbliiiing/mobilenet-yolov4-pytorch: 這是一個mobilenet-yolov4的庫,把yolov4主干網路修改成了mobilenet,修改了Panet的卷積組成,使引數量大幅度縮小,

表2 各個演算法模型對比

演算法

MAP

模型大小

FPS

模型引數量

FLOPS

ShuffleNetv2-YOLOv4

75.04

44.8M

51.59

10.64M

3.71GFlops

Mobilenetv1-YOLOv4

79.72

53.5M

65.72

12.69M

5.3GFlops

Mobilenetv2-YOLOv4

80.12

47.6M

52.83

10.80M

(*)

Mobilenetv3-YOLOv4

79.01

55.53M

47.20

11.2M

3.82GFlops

Gostnet- YOLOv4

78.69

43.76M

38.92

11.2M

3.82GFlops

首先本文將ShuffleNetv2-YOLOv4、MobileNetv1-YOLOv4、MobileNetv2-YOLOv4、MobileNetv3-YOLOv4、Gostnet-YOLOv4、YOLOv4-Tiny相同軟硬體環境和資料集下進行VOC2007+2012目標檢測實驗,輸入圖片固定為416*416 像素,對比二者在正確率、檢測速度以及模型大小上的差距,其實驗結果如表所示,

4、部分代碼演示

4.1 shufflenetv2(來自torchvrsion)


import torch as t
import torch.nn as nn
import math
from collections import OrderedDict
import torch 
__all__ = ['shufflenet2']

#### The model below is defined by myself


def channel_shuffle(x, groups=2):
  bat_size, channels, w, h = x.shape
  group_c = channels // groups
  x = x.view(bat_size, groups, group_c, w, h)
  x = t.transpose(x, 1, 2).contiguous()
  x = x.view(bat_size, -1, w, h)
  return x

# used in the block
def conv_1x1_bn(in_c, out_c, stride=1):
  return nn.Sequential(
    nn.Conv2d(in_c, out_c, 1, stride, 0, bias=False),
    nn.BatchNorm2d(out_c),
    nn.ReLU(True)
  )

def conv_bn(in_c, out_c, stride=2):
  return nn.Sequential(
    nn.Conv2d(in_c, out_c, 3, stride, 1, bias=False),
    nn.BatchNorm2d(out_c),
    nn.ReLU(True)
  )


class ShuffleBlock(nn.Module):
  def __init__(self, in_c, out_c, downsample=False):
    super(ShuffleBlock, self).__init__()
    self.downsample = downsample
    half_c = out_c // 2
    if downsample:
      self.branch1 = nn.Sequential(
          # 3*3 dw conv, stride = 2
          nn.Conv2d(in_c, in_c, 3, 2, 1, groups=in_c, bias=False),
          nn.BatchNorm2d(in_c),
          # 1*1 pw conv
          nn.Conv2d(in_c, half_c, 1, 1, 0, bias=False),
          nn.BatchNorm2d(half_c),
          nn.ReLU(True)
      )
      
      self.branch2 = nn.Sequential(
          # 1*1 pw conv
          nn.Conv2d(in_c, half_c, 1, 1, 0, bias=False),
          nn.BatchNorm2d(half_c),
          nn.ReLU(True),
          # 3*3 dw conv, stride = 2
          nn.Conv2d(half_c, half_c, 3, 2, 1, groups=half_c, bias=False),
          nn.BatchNorm2d(half_c),
          # 1*1 pw conv
          nn.Conv2d(half_c, half_c, 1, 1, 0, bias=False),
          nn.BatchNorm2d(half_c),
          nn.ReLU(True)
      )
    else:
      # in_c = out_c
      assert in_c == out_c
        
      self.branch2 = nn.Sequential(
          # 1*1 pw conv
          nn.Conv2d(half_c, half_c, 1, 1, 0, bias=False),
          nn.BatchNorm2d(half_c),
          nn.ReLU(True),
          # 3*3 dw conv, stride = 1
          nn.Conv2d(half_c, half_c, 3, 1, 1, groups=half_c, bias=False),
          nn.BatchNorm2d(half_c),
          # 1*1 pw conv
          nn.Conv2d(half_c, half_c, 1, 1, 0, bias=False),
          nn.BatchNorm2d(half_c),
          nn.ReLU(True)
      )
      
      
  def forward(self, x):
    out = None
    if self.downsample:
      # if it is downsampling, we don't need to do channel split
      out = t.cat((self.branch1(x), self.branch2(x)), 1)
    else:
      # channel split
      channels = x.shape[1]
      c = channels // 2
      x1 = x[:, :c, :, :]
      x2 = x[:, c:, :, :]
      out = t.cat((x1, self.branch2(x2)), 1)
    return channel_shuffle(out, 2)
    

class ShuffleNet2(nn.Module):
  def __init__(self, input_size=416, net_type=1):
    super(ShuffleNet2, self).__init__()
    assert input_size % 32 == 0 # 因為一共會下采樣32倍
    self.layers_out_filters = [24, 116, 232, 1024] # used for shufflenet v2
    
    self.stage_repeat_num = [4, 8, 4]
    if net_type == 0.5:
      self.out_channels = [3, 24, 48, 96, 192, 1024]
    elif net_type == 1:
      self.out_channels = [3, 24, 116, 232, 464, 1024]
    elif net_type == 1.5:
      self.out_channels = [3, 24, 176, 352, 704, 1024]
    elif net_type == 2:
      self.out_channels = [3, 24, 244, 488, 976, 2948]
    elif net_type == -1:
      self.out_channels = [3, 24, 128, 256, 512, 1024]
    else:
      print("the type is error, you should choose 0.5, 1, 1.5 or 2")
      
    # let's start building layers
    self.conv1 = nn.Conv2d(3, self.out_channels[1], 3, 2, 1)
    self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
    in_c = self.out_channels[1]
    
    self.stage2 = []
    self.stage3 = []
    self.stage4 = []
    for stage_idx in range(len(self.stage_repeat_num)):
      out_c = self.out_channels[2+stage_idx]
      repeat_num = self.stage_repeat_num[stage_idx]
      stage = []
      for i in range(repeat_num):
        if i == 0:
          stage.append(ShuffleBlock(in_c, out_c, downsample=True))
        else:
          stage.append(ShuffleBlock(in_c, in_c, downsample=False))
        in_c = out_c
      if stage_idx == 0:
        self.stage2 = stage
      elif stage_idx == 1:
        self.stage3 = stage
      elif stage_idx == 2:
        self.stage4 = stage
      else:
        print("error")
    # self.stages = nn.Sequential(*self.stages)
    self.stage2 = nn.Sequential(*self.stage2) # 58 * 58 * 116
    self.stage3 = nn.Sequential(*self.stage3) # 26 * 26 * 232
    self.stage4 = nn.Sequential(*self.stage4)
    in_c = self.out_channels[-2]
    out_c = self.out_channels[-1]
    self.conv5 = conv_1x1_bn(in_c, out_c, 1) # 13 * 13 * 1024
    # self.g_avg_pool = nn.AvgPool2d(kernel_size=(int)(input_size/32)) # 如果輸入的是224,則此處為7
    
    # # fc layer
    # self.fc = nn.Linear(out_c, num_classes)
    

  def forward(self, x):
    x = self.conv1(x)
    x = self.maxpool(x)
    out3 = self.stage2(x)
    out4 = self.stage3(out3)
    out5 = self.stage4(out4)
    out5 = self.conv5(out5)
    # x = self.g_avg_pool(x)
    # x = x.view(-1, self.out_channels[-1])
    # x = self.fc(x)
    return out3, out4, out5

def shufflenet2(pretrained, **kwargs):
    """Constructs a darknet-53 model.
    """
    model = ShuffleNet2()
    if pretrained:
        state_dict = torch.load('./shufflenetv2_x1-5666bf0f80.pth')
        # model.load_state_dict(t.load(pretrained)) 
        model.load_state_dict(state_dict, strict=True)
        
    return model


if __name__ == "__main__":
    from torchsummary import summary

    # 需要使用device來指定網路在GPU還是CPU運行
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model = shufflenet2(pretrained=False).to(device);
    summary(model, input_size=(3,416,416))

4.2 yolo本體

from collections import OrderedDict

import torch
import torch.nn as nn
import torch.nn.functional as F

from nets.densenet import _Transition, densenet121, densenet169, densenet201
from nets.ghostnet import ghostnet
from nets.mobilenet_v1 import mobilenet_v1
from nets.mobilenet_v2 import mobilenet_v2
from nets.mobilenet_v3 import mobilenet_v3
from nets.shufflenet import shufflenet2
from nets.shufflenetv2 import shufflenet_v2_x1_0

class MobileNetV1(nn.Module):
    def __init__(self, pretrained = False):
        super(MobileNetV1, self).__init__()
        self.model = mobilenet_v1(pretrained=pretrained)

    def forward(self, x):
        out3 = self.model.stage1(x)
        out4 = self.model.stage2(out3)
        out5 = self.model.stage3(out4)
        return out3, out4, out5

class MobileNetV2(nn.Module):
    def __init__(self, pretrained = False):
        super(MobileNetV2, self).__init__()
        self.model = mobilenet_v2(pretrained=pretrained)

    def forward(self, x):
        out3 = self.model.features[:7](x)
        out4 = self.model.features[7:14](out3)
        out5 = self.model.features[14:18](out4)
        return out3, out4, out5

class MobileNetV3(nn.Module):
    def __init__(self, pretrained = False):
        super(MobileNetV3, self).__init__()
        self.model = mobilenet_v3(pretrained=pretrained)

    def forward(self, x):
        out3 = self.model.features[:7](x)
        out4 = self.model.features[7:13](out3)
        out5 = self.model.features[13:16](out4)
        return out3, out4, out5

class GhostNet(nn.Module):
    def __init__(self, pretrained=True):
        super(GhostNet, self).__init__()
        model = ghostnet()
        if pretrained:
            state_dict = torch.load("model_data/ghostnet_weights.pth")
            model.load_state_dict(state_dict)
        del model.global_pool
        del model.conv_head
        del model.act2
        del model.classifier
        del model.blocks[9]
        self.model = model

    def forward(self, x):
        x = self.model.conv_stem(x)
        x = self.model.bn1(x)
        x = self.model.act1(x)
        feature_maps = []

        for idx, block in enumerate(self.model.blocks):
            x = block(x)
            if idx in [2,4,6,8]:
                feature_maps.append(x)
        return feature_maps[1:]
    
class ShufflenetV2(nn.Module):
    def __init__(self, pretrained = False):
        super(ShufflenetV2, self).__init__()
        # self.model = shufflenet2(pretrained=pretrained)
        self.model = shufflenet_v2_x1_0(pretrained=pretrained)
    def forward(self, x):
        # out3, out4, out5 = self.model(x)
        
        # return out3, out4, out5
        x = self.model.conv1(x)
        x = self.model.maxpool(x)
        out3 = self.model.stage2(x)
        out4 = self.model.stage3(out3)
        out5 = self.model.stage4(out4)
        out5 = self.model.conv5(out5)
        return out3, out4, out5
    

class Densenet(nn.Module):
    def __init__(self, backbone, pretrained=False):
        super(Densenet, self).__init__()
        densenet = {
            "densenet121" : densenet121, 
            "densenet169" : densenet169, 
            "densenet201" : densenet201
        }[backbone]
        model = densenet(pretrained)
        del model.classifier
        self.model = model

    def forward(self, x):
        feature_maps = []
        for block in self.model.features:
            if type(block)==_Transition:
                for _, subblock in enumerate(block):
                    x = subblock(x)
                    if type(subblock)==nn.Conv2d:
                        feature_maps.append(x)
            else:
                x = block(x)
        x = F.relu(x, inplace=True)
        feature_maps.append(x)
        return feature_maps[1:]

def conv2d(filter_in, filter_out, kernel_size, groups=1, stride=1):
    pad = (kernel_size - 1) // 2 if kernel_size else 0
    return nn.Sequential(OrderedDict([
        ("conv", nn.Conv2d(filter_in, filter_out, kernel_size=kernel_size, stride=stride, padding=pad, groups=groups, bias=False)),
        ("bn", nn.BatchNorm2d(filter_out)),
        ("relu", nn.ReLU6(inplace=True)),
    ]))

def conv_dw(filter_in, filter_out, stride = 1):
    return nn.Sequential(
        nn.Conv2d(filter_in, filter_in, 3, stride, 1, groups=filter_in, bias=False),
        nn.BatchNorm2d(filter_in),
        nn.ReLU6(inplace=True),

        nn.Conv2d(filter_in, filter_out, 1, 1, 0, bias=False),
        nn.BatchNorm2d(filter_out),
        nn.ReLU6(inplace=True),
    )

#---------------------------------------------------#
#   SPP結構,利用不同大小的池化核進行池化
#   池化后堆疊
#---------------------------------------------------#
class SpatialPyramidPooling(nn.Module):
    def __init__(self, pool_sizes=[5, 9, 13]):
        super(SpatialPyramidPooling, self).__init__()

        self.maxpools = nn.ModuleList([nn.MaxPool2d(pool_size, 1, pool_size//2) for pool_size in pool_sizes])

    def forward(self, x):
        features = [maxpool(x) for maxpool in self.maxpools[::-1]]
        features = torch.cat(features + [x], dim=1)

        return features

#---------------------------------------------------#
#   卷積 + 上采樣
#---------------------------------------------------#
class Upsample(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(Upsample, self).__init__()

        self.upsample = nn.Sequential(
            conv2d(in_channels, out_channels, 1),
            nn.Upsample(scale_factor=2, mode='nearest')
        )

    def forward(self, x,):
        x = self.upsample(x)
        return x

#---------------------------------------------------#
#   三次卷積塊
#---------------------------------------------------#
def make_three_conv(filters_list, in_filters):
    m = nn.Sequential(
        conv2d(in_filters, filters_list[0], 1),
        conv_dw(filters_list[0], filters_list[1]),
        conv2d(filters_list[1], filters_list[0], 1),
    )
    return m

#---------------------------------------------------#
#   五次卷積塊
#---------------------------------------------------#
def make_five_conv(filters_list, in_filters):
    m = nn.Sequential(
        conv2d(in_filters, filters_list[0], 1),
        conv_dw(filters_list[0], filters_list[1]),
        conv2d(filters_list[1], filters_list[0], 1),
        conv_dw(filters_list[0], filters_list[1]),
        conv2d(filters_list[1], filters_list[0], 1),
    )
    return m

#---------------------------------------------------#
#   最后獲得yolov4的輸出
#---------------------------------------------------#
def yolo_head(filters_list, in_filters):
    m = nn.Sequential(
        conv_dw(in_filters, filters_list[0]),
        
        nn.Conv2d(filters_list[0], filters_list[1], 1),
    )
    return m

    
#---------------------------------------------------#
#   yolo_body
#---------------------------------------------------#
class YoloBody(nn.Module):
    def __init__(self, anchors_mask, num_classes, backbone="mobilenetv2", pretrained=False):
        super(YoloBody, self).__init__()
        #---------------------------------------------------#   
        #   生成mobilnet的主干模型,獲得三個有效特征層,
        #---------------------------------------------------#
        if backbone == "mobilenetv1":
            #---------------------------------------------------#   
            #   52,52,256;26,26,512;13,13,1024
            #---------------------------------------------------#
            self.backbone   = MobileNetV1(pretrained=pretrained)
            in_filters      = [256, 512, 1024]
        elif backbone == "mobilenetv2":
            #---------------------------------------------------#   
            #   52,52,32;26,26,92;13,13,320
            #---------------------------------------------------#
            self.backbone   = MobileNetV2(pretrained=pretrained)
            in_filters      = [32, 96, 320]
        elif backbone == "mobilenetv3":
            #---------------------------------------------------#   
            #   52,52,40;26,26,112;13,13,160
            #---------------------------------------------------#
            self.backbone   = MobileNetV3(pretrained=pretrained)
            in_filters      = [40, 112, 160]
        elif backbone == "ghostnet":
            #---------------------------------------------------#   
            #   52,52,40;26,26,112;13,13,160
            #---------------------------------------------------#
            self.backbone   = GhostNet(pretrained=pretrained)
            in_filters      = [40, 112, 160]
            
        elif backbone == "shufflenet2":
            #---------------------------------------------------#   
            #   58 * 58 * 116; 26 * 26 * 232; 13 * 13 * 1024
            #---------------------------------------------------#
            self.backbone   = ShufflenetV2(pretrained=pretrained)
            
            
            in_filters      = [116, 232, 1024]  
            
            
            
        elif backbone in ["densenet121", "densenet169", "densenet201"]:
            #---------------------------------------------------#   
            #   52,52,256;26,26,512;13,13,1024
            #---------------------------------------------------#
            self.backbone   = Densenet(backbone, pretrained=pretrained)
            in_filters = {
                "densenet121" : [256, 512, 1024], 
                "densenet169" : [256, 640, 1664], 
                "densenet201" : [256, 896, 1920]
            }[backbone]
        else:
            raise ValueError('Unsupported backbone - `{}`, Use mobilenetv1, mobilenetv2, mobilenetv3, ghostnet, densenet121, densenet169, densenet201.'.format(backbone))

        self.conv1           = make_three_conv([512, 1024], in_filters[2])
        self.SPP             = SpatialPyramidPooling()
        self.conv2           = make_three_conv([512, 1024], 2048)

        self.upsample1       = Upsample(512, 256)
        self.conv_for_P4     = conv2d(in_filters[1], 256,1)
        self.make_five_conv1 = make_five_conv([256, 512], 512)

        self.upsample2       = Upsample(256, 128)
        self.conv_for_P3     = conv2d(in_filters[0], 128,1)
        self.make_five_conv2 = make_five_conv([128, 256], 256)

        # 3*(5+num_classes) = 3*(5+20) = 3*(4+1+20)=75
        self.yolo_head3      = yolo_head([256, len(anchors_mask[0]) * (5 + num_classes)], 128)

        self.down_sample1    = conv_dw(128, 256, stride = 2)
        self.make_five_conv3 = make_five_conv([256, 512], 512)

        # 3*(5+num_classes) = 3*(5+20) = 3*(4+1+20)=75
        self.yolo_head2      = yolo_head([512, len(anchors_mask[1]) * (5 + num_classes)], 256)

        self.down_sample2    = conv_dw(256, 512, stride = 2)
        self.make_five_conv4 = make_five_conv([512, 1024], 1024)

        # 3*(5+num_classes)=3*(5+20)=3*(4+1+20)=75
        self.yolo_head1      = yolo_head([1024, len(anchors_mask[2]) * (5 + num_classes)], 512)


    def forward(self, x):
        #  backbone
        x2, x1, x0 = self.backbone(x)

        # 13,13,1024 -> 13,13,512 -> 13,13,1024 -> 13,13,512 -> 13,13,2048 
        P5 = self.conv1(x0)
        P5 = self.SPP(P5)
        # 13,13,2048 -> 13,13,512 -> 13,13,1024 -> 13,13,512
        P5 = self.conv2(P5)

        # 13,13,512 -> 13,13,256 -> 26,26,256
        P5_upsample = self.upsample1(P5)
        # 26,26,512 -> 26,26,256
        P4 = self.conv_for_P4(x1)
        # 26,26,256 + 26,26,256 -> 26,26,512
        P4 = torch.cat([P4,P5_upsample],axis=1)
        # 26,26,512 -> 26,26,256 -> 26,26,512 -> 26,26,256 -> 26,26,512 -> 26,26,256
        P4 = self.make_five_conv1(P4)

        # 26,26,256 -> 26,26,128 -> 52,52,128
        P4_upsample = self.upsample2(P4)
        # 52,52,256 -> 52,52,128
        P3 = self.conv_for_P3(x2)
        # 52,52,128 + 52,52,128 -> 52,52,256
        P3 = torch.cat([P3,P4_upsample],axis=1)
        # 52,52,256 -> 52,52,128 -> 52,52,256 -> 52,52,128 -> 52,52,256 -> 52,52,128
        P3 = self.make_five_conv2(P3)

        # 52,52,128 -> 26,26,256
        P3_downsample = self.down_sample1(P3)
        # 26,26,256 + 26,26,256 -> 26,26,512
        P4 = torch.cat([P3_downsample,P4],axis=1)
        # 26,26,512 -> 26,26,256 -> 26,26,512 -> 26,26,256 -> 26,26,512 -> 26,26,256
        P4 = self.make_five_conv3(P4)

        # 26,26,256 -> 13,13,512
        P4_downsample = self.down_sample2(P4)
        # 13,13,512 + 13,13,512 -> 13,13,1024
        P5 = torch.cat([P4_downsample,P5],axis=1)
        # 13,13,1024 -> 13,13,512 -> 13,13,1024 -> 13,13,512 -> 13,13,1024 -> 13,13,512
        P5 = self.make_five_conv4(P5)

        #---------------------------------------------------#
        #   第三個特征層
        #   y3=(batch_size,75,52,52)
        #---------------------------------------------------#
        out2 = self.yolo_head3(P3)
        #---------------------------------------------------#
        #   第二個特征層
        #   y2=(batch_size,75,26,26)
        #---------------------------------------------------#
        out1 = self.yolo_head2(P4)
        #---------------------------------------------------#
        #   第一個特征層
        #   y1=(batch_size,75,13,13)
        #---------------------------------------------------#
        out0 = self.yolo_head1(P5)

        return out0, out1, out2

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/413648.html

標籤:其他

上一篇:Python OpenCv學習基礎知識二

下一篇:Auto-Encoders原理

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more