<<< $\color{blue}{ (●’◡’●) 点赞 <(- ●’◡’●) }$
/\\ //>>>>
/__\\ // 关注加RP,AC更容易!
/ \\ //>>>>>
<<< $\color{blue}{ (●’◡’●) 收藏 <(- ●’◡’●) }$
这里实现几个经典的pytorch模型,并在FASION-MNIST数据集中进行测试和验证
数据集及输入输出
// 数据载入
import torchvision
mnist_train = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=True, download=True, transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=False, download=True, transform=transforms.ToTensor())
batch_size = 256
if sys.platform.startswith('win'):
num_workers = 0 # 0表示不用额外的进程来加速读取数据
else:
num_workers = 4
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=1)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=1)
// 训练函数
def train(model, loss, opt, max_iter=10):
cnt = 0
for epoch in range(max_iter):
train_l_sum = 0
batch_count = 0
for X, y in train_iter:
y_hat = net(X)
l = loss(y_hat, y)
opt.zero_grad()
l.backward()
opt.step()
train_l_sum += l.item()
batch_count += 1
print('epoch %d, loss %.4f'
% (epoch + 1, train_l_sum / batch_count))
第一个卷积神经网络:LeNet
网络结构如上图所示.
网络分为卷积层和全连接层两部分。
卷积层部分,根据图片,第一层的维度变化:(1, 32, 32) -> (6, 28, 28)
可以知道这是个655的卷积操作;然后是subsample,可以使用22的池化操作进行降采样。
每个节点使用sigmoid作为激活函数。
全连接层部分,第一层将所有维度”摊平”到120维,它的输入维度与输入数据有关。假设输入数据维度为NN,
则这一层输入维度为(16, ((N -4) / 2 - 4) / 2, ((N - 4) / 2 - 4) / 2)
这类没有旁路的网络可以直接用Sequential实现:
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv = nn.Sequential(
nn.Conv2d(1, 6, 5), # in_channels, out_channels, kernel_size
nn.Sigmoid(),
nn.MaxPool2d(2, 2), # kernel_size, stride
nn.Conv2d(6, 16, 5),
nn.Sigmoid(),
nn.MaxPool2d(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(img.shape[0], -1))
return output
LeNet训练及结果:
net = LeModel()
loss_func = nn.CrossEntropyLoss()
opt_func = torch.optim.Adam(net.parameters(), lr = 0.001, weight_decay = 0.0005)
train(
net,
loss_func,
opt_func
)
训练结果:
epoch 1, loss 0.6551
epoch 2, loss 0.6172
epoch 3, loss 0.5909
epoch 4, loss 0.5683
epoch 5, loss 0.5511
epoch 6, loss 0.5383
epoch 7, loss 0.5268
epoch 8, loss 0.5167
epoch 9, loss 0.5069
epoch 10, loss 0.4977
测试准确率0.8
残差神经网络ResNet:
ResNet引入了残差块设计,用来解决网络过深时产生的退化问题。结构如下:
具体原理这里不细究,实现如下:
from torch import nn, optim
import torch.nn.functional as F
class ResBlock(nn.Module):
def __init__(self, input_dim, output_dim, stride, use_1x1 = False):
super(ResBlock, self).__init__()
self.conv1 = nn.Conv2d(input_dim, output_dim, 3, padding = 1, stride = stride)
self.conv2 = nn.Conv2d(output_dim, output_dim, 3, padding = 1)
if (use_1x1):
self.conv3 = nn.Conv2d(input_dim, output_dim, 1, stride = stride)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm2d(output_dim)
self.bn2 = nn.BatchNorm2d(output_dim)
def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
return F.relu(Y + X)
接下来实现传统的ResNet18模型
ResNet-18前两层在输出通道数为64、步幅为2的7×7卷积层后接步幅为2的3×3的最大池化层;使用ReLU作为激活函数。
和Block里一样,每次卷积层后都加上 BatchNorm2d
进行归一化。
之后是4个Res块,最后接全局平均池化层。
resnet = nn.Sequential(
nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
resnet.add_module('res_layer1', nn.Sequential(
ResBlock(64, 64, 1, False),
ResBlock(64, 64, 1, False))
)
resnet.add_module('res_layer2', nn.Sequential(
ResBlock(64, 128, 2, True),
ResBlock(128, 128, 1, False))
)
resnet.add_module('res_layer3', nn.Sequential(
ResBlock(128, 256, 2, True),
ResBlock(256, 256, 1, False))
)
resnet.add_module('res_layer4', nn.Sequential(
ResBlock(256, 512, 2, True),
ResBlock(512, 512, 1, False))
)
resnet.add_module("global_avg_pool", nn.AdaptiveAvgPool2d(1))
resnet.add_module("fc", nn.Sequential(Flatten(), nn.Linear(512, 10)))
Flatten只是做了shape的改变,把上一层BatchNum * 512 * 1 * 1的shape变为BatchNum * 512, 实现如下:
class Flatten(nn.Module):
def __init__(self):
super(Flatten, self).__init__()
def forward(self, X):
return X.view((X.shape[0], X.shape[1]))
训练:
loss_func = nn.CrossEntropyLoss()
opt_func = torch.optim.Adam(net.parameters(), lr = 0.001, weight_decay = 0.0005)
train(resnet, loss_func, opt_func)
epoch 1, loss 0.4420
epoch 2, loss 0.2978
epoch 3, loss 0.2690
epoch 4, loss 0.2460
epoch 5, loss 0.2284
epoch 6, loss 0.2155
epoch 7, loss 0.2005
epoch 8, loss 0.1923
epoch 9, loss 0.1813
epoch 10, loss 0.1721
测试的准确率为0.90,优于朴素的LeNet