• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

tintin7790

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

3.1-3.5随机梯度,优化器,损失函数,参数初始化方法

PyTorch 网络参数、初始化与优化器练习

本节主要练习 PyTorch 中神经网络参数相关的基本操作,包括:

  • nn.Module 定义神经网络
  • parameters() 查看模型参数
  • nn.Sequential 组织网络层
  • weight 与 bias
  • 神经网络参数初始化
  • model.apply() 批量初始化
  • 为不同网络模块设置不同学习率
  • Adam 优化器
  • PyTorch 标准训练流程
  • 权重分布可视化

1. 查看神经网络参数

首先创建一个简单的全连接层:

import torch.nn as nn

hidden = nn.Linear(10, 5)

这一层表示:

10维输入
   ↓
Linear(10, 5)
   ↓
5维输出

全连接层本质上执行:

[
y = Wx + b
]

其中:

  • weight 的 shape 为 [5, 10]
  • bias 的 shape 为 [5]

可以使用:

print(type(hidden.parameters()))

查看 parameters() 返回的类型:

<class 'generator'>

也就是说,parameters() 返回的是一个生成器,可以把它转换成列表观察:

params_list = list(hidden.parameters())

print(f"这一层有 {len(params_list)} 个参数变量")

print(params_list[0].shape)
print(params_list[1].shape)

输出:

这一层有 2 个参数变量

torch.Size([5, 10])
torch.Size([5])

因此:

params_list[0] → weight → 5 × 10
params_list[1] → bias   → 5

也可以直接查看权重中的具体数值:

print(params_list[0])

这些数值就是网络中真正需要通过训练不断调整的参数。


2. 使用 nn.Module 定义网络

PyTorch 中通常通过继承 nn.Module 定义自己的神经网络:

import torch
import torch.nn as nn
import torch.optim as optim


class TestNet(nn.Module):
    def __init__(self):
        super(TestNet, self).__init__()

        self.hidden = nn.Sequential(
            nn.Linear(13, 10),
            nn.ReLU(),
        )

        self.regression = nn.Linear(10, 1)

    def forward(self, x):
        x = self.hidden(x)
        output = self.regression(x)
        return output


testnet = TestNet()

网络的数据流:

13维输入
   ↓
Linear(13, 10)
   ↓
ReLU
   ↓
Linear(10, 1)
   ↓
1维输出

其中:

nn.Sequential(...)

可以把多个网络层按照顺序组合起来。


3. 为不同网络模块设置不同学习率

PyTorch 的优化器可以把模型参数分成多个 parameter group,并为不同参数组指定不同学习率。

例如:

optimizer = optim.Adam(
    [
        {
            "params": testnet.hidden.parameters(),
            "lr": 0.0001
        },
        {
            "params": testnet.regression.parameters(),
            "lr": 0.02
        }
    ],
    lr=1e-2
)

这里相当于告诉 Adam:

TestNet
│
├── hidden
│   └── lr = 0.0001
│
└── regression
    └── lr = 0.02

也就是说:

  • hidden 部分学习得慢一些
  • regression 部分学习得快一些

最外面的:

lr=1e-2

是优化器的默认学习率。

如果某个 parameter group 已经单独设置了 lr,则该组使用自己的学习率。


4. PyTorch 标准训练流程

基本训练循环:

for input, target in dataset:

    optimizer.zero_grad()

    output = testnet(input)

    loss = loss_fn(output, target)

    loss.backward()

    optimizer.step()

整个过程:

input
  ↓
forward()
  ↓
output
  ↓
与 target 比较
  ↓
loss
  ↓
backward()
  ↓
计算参数梯度
  ↓
optimizer.step()
  ↓
更新 weight / bias

optimizer.zero_grad()

optimizer.zero_grad()

清除上一轮训练留下的梯度。


forward

output = testnet(input)

调用:

TestNet.forward()

进行前向传播,计算网络输出。


loss

loss = loss_fn(output, target)

