Add `yolov5s-ghost.yaml` (#4412)
Browse files* Add yolov5s-ghost.yaml
* Finish C3Ghost
* Add C3Ghost to list
* Add C3Ghost to number of repeats if statement
* Fixes
* Cleanup
- models/common.py +36 -0
- models/experimental.py +0 -28
- models/hub/yolov5s-ghost.yaml +46 -0
- models/yolo.py +2 -2
models/common.py
CHANGED
@@ -149,6 +149,14 @@ class C3SPP(C3):
|
|
149 |
self.m = SPP(c_, c_, k)
|
150 |
|
151 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
class SPP(nn.Module):
|
153 |
# Spatial pyramid pooling layer used in YOLOv3-SPP
|
154 |
def __init__(self, c1, c2, k=(5, 9, 13)):
|
@@ -177,6 +185,34 @@ class Focus(nn.Module):
|
|
177 |
# return self.conv(self.contract(x))
|
178 |
|
179 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
class Contract(nn.Module):
|
181 |
# Contract width-height into channels, i.e. x(1,64,80,80) to x(1,256,40,40)
|
182 |
def __init__(self, gain=2):
|
|
|
149 |
self.m = SPP(c_, c_, k)
|
150 |
|
151 |
|
152 |
+
class C3Ghost(C3):
|
153 |
+
# C3 module with GhostBottleneck()
|
154 |
+
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
|
155 |
+
super().__init__(c1, c2, n, shortcut, g, e)
|
156 |
+
c_ = int(c2 * e) # hidden channels
|
157 |
+
self.m = nn.Sequential(*[GhostBottleneck(c_, c_) for _ in range(n)])
|
158 |
+
|
159 |
+
|
160 |
class SPP(nn.Module):
|
161 |
# Spatial pyramid pooling layer used in YOLOv3-SPP
|
162 |
def __init__(self, c1, c2, k=(5, 9, 13)):
|
|
|
185 |
# return self.conv(self.contract(x))
|
186 |
|
187 |
|
188 |
+
class GhostConv(nn.Module):
|
189 |
+
# Ghost Convolution https://github.com/huawei-noah/ghostnet
|
190 |
+
def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out, kernel, stride, groups
|
191 |
+
super().__init__()
|
192 |
+
c_ = c2 // 2 # hidden channels
|
193 |
+
self.cv1 = Conv(c1, c_, k, s, None, g, act)
|
194 |
+
self.cv2 = Conv(c_, c_, 5, 1, None, c_, act)
|
195 |
+
|
196 |
+
def forward(self, x):
|
197 |
+
y = self.cv1(x)
|
198 |
+
return torch.cat([y, self.cv2(y)], 1)
|
199 |
+
|
200 |
+
|
201 |
+
class GhostBottleneck(nn.Module):
|
202 |
+
# Ghost Bottleneck https://github.com/huawei-noah/ghostnet
|
203 |
+
def __init__(self, c1, c2, k=3, s=1): # ch_in, ch_out, kernel, stride
|
204 |
+
super().__init__()
|
205 |
+
c_ = c2 // 2
|
206 |
+
self.conv = nn.Sequential(GhostConv(c1, c_, 1, 1), # pw
|
207 |
+
DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(), # dw
|
208 |
+
GhostConv(c_, c2, 1, 1, act=False)) # pw-linear
|
209 |
+
self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False),
|
210 |
+
Conv(c1, c2, 1, 1, act=False)) if s == 2 else nn.Identity()
|
211 |
+
|
212 |
+
def forward(self, x):
|
213 |
+
return self.conv(x) + self.shortcut(x)
|
214 |
+
|
215 |
+
|
216 |
class Contract(nn.Module):
|
217 |
# Contract width-height into channels, i.e. x(1,64,80,80) to x(1,256,40,40)
|
218 |
def __init__(self, gain=2):
|
models/experimental.py
CHANGED
@@ -43,34 +43,6 @@ class Sum(nn.Module):
|
|
43 |
return y
|
44 |
|
45 |
|
46 |
-
class GhostConv(nn.Module):
|
47 |
-
# Ghost Convolution https://github.com/huawei-noah/ghostnet
|
48 |
-
def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out, kernel, stride, groups
|
49 |
-
super().__init__()
|
50 |
-
c_ = c2 // 2 # hidden channels
|
51 |
-
self.cv1 = Conv(c1, c_, k, s, None, g, act)
|
52 |
-
self.cv2 = Conv(c_, c_, 5, 1, None, c_, act)
|
53 |
-
|
54 |
-
def forward(self, x):
|
55 |
-
y = self.cv1(x)
|
56 |
-
return torch.cat([y, self.cv2(y)], 1)
|
57 |
-
|
58 |
-
|
59 |
-
class GhostBottleneck(nn.Module):
|
60 |
-
# Ghost Bottleneck https://github.com/huawei-noah/ghostnet
|
61 |
-
def __init__(self, c1, c2, k=3, s=1): # ch_in, ch_out, kernel, stride
|
62 |
-
super().__init__()
|
63 |
-
c_ = c2 // 2
|
64 |
-
self.conv = nn.Sequential(GhostConv(c1, c_, 1, 1), # pw
|
65 |
-
DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(), # dw
|
66 |
-
GhostConv(c_, c2, 1, 1, act=False)) # pw-linear
|
67 |
-
self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False),
|
68 |
-
Conv(c1, c2, 1, 1, act=False)) if s == 2 else nn.Identity()
|
69 |
-
|
70 |
-
def forward(self, x):
|
71 |
-
return self.conv(x) + self.shortcut(x)
|
72 |
-
|
73 |
-
|
74 |
class MixConv2d(nn.Module):
|
75 |
# Mixed Depth-wise Conv https://arxiv.org/abs/1907.09595
|
76 |
def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True):
|
|
|
43 |
return y
|
44 |
|
45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
class MixConv2d(nn.Module):
|
47 |
# Mixed Depth-wise Conv https://arxiv.org/abs/1907.09595
|
48 |
def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True):
|
models/hub/yolov5s-ghost.yaml
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Parameters
|
2 |
+
nc: 80 # number of classes
|
3 |
+
depth_multiple: 0.33 # model depth multiple
|
4 |
+
width_multiple: 0.50 # layer channel multiple
|
5 |
+
anchors:
|
6 |
+
- [10,13, 16,30, 33,23] # P3/8
|
7 |
+
- [30,61, 62,45, 59,119] # P4/16
|
8 |
+
- [116,90, 156,198, 373,326] # P5/32
|
9 |
+
|
10 |
+
# YOLOv5 backbone
|
11 |
+
backbone:
|
12 |
+
# [from, number, module, args]
|
13 |
+
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
14 |
+
[-1, 1, GhostConv, [128, 3, 2]], # 1-P2/4
|
15 |
+
[-1, 3, C3Ghost, [128]],
|
16 |
+
[-1, 1, GhostConv, [256, 3, 2]], # 3-P3/8
|
17 |
+
[-1, 9, C3Ghost, [256]],
|
18 |
+
[-1, 1, GhostConv, [512, 3, 2]], # 5-P4/16
|
19 |
+
[-1, 9, C3Ghost, [512]],
|
20 |
+
[-1, 1, GhostConv, [1024, 3, 2]], # 7-P5/32
|
21 |
+
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
22 |
+
[-1, 3, C3Ghost, [1024, False]], # 9
|
23 |
+
]
|
24 |
+
|
25 |
+
# YOLOv5 head
|
26 |
+
head:
|
27 |
+
[[-1, 1, GhostConv, [512, 1, 1]],
|
28 |
+
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
29 |
+
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
30 |
+
[-1, 3, C3Ghost, [512, False]], # 13
|
31 |
+
|
32 |
+
[-1, 1, GhostConv, [256, 1, 1]],
|
33 |
+
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
34 |
+
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
35 |
+
[-1, 3, C3Ghost, [256, False]], # 17 (P3/8-small)
|
36 |
+
|
37 |
+
[-1, 1, GhostConv, [256, 3, 2]],
|
38 |
+
[[-1, 14], 1, Concat, [1]], # cat head P4
|
39 |
+
[-1, 3, C3Ghost, [512, False]], # 20 (P4/16-medium)
|
40 |
+
|
41 |
+
[-1, 1, GhostConv, [512, 3, 2]],
|
42 |
+
[[-1, 10], 1, Concat, [1]], # cat head P5
|
43 |
+
[-1, 3, C3Ghost, [1024, False]], # 23 (P5/32-large)
|
44 |
+
|
45 |
+
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
46 |
+
]
|
models/yolo.py
CHANGED
@@ -236,13 +236,13 @@ def parse_model(d, ch): # model_dict, input_channels(3)
|
|
236 |
|
237 |
n = n_ = max(round(n * gd), 1) if n > 1 else n # depth gain
|
238 |
if m in [Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP,
|
239 |
-
C3, C3TR, C3SPP]:
|
240 |
c1, c2 = ch[f], args[0]
|
241 |
if c2 != no: # if not output
|
242 |
c2 = make_divisible(c2 * gw, 8)
|
243 |
|
244 |
args = [c1, c2, *args[1:]]
|
245 |
-
if m in [BottleneckCSP, C3, C3TR]:
|
246 |
args.insert(2, n) # number of repeats
|
247 |
n = 1
|
248 |
elif m is nn.BatchNorm2d:
|
|
|
236 |
|
237 |
n = n_ = max(round(n * gd), 1) if n > 1 else n # depth gain
|
238 |
if m in [Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP,
|
239 |
+
C3, C3TR, C3SPP, C3Ghost]:
|
240 |
c1, c2 = ch[f], args[0]
|
241 |
if c2 != no: # if not output
|
242 |
c2 = make_divisible(c2 * gw, 8)
|
243 |
|
244 |
args = [c1, c2, *args[1:]]
|
245 |
+
if m in [BottleneckCSP, C3, C3TR, C3Ghost]:
|
246 |
args.insert(2, n) # number of repeats
|
247 |
n = 1
|
248 |
elif m is nn.BatchNorm2d:
|