[Введение GiantPandaCV] Эта статья в основном направлена на реализацию кода сетевой структуры версии YOLOV5-Pytorch, упрощение понимания кода и упрощение файла конфигурации, а также дальнейшее объединение некоторых из четырех сетевых структур YOLOV5, В этом процессе сеть V5 имеет более глубокое понимание понимания. Наконец, я надеюсь, что читатели, прочитавшие эту статью, смогут что-то почерпнуть, и я надеюсь пообщаться с вами по поводу оптимизации некоторых методов написания в коде.
1. Полный сетевой код
-
Идеи реализации, общая структура кода в v5 была сохранена, потому что эта часть кода относительно проста для понимания, общий код выглядит относительно просто, в основном построение общей структуры сети путем разбора файла yaml для некоторых разработчиков не очень дружелюбный.
-
некоторые переменные в сети
c1:输入通道 c2:输出通道 k:卷积核大小 s:步长 p:padding g:分组 act;激活函数 e:扩展倍数 gw:网络宽度因子 gd:网络深度因子 n:模块重复次数 nc:类别数
-
код магистральной сети
CSPDarknet53
-
1
import torch import torch.nn as nn def autopad(k, p=None): # kernel, padding # Pad to 'same' if p is None: p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad return p class CBL(nn.Module): def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True, e=1.0): super(CBL, self).__init__() c1 = round(c1 * e) c2 = round(c2 * e) self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) self.bn = nn.BatchNorm2d(c2) self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) def forward(self, x): return self.act(self.bn(self.conv(x))) class Focus(nn.Module): def __init__(self, c1, c2, k=3, s=1, p=1, g=1, act=True, e=1.0): super(Focus, self).__init__() c2 = round(c2 * e) self.conv = CBL(c1 * 4, c2, k, s, p, g, act) def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2) flatten_channel = torch.cat([x[..., 0::2, 0::2], x[..., 1::2, 0::2], x[..., 0::2, 1::2], x[..., 1::2, 1::2]], dim=1) return self.conv(flatten_channel) class SPP(nn.Module): def __init__(self, c1, c2, k=(5, 9, 13), e=1.0): super(SPP, self).__init__() c1 = round(c1 * e) c2 = round(c2 * e) c_ = c1 // 2 self.cbl_before = CBL(c1, c_, 1, 1) self.max_pool = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k]) self.cbl_after = CBL(c_ * 4, c2, 1, 1) def forward(self, x): x = self.cbl_before(x) x_cat = torch.cat([x] + [m(x) for m in self.max_pool], 1) return self.cbl_after(x_cat) class ResUnit_n(nn.Module): def __init__(self, c1, c2, n): super(ResUnit_n, self).__init__() self.shortcut = c1 == c2 res_unit = nn.Sequential( CBL(c1, c1, k=1, s=1, p=0), CBL(c1, c2, k=3, s=1, p=1) ) self.res_unit_n = nn.Sequential(*[res_unit for _ in range(n)]) def forward(self, x): return x + self.res_unit_n(x) if self.shortcut else self.res_unit_n(x) class CSP1_n(nn.Module): def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True, n=1, e=None): super(CSP1_n, self).__init__() c1 = round(c1 * e[1]) c2 = round(c2 * e[1]) n = round(n * e[0]) c_ = c2 // 2 self.up = nn.Sequential( CBL(c1, c_, k, s, autopad(k, p), g, act), ResUnit_n(c_, c_, n), # nn.Conv2d(c_, c_, 1, 1, 0, bias=False) 这里最新yolov5结构中去掉了,与网上的结构图稍微有些区别 ) self.bottom = nn.Conv2d(c1, c_, 1, 1, 0) self.tie = nn.Sequential( nn.BatchNorm2d(c_ * 2), nn.LeakyReLU(), nn.Conv2d(c_ * 2, c2, 1, 1, 0, bias=False) ) def forward(self, x): total = torch.cat([self.up(x), self.bottom(x)], dim=1) out = self.tie(total) return out class CSPDarkNet(nn.Module): def __init__(self, gd=0.33, gw=0.5): super(CSPDarkNet, self).__init__() self.truck_big = nn.Sequential( Focus(3, 64, e=gw), CBL(64, 128, k=3, s=2, p=1, e=gw), CSP1_n(128, 128, n=3, e=[gd, gw]), CBL(128, 256, k=3, s=2, p=1, e=gw), CSP1_n(256, 256, n=9, e=[gd, gw]), ) self.truck_middle = nn.Sequential( CBL(256, 512, k=3, s=2, p=1, e=gw), CSP1_n(512, 512, n=9, e=[gd, gw]), ) self.truck_small = nn.Sequential( CBL(512, 1024, k=3, s=2, p=1, e=gw), SPP(1024, 1024, e=gw) ) def forward(self, x): h_big = self.truck_big(x) # torch.Size([2, 128, 76, 76]) h_middle = self.truck_middle(h_big) h_small = self.truck_small(h_middle) return h_big, h_middle, h_small def darknet53(gd, gw, pretrained, **kwargs): model = CSPDarkNet(gd, gw) if pretrained: if isinstance(pretrained, str): model.load_state_dict(torch.load(pretrained)) else: raise Exception(f"darknet request a pretrained path. got[{pretrained}]") return model
-
Построение общей сети
import torch import torch.nn as nn from cspdarknet53v5 import darknet53 def autopad(k, p=None): # kernel, padding # Pad to 'same' if p is None: p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad return p class UpSample(nn.Module): def __init__(self): super(UpSample, self).__init__() self.up_sample = nn.Upsample(scale_factor=2, mode='nearest') def forward(self, x): return self.up_sample(x) class CBL(nn.Module): def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True, e=1.0): super(CBL, self).__init__() c1 = round(c1 * e) c2 = round(c2 * e) self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) self.bn = nn.BatchNorm2d(c2) self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) def forward(self, x): return self.act(self.bn(self.conv(x))) class ResUnit_n(nn.Module): def __init__(self, c1, c2, n): super(ResUnit_n, self).__init__() self.shortcut = c1 == c2 res_unit = nn.Sequential( CBL(c1, c1, k=1, s=1, p=0), CBL(c1, c2, k=3, s=1, p=1) ) self.res_unit_n = nn.Sequential(*[res_unit for _ in range(n)]) def forward(self, x): return x + self.res_unit_n(x) if self.shortcut else self.res_unit_n(x) class CSP1_n(nn.Module): def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True, n=1, e=None): super(CSP1_n, self).__init__() c1 = round(c1 * e[1]) c2 = round(c2 * e[1]) n = round(n * e[0]) c_ = c2 // 2 self.up = nn.Sequential( CBL(c1, c_, k, s, autopad(k, p), g, act), ResUnit_n(c_, c_, n), # nn.Conv2d(c_, c_, 1, 1, 0, bias=False) 这里最新yolov5结构中去掉了,与网上的结构图稍微有些区别 ) self.bottom = nn.Conv2d(c1, c_, 1, 1, 0) self.tie = nn.Sequential( nn.BatchNorm2d(c_ * 2), nn.LeakyReLU(), nn.Conv2d(c_ * 2, c2, 1, 1, 0, bias=False) ) def forward(self, x): total = torch.cat([self.up(x), self.bottom(x)], dim=1) out = self.tie(total) return out class CSP2_n(nn.Module): def __init__(self, c1, c2, e=0.5, n=1): super(CSP2_n, self).__init__() c_ = int(c1 * e) cbl_2 = nn.Sequential( CBL(c1, c_, 1, 1, 0), CBL(c_, c_, 1, 1, 0), ) self.cbl_2n = nn.Sequential(*[cbl_2 for _ in range(n)]) self.conv_up = nn.Conv2d(c_, c_, 1, 1, 0) self.conv_bottom = nn.Conv2d(c1, c_, 1, 1, 0) self.tie = nn.Sequential( nn.BatchNorm2d(c_ * 2), nn.LeakyReLU(), nn.Conv2d(c_ * 2, c2, 1, 1, 0) ) def forward(self, x): up = self.conv_up(self.cbl_2n(x)) total = torch.cat([up, self.conv_bottom(x)], dim=1) out = self.tie(total) return out class yolov5(nn.Module): def __init__(self, nc=80, gd=0.33, gw=0.5): super(yolov5, self).__init__() # ------------------------------Backbone-------------------------------- self.backbone = darknet53(gd, gw, None) # ------------------------------Neck------------------------------------ self.neck_small = nn.Sequential( CSP1_n(1024, 1024, n=3, e=[gd, gw]), CBL(1024, 512, 1, 1, 0, e=gw) ) self.up_middle = nn.Sequential( UpSample() ) self.out_set_middle = nn.Sequential( CSP1_n(1024, 512, n=3, e=[gd, gw]), CBL(512, 256, 1, 1, 0, e=gw), ) self.up_big = nn.Sequential( UpSample() ) self.out_set_tie_big = nn.Sequential( CSP1_n(512, 256, n=3, e=[gd, gw]) ) self.pan_middle = nn.Sequential( CBL(256, 256, 3, 2, 1, e=gw) ) self.out_set_tie_middle = nn.Sequential( CSP1_n(512, 512, n=3, e=[gd, gw]) ) self.pan_small = nn.Sequential( CBL(512, 512, 3, 2, 1, e=gw) ) self.out_set_tie_small = nn.Sequential( CSP1_n(1024, 1024, n=3, e=[gd, gw]) ) # ------------------------------Prediction-------------------------------- # prediction big_ = round(256 * gw) middle = round(512 * gw) small_ = round(1024 * gw) self.out_big = nn.Sequential( nn.Conv2d(big_, 3 * (5 + nc), 1, 1, 0) ) self.out_middle = nn.Sequential( nn.Conv2d(middle, 3 * (5 + nc), 1, 1, 0) ) self.out_small = nn.Sequential( nn.Conv2d(small_, 3 * (5 + nc), 1, 1, 0) ) def forward(self, x): h_big, h_middle, h_small = self.backbone(x) neck_small = self.neck_small(h_small) # ----------------------------up sample 38*38------------------------------- up_middle = self.up_middle(neck_small) middle_cat = torch.cat([up_middle, h_middle], dim=1) out_set_middle = self.out_set_middle(middle_cat) # ----------------------------up sample 76*76------------------------------- up_big = self.up_big(out_set_middle) # torch.Size([2, 128, 76, 76]) big_cat = torch.cat([up_big, h_big], dim=1) out_set_tie_big = self.out_set_tie_big(big_cat) # ----------------------------PAN 36*36------------------------------------- neck_tie_middle = torch.cat([self.pan_middle(out_set_tie_big), out_set_middle], dim=1) up_middle = self.out_set_tie_middle(neck_tie_middle) # ----------------------------PAN 18*18------------------------------------- neck_tie_small = torch.cat([self.pan_small(up_middle), neck_small], dim=1) out_set_small = self.out_set_tie_small(neck_tie_small) # ----------------------------prediction------------------------------------- out_small = self.out_small(out_set_small) out_middle = self.out_middle(up_middle) out_big = self.out_big(out_set_tie_big) return out_small, out_middle, out_big if __name__ == '__main__': # 配置文件的写法 config = { # gd gw 'yolov5s': [0.33, 0.50], 'yolov5m': [0.67, 0.75], 'yolov5l': [1.00, 1.00], 'yolov5x': [1.33, 1.25] } # 修改一次文件名字 net_size = config['yolov5x'] net = yolov5(nc=80, gd=net_size[0], gw=net_size[1]) print(net) a = torch.randn(2, 3, 416, 416) y = net(a) print(y[0].shape, y[1].shape, y[2].shape)
Во-вторых, анализ сетевой структуры
-
Остаточный блок ResUnit_n
class ResUnit_n(nn.Module): def __init__(self, c1, c2, n): super(ResUnit_n, self).__init__() self.shortcut = c1 == c2 res_unit = nn.Sequential( CBL(c1, c1, k=1, s=1, p=0), CBL(c1, c2, k=3, s=1, p=1) ) self.res_unit_n = nn.Sequential(*[res_unit for _ in range(n)]) def forward(self, x): return x + self.res_unit_n(x) if self.shortcut else self.res_unit_n(x)
-
Структура CSP1_x
Идеи построения: код CSP1_n оптимизирован, и CSP рассматривается как лежащее животное, с головой слева и хвостом справа, вверху место ближе к небу, внизу ближе к земле, а галстук хвост животного
class CSP1_n(nn.Module): def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True, n=1, e=None): super(CSP1_n, self).__init__() c1 = round(c1 * e[1]) c2 = round(c2 * e[1]) n = round(n * e[0]) c_ = c2 // 2 self.up = nn.Sequential( CBL(c1, c_, k, s, autopad(k, p), g, act), ResUnit_n(c_, c_, n), # nn.Conv2d(c_, c_, 1, 1, 0, bias=False) 这里最新yolov5结构中去掉了,与网上的结构图稍微有些区别 ) self.bottom = nn.Conv2d(c1, c_, 1, 1, 0) self.tie = nn.Sequential( nn.BatchNorm2d(c_ * 2), nn.LeakyReLU(), nn.Conv2d(c_ * 2, c2, 1, 1, 0, bias=False) ) def forward(self, x): total = torch.cat([self.up(x), self.bottom(x)], dim=1) out = self.tie(total) return out
-
Строительство магистральной сети CSPDarknet
class CSPDarkNet(nn.Module): def __init__(self, gd=0.33, gw=0.5): super(CSPDarkNet, self).__init__() self.truck_big = nn.Sequential( Focus(3, 64, e=gw), CBL(64, 128, k=3, s=2, p=1, e=gw), CSP1_n(128, 128, n=3, e=[gd, gw]), CBL(128, 256, k=3, s=2, p=1, e=gw), CSP1_n(256, 256, n=9, e=[gd, gw]), ) self.truck_middle = nn.Sequential( CBL(256, 512, k=3, s=2, p=1, e=gw), CSP1_n(512, 512, n=9, e=[gd, gw]), ) self.truck_small = nn.Sequential( CBL(512, 1024, k=3, s=2, p=1, e=gw), SPP(1024, 1024, e=gw) ) def forward(self, x): h_big = self.truck_big(x) h_middle = self.truck_middle(h_big) h_small = self.truck_small(h_middle) return h_big, h_middle, h_small
-
Общая конструкция сети
class yolov5(nn.Module): def __init__(self, nc=80, gd=0.33, gw=0.5): super(yolov5, self).__init__() # ------------------------------Backbone------------------------------------ self.backbone = darknet53(gd, gw, None) # ------------------------------Neck------------------------------------ self.neck_small = nn.Sequential( CSP1_n(1024, 1024, n=3, e=[gd, gw]), CBL(1024, 512, 1, 1, 0, e=gw) ) # FPN:2次上采样 自顶而下 完成语义信息增强 self.up_middle = nn.Sequential( UpSample() ) self.out_set_middle = nn.Sequential( CSP1_n(1024, 512, n=3, e=[gd, gw]), CBL(512, 256, 1, 1, 0, e=gw), ) self.up_big = nn.Sequential( UpSample() ) self.out_set_tie_big = nn.Sequential( CSP1_n(512, 256, n=3, e=[gd, gw]) ) # PAN:2次下采样 自底而上 完成位置信息增强 self.pan_middle = nn.Sequential( CBL(256, 256, 3, 2, 1, e=gw) ) self.out_set_tie_middle = nn.Sequential( CSP1_n(512, 512, n=3, e=[gd, gw]) ) self.pan_small = nn.Sequential( CBL(512, 512, 3, 2, 1, e=gw) ) self.out_set_tie_small = nn.Sequential( # CSP2_n(512, 512) CSP1_n(1024, 1024, n=3, e=[gd, gw]) ) # ------------------------------Prediction------------------------------------ # prediction big_ = round(256 * gw) middle = round(512 * gw) small_ = round(1024 * gw) self.out_big = nn.Sequential( nn.Conv2d(big_, 3 * (5 + nc), 1, 1, 0) ) self.out_middle = nn.Sequential( nn.Conv2d(middle, 3 * (5 + nc), 1, 1, 0) ) self.out_small = nn.Sequential( nn.Conv2d(small_, 3 * (5 + nc), 1, 1, 0) ) def forward(self, x): h_big, h_middle, h_small = self.backbone(x) neck_small = self.neck_small(h_small) # ----------------------------up sample 38*38-------------------------------- up_middle = self.up_middle(neck_small) middle_cat = torch.cat([up_middle, h_middle], dim=1) out_set_middle = self.out_set_middle(middle_cat) # ----------------------------up sample 76*76-------------------------------- up_big = self.up_big(out_set_middle) # torch.Size([2, 128, 76, 76]) big_cat = torch.cat([up_big, h_big], dim=1) out_set_tie_big = self.out_set_tie_big(big_cat) # ----------------------------PAN 36*36------------------------------------- neck_tie_middle = torch.cat([self.pan_middle(out_set_tie_big), out_set_middle], dim=1) up_middle = self.out_set_tie_middle(neck_tie_middle) # ----------------------------PAN 18*18------------------------------------- neck_tie_small = torch.cat([self.pan_small(up_middle), neck_small], dim=1) out_set_small = self.out_set_tie_small(neck_tie_small) # ----------------------------prediction------------------------------------- out_small = self.out_small(out_set_small) out_middle = self.out_middle(up_middle) out_big = self.out_big(out_set_tie_big) return out_small, out_middle, out_big
-
Четыре размера файлов конфигурации записываются в словарь конфигурации, которые являются параметрами конфигурации сетевой модели.Другие параметры не помещаются в файл конфигурации, и категория также может быть помещена в файл конфигурации. В приведенном выше сетевом коде параметр ширины является переменной
e
а затем передается в каждую сеть.config = { # gd gw 'yolov5s': [0.33, 0.50], 'yolov5m': [0.67, 0.75], 'yolov5l': [1.00, 1.00], 'yolov5x': [1.33, 1.25] } # 修改一次文件名字 net_size = config['yolov5x'] net = yolov5(nc=80, gd=net_size[0], gw=net_size[1])
Исходный код v5 записывает часть Head в v3 в класс Detect.Основная причина в том, что в v5 используются некоторые приемы обучения.В Detect есть обучение и две части.Исходный код v5 представляет собой сравнение для начинающих.Сначала сложно прежде всего, метод записи сети требует относительно высокой способности кодирования. Тем не менее, этот метод конфигурационного файла yaml для настройки сети использовался во многих компаниях, это может быть способом написания кода в будущих проектах, и этот способ написания еще необходимо освоить.
3. Резюме
-
Мое личное мнение состоит в том, что дизайн такого рода сети и способ написания кода должны быть творческими и образными, и код должен быть написан как элегантное чувство в романах о боевых искусствах. (В Интернете есть много диаграмм структуры сети. Я смоделировал диаграмму структуры Jiang Dabai и скорректировал ее на основе диаграммы структуры и последней версии кода v5).
-
Появился в последней сетевой структуре v5
Transformer
Структура, есть своего рода ритм, который инженерия в области CV собирается изменить, вы можете узнать об этом.
Добро пожаловать в GiantPandaCV, где вы увидите эксклюзивный обмен глубокими знаниями, настаиваете на оригинальности и делитесь свежими знаниями, которые мы изучаем каждый день. ( • ̀ω•́ )✧
Если у вас есть вопросы по статье или вы хотите присоединиться к группе обмена, добавьте BBuf WeChat:
QR код
Чтобы облегчить читателям получение информации и автору нашей официальной учетной записи, чтобы выпустить некоторые обновления проекта Github, мы создали группу QQ, QR-код выглядит следующим образом, вы можете присоединиться, если вы заинтересованы.
Публичный аккаунт QQ exchange group
В этой статье используетсяПомощник по синхронизации статейСинхронизировать