123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- def Hswish(x,inplace=True):
- return x * F.relu6(x + 3., inplace=inplace) / 6.
- def Hsigmoid(x,inplace=True):
- return F.relu6(x + 3., inplace=inplace) / 6.
- # Squeeze-And-Excite模块
- class SEModule(nn.Module):
- def __init__(self, channel, reduction=4):
- super(SEModule, self).__init__()
- self.avg_pool = nn.AdaptiveAvgPool2d(1)
- self.se = nn.Sequential(
- nn.Linear(channel, channel // reduction, bias=False),
- nn.ReLU(inplace=True),
- nn.Linear(channel // reduction, channel, bias=False),
- )
- def forward(self, x):
- b, c, _, _ = x.size()
- y=self.avg_pool(x).view(b, c)
- y=self.se(y)
- y = Hsigmoid(y).view(b, c, 1, 1)
- return x * y.expand_as(x)
- class Bottleneck(nn.Module):
- def __init__(self,in_channels,out_channels,kernel_size,exp_channels,stride,se='True',nl='HS'):
- super(Bottleneck, self).__init__()
- padding = (kernel_size - 1) // 2
- if nl == 'RE':
- self.nlin_layer = F.relu6
- elif nl == 'HS':
- self.nlin_layer = Hswish
- self.stride=stride
- if se:
- self.se=SEModule(exp_channels)
- else:
- self.se=None
- self.conv1=nn.Conv2d(in_channels,exp_channels,kernel_size=1,stride=1,padding=0,bias=False)
- self.bn1 = nn.BatchNorm2d(exp_channels)
- self.conv2=nn.Conv2d(exp_channels,exp_channels,kernel_size=kernel_size,stride=stride,
- padding=padding,groups=exp_channels,bias=False)
- self.bn2=nn.BatchNorm2d(exp_channels)
- self.conv3=nn.Conv2d(exp_channels,out_channels,kernel_size=1,stride=1,padding=0,bias=False)
- self.bn3=nn.BatchNorm2d(out_channels)
- # 先初始化一个空序列,之后改造其成为残差链接
- self.shortcut = nn.Sequential()
- # 只有步长为1且输入输出通道不相同时才采用跳跃连接(想一下跳跃链接的过程,输入输出通道相同这个跳跃连接就没意义了)
- if stride == 1 and in_channels != out_channels:
- self.shortcut = nn.Sequential(
- # 下面的操作卷积不改变尺寸和通道数
- nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False),
- nn.BatchNorm2d(out_channels)
- )
- def forward(self,x):
- out=self.nlin_layer(self.bn1(self.conv1(x)))
- if self.se is not None:
- out=self.bn2(self.conv2(out))
- out=self.nlin_layer(self.se(out))
- else:
- out = self.nlin_layer(self.bn2(self.conv2(out)))
- out = self.bn3(self.conv3(out))
- out = out + self.shortcut(x) if self.stride == 1 else out
- return out
- class MobileNetV3_large(nn.Module):
- # (out_channels,kernel_size,exp_channels,stride,se,nl)
- cfg=[
- (16,3,16,1,False,'RE'),
- (24,3,64,2,False,'RE'),
- (24,3,72,1,False,'RE'),
- (40,5,72,2,True,'RE'),
- (40,5,120,1,True,'RE'),
- (40,5,120,1,True,'RE'),
- (80,3,240,2,False,'HS'),
- (80,3,200,1,False,'HS'),
- (80,3,184,1,False,'HS'),
- (80,3,184,1,False,'HS'),
- (112,3,480,1,True,'HS'),
- (112,3,672,1,True,'HS'),
- (160,5,672,2,True,'HS'),
- (160,5,960,1,True,'HS'),
- (160,5,960,1,True,'HS')
- ]
- def __init__(self,num_classes=17):
- super(MobileNetV3_large,self).__init__()
- self.conv1=nn.Conv2d(3,16,3,2,padding=1,bias=False)
- self.bn1=nn.BatchNorm2d(16)
- # 根据cfg数组自动生成所有的Bottleneck层
- self.layers = self._make_layers(in_channels=16)
- self.conv2=nn.Conv2d(160,960,1,stride=1,bias=False)
- self.bn2=nn.BatchNorm2d(960)
- # 卷积后不跟BN,就应该把bias设置为True
- self.conv3=nn.Conv2d(960,1280,1,1,padding=0,bias=True)
- self.conv4=nn.Conv2d(1280,num_classes,1,stride=1,padding=0,bias=True)
- def _make_layers(self,in_channels):
- layers=[]
- for out_channels,kernel_size,exp_channels,stride,se,nl in self.cfg:
- layers.append(
- Bottleneck(in_channels,out_channels,kernel_size,exp_channels,stride,se,nl)
- )
- in_channels=out_channels
- return nn.Sequential(*layers)
- def forward(self,x):
- out=Hswish(self.bn1(self.conv1(x)))
- out=self.layers(out)
- out=Hswish(self.bn2(self.conv2(out)))
- out=F.avg_pool2d(out,7)
- out=Hswish(self.conv3(out))
- out=self.conv4(out)
- # 因为原论文中最后一层是卷积层来实现全连接的效果,维度是四维的,后两维是1,在计算损失函数的时候要求二维,因此在这里需要做一个resize
- a,b=out.size(0),out.size(1)
- out=out.view(a,b)
- return out
- class MobileNetV3_small(nn.Module):
- # (out_channels,kernel_size,exp_channels,stride,se,nl)
- cfg = [
- (16,3,16,2,True,'RE'),
- (24,3,72,2,False,'RE'),
- (24,3,88,1,False,'RE'),
- (40,5,96,2,True,'HS'),
- (40,5,240,1,True,'HS'),
- (40,5,240,1,True,'HS'),
- (48,5,120,1,True,'HS'),
- (48,5,144,1,True,'HS'),
- (96,5,288,2,True,'HS'),
- (96,5,576,1,True,'HS'),
- (96,5,576,1,True,'HS')
- ]
- def __init__(self,num_classes=17):
- super(MobileNetV3_small,self).__init__()
- self.conv1=nn.Conv2d(3,16,3,2,padding=1,bias=False)
- self.bn1=nn.BatchNorm2d(16)
- # 根据cfg数组自动生成所有的Bottleneck层
- self.layers = self._make_layers(in_channels=16)
- self.conv2=nn.Conv2d(96,576,1,stride=1,bias=False)
- self.bn2=nn.BatchNorm2d(576)
- # 卷积后不跟BN,就应该把bias设置为True
- self.conv3=nn.Conv2d(576,1280,1,1,padding=0,bias=True)
- self.conv4=nn.Conv2d(1280,num_classes,1,stride=1,padding=0,bias=True)
- def _make_layers(self,in_channels):
- layers=[]
- for out_channels,kernel_size,exp_channels,stride,se,nl in self.cfg:
- layers.append(
- Bottleneck(in_channels,out_channels,kernel_size,exp_channels,stride,se,nl)
- )
- in_channels=out_channels
- return nn.Sequential(*layers)
- def forward(self,x):
- out=Hswish(self.bn1(self.conv1(x)))
- out=self.layers(out)
- out=self.bn2(self.conv2(out))
- se=SEModule(out.size(1))
- out=Hswish(se(out))
- out = F.avg_pool2d(out, 7)
- out = Hswish(self.conv3(out))
- out = self.conv4(out)
- # 因为原论文中最后一层是卷积层来实现全连接的效果,维度是四维的,后两维是1,在计算损失函数的时候要求二维,因此在这里需要做一个resize
- a, b = out.size(0), out.size(1)
- out = out.view(a, b)
- return out
- # def test():
- # net=MobileNetV3_small()
- # x=torch.randn(2,3,224,224)
- # y=net(x)
- # print(y.size())
- # print(y)
- #
- # if __name__=="__main__":
- # test()
|