您的位置:首页 > 健康 > 美食 > 动手学深度学习(Pytorch版)代码实践 -计算机视觉-48全连接卷积神经网络(FCN)

动手学深度学习(Pytorch版)代码实践 -计算机视觉-48全连接卷积神经网络(FCN)

2024/12/22 20:37:18 来源:https://blog.csdn.net/weixin_46560570/article/details/140068338  浏览:    关键词:动手学深度学习(Pytorch版)代码实践 -计算机视觉-48全连接卷积神经网络(FCN)

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 - 1else:# 如果卷积核大小是偶数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])# 训练循环,遍历每个epochfor 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()

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com