比较:

模型预测 output
        ↓
      loss
        ↑
真实结果 target

用于衡量模型当前预测结果和目标之间的差异。


backward

loss.backward()

通过 PyTorch 的自动微分系统计算 loss 对各个可学习参数的梯度。

计算完成之后,可以通过:

parameter.grad

查看对应参数的梯度。


optimizer.step()

optimizer.step()

优化器根据:

参数当前值
+
参数梯度
+
优化算法
+
learning rate

更新模型参数。


5. 定义包含卷积层的网络

例如:

import torch
import torch.nn as nn


class TestNet(nn.Module):
    def __init__(self):
        super(TestNet, self).__init__()

        self.conv1 = nn.Conv2d(3, 16, 3)

        self.hidden = nn.Sequential(
            nn.Linear(100, 100),
            nn.ReLU(),
            nn.Linear(100, 50),
            nn.ReLU(),
        )

        self.cla = nn.Linear(50, 10)

    def forward(self, x):

        x = self.conv1(x)

        x = x.view(x.shape[0], -1)

        x = self.hidden(x)

        output = self.cla(x)

        return output


testnet = TestNet()

print(testnet)

打印网络:

TestNet(
  (conv1): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1))
  (hidden): Sequential(
    (0): Linear(in_features=100, out_features=100, bias=True)
    (1): ReLU()
    (2): Linear(in_features=100, out_features=50, bias=True)
    (3): ReLU()
  )
  (cla): Linear(in_features=50, out_features=10, bias=True)
)

整体结构:

Image
  ↓
Conv2d
  ↓
Feature Maps
  ↓
Flatten
  ↓
Linear(100, 100)
  ↓
ReLU
  ↓
Linear(100, 50)
  ↓
ReLU
  ↓
Linear(50, 10)
  ↓
10维输出

其中:

x = x.view(x.shape[0], -1)

表示:

保留 batch 维度
+
把后面的所有维度展开

例如:

[32, 16, 5, 5]

↓

[32, 400]

这样卷积层产生的特征才能继续输入 Linear。

需要注意:

nn.Linear(100, 100)

要求前面的卷积结果展开后必须正好有 100 个特征。

因此这个网络目前主要用于练习网络定义,实际使用时需要根据输入图片大小计算卷积后的 shape。


6. 神经网络参数初始化

创建网络层时,PyTorch 会自动初始化参数。

例如:

conv1 = torch.nn.Conv2d(3, 16, 3)

此时 conv1 已经拥有:

weight
bias

也可以手动重新初始化。


7. 正态分布初始化

例如:

torch.nn.init.normal_(
    conv1.weight,
    mean=0,
    std=1
)

表示使用正态分布初始化权重:

[
W \sim N(0,1)
]

其中:

mean = 0
std  = 1

8. 均匀分布初始化

例如:

torch.nn.init.uniform_(
    m.weight,
    a=-0.1,
    b=0.1
)

表示:

[
W \sim U(-0.1,0.1)
]

权重会从:

[-0.1, 0.1]

之间随机产生。


9. 常数初始化

偏置可以直接初始化成固定值:

torch.nn.init.constant_(
    conv1.bias,
    val=0.1
)

也可以:

m.bias.data.fill_(0.01)

将所有 bias 设置为:

0.01

10. 使用 apply() 初始化整个网络

可以定义自己的初始化函数:

def init_weight(m):

    if type(m) == nn.Conv2d:

        torch.nn.init.normal_(
            m.weight,
            mean=0,
            std=0.5
        )

    if type(m) == nn.Linear:

        torch.nn.init.uniform_(
            m.weight,
            a=-0.1,
            b=0.1
        )

        m.bias.data.fill_(0.01)

然后:

testnet.apply(init_weight)

apply() 会遍历网络中的各个 module,并把它们依次传给 init_weight()。

因此可以根据网络层的类型选择不同初始化方法:

