48全连接卷积神经网络(FCN
)
1.构造函数
import torch
import torchvision
from torch import nn
from torch.nn import functional as F
import matplotlib.pyplot as plt
import liliPytorch as lp
from d2l import torch as d2l
# 构造模型
pretrained_net = torchvision.models.resnet18(pretrained=True)
# print(list(pretrained_net.children())[-3:]) # ResNet-18模型的最后几层
"""
[Sequential(
(0): BasicBlock(
(conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(1): BasicBlock(
(conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
), AdaptiveAvgPool2d(output_size=(1, 1)), Linear(in_features=512, out_features=1000, bias=True)]
"""
# 创建一个全卷积网络net。 它复制了ResNet-18中大部分的预训练层,
# 除了最后的全局平均汇聚层和最接近输出的全连接层。
net = nn.Sequential(*list(pretrained_net.children())[:-2])
# ResNet-18中
"""
X = torch.rand(size=(1, 1, 96, 96))
for layer in net:
X = layer(X)
print(layer.__class__.__name__, 'output shape:\t', X.shape)
# Sequential output shape: torch.Size([1, 64, 24, 24])
# Sequential output shape: torch.Size([1, 64, 24, 24])
# Sequential output shape: torch.Size([1, 128, 12, 12])
# Sequential output shape: torch.Size([1, 256, 6, 6])
# Sequential output shape: torch.Size([1, 512, 3, 3])
前向传播将输入的高和宽减小至原来的 1/32
"""
# 使用1 X 1 卷积层将输出通道数转换为Pascal VOC2012数据集的类数(21类)。
# 最后需要将特征图的高度和宽度增加32倍,从而将其变回输入图像的高和宽。
num_classes = 21
net.add_module('final_conv', nn.Conv2d(512, num_classes, kernel_size=1))
net.add_module('transpose_conv',
nn.ConvTranspose2d(num_classes,
num_classes,
kernel_size=64,
padding=16,
stride=32))
# print(list(net.children())[-2:])
"""
[Conv2d(512, 21, kernel_size=(1, 1), stride=(1, 1)),
ConvTranspose2d(21, 21, kernel_size=(64, 64), stride=(32, 32), padding=(16, 16))]
"""
2.双线性插值
# 初始化转置卷积层
# 在图像处理中,我们有时需要将图像放大,即上采样(upsampling)
# 双线性插值(bilinear interpolation) 是常用的上采样方法之一,
# 它也经常用于初始化转置卷积层
# 双线性插值的上采样可以通过转置卷积层实现,内核由以下bilinear_kernel函数构造。
def bilinear_kernel(in_channels, out_channels, kernel_size):
factor = (kernel_size + 1) // 2 # 计算中心因子,用于确定卷积核的中心位置
if kernel_size % 2 == 1: # 确定卷积核的中心位置
# 如果卷积核大小是奇数
center = factor - 1
else:
# 如果卷积核大小是偶数
center = factor - 0.5
# 生成坐标网格,用于计算每个位置的双线性内核值
og = (torch.arange(kernel_size).reshape(-1, 1), # 列向量
torch.arange(kernel_size).reshape(1, -1)) # 行向量
# 计算双线性内核值,基于当前位置与中心位置的距离并归一化
filt = (1 - torch.abs(og[0] - center) / factor) * \
(1 - torch.abs(og[1] - center) / factor)
# 初始化权重张量
weight = torch.zeros((in_channels, out_channels, kernel_size, kernel_size))
# 填充权重张量,将计算好的双线性内核值赋值给权重张量
weight[range(in_channels), range(out_channels), :, :] = filt
# 返回生成的双线性卷积核权重张量
return weight
# 定义转置卷积层 (n + 4 - 2 - 2 ) * 2
conv_trans = nn.ConvTranspose2d(3, 3, kernel_size=4, padding=1, stride=2,
bias=False)
# 将双线性卷积核权重复制到转置卷积层的权重
conv_trans.weight.data.copy_(bilinear_kernel(3, 3, 4))
img = torchvision.transforms.ToTensor()(d2l.Image.open('../limuPytorch/images/catdog.jpg'))
"""
d2l.Image.open('../limuPytorch/images/catdog.jpg') 首先被执行,返回一个 PIL.Image 对象。
然后,torchvision.transforms.ToTensor() 创建一个 ToTensor 对象。
最后,ToTensor 对象被调用(通过 () 运算符),将 PIL.Image 对象作为参数传递给 ToTensor 的 __call__ 方法,
转换为 PyTorch 张量。
"""
X = img.unsqueeze(0) # 添加一个新的维度,形成形状为 (1, C, H, W) 的张量 X,
Y = conv_trans(X)
out_img = Y[0].permute(1, 2, 0).detach()
print('input image shape:', img.permute(1, 2, 0).shape)
# input image shape: torch.Size([561, 728, 3])
plt.imshow(img.permute(1, 2, 0))
plt.show()
print('output image shape:', out_img.shape)
# output image shape: torch.Size([1122, 1456, 3])
# 图片放大了两倍
plt.imshow(out_img)
plt.show()
3.模型训练
# 37微调章节的代码
def train_batch_ch13(net, X, y, loss, trainer, devices):
"""使用多GPU训练一个小批量数据。
参数:
net: 神经网络模型。
X: 输入数据,张量或张量列表。
y: 标签数据。
loss: 损失函数。
trainer: 优化器。
devices: GPU设备列表。
返回:
train_loss_sum: 当前批次的训练损失和。
train_acc_sum: 当前批次的训练准确度和。
"""
# 如果输入数据X是列表类型
if isinstance(X, list):
# 将列表中的每个张量移动到第一个GPU设备
X = [x.to(devices[0]) for x in X]
else:
X = X.to(devices[0])# 如果X不是列表,直接将X移动到第一个GPU设备
y = y.to(devices[0])# 将标签数据y移动到第一个GPU设备
net.train() # 设置网络为训练模式
trainer.zero_grad()# 梯度清零
pred = net(X) # 前向传播,计算预测值
l = loss(pred, y) # 计算损失
l.sum().backward()# 反向传播,计算梯度
trainer.step() # 更新模型参数
train_loss_sum = l.sum()# 计算当前批次的总损失
train_acc_sum = d2l.accuracy(pred, y)# 计算当前批次的总准确度
return train_loss_sum, train_acc_sum# 返回训练损失和与准确度和
def train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,
devices=d2l.try_all_gpus()):
"""训练模型在多GPU
参数:
net: 神经网络模型。
train_iter: 训练数据集的迭代器。
test_iter: 测试数据集的迭代器。
loss: 损失函数。
trainer: 优化器。
num_epochs: 训练的轮数。
devices: GPU设备列表,默认使用所有可用的GPU。
"""
# 初始化计时器和训练批次数
timer, num_batches = d2l.Timer(), len(train_iter)
# 初始化动画器,用于实时绘制训练和测试指标
animator = lp.Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0, 1],
legend=['train loss', 'train acc', 'test acc'])
# 将模型封装成 DataParallel 模式以支持多GPU训练,并将其移动到第一个GPU设备
net = nn.DataParallel(net, device_ids=devices).to(devices[0])
# 训练循环,遍历每个epoch
for epoch in range(num_epochs):
# 初始化指标累加器,metric[0]表示总损失,metric[1]表示总准确度,
# metric[2]表示样本数量,metric[3]表示标签数量
metric = lp.Accumulator(4)
# 遍历训练数据集
for i, (features, labels) in enumerate(train_iter):
timer.start() # 开始计时
# 训练一个小批量数据,并获取损失和准确度
l, acc = train_batch_ch13(net, features, labels, loss, trainer, devices)
metric.add(l, acc, labels.shape[0], labels.numel()) # 更新指标累加器
timer.stop() # 停止计时
# 每训练完五分之一的批次或者是最后一个批次时,更新动画器
if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
animator.add(epoch + (i + 1) / num_batches,
(metric[0] / metric[2], metric[1] / metric[3], None))
test_acc = d2l.evaluate_accuracy_gpu(net, test_iter) # 在测试数据集上评估模型准确度
animator.add(epoch + 1, (None, None, test_acc))# 更新动画器
# 打印最终的训练损失、训练准确度和测试准确度
print(f'loss {metric[0] / metric[2]:.3f}, train acc '
f'{metric[1] / metric[3]:.3f}, test acc {test_acc:.3f}')
# 打印每秒处理的样本数和使用的GPU设备信息
print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec on '
f'{str(devices)}')
# 全卷积网络用双线性插值的上采样初始化转置卷积层
W = bilinear_kernel(num_classes, num_classes, 64)
net.transpose_conv.weight.data.copy_(W)
# 读取数据集
batch_size, crop_size = 32, (320, 480)
train_iter, test_iter = lp.load_data_voc(batch_size, crop_size) # 46语义分割和数据集代码
# 损失函数
def loss(inputs, targets):
return F.cross_entropy(inputs, targets, reduction='none').mean(1).mean(1)
num_epochs, lr, wd, devices = 5, 0.001, 1e-3, d2l.try_all_gpus()
trainer = torch.optim.SGD(net.parameters(), lr=lr, weight_decay=wd)
train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)
# loss 0.443, train acc 0.863, test acc 0.848
# 254.0 examples/sec on [device(type='cuda', index=0), device(type='cuda', index=1)]
plt.show()
标签:kernel,48,torch,Pytorch,train,net,512,FCN,size
From: https://blog.csdn.net/weixin_46560570/article/details/140068338