33

PyTorch实现经典分类网络 | 鸢尾花开

 4 years ago
source link: http://ishero.net/PyTorch%E5%AE%9E%E7%8E%B0%E7%BB%8F%E5%85%B8%E5%88%86%E7%B1%BB%E7%BD%91%E7%BB%9C.html?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

使用PyTorch实现经典的卷积神经网络。
从LeNet到DenseNet介绍
项目地址

LeNet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv = nn.Sequential(
nn.Conv2d(1, 6, 5),
nn.Sigmoid(),
nn.AvgPool2d(2, 2),
nn.Conv2d(6, 16, 5),
nn.Sigmoid(),
nn.AvgPool2d(2, 2)
)
self.fc = nn.Sequential(
nn.Linear(16 * 4 * 4, 120),
nn.Sigmoid(),
nn.Linear(120, 84),
nn.Sigmoid(),
nn.Linear(84, 10)
)

def forward(self, img):
feature = self.conv(img)
output = self.fc(feature.view(feature.shape[0], -1))
return output

AlexNet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class AlexNet(nn.Module):
def __init__(self, classes=1000):
super(AlexNet, self).__init__()
self.classes = classes
self.conv = nn.Sequential(
nn.Conv2d(3, 96, 11, 4),
nn.ReLU(),
nn.MaxPool2d(3, 2),
nn.Conv2d(96, 256, 5, 1, 2),
nn.ReLU(),
nn.MaxPool2d(3, 2),
nn.Conv2d(256, 384, 3, 1, 1),
nn.ReLU(),
nn.Conv2d(384, 384, 3, 1, 1),
nn.ReLU(),
nn.Conv2d(384, 256, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(3, 2)
)
self.fc = nn.Sequential(
nn.Linear(6 * 6 * 256, 4096),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(4096, 4096),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(4096, self.classes) # 使用交叉熵作为损失函数,无需使用Softmax
)

def forward(self, img):
self.feature = self.conv(img)
self.out = self.fc(self.feature.view(self.feature.shape[0], -1))
return self.out

VGG16

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class FlattenLayer(nn.Module):
def __init__(self):
super(FlattenLayer, self).__init__()

def forward(self, X):
return X.view(X.shape[0], -1)


class VGG16(nn.Module):
def __init__(self, conv_arch, fc_features, fc_hidden_units=4096, classes=1000):
super(VGG16, self).__init__()
self.conv_arch = conv_arch
self.fc_features = fc_features
self.fc_hidden_units = fc_hidden_units
self.classes = classes
self.net = nn.Sequential()
for i, (num_convs, in_channels, out_channels) in enumerate(self.conv_arch):
self.net.add_module("vgg_block_" + str(i), self.vgg_block(num_convs, in_channels, out_channels))
self.fc = nn.Sequential(
FlattenLayer(),
nn.Linear(self.fc_features, self.fc_hidden_units),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(self.fc_hidden_units, self.fc_hidden_units),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(self.fc_hidden_units, self.classes)
)
self.net.add_module("fc", self.fc)

def vgg_block(self, num_convs, in_channels, out_channels):
blocks = []
for i in range(num_convs):
if i == 0:
blocks.append(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1))
else:
blocks.append(nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1))
blocks.append(nn.ReLU())
blocks.append(nn.MaxPool2d(3, 2))
return nn.Sequential(*blocks)

def forward(self, X):
return self.net(X)



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class GlobalAvgPool2d(nn.Module):
def __init__(self):
super(GlobalAvgPool2d, self).__init__()

def forward(self, x):
return F.avg_pool2d(x, kernel_size=x.size()[2:])


class FlattenLayer(nn.Module):
def __init__(self):
super(FlattenLayer, self).__init__()

def forward(self, X):
return X.view(X.shape[0], -1)