TestNet
│
├── Conv2d
│   └── Normal(mean=0, std=0.5)
│
├── Linear
│   └── Uniform(-0.1, 0.1)
│
├── ReLU
│
├── Linear
│   └── Uniform(-0.1, 0.1)
│
├── ReLU
│
└── Linear
    └── Uniform(-0.1, 0.1)

ReLU 没有需要初始化的 weight 和 bias,因此函数不会对它进行处理。


11. 随机种子

可以使用:

torch.manual_seed(13)

设置 PyTorch 随机数生成器的随机种子。

这样在相同环境和相同操作顺序下,更容易复现随机初始化结果。

需要注意代码执行顺序。

例如:

conv1 = torch.nn.Conv2d(3, 16, 3)

torch.manual_seed(12)

创建 Conv2d 时默认初始化已经发生了,因此后面设置 seed 不会改变刚才已经产生的随机参数。

如果希望控制 Conv2d 创建时的默认随机初始化,应写:

torch.manual_seed(12)

conv1 = torch.nn.Conv2d(3, 16, 3)

即:

设置 seed
    ↓
产生随机数

12. 查看 Conv2d 的 weight

创建:

conv1 = torch.nn.Conv2d(3, 16, 3)

查看:

print(conv1.weight.shape)

得到:

torch.Size([16, 3, 3, 3])

四个维度分别表示:

[out_channels, in_channels, kernel_height, kernel_width]

因此:

16 → 16个卷积核 / 输出通道
3  → 每个卷积核处理3个输入通道
3  → kernel height
3  → kernel width

可以进一步:

print(conv1.weight[0])

查看第一个卷积核的权重。


13. 可视化权重分布

可以使用 Matplotlib 查看网络权重的分布。

import torch
import matplotlib.pyplot as plt


conv1 = torch.nn.Conv2d(3, 16, 3)

torch.nn.init.normal_(
    conv1.weight,
    mean=0,
    std=1
)

plt.figure(figsize=(8, 6))

plt.hist(
    conv1.weight.data.numpy().reshape(-1),
    bins=30
)

plt.show()

如果使用:

torch.nn.init.normal_(
    conv1.weight,
    mean=0,
    std=1
)

那么权重理论上服从:

[
N(0,1)
]

直方图应该大致呈现正态分布。

这个实验可以帮助理解:

神经网络中的 weight 并不是什么抽象的东西,本质上就是一个由大量浮点数组成的 Tensor。

训练网络的过程,就是不断调整这些 Tensor 中的数值。


整体理解

这一部分练习可以串成一条完整逻辑:

定义网络
   ↓
nn.Module
   ↓
Linear / Conv2d 等网络层
   ↓
产生可学习 Parameter
   ↓
weight + bias
   ↓
初始化参数
   ↓
model.parameters()
   ↓
交给 optimizer
   ↓
输入 x
   ↓
forward()
   ↓
得到 output
   ↓
与 target 计算 loss
   ↓
loss.backward()
   ↓
得到 parameter.grad
   ↓
optimizer.step()
   ↓
修改 weight / bias
   ↓
进入下一轮训练

因此,可以把目前对 PyTorch 神经网络训练的理解概括为:

神经网络本质上是一个包含大量可学习参数的函数。输入数据 x 经过网络得到预测结果 output,再与真实目标 target 计算 loss。PyTorch 通过自动微分计算每个参数对 loss 的梯度,优化器再根据这些梯度调整 weight 和 bias,使模型的预测结果逐渐接近目标。


本节涉及的核心 API

nn.Module
nn.Linear
nn.Conv2d
nn.Sequential
nn.ReLU

model.parameters()
model.named_parameters()
model.apply()

torch.nn.init.normal_
torch.nn.init.uniform_
torch.nn.init.constant_

torch.manual_seed()

optim.Adam()

optimizer.zero_grad()
loss.backward()
optimizer.step()

posted on 2026-08-15 14:01  tintin7790  阅读(5)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3