model.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. def Hswish(x,inplace=True):
  5. return x * F.relu6(x + 3., inplace=inplace) / 6.
  6. def Hsigmoid(x,inplace=True):
  7. return F.relu6(x + 3., inplace=inplace) / 6.
  8. # Squeeze-And-Excite模块
  9. class SEModule(nn.Module):
  10. def __init__(self, channel, reduction=4):
  11. super(SEModule, self).__init__()
  12. self.avg_pool = nn.AdaptiveAvgPool2d(1)
  13. self.se = nn.Sequential(
  14. nn.Linear(channel, channel // reduction, bias=False),
  15. nn.ReLU(inplace=True),
  16. nn.Linear(channel // reduction, channel, bias=False),
  17. )
  18. def forward(self, x):
  19. b, c, _, _ = x.size()
  20. y=self.avg_pool(x).view(b, c)
  21. y=self.se(y)
  22. y = Hsigmoid(y).view(b, c, 1, 1)
  23. return x * y.expand_as(x)
  24. class Bottleneck(nn.Module):
  25. def __init__(self,in_channels,out_channels,kernel_size,exp_channels,stride,se='True',nl='HS'):
  26. super(Bottleneck, self).__init__()
  27. padding = (kernel_size - 1) // 2
  28. if nl == 'RE':
  29. self.nlin_layer = F.relu6
  30. elif nl == 'HS':
  31. self.nlin_layer = Hswish
  32. self.stride=stride
  33. if se:
  34. self.se=SEModule(exp_channels)
  35. else:
  36. self.se=None
  37. self.conv1=nn.Conv2d(in_channels,exp_channels,kernel_size=1,stride=1,padding=0,bias=False)
  38. self.bn1 = nn.BatchNorm2d(exp_channels)
  39. self.conv2=nn.Conv2d(exp_channels,exp_channels,kernel_size=kernel_size,stride=stride,
  40. padding=padding,groups=exp_channels,bias=False)
  41. self.bn2=nn.BatchNorm2d(exp_channels)
  42. self.conv3=nn.Conv2d(exp_channels,out_channels,kernel_size=1,stride=1,padding=0,bias=False)
  43. self.bn3=nn.BatchNorm2d(out_channels)
  44. # 先初始化一个空序列,之后改造其成为残差链接
  45. self.shortcut = nn.Sequential()
  46. # 只有步长为1且输入输出通道不相同时才采用跳跃连接(想一下跳跃链接的过程,输入输出通道相同这个跳跃连接就没意义了)
  47. if stride == 1 and in_channels != out_channels:
  48. self.shortcut = nn.Sequential(
  49. # 下面的操作卷积不改变尺寸和通道数
  50. nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False),
  51. nn.BatchNorm2d(out_channels)
  52. )
  53. def forward(self,x):
  54. out=self.nlin_layer(self.bn1(self.conv1(x)))
  55. if self.se is not None:
  56. out=self.bn2(self.conv2(out))
  57. out=self.nlin_layer(self.se(out))
  58. else:
  59. out = self.nlin_layer(self.bn2(self.conv2(out)))
  60. out = self.bn3(self.conv3(out))
  61. out = out + self.shortcut(x) if self.stride == 1 else out
  62. return out
  63. class MobileNetV3_large(nn.Module):
  64. # (out_channels,kernel_size,exp_channels,stride,se,nl)
  65. cfg=[
  66. (16,3,16,1,False,'RE'),
  67. (24,3,64,2,False,'RE'),
  68. (24,3,72,1,False,'RE'),
  69. (40,5,72,2,True,'RE'),
  70. (40,5,120,1,True,'RE'),
  71. (40,5,120,1,True,'RE'),
  72. (80,3,240,2,False,'HS'),
  73. (80,3,200,1,False,'HS'),
  74. (80,3,184,1,False,'HS'),
  75. (80,3,184,1,False,'HS'),
  76. (112,3,480,1,True,'HS'),
  77. (112,3,672,1,True,'HS'),
  78. (160,5,672,2,True,'HS'),
  79. (160,5,960,1,True,'HS'),
  80. (160,5,960,1,True,'HS')
  81. ]
  82. def __init__(self,num_classes=17):
  83. super(MobileNetV3_large,self).__init__()
  84. self.conv1=nn.Conv2d(3,16,3,2,padding=1,bias=False)
  85. self.bn1=nn.BatchNorm2d(16)
  86. # 根据cfg数组自动生成所有的Bottleneck层
  87. self.layers = self._make_layers(in_channels=16)
  88. self.conv2=nn.Conv2d(160,960,1,stride=1,bias=False)
  89. self.bn2=nn.BatchNorm2d(960)
  90. # 卷积后不跟BN,就应该把bias设置为True
  91. self.conv3=nn.Conv2d(960,1280,1,1,padding=0,bias=True)
  92. self.conv4=nn.Conv2d(1280,num_classes,1,stride=1,padding=0,bias=True)
  93. def _make_layers(self,in_channels):
  94. layers=[]
  95. for out_channels,kernel_size,exp_channels,stride,se,nl in self.cfg:
  96. layers.append(
  97. Bottleneck(in_channels,out_channels,kernel_size,exp_channels,stride,se,nl)
  98. )
  99. in_channels=out_channels
  100. return nn.Sequential(*layers)
  101. def forward(self,x):
  102. out=Hswish(self.bn1(self.conv1(x)))
  103. out=self.layers(out)
  104. out=Hswish(self.bn2(self.conv2(out)))
  105. out=F.avg_pool2d(out,7)
  106. out=Hswish(self.conv3(out))
  107. out=self.conv4(out)
  108. # 因为原论文中最后一层是卷积层来实现全连接的效果,维度是四维的,后两维是1,在计算损失函数的时候要求二维,因此在这里需要做一个resize
  109. a,b=out.size(0),out.size(1)
  110. out=out.view(a,b)
  111. return out
  112. class MobileNetV3_small(nn.Module):
  113. # (out_channels,kernel_size,exp_channels,stride,se,nl)
  114. cfg = [
  115. (16,3,16,2,True,'RE'),
  116. (24,3,72,2,False,'RE'),
  117. (24,3,88,1,False,'RE'),
  118. (40,5,96,2,True,'HS'),
  119. (40,5,240,1,True,'HS'),
  120. (40,5,240,1,True,'HS'),
  121. (48,5,120,1,True,'HS'),
  122. (48,5,144,1,True,'HS'),
  123. (96,5,288,2,True,'HS'),
  124. (96,5,576,1,True,'HS'),
  125. (96,5,576,1,True,'HS')
  126. ]
  127. def __init__(self,num_classes=17):
  128. super(MobileNetV3_small,self).__init__()
  129. self.conv1=nn.Conv2d(3,16,3,2,padding=1,bias=False)
  130. self.bn1=nn.BatchNorm2d(16)
  131. # 根据cfg数组自动生成所有的Bottleneck层
  132. self.layers = self._make_layers(in_channels=16)
  133. self.conv2=nn.Conv2d(96,576,1,stride=1,bias=False)
  134. self.bn2=nn.BatchNorm2d(576)
  135. # 卷积后不跟BN,就应该把bias设置为True
  136. self.conv3=nn.Conv2d(576,1280,1,1,padding=0,bias=True)
  137. self.conv4=nn.Conv2d(1280,num_classes,1,stride=1,padding=0,bias=True)
  138. def _make_layers(self,in_channels):
  139. layers=[]
  140. for out_channels,kernel_size,exp_channels,stride,se,nl in self.cfg:
  141. layers.append(
  142. Bottleneck(in_channels,out_channels,kernel_size,exp_channels,stride,se,nl)
  143. )
  144. in_channels=out_channels
  145. return nn.Sequential(*layers)
  146. def forward(self,x):
  147. out=Hswish(self.bn1(self.conv1(x)))
  148. out=self.layers(out)
  149. out=self.bn2(self.conv2(out))
  150. se=SEModule(out.size(1))
  151. out=Hswish(se(out))
  152. out = F.avg_pool2d(out, 7)
  153. out = Hswish(self.conv3(out))
  154. out = self.conv4(out)
  155. # 因为原论文中最后一层是卷积层来实现全连接的效果,维度是四维的,后两维是1,在计算损失函数的时候要求二维,因此在这里需要做一个resize
  156. a, b = out.size(0), out.size(1)
  157. out = out.view(a, b)
  158. return out
  159. # def test():
  160. # net=MobileNetV3_small()
  161. # x=torch.randn(2,3,224,224)
  162. # y=net(x)
  163. # print(y.size())
  164. # print(y)
  165. #
  166. # if __name__=="__main__":
  167. # test()