class NIN(nn.Module):
def __init__(self, classes):
self.classes = classes
super(NIN, self).__init__()
self.net = nn.Sequential(
self.nin_block(3, 96, kernel_size=11, stride=4, padding=0),
nn.MaxPool2d(kernel_size=3, stride=2),
self.nin_block(96, 256, kernel_size=5, stride=1, padding=2),
nn.MaxPool2d(kernel_size=3, stride=2),
self.nin_block(256, 384, kernel_size=3, stride=1, padding=1),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Dropout(0.5),
self.nin_block(384, self.classes, kernel_size=3, stride=1, padding=1),
GlobalAvgPool2d(),
FlattenLayer()
)

def nin_block(self, in_channels, out_channels, kernel_size, stride, padding):
block = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding),
nn.ReLU(),
nn.Conv2d(out_channels, out_channels, kernel_size=1),
nn.ReLU(),
nn.Conv2d(out_channels, out_channels, kernel_size=1),
nn.ReLU()
)
return block

def forward(self, img):
return self.net(img)

GoogLeNet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
class FlattenLayer(nn.Module):
def __init__(self):
super(FlattenLayer, self).__init__()

def forward(self, X):
return X.view(X.shape[0], -1)

class GlobalAvgPool2d(nn.Module):
def __init__(self):
super(GlobalAvgPool2d, self).__init__()

def forward(self, x):
return F.avg_pool2d(x, kernel_size=x.size()[2:])


class Inception(nn.Module):
# c1-c4为四条线路输出的通道数
def __init__(self, in_c, c1, c2, c3, c4):
super(Inception, self).__init__()
self.p1_1 = nn.Conv2d(in_c, c1, kernel_size=1)

self.p2_1 = nn.Conv2d(in_c, c2[0], 1)
self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)

self.p3_1 = nn.Conv2d(in_c, c3[0], kernel_size=1)
self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)

self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
self.p4_2 = nn.Conv2d(in_c, c4, kernel_size=1)

def forward(self, x):
p1 = F.relu(self.p1_1(x))
p2 = F.relu(self.p2_2(F.relu((self.p2_1(x)))))
p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))
p4 = F.relu(self.p4_2(self.p4_1(x)))
return torch.cat((p1, p2, p3, p4), dim=1)


