0.写在前面:
轻量级网络SqueezeNet,MobileNet,ShuffleNet等。一切为了速度。纯pyotrch代码干货,无理论,理论自己去查吧一堆堆的,代码可直接用。在代码中加打印与网络图一一对应着看效果更佳。全连接层用平均池化层代替是这几个网络参数量大幅下降的基础。
1.SqueezeNet
网络结构图:盗个图。多说一句,这个所谓比alexnet轻量,去掉全连接层才是参数下降的大头,用了个全局池化。
代码:直接可用,这个代码实现的是中间这个图的结构
代码注意:a.x = self.avg(c10)这行代码强制把特征图给做平均值了因为它是个1*1的池化,就相当于求平均了。这块是替换掉全连接层,使得此网络参数大幅降低的根本所在
#encoding=utf-8
import warnings
from collections import namedtuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.model_zoo import load_url as load_state_dict_from_url
class Fire(nn.Module):
def __init__(self, in_channel, out_channel, squzee_channel):
super().__init__()
self.squeeze = nn.Sequential(
nn.Conv2d(in_channel, squzee_channel, 1),
nn.BatchNorm2d(squzee_channel),
nn.ReLU(inplace=True)
)
self.expand_1x1 = nn.Sequential(
nn.Conv2d(squzee_channel, int(out_channel / 2), 1),
nn.BatchNorm2d(int(out_channel / 2)),
nn.ReLU(inplace=True)
)
self.expand_3x3 = nn.Sequential(
nn.Conv2d(squzee_channel, int(out_channel / 2), 3, padding=1),
nn.BatchNorm2d(int(out_channel / 2)),
nn.ReLU(inplace=True)
)
def forward(self, x):
x = self.squeeze(x)
x = torch.cat([
self.expand_1x1(x),
self.expand_3x3(x)
], 1)
return x
class SqueezeNet(nn.Module):
"""mobile net with simple bypass"""
def __init__(self, class_num=100):
super().__init__()
self.stem = nn.Sequential(
nn.Conv2d(3, 96, 3, padding=1),
nn.BatchNorm2d(96),
nn.ReLU(inplace=True),
nn.MaxPool2d(2, 2)
)
self.fire2 = Fire(96, 128, 16)
self.fire3 = Fire(128, 128, 16)
self.fire4 = Fire(128, 256, 32)
self.fire5 = Fire(256, 256, 32)
self.fire6 = Fire(256, 384, 48)
self.fire7 = Fire(384, 384, 48)
self.fire8 = Fire(384, 512, 64)
self.fire9 = Fire(512, 512, 64)
self.conv10 = nn.Conv2d(512, class_num, 1)
self.avg = nn.AdaptiveAvgPool2d(1)
self.maxpool = nn.MaxPool2d(2, 2)
def forward(self, x):
x = self.stem(x)
f2 = self.fire2(x)
f3 = self.fire3(f2) + f2
f4 = self.fire4(f3)
f4 = self.maxpool(f4)
f5 = self.fire5(f4) + f4
f6 = self.fire6(f5)
f7 = self.fire7(f6) + f6
f8 = self.fire8(f7)
f8 = self.maxpool(f8)
f9 = self.fire9(f8)
c10 = self.conv10(f9)
x = self.avg(c10)
x = x.view(x.size(0), -1)
return x
def squeezenet(class_num=7):
return SqueezeNet(class_num=class_num)
if __name__=="__main__":
net = squeezenet()
print("0000000000")
img = torch.randn(1,3,224,224)
y = net(img)
print(y)
2.MobileNet
2.1MobileNet-v1
网络结构图:盗个图..
代码:带测试例子,可以直接运行。
深度可分离卷积来也,先Depthwise Separable Convolution再Depthwise Convolution。
链接https://blog.csdn.net/gbz3300255/article/details/108749709 写的超详细,清晰。
注意地方:
a.代码第18行groups=input_channels就是对单通道单独实施卷积的代码位置
b.代码第24行nn.Conv2d(input_channels, output_channels, 1),就是对通道做1*1卷积升维和降维的过程。
c.代码与上面的网络结构一一对应去看吧。想看哪块直接在下面代码打印就好。
#encoding=utf-8
import warnings
from collections import namedtuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.model_zoo import load_url as load_state_dict_from_url
class DepthSeperabelConv2d(nn.Module):
def __init__(self, input_channels, output_channels, kernel_size, **kwargs):
super().__init__()
self.depthwise = nn.Sequential(
nn.Conv2d(
input_channels,
input_channels,
kernel_size,
groups=input_channels,
**kwargs),
nn.BatchNorm2d(input_channels),
nn.ReLU(inplace=True)
)
self.pointwise = nn.Sequential(
nn.Conv2d(input_channels, output_channels, 1),
nn.BatchNorm2d(output_channels),
nn.ReLU(inplace=True)
)
def forward(self, x):
x = self.depthwise(x)
x = self.pointwise(x)
return x
class BasicConv2d(nn.Module):
def __init__(self, input_channels, output_channels, kernel_size, **kwargs):
super().__init__()
self.conv = nn.Conv2d(
input_channels, output_channels, kernel_size, **kwargs)
self.bn = nn.BatchNorm2d(output_channels)
self.relu = nn.ReLU(inplace=True)
def forward(self, x):
x = self.conv(x)
x = self.bn(x)
x = self.relu(x)
return x
def mobilenet(alpha=1, class_num=7):
return MobileNet(alpha, class_num)
class MobileNet(nn.Module):
def __init__(self, width_multiplier=1, class_num=7):
super().__init__()
alpha = width_multiplier
self.stem = nn.Sequential(
BasicConv2d(3, int(32 * alpha), 3,stride=2, padding=1, bias=False),
DepthSeperabelConv2d(
int(32 * alpha),
int(64 * alpha),
3,
padding=1,
bias=False
)
)
#downsample
self.conv1 = nn.Sequential(
DepthSeperabelConv2d(
int(64 * alpha),
int(128 * alpha),
3,
stride=2,
padding=1,
bias=False
),
DepthSeperabelConv2d(
int(128 * alpha),
int(128 * alpha),
3,
padding=1,
bias=False
)
)
#downsample
self.conv2 = nn.Sequential(
DepthSeperabelConv2d(
int(128 * alpha),
int(256 * alpha),
3,
stride=2,
padding=1,
bias=False
),
DepthSeperabelConv2d(
int(256 * alpha),
int(256 * alpha),
3,
padding=1,
bias=False
)
)
#downsample
self.conv3 = nn.Sequential(
DepthSeperabelConv2d(
int(256 * alpha),
int(512 * alpha),
3,
stride=2,
padding=1,
bias=False
),
DepthSeperabelConv2d(
int(512 * alpha),
int(512 * alpha),
3,
padding=1,
bias=False
),
DepthSeperabelConv2d(
int(512 * alpha),
int(512 * alpha),
3,
padding=1,
bias=False
),
DepthSeperabelConv2d(
int(512 * alpha),
int(512 * alpha),
3,
padding=1,
bias=False
),
DepthSeperabelConv2d(
int(512 * alpha),
int(512 * alpha),
3,
padding=1,
bias=False
),
DepthSeperabelConv2d(
int(512 * alpha),
int(512 * alpha),
3,
padding=1,
bias=False
)
)
#downsample
self.conv4 = nn.Sequential(
DepthSeperabelConv2d(
int(512 * alpha),
int(1024 * alpha),
3,
stride=2,
padding=1,
bias=False
),
DepthSeperabelConv2d(
int(1024 * alpha),
int(1024 * alpha),
3,
padding=1,
bias=False
)
)
self.fc = nn.Linear(int(1024 * alpha), class_num)
self.avg = nn.AdaptiveAvgPool2d(1)
def forward(self, x):
x = self.stem(x)
x = self.conv1(x)
x = self.conv2(x)
x = self.conv3(x)
x = self.conv4(x)
x = self.avg(x)
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
def googlenetv1():
return MobileNet()
if __name__=="__main__":
net = googlenetv1()
print("0000000000")
img = torch.randn(1,3,224,224)
y = net(img)
print(y)
2.2.MobileNet-v2
网络结构图:
代码:
这个也是代码和网络结构图要一一对应。这个我加了些打印,可以看到与上图是对应上的自己看看吧 我的k取7
x = F.adaptive_avg_pool2d(x, 1)这类的语句 就是去掉全连接层呢。
#encoding=utf-8
import warnings
from collections import namedtuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.model_zoo import load_url as load_state_dict_from_url
###################################################### MobileNetV2 #########################################################
class LinearBottleNeck(nn.Module):
def __init__(self, in_channels, out_channels, stride, t=6, class_num=7):
super().__init__()
self.residual = nn.Sequential(
nn.Conv2d(in_channels, in_channels * t, 1),
nn.BatchNorm2d(in_channels * t),
nn.ReLU6(inplace=True),
nn.Conv2d(in_channels * t, in_channels * t, 3, stride=stride, padding=1, groups=in_channels * t),
nn.BatchNorm2d(in_channels * t),
nn.ReLU6(inplace=True),
nn.Conv2d(in_channels * t, out_channels, 1),
nn.BatchNorm2d(out_channels)
)
self.stride = stride
self.in_channels = in_channels
self.out_channels = out_channels
def forward(self, x):
residual = self.residual(x)
if self.stride == 1 and self.in_channels == self.out_channels:
residual += x
return residual
class MobileNetV2(nn.Module):
def __init__(self, class_num=7):
super().__init__()
self.pre = nn.Sequential(
nn.Conv2d(3, 32, 1, stride = 2),
nn.BatchNorm2d(32),
nn.ReLU6(inplace=True)
)
self.stage1 = LinearBottleNeck(32, 16, 1, 1)
self.stage2 = self._make_stage(2, 16, 24, 2, 6)
self.stage3 = self._make_stage(3, 24, 32, 2, 6)
self.stage4 = self._make_stage(4, 32, 64, 2, 6)
self.stage5 = self._make_stage(3, 64, 96, 1, 6)
self.stage6 = self._make_stage(3, 96, 160, 1, 6)
self.stage7 = LinearBottleNeck(160, 320, 1, 6)
self.conv1 = nn.Sequential(
nn.Conv2d(320, 1280, 1),
nn.BatchNorm2d(1280),
nn.ReLU6(inplace=True)
)
self.conv2 = nn.Conv2d(1280, class_num, 1)
def forward(self, x):
print("x shape 0000 = ", x.shape)
x = self.pre(x)
print("x shape 11111 = ", x.shape)
x = self.stage1(x)
print("x shape 222222 = ", x.shape)
x = self.stage2(x)
print("x shape 33333 = ", x.shape)
x = self.stage3(x)
print("x shape 4444 = ", x.shape)
x = self.stage4(x)
print("x shape 5555 = ", x.shape)
x = self.stage5(x)
print("x shape 6666 = ", x.shape)
x = self.stage6(x)
print("x shape 7777 = ", x.shape)
x = self.stage7(x)
print("x shape 8888 = ", x.shape)
x = self.conv1(x)
print("x shape 9999 = ", x.shape)
x = F.adaptive_avg_pool2d(x, 1)
print("x shape 10101010 = ", x.shape)
x = self.conv2(x)
print("x shape xxxxxxxxxx = ", x.shape)
x = x.view(x.size(0), -1)
return x
def _make_stage(self, repeat, in_channels, out_channels, stride, t):
layers = []
layers.append(LinearBottleNeck(in_channels, out_channels, stride, t))
while repeat - 1:
layers.append(LinearBottleNeck(out_channels, out_channels, 1, t))
repeat -= 1
return nn.Sequential(*layers)
def mobilenetv2():
return MobileNetV2()
if __name__=="__main__":
net = mobilenetv2()
img = torch.randn(1,3,224,224)
y = net(img)
print(y)
3.ShuffleNet
3.1 ShuffleNet v1
网络结构图:
难点:通道混淆,关于解释上链接https://blog.csdn.net/gbz3300255/article/details/108530788
代码:注意类别数写一下。这块还需要对照代码理解下 先发上去 后续改
# -*- coding: utf-8 -*-
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
class Bottleneck(nn.Module):
def __init__(self, in_planes, out_planes, stride, groups):
super(Bottleneck, self).__init__()
self.stride = stride
mid_planes = out_planes // 4
self.groups = 1 if in_planes == 24 else groups
self.conv1 = nn.Conv2d(in_planes, mid_planes, kernel_size=1, groups=self.groups, bias=False)
self.bn1 = nn.BatchNorm2d(mid_planes)
self.conv2 = nn.Conv2d(mid_planes, mid_planes, kernel_size=3, stride=stride, padding=1, groups=mid_planes, bias=False)
self.bn2 = nn.BatchNorm2d(mid_planes)
self.conv3 = nn.Conv2d(mid_planes, out_planes, kernel_size=1, groups=groups, bias=False)
self.bn3 = nn.BatchNorm2d(out_planes)
if stride == 2:
self.shortcut = nn.Sequential(nn.AvgPool2d(3, stride=2, padding=1))
@staticmethod
def shuffle_channels(x, groups):
N, C, H, W = x.size()
return x.view(N, groups, C // groups, H, W).permute(0, 2, 1, 3, 4).contiguous().view(N, C, H, W)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.shuffle_channels(out, self.groups)
out = F.relu(self.bn2(self.conv2(out)))
out = self.bn3(self.conv3(out))
if self.stride == 2:
res = self.shortcut(x)
out = F.relu(torch.cat([out, res], 1))
else:
out = F.relu(out + x)
return out
class ShuffleNet(nn.Module):
def __init__(self, out_planes, num_blocks, groups, num_classes=7, depth_multiplier=1.):
super(ShuffleNet, self).__init__()
self.num_classes = num_classes
self.in_planes = int(24 * depth_multiplier)
self.out_planes = [int(depth_multiplier * x) for x in out_planes]
self.conv1 = nn.Conv2d(3, self.in_planes, kernel_size=3, stride=2,padding = 1 bias=False)
self.bn1 = nn.BatchNorm2d(self.in_planes)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
layers = []
for out_plane, num_block in zip(out_planes, num_blocks):
layers.append(self._make_layer(out_plane, num_block, groups))
self.layers = nn.ModuleList(layers)
if num_classes is not None:
self.avgpool = nn.AdaptiveMaxPool2d(1)
self.fc = nn.Linear(out_planes[-1], num_classes)
for m in self.modules():
if isinstance(m, nn.Conv2d):
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2. / n))
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zero_()
def _make_layer(self, out_planes, num_blocks, groups):
layers = []
for i in range(num_blocks):
stride = 2 if i == 0 else 1
cat_planes = self.in_planes if i == 0 else 0
layers.append(Bottleneck(self.in_planes, out_planes - cat_planes, stride=stride, groups=groups))
self.in_planes = out_planes
return nn.Sequential(*layers)
@property
def layer_channels(self):
return self.out_planes
def forward(self, x):
x = F.relu(self.bn1(self.conv1(x)))
x = self.maxpool(x)
c = []
for i in range(len(self.layers)):
x = self.layers[i](x)
c.append(x)
if self.num_classes is not None:
x = self.avgpool(x)
x = x.view(x.size(0), -1)
print("x000000 = ", x.shape)
x = self.fc(x)
print("x1111111 = ", x.shape)
return x
else:
return c
def shufflenet(**kwargs):
planes = [240, 480, 960]
layers = [4, 8, 4]
model = ShuffleNet(planes, layers, groups=3, **kwargs)
return model
def shufflenet_4(**kwargs): # group = 4
planes = [272, 544, 1088]
layers = [4, 8, 4]
model = ShuffleNet(planes, layers, groups=4, **kwargs)
return model
def shufflenet_2(**kwargs):
planes = [200, 400, 800]
layers = [4, 8, 4]
model = ShuffleNet(planes, layers, groups=2, **kwargs)
return model
def shufflenet_1(**kwargs):
planes = [144, 288, 576]
layers = [4, 8, 4]
model = ShuffleNet(planes, layers, groups=1, **kwargs)
return model
if __name__=="__main__":
net = shufflenet_2()
img = torch.randn(1,3,224,224)
y = net(img)
print(y)
未完,待续......
参考文献:1 https://www.cnblogs.com/vincent1997/p/10916734.html
参考文献:2 https://blog.csdn.net/weixin_44538273/article/details/88856239