整体说明
- PyTorch 中,包含很多随机操作,比如
- 可以使用
torch.rand()等函数获取随机数 - 可以使用
torch.nn.functional.dropout()实现随机 drop 一些神经元 - 可以使用
tensor.random_()等函数随机初始化参数
- 可以使用
- 这些涉及随机数/采样的方法均受限于一个随机状态管理
torch Seed 打印
torchSeed 打印代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22# 打印 torch 的随机种子情况
def print_torch_seeds():
print("=" * 30 + "PyTorch Random Seeds Status")
print("=" * 30)
cpu_seed = torch.initial_seed()
print(f"[CPU] Seed: {cpu_seed}")
if torch.cuda.is_available():
try:
gpu_seed = torch.cuda.initial_seed()
current_device = torch.cuda.current_device()
device_name = torch.cuda.get_device_name(current_device)
print(f"[GPU] Seed: {gpu_seed}")
print(f" Device: {current_device} ({device_name})")
except Exception as e:
print(f"[GPU] Error getting seed: {e}")
else:
print("[GPU] CUDA is not available.")
print("=" * 30)
print_torch_seeds()
torch Seed 设置
全局
torchSeed 设置代码:1
2
3
4
5
6
7
8
9
10
11
12
13import torch
# 固定CPU种子
torch.manual_seed(42)
# 固定所有GPU的种子(单GPU/多GPU通用)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(42) # 替代 torch.cuda.manual_seed(42)(单GPU)
# GPU上生成随机排列
perm = torch.randperm(10, device="cuda") # 注意:需要指定 "cuda" 才会在 GPU 上执行
print("GPU随机排列:", perm) # 每次运行结果一致
print("draw a random number:", torch.rand()) # 每次运行结果一致使用独立的
torch生成器(独立管理自己的随机生成器):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import torch
# 创建独立的生成器并设置种子
generator = torch.Generator()
generator.manual_seed(42)
# generator = torch.Generator().manual_seed(42) # 等价实现
# 生成随机排列时指定生成器
perm1 = torch.randperm(10, generator=generator)
perm2 = torch.randperm(10, generator=generator)
print("独立生成器-第一次:", perm1) # tensor([2, 7, 3, 1, 0, 9, 4, 5, 8, 6])
print("独立生成器-第二次:", perm2) # tensor([2, 0, 7, 9, 8, 4, 3, 6, 1, 5])
# 重置生成器种子,结果重复
generator.manual_seed(42)
perm3 = torch.randperm(10, generator=generator)
print("重置生成器后:", perm3) # tensor([2, 7, 3, 1, 0, 9, 4, 5, 8, 6])(和perm1一致)- 说明:
torch.Generator是 PyTorch 中统一的随机数生成器(RNG)核心对象,几乎所有 PyTorch 内置的随机操作都支持通过generator参数指定该生成器
- 说明:
附录:torch.Generator 详细说明
torch.Generator是 PyTorch 中统一的随机数生成器(RNG)核心对象 ,几乎所有 PyTorch 内置的随机操作都支持通过generator参数指定该生成器,仅极少数场景不支持(或无需支持)torch.Generator的核心作用是隔离随机状态 :任何依赖 PyTorch 内置随机数生成的操作,只要支持generator参数,就能通过该生成器控制随机行为;无generator参数的操作,要么不依赖随机数,要么复用全局生成器(CPU/CUDA)- 所有需要随机逻辑的场景均支持
torch.Generator的随机操作(全场景) - 注:无随机逻辑的操作本身无随机行为,因此不需要(也无法)指定
generator:- 张量基础操作:
torch.ones()、torch.zeros()、torch.arange()、torch.cat()、torch.matmul()等 - 数学运算:
torch.sin()、torch.exp()、torch.mean()、torch.argmax()等 - 索引/切片:
x[:, 0]、x.index_select()等 - 设备/类型转换:
x.to('cuda')、x.float()等
- 张量基础操作:
随机逻辑的场景示例
- 所有操作均可通过
generator参数指定自定义torch.Generator,实现随机状态隔离 - 基础随机数生成
函数/方法 用途 示例 torch.rand()均匀分布随机数 torch.rand(3, generator=g)torch.randn()标准正态分布随机数 torch.randn(2, 4, generator=g)torch.randint()整数随机数 torch.randint(0, 10, (3,), generator=g)torch.randperm()随机排列 torch.randperm(5, generator=g)torch.rand_like()按形状生成均匀随机数 torch.rand_like(torch.ones(2), generator=g)torch.randn_like()按形状生成正态随机数 torch.randn_like(torch.ones(2), generator=g)torch.normal()自定义均值/方差正态分布 torch.normal(0, 1, (3,), generator=g)torch.poisson()泊松分布随机数 torch.poisson(torch.ones(3), generator=g)torch.exponential()指数分布随机数 torch.exponential(1.0, (3,), generator=g)torch.cauchy()柯西分布随机数 torch.cauchy(0, 1, (3,), generator=g)torch.log_normal()对数正态分布随机数 torch.log_normal(0, 1, (3,), generator=g)torch.multinomial()多项分布采样 torch.multinomial(torch.ones(5), 3, generator=g)torch.bernoulli()伯努利分布(0/1) torch.bernoulli(torch.ones(3)*0.5, generator=g)- 注:指定参数
generator时,前面的参数也需要指定(Python 本身的规则)
- 注:指定参数
- 张量随机初始化
函数/方法 用途 示例 tensor.random_()原地随机初始化(整数) tensor.random_(generator=g)tensor.uniform_()原地均匀分布初始化 tensor.uniform_(0, 1, generator=g)tensor.normal_()原地正态分布初始化 tensor.normal_(0, 1, generator=g)tensor.cauchy_()原地柯西分布初始化 tensor.cauchy_(0, 1, generator=g) - 随机采样/变换(数据增强等)
函数/方法 用途 示例 torch.utils.data.RandomSampler数据集随机采样 RandomSampler(dataset, generator=g)torch.nn.functional.dropout()Dropout层随机失活 F.dropout(x, p=0.5, generator=g)torch.nn.functional.dropout2d()2D Dropout F.dropout2d(x, p=0.5, generator=g)torch.nn.functional.dropout3d()3D Dropout F.dropout3d(x, p=0.5, generator=g)torchvision.transforms中的随机变换图像随机增强(如RandomCrop) transforms.RandomCrop(32, generator=g)(需torchvision)torch.distributions分布采样概率分布采样(如Normal、Uniform) dist = Normal(0, 1); dist.sample((3,), generator=g)
特殊说明:随机场景但不支持 torch.Generator 的场景
- 有随机逻辑但不支持自定义
generator的场景;依赖随机数,但 PyTorch 未开放generator参数,只能复用全局生成器(CPU/CUDA):操作 原因 替代方案 torch.shuffle()底层绑定全局生成器 用 torch.randperm(generator=g)手动实现洗牌torch.nn.Dropout模块(默认)模块初始化时未绑定生成器 改用 F.dropout(generator=g)或自定义模块绑定生成器部分第三方库的随机操作(如某些数据增强) 未适配 generator参数替换为 PyTorch 原生实现或手动设置全局种子 torch.multiprocessing多进程随机进程间生成器隔离限制 每个进程内重新初始化 generator - 实践思考:
- 1)凡是生成随机数的 PyTorch 原生函数,优先检查是否有
generator参数,有则建议使用(隔离随机状态) - 2)对不支持
generator的随机操作,要么手动实现(如用randperm替代shuffle),要么临时设置全局种子并尽快恢复 - 3)CUDA 场景务必创建对应设备的
generator,避免跨设备混用导致随机状态混乱
- 1)凡是生成随机数的 PyTorch 原生函数,优先检查是否有
最佳实践:torch.Generator 隔离随机状态
- 多个
torch.Generator隔离随机状态示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import torch
# 创建两个独立生成器
g1 = torch.Generator().manual_seed(42)
g2 = torch.Generator().manual_seed(42)
# 用g1生成随机数(消耗g1的状态)
a = torch.rand(2, generator=g1)
b = torch.rand(2, generator=g1)
# 用g2生成随机数(g2状态未被消耗,结果和g1初始一致)
c = torch.rand(2, generator=g2)
print("a (g1第一次):", a) # tensor([0.8823, 0.9150])
print("b (g1第二次):", b) # tensor([0.3829, 0.9593])
print("c (g2第一次):", c) # tensor([0.8823, 0.9150])(和a一致)
附录:GPU 下的 torch.Generator
torch.Generator必须与操作的设备(CPU/CUDA)对齐 ,否则会导致隐式设备拷贝、性能损耗,甚至随机状态混乱- CPU 操作时使用 CPU 生成器
- CUDA 操作时使用对应 CUDA 设备的生成器
- 核心目的:避免隐式跨设备拷贝,保证随机状态的隔离性和可复现性
- 所有支持 CUDA 的随机操作(如
torch.rand(3, device='cuda', generator=g)),需指定与生成器同设备的generator - CUDA 生成器的随机状态与 CPU 生成器完全隔离,互不干扰
生成器的设备属性
torch.Generator可通过device参数绑定具体设备,默认是 CPU:1
2
3
4
5
6
7
8
9
10import torch
# CPU生成器(默认)
g_cpu = torch.Generator() # 等价于 torch.Generator(device="cpu")
print("CPU生成器设备:", g_cpu.device) # 输出:cpu
# CUDA生成器(需显式指定)
if torch.cuda.is_available():
g_cuda = torch.Generator(device="cuda:0") # 绑定cuda:0
print("CUDA生成器设备:", g_cuda.device) # 输出:cuda:0
对齐 vs 不对齐的示例对比
正确:生成器设备 等于 操作 device(推荐)
1
2
3
4
5
6
7
8if torch.cuda.is_available():
# 创建cuda:0的生成器
g_cuda = torch.Generator(device="cuda:0").manual_seed(42)
# 操作指定device=cuda:0,与生成器对齐
perm = torch.randperm(10, device="cuda:0", generator=g_cuda)
print("结果设备:", perm.device) # cuda:0
print("无隐式拷贝,效率最高")错误:生成器设备 不等于 操作 device(性能坑)
1
2
3
4
5
6
7if torch.cuda.is_available():
# 生成器是cuda:0,但操作指定 device=cpu
g_cuda = torch.Generator(device="cuda:0").manual_seed(42)
perm = torch.randperm(10, device="cpu", generator=g_cuda)
print("结果设备:", perm.device) # cpu
print("隐式拷贝:GPU生成随机数 拷贝到CPU(额外开销)")更隐蔽的错误:CUDA操作用CPU生成器
1
2
3
4
5
6
7if torch.cuda.is_available():
# 生成器是CPU,操作指定device=cuda
g_cpu = torch.Generator().manual_seed(42)
perm = torch.randperm(10, device="cuda", generator=g_cpu)
print("结果设备:", perm.device) # cuda:0
print("隐式拷贝:CPU生成随机数 拷贝到GPU(额外开销)")
为什么必须对齐?
- 随机数生成器的硬件绑定 :
- CPU 生成器依赖 CPU 的随机数算法
- CUDA 生成器依赖 GPU 的 cuRAND 库,直接在 GPU 显存生成随机数;
- 跨设备使用时,PyTorch 会先在生成器设备生成随机数,再通过 PCIe 总线拷贝到操作指定的设备,产生额外耗时
- 随机状态的隔离性(容易因为误用而出错) :
- CUDA生成器的随机状态(
get_state())和 CPU 生成器完全隔离,若跨设备使用,会导致“生成器状态和操作设备不匹配”,破坏随机种子的可复现性:1
2
3
4
5
6
7if torch.cuda.is_available():
g_cuda = torch.Generator(device="cuda").manual_seed(42)
# 第一次:跨设备使用(cuda生成器 到 cpu操作)
perm1 = torch.randperm(10, device="cpu", generator=g_cuda)
# 第二次:直接用cuda生成器 到 cuda操作
perm2 = torch.randperm(10, device="cuda", generator=g_cuda)
# perm2的结果不等于“重新seed后cuda操作的结果”(状态已被跨设备操作消耗)
- CUDA生成器的随机状态(
torch.Generator 的最佳实践
创建生成器时显式指定设备 :
- 不要依赖默认的CPU生成器,GPU场景务必创建
device="cuda"的生成器
- 不要依赖默认的CPU生成器,GPU场景务必创建
封装成函数,强制对齐 :
1
2
3
4
5
6
7
8
9def get_generator(device: str = "cpu", seed: int = 42):
g = torch.Generator(device=device)
g.manual_seed(seed)
return g
# 使用
if torch.cuda.is_available():
g = get_generator(device="cuda:0", seed=42)
perm = torch.randperm(10, device="cuda:0", generator=g)多GPU场景:每个GPU对应独立生成器 :
1
2
3
4
5
6
7if torch.cuda.is_available() and torch.cuda.device_count() > 1:
# 为cuda:0和cuda:1分别创建生成器
g0 = torch.Generator(device="cuda:0").manual_seed(42)
g1 = torch.Generator(device="cuda:1").manual_seed(100)
perm0 = torch.randperm(10, device="cuda:0", generator=g0)
perm1 = torch.randperm(10, device="cuda:1", generator=g1)


