ResNet (Residual Network) 是由微软研究院的何凯明等人在2015年提出的一种深度卷积神经网络结构。ResNet的设计目标是解决深层网络训练中的梯度消失和梯度爆炸问题,进一步提高网络的表现。下面是一个ResNet模型实现,使用PyTorch框架来展示如何实现基本的ResNet结构。这个例子包括了一个基本的残差块(Residual Block)以及ResNet-18的实现,代码结构分为model.py(模型文件)和train.py(训练文件)。
model.py
首先,我们导入所需要的包
import torch
from torch import nn
from torch.nn import functional as F
然后,定义Resnet Block(ResBlk)类。
class ResBlk(nn.Module):
def __init__(self):
super(ResBlk, self).__init__()
self.conv1 = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1)
self.bn1 = nn.BatchNorm2d(ch_out)
self.conv2 = nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1)
self.bn2 = nn.BatchNorm2d(ch_out)
self.extra = nn.Sequential()
if ch_out != ch_in
self.extra = nn.Sequential(
nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1)
nn.BatchNorm2d(ch_out)
)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = F.relu(self.bn2(self.conv2(x)))
out = self.extra(x) + out
return out
最后,根据ResNet18的结构对ResNet Block进行堆叠。
class Resnet18(nn.Module):
def __init__(self):
super(Resnet18, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)
nn.BatchNorm2d(64)
)
self.blk1 = ResBlk(64, 128)
self.blk2 = ResBlk(128, 256)
self.blk3 = ResBlk(256, 512)
self.blk4 = ResBlk(512, 1024)
self.outlayer = nn.Linear(512, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.blk1(x)
x = self.blk2(x)
x = self.blk3(x)
x = self.blk4(x)
# print('after conv1:', x.shape)
x = F.adaptive_avg_pool2d(x, [1,1])
x = x.view(x.size(0), -1)
x = self.outlayer(x)
return x
其中,在网络结构搭建过程中,需要用到中间阶段的图片参数,用下述测试过程求得。
def main():
tmp = torch.randn(2, 3, 32, 32)
out = blk(tmp)
print('block', out.shape)
x = torch.randn(2, 3, 32, 32)
model = ResNet18()
out = model(x)
print('resnet:', out.shape)
train.py
首先,导入所需要的包
import torch
from torchvision import datasets
from torchvision import transforms
from torch import nn, optimizer
然后,定义main()函数
def main():
batchsz = 32
cifar_train = datasets.CIFAR10('cifar', True, transform=transforms.Compose([
transforms.Resize((32, 32)),
transforms.ToTensor()
]), download=True)
cifar_train = DataLoader(cifar_train, batch_size=batchsz, shuffle=True)
cifar_test = datasets.CIFAR10('cifar', False, transform=transforms.Compose([
transforms.Resize((32, 32)),
transforms.ToTensor()
]), download=True)
cifar_test = DataLoader(cifar_test, batch_size=batchsz, shuffle=True)
x, label = iter(cifar_train).next()
print('x:', x.shape, 'label:', label.shape)
device = torch.device('cuda')
model = ResNet18().to(device)
criteon = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)
print(model)
for epoch in range(100):
for batchidx, (x, label) in enumerate(cifar_train):
x, label = x.to(device), label.to(device)
logits = model(x)
loss = criteon(logitsm label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(loss.item())
with torch.no_grad():
total_correct = 0
total_num = 0
for x, label in cifar_test:
x, label = x.to(device), label.to(device)
logits = model(x)
pred = logits.argmax(dim=1)
total_correct += torch.eq(pred, label).floot().sum().item()
total_num += x.size(0)
acc = total_correct / total_num
print(epoch, acc)