介绍
torch.manual_seed() 是 PyTorch 中用于设置CPU 端随机数生成器(RNG)种子的函数。
简单来说:
- 随机数生成器默认是“随机”的(每次运行代码生成的随机数都不同);
- 设置种子后,随机数生成器会按照固定的算法生成可预测的随机数序列,保证多次运行代码时,随机相关的操作(如创建随机张量、模型初始化、数据洗牌等)结果完全一致;
- 这个函数只影响 CPU 端的随机数,如果使用 GPU,还需要额外设置
torch.cuda.manual_seed()或torch.cuda.manual_seed_all()。
应用场景
设置种子后,随机操作的结果固定,这在 实验复现、调试 时非常有用;但在 实际训练 模型时(尤其是最终部署前),通常会关闭固定种子,以引入更多随机性提升模型泛化能力。
语法格式
torch.manual_seed(seed)
参数:
seed:整数(int),随机数种子值(可以是任意整数,比如 0、42、12345 等)。
返回值:返回 torch.Generator 对象(随机数生成器),一般不需要关注这个返回值。
相关随机函数
torch.manual_seed() 最直接的作用对象是 PyTorch 中所有生成随机张量的函数,固定种子后这些函数的输出会完全可复现。
| 函数/操作 | 功能 | 搭配示例 |
|---|---|---|
torch.rand() |
生成 [0,1) 均匀分布随机张量 | torch.manual_seed(42); torch.rand(2,2) → 输出固定 |
torch.randn() |
生成标准正态分布随机张量 | torch.manual_seed(42); torch.randn(2,2) → 输出固定 |
torch.randint() |
生成整数随机张量 | torch.manual_seed(42); torch.randint(0, 10, (2,2)) → 输出固定 |
torch.randperm() |
生成随机排列的整数张量 | torch.manual_seed(42); torch.randperm(5) → 输出固定 |
torch.empty().uniform_() |
填充均匀分布随机值 | torch.manual_seed(42); torch.empty(2,2).uniform_(0,1) → 输出固定 |
例子:不设置种子 → 随机结果不可复现
import torch
# 第一次生成随机张量
print("不设置种子 - 第一次运行:")
print(torch.rand(2, 2)) # 随机值,每次运行不同
# 输出示例:
# tensor([[0.4139, 0.6139],
# [0.4322, 0.6486]])
# 第二次生成随机张量(和第一次结果不同)
print("\n不设置种子 - 第二次运行:")
print(torch.rand(2, 2))
# 输出示例(和上面不同):
# tensor([[0.5349, 0.6603],
# [0.5862, 0.8832]])
例子:设置种子 → 随机结果可复现
import torch
# 设置 CPU 随机种子为 42
torch.manual_seed(42)
# 第一次生成随机张量
print("设置种子 42 - 第一次运行:")
print(torch.rand(2, 2))
# 固定输出:
# tensor([[0.8823, 0.9150],
# [0.3829, 0.9593]])
# 重置种子后,第二次生成的结果和第一次完全一致
torch.manual_seed(42)
print("\n设置种子 42 - 第二次运行:")
print(torch.rand(2, 2))
# 固定输出(和第一次完全相同):
# tensor([[0.8823, 0.9150],
# [0.3829, 0.9593]])
GPU 场景下的种子设置
如果使用 GPU,仅设置 torch.manual_seed() 不够,必须搭配 CUDA 种子函数,否则 GPU 上的随机操作仍不可复现。
| CUDA 种子函数 | 作用 | 搭配示例 |
|---|---|---|
torch.cuda.manual_seed() |
为单个 GPU 设置随机种子 | torch.manual_seed(42); torch.cuda.manual_seed(42) |
torch.cuda.manual_seed_all() |
为所有 GPU 设置随机种子 | torch.manual_seed(42); torch.cuda.manual_seed_all(42) |
示例:固定 GPU 随机操作
import torch
seed = 42
torch.manual_seed(seed) # CPU 种子
if torch.cuda.is_available():
torch.cuda.manual_seed_all(seed) # 所有 GPU 种子
# GPU 上生成随机张量(结果固定)
if torch.cuda.is_available():
print("GPU 上的 torch.rand(2,2):")
print(torch.rand(2, 2).cuda())
# 固定输出:tensor([[0.8823, 0.9150], [0.3829, 0.9593]], device='cuda:0')
模型参数初始化
PyTorch 中模型层(如 nn.Linear、nn.Conv2d、nn.Embedding)的参数默认是随机初始化的,固定种子后,每次运行模型的初始参数完全一致,保证实验可复现。
示例:固定模型参数初始化结果
import torch
import torch.nn as nn
# 设置种子(必须在模型定义前执行)
torch.manual_seed(42)
# 定义简单模型
class SimpleModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(3, 2) # 随机初始化权重
self.embedding = nn.Embedding(10, 4) # 随机初始化嵌入向量
# 创建模型(每次运行,初始参数都相同)
model = SimpleModel()
# 打印线性层的权重(固定值)
print("nn.Linear 初始权重:")
print(model.linear.weight)
# 固定输出:
# Parameter containing:
# tensor([[ 0.4414, 0.4792, -0.1353],
# [-0.4074, -0.2251, 0.3219]], requires_grad=True)
数据层面的随机操作
训练模型时,数据层面的随机操作(如随机洗牌、随机裁剪、随机翻转)也需要固定种子,否则即使模型参数初始化固定,数据输入顺序/形态不同,结果也无法复现。
| 操作类型 | 搭配方式 |
|---|---|
DataLoader 随机洗牌 |
设置 DataLoader 的 generator 参数为固定种子的生成器 |
图像随机变换(torchvision.transforms) |
需同时设置 Python 内置 random、NumPy 和 PyTorch 的种子 |
示例:固定 DataLoader 随机洗牌结果
import torch
from torch.utils.data import DataLoader, TensorDataset
# 全量设置种子(保证数据操作可复现)
seed = 42
torch.manual_seed(seed)
import random
random.seed(seed)
import numpy as np
np.random.seed(seed)
# 构造示例数据
data = torch.randn(10, 3)
labels = torch.randint(0, 2, (10,))
dataset = TensorDataset(data, labels)
# 创建 DataLoader(设置 generator 保证洗牌可复现)
generator = torch.Generator().manual_seed(seed)
dataloader = DataLoader(
dataset,
batch_size=2,
shuffle=True, # 随机洗牌
generator=generator # 绑定固定种子的生成器
)
# 打印第一批数据(每次运行结果相同)
print("DataLoader 第一批数据标签:")
for batch_data, batch_labels in dataloader:
print(batch_labels)
break # 只看第一批
# 固定输出:tensor([1, 0])
注意
- 执行时机:种子需要在所有随机操作之前设置,否则前面的随机操作仍会是不可预测的。
坑
torch.manual_seed(seed)执行多次和执行一次的随机结果不同,看下面的情况1和情况2
情况1:只执行一次torch.manual_seed(10)(推荐的复现方式)
import torch
# 仅初始化一次随机种子
torch.manual_seed(10)
# 连续3次生成随机数
a = torch.randn(1)
b = torch.randn(1)
c = torch.randn(1)
print(f"一次seed:a={a.item():.4f}, b={b.item():.4f}, c={c.item():.4f}")
# 输出:一次seed:a=0.6045, b=0.8018, c=-0.0199
逻辑:seed初始化后,随机状态按「初始值→状态1→状态2→状态3」推进,3次随机数取的是序列中连续的3个不同值。
情况2:执行多次torch.manual_seed(10)(手动重置状态)
import torch
# 每次生成随机数前,都重置一次种子
torch.manual_seed(10)
a = torch.randn(1)
torch.manual_seed(10) # 强行把状态拉回初始值
b = torch.randn(1)
torch.manual_seed(10) # 再次拉回初始值
c = torch.randn(1)
print(f"多次seed:a={a.item():.4f}, b={b.item():.4f}, c={c.item():.4f}")
# 输出:多次seed:a=0.6045, b=0.6045, c=0.6045
逻辑:每次执行seed都把随机状态重置为初始值,3次随机数取的是序列中同一个初始值,结果完全重复。
不同的原因
PyTorch的随机数生成器(RNG)是「状态式」的——种子的作用是初始化随机数生成器的初始状态,而每次调用随机函数(如torch.randn/torch.rand)都会让这个状态向前推进;
重复执行torch.manual_seed(seed)会强行把随机状态重置回初始值,相当于让随机数生成器“倒带重来”,最终导致随机结果和只执行一次的情况完全不同。
概念
随机数生成器(RNG):PyTorch所有随机操作(生成随机张量、模型初始化、dropout等)都依赖一个全局的随机状态,这个状态是一个连续的“数字序列”,每次取随机数都会从序列中按顺序取数,状态随之更新。
torch.manual_seed(seed):唯一作用是将这个全局随机状态重置为一个固定的初始值(由seed决定)。相同的seed,重置后得到的初始状态完全相同,后续按顺序取的随机数也完全相同(这就是“固定随机种子实现结果复现”的原理)。关键差异:
- 执行一次seed:随机状态只会被初始化一次,后续随机操作按自然顺序推进,随机数是“连续的固定序列”;
- 执行多次seed:每次执行都会把随机状态拉回初始值,后续随机操作会重复取初始序列的前几位,随机数会出现“重复”。
误区:执行多次seed
在代码中无意识地重复调用了seed,常见场景有:
- 把seed写在循环里:比如训练循环/数据加载循环中,每次循环都执行
torch.manual_seed(seed),导致每次循环的随机状态都重置; - 模块化代码重复seed:比如把模型初始化、数据增强、批量生成分别写在不同函数里,每个函数开头都加了seed,调用函数时就会多次执行;
- 混用PyTorch和其他库的seed:比如同时用
torch.manual_seed、numpy.random.seed、random.seed,且重复执行,导致随机状态相互干扰。
正确的seed使用规范(保证结果可复现)
想要让PyTorch代码的随机结果完全可复现,且不会因多次seed导致结果异常,只需遵循3个原则:
1. 只执行一次
所有随机操作前,全局初始化一次seed
把torch.manual_seed(seed)写在代码的最开头(import之后,其他操作之前),仅执行一次,后续不再调用。
2. 多环境联动:同步其他库的seed(如有混用)
如果代码中用到了numpy、Python内置random、数据加载DataLoader(多进程),需要同步设置所有库的seed,避免随机状态干扰:
import torch
import numpy as np
import random
import torch.utils.data as DataLoader
# 固定全局seed(仅执行一次)
seed = 42
torch.manual_seed(seed) # PyTorch全局seed
np.random.seed(seed) # numpy seed
random.seed(seed) # Python内置random seed
torch.cuda.manual_seed(seed) # 单GPU seed
torch.cuda.manual_seed_all(seed) # 多GPU seed(如有)
# 额外:保证DataLoader多进程的随机性一致
torch.backends.cudnn.deterministic = True # 固定cudnn算法
torch.backends.cudnn.benchmark = False # 关闭cudnn自动优化(避免算法随机)
3. 禁止局部重置:函数/循环内不重复执行seed
所有自定义函数(模型初始化、数据增强、批量生成)中,不要加torch.manual_seed,仅依赖全局的一次seed初始化。
总结
- 随机状态是“状态式”的:
torch.manual_seed(seed)初始化初始状态,每次随机操作会让状态向前推进,取序列中下一个值; - 多次seed=强制重置:重复执行seed会把随机状态拉回初始值,导致后续随机数重复取初始序列,结果和“一次seed”的连续序列完全不同;
- 正确用法:全局一次初始化:把seed写在代码最开头,仅执行一次,同步其他库seed(如有),函数/循环内不重复调用,才能保证随机结果可复现且符合预期。
总结
torch.manual_seed(seed)的核心作用是固定 CPU 端随机数生成器的种子,让随机操作的结果可复现。搭配所有生成随机数的 PyTorch 函数(rand()/randn()/randint()等),保证随机张量可复现;- GPU 场景需额外使用
torch.cuda.manual_seed()/torch.cuda.manual_seed_all(); - 全流程可复现需同时设置 P
- 可搭配模型参数初始化(
nn.Linear/nn.Embedding等),保证模型初始状态一致; - 可搭配数据处理的随机操作(
DataLoader洗牌、图像增强),且要同时设置 Python/NumPy 种子;ython、NumPy、PyTorch 的种子,且种子要在随机操作前设置。