Pytorch初上手——模型构建

引言

我们接着上一篇文章继续,上次我们创建了数据集,加载了数据集,那么接下来就是搭建模型了。

构建模型

这里推荐看一下Pytorch官方的描述,比较具体。简单来说,神经网络由对数据执行操作的层/模块组成。我们构建的神经网络类继承自torch.nn.Module。我们需要在__init__中初始化神经网络层,然后在forward方法中实现对输入数据的操作。

python 复制代码
class SegmentationNet(nn.Module):
    def __init__(self):

    def forward(self,x):

__init__定义

首先使用super().init()继承一下nn.Module的__init__部分。在自定义模型时,必须在__init__方法的第一行调用,它构建了一个神经网络所需要的容器。之后才可以编写自己的模型层。

python 复制代码
    def __init__(self,n_classes=1):
        super().__init__()
        # 编码器
        self.encoder = nn.Sequential(
            nn.Conv2d(3, 64, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
            nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
            nn.Conv2d(128, 256, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
        )
        # 解码器
        self.decoder = nn.Sequential(
            nn.ConvTranspose2d(256, 128, 2, stride=2),# 64->128
            nn.Conv2d(128, 64, 3, padding=1), nn.ReLU(),# 平滑作用,不改变矩阵大小
            nn.ConvTranspose2d(64, 64, 2, stride=2),# 128->256
            nn.Conv2d(64,32, 3, stride = 1,padding = 1),  nn.ReLU(), # 平滑作用,不改变矩阵大小
            nn.ConvTranspose2d(32, 32, 2, stride=2), # 256->512
            nn.Conv2d(32, n_classes,1), # 1*1卷积,降维到单通道
            nn.Sigmoid()  # 二分类概率
        )

这里我们定义了self.encoder和self.decoder两个模型层,但是仔细观察便能发现这两个是容器的类型,里面包含了一系列层。我们用nn.Sequential实现该结构。nn.Sequential 是一个有序的模块容器。数据按照定义的相同顺序通过所有模块。

forward定义

forward 方法实现了从输入到输出的完整计算逻辑,也就是定义了数据如何在网络中流动、变换,最终产生预测结果。定义好forward方法不要直接调用,要通过model(x)间接调用,直接调用会跳过__call__方法,使得其中的钩子逻辑失效,使得模型训练异常。

python 复制代码
# 错误:跳过钩子
output = model.forward(x)

# 正确:调用 __call__,自动触发 hooks
output = model(x)

进行forward方法定义:

python 复制代码
        def forward(self, x):
            return self.decoder(self.encoder(x))

内容逻辑很简单,先进行self.encoder然后进行self.decoder。

模型层

这里介绍一下本示例中用到的模型层,如卷积层,池化层,激活函数层

卷积层

卷积层

实现下采样(提取特征),通过调整卷积核参数进行学习。

输出尺寸 = (W - K + 2P) / S + 1

W:输入矩阵宽度,K:卷积核宽度,P:padding长度,S:stride长度

nn.Conv2d(1, 1, kernel_size=3, stride=1, padding=0)

这个卷积层输入通道数为1,输出通道数为1,卷积核宽度为3,步长为1,padding为0。假设输入矩阵为4*4,那么输出矩阵的尺寸为2*2。

转置卷积层

转置卷积层,实现上采样(恢复分辨率),通过调整卷积核参数进行学习。

输出尺寸 = (W - 1) × S - 2P + K + 输出填充

W:输入矩阵宽度,K:卷积核宽度,P:padding长度,S:stride长度

nn.ConvTranspose2d(1, 1, kernel_size=3, stride=1, padding=0)

这个卷积层输入通道数为1,输出通道数为1,卷积核宽度为3,步长为1,padding为0。假设输入矩阵为2*2,那么输出矩阵的尺寸为4*4。简单来说,就是输入矩阵的每一个数点乘卷积核,得到的矩阵放入对应的位置,最后将每个位置的数叠加后即得到输出矩阵。

不过转置卷积虽然能增加参数,提高分辨率,单容易产生棋盘效应,即因为重叠区域不均匀导致可能产生棋盘状伪影。解决方法是转置卷积+普通卷积,这里的普通卷积的作用是平滑矩阵中的伪影。

池化层

该层主要做的就是降参,降低分辨率。

示例所用的为最大池化层,即:

nn.MaxPool2d(2)

池化窗口宽度为2,将输入矩阵中固定位置的数换为池化窗口范围中最大的数。

激活函数层

非线性激活是模型输入和输出之间产生复杂映射的原因。它们在线性变换后应用,以引入非线性,帮助神经网络学习各种各样的现象。如果没有激活函数,那么多层卷积等价于单层卷积,即线性变换的复合仍是线性。引入非线性后,网络才能学习复杂模式。

nn.ReLU即为一个非线性激活函数。

f(x) = max(0, x)

nn.sigmoid激活函数适合概率值的输出

f(x) = 1 / (1 + e^(-x))

模型参数

如官方文档所说,神经网络内部的许多层都是参数化的,即具有在训练期间优化的相关权重和偏置。继承 nn.Module 会自动跟踪模型对象内定义的所有字段,并使所有参数都可以通过模型的 parameters() 或 named_parameters() 方法访问。

python 复制代码
model = SegmentationNet()
for i in model.named_parameters():
    print(i[0],i[1].size())
python 复制代码
encoder.0.weight torch.Size([64, 3, 3, 3])
encoder.0.bias torch.Size([64])
encoder.3.weight torch.Size([128, 64, 3, 3])
encoder.3.bias torch.Size([128])
encoder.6.weight torch.Size([256, 128, 3, 3])
encoder.6.bias torch.Size([256])
decoder.0.weight torch.Size([256, 128, 2, 2])
decoder.0.bias torch.Size([128])
decoder.1.weight torch.Size([64, 128, 3, 3])
decoder.1.bias torch.Size([64])
decoder.3.weight torch.Size([64, 64, 2, 2])
decoder.3.bias torch.Size([64])
decoder.4.weight torch.Size([32, 64, 3, 3])
decoder.4.bias torch.Size([32])
decoder.6.weight torch.Size([32, 32, 2, 2])
decoder.6.bias torch.Size([32])
decoder.7.weight torch.Size([1, 32, 1, 1])
decoder.7.bias torch.Size([1])

我们也可以打印出该网络总参数量

python 复制代码
total_params = sum(p.numel() for p in model.parameters())
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f"总参数量: {total_params:,}")
print(f"可训练参数量: {trainable_params:,}")
python 复制代码
总参数量: 614,881
可训练参数量: 614,881

当然,我们可以测试一下该模型的输出,应该和输入矩阵尺寸相同

python 复制代码
model = SegmentationNet()
x = torch.randn(1, 3, 512, 512)
output = model(x)
print(f"输出尺寸: {output.shape}") 
python 复制代码
输出尺寸: torch.Size([1, 1, 512, 512])

结尾及相关链接

很好,我们已经搭建了神经网络,接下来定义好评估函数和优化器就可以开始训练了呢。

下期见 ദ്ദി (⩌ᴗ⩌ )

相关链接如下:

示例所用数据集:

Huỳnh Trịnh Ngọc. A02025-Medical-Image-Segmentation. https://kaggle.com/competitions/a0-2025-medical-image-segmentation, 2025. Kaggle.

Pytorch链接:

https://pytorch.org/

相关推荐
仙魁XAN10 小时前
【WorkBuddy·基础入门】第 四 篇 :模式、技能、专家、连接器一次讲清
人工智能·workbuddy·workbuddy 基础入门·workbuddy 工具
呆萌很10 小时前
torch.nan_to_num 函数
人工智能
2601_9583529010 小时前
还要写 AEC 算法?0 代码 + 6 个引脚,F-18 让通话清晰度提升 300%
人工智能·算法·降噪消回音
千里码aicood11 小时前
基于机器学习的雷达低慢小目标识别技术的研究
人工智能·机器学习
llilian_1611 小时前
标准时间间隔发生器应用解决方案 脉冲发生器 时间测量仪
大数据·网络·人工智能·功能测试·单片机·嵌入式硬件·51单片机
世岩清上11 小时前
展厅数字内容同质化严重,怎样打造专属叙事风格?
大数据·前端·javascript·人工智能·html·音视频·展厅改造
幻影123!11 小时前
AlphaZero 五子棋实战(一):单卡从零自举,我的v36 最终版配置
人工智能·强化学习·马尔科夫·决策过程
固定资产管理系统软件11 小时前
该去哪里找专业靠谱的智慧智能设备固定资产管理系统?
人工智能·python
具身AGI11 小时前
线缆绳索怎么操控,物理AI 物理推理 的新解法
人工智能
广州山泉婚姻11 小时前
列表初始化:C++11全新初始化体系
c++·人工智能