class GoogleNet(nn.Module):
def __init__(self, classes):
self.classes = classes
super(GoogleNet, self).__init__()
self.b1 = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
self.b2 = nn.Sequential(
nn.Conv2d(64, 64, kernel_size=1),
nn.Conv2d(64, 192, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
self.b3 = nn.Sequential(
Inception(192, 64, (96, 128), (16, 32), 32),
Inception(256, 128, (128, 192), (32, 96), 64),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
self.b4 = nn.Sequential(
Inception(480, 192, (96, 208), (16, 48), 64),
Inception(512, 160, (112, 224), (24, 64), 64),
Inception(512, 128, (128, 256), (24, 64), 64),
Inception(512, 112, (144, 288), (32, 64), 64),
Inception(528, 256, (160, 320), (32, 128), 128),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
self.b5 = nn.Sequential(
Inception(832, 256, (160, 320), (32, 128), 128),
Inception(832, 384, (192, 384), (48, 128), 128),
GlobalAvgPool2d(),
nn.Dropout(0.4),
FlattenLayer(),
nn.Linear(1024, self.classes)
)
self.net = nn.Sequential(
self.b1, self.b2, self.b3, self.b4, self.b5
)

def forward(self, x):
return self.net(x)

ResNet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
class FlattenLayer(nn.Module):
def __init__(self):
super(FlattenLayer, self).__init__()

def forward(self, X):
return X.view(X.shape[0], -1)


class GlobalAvgPool2d(nn.Module):
def __init__(self):
super(GlobalAvgPool2d, self).__init__()

def forward(self, x):
return F.avg_pool2d(x, kernel_size=x.size()[2:])


class Residual(nn.Module):
def __init__(self, in_channels, out_channels, same_shape=True):
super(Residual, self).__init__()
self.stride = 1 if same_shape else 2
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=self.stride, padding=1)
self.bn1 = nn.BatchNorm2d(out_channels)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)
self.bn2 = nn.BatchNorm2d(out_channels)
if not same_shape: # 通过1x1卷积核,步长为2.统一两个特征图shape,保证加法运算正常
self.conv3 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=2)
else:
self.conv3 = None

def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = F.relu(self.bn2(self.conv2(Y)))
if self.conv3 is not None:
X = self.conv3(X)
return F.relu(X + Y)


class ResNet(nn.Module):
def __init__(self, classes=1000):
super(ResNet, self).__init__()
self.classes = classes
self.net = nn.Sequential()
self.b1 = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
self.net.add_module("block1", self.b1)
self.net.add_module("resnet_block_1", self.resnet_block(64, 64, 2, is_first_block=True))
self.net.add_module("resnet_block_2", self.resnet_block(64, 128, 2))
self.net.add_module("resnet_block_3", self.resnet_block(128, 256, 2))
self.net.add_module("resnet_block_4", self.resnet_block(256, 512, 2))
self.net.add_module("global_avg_pool", GlobalAvgPool2d())
self.net.add_module("flatten", FlattenLayer())
self.net.add_module('fc', nn.Linear(512, self.classes))

def resnet_block(self, in_channels, out_channels, num_residuals, is_first_block=False):
if is_first_block:
assert in_channels == out_channels # 整个模型的第一块的输入通道数等于输出通道数

block = []
for i in range(num_residuals):
if i == 0 and not is_first_block:
block.append(Residual(in_channels, out_channels, same_shape=False))
else:
block.append(Residual(out_channels, out_channels)) # 第一块输入通道数与输出通道数相等
return nn.Sequential(*block)

def forward(self, X):
return self.net(X)

DenseNet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
class FlattenLayer(nn.Module):
def __init__(self):
super(FlattenLayer, self).__init__()

def forward(self, X):
return X.view(X.shape[0], -1)


class GlobalAvgPool2d(nn.Module):
def __init__(self):
super(GlobalAvgPool2d, self).__init__()

def forward(self, x):
return F.avg_pool2d(x, kernel_size=x.size()[2:])


class DenseBlock(nn.Module):
def __init__(self, in_channels, out_channels, num_conv):
super(DenseBlock, self).__init__()
self.out_channels = out_channels

layers = []
for i in range(num_conv):
in_c = in_channels + i * self.out_channels
layers.append(self.conv_block(in_c, self.out_channels))

self.net = nn.ModuleList(layers)
self.out_channels = in_channels + num_conv * out_channels

def conv_block(self, in_channels, out_channels):
block = nn.Sequential(
nn.BatchNorm2d(in_channels),
nn.ReLU(),
nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)
)
return block

def forward(self, X):
for layer in self.net:
Y = layer(X)
X = torch.cat((X, Y), dim=1)
return X


class DenseNet(nn.Module):
def __init__(self, classes):
super(DenseNet, self).__init__()
self.classes = classes
self.net = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2)
)

num_channels, growth_rate = 64, 32 # 当前通道数64,每层增加32通道(即每层卷积输出通道为32)
num_convs_in_dense_block = [4, 4, 4, 4] # 每个dense block 卷积数

for i, num_convs in enumerate(num_convs_in_dense_block):
block = DenseBlock(num_channels, growth_rate, num_convs)
self.net.add_module("dense_block_%d" % i, block)

num_channels = block.out_channels # 上一个块的输出通道数

if i != len(num_convs_in_dense_block) - 1:
self.net.add_module("trainsition_block_%d" % i, self.transition_block(num_channels, num_channels // 2))
num_channels = num_channels // 2

self.net.add_module("BN", nn.BatchNorm2d(num_channels))
self.net.add_module("relu", nn.ReLU())
self.net.add_module("global_avg_pool", GlobalAvgPool2d())
self.net.add_module("flatten", FlattenLayer())
self.net.add_module("fc", nn.Linear(num_channels, self.classes))

def forward(self, X):
return self.net(X)

def transition_block(self, in_channels, out_channels):
block = nn.Sequential(
nn.BatchNorm2d(in_channels),
nn.ReLU(),
nn.Conv2d(in_channels, out_channels, kernel_size=1),
nn.AvgPool2d(kernel_size=2, stride=2)
)
return block

代码结构主要参考李沐老师的《动手学深度学习》,原书使用MXNET框架实现,这里简单转为PyTorch实现。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK