如何使用PyTorch实现带参数约束的最大似然估计优化
PyTorch实现带正参数约束的正态分布最大似然估计
PyTorch原生优化器确实没有提供硬约束优化的内置接口,针对标准差sigma必须为正的要求,概率建模领域最通用、训练最稳定的方案是无约束重参数化,不需要修改优化器逻辑,也不会破坏梯度传播。
核心思路
不对参数本身做直接约束,而是把带约束的参数通过可逆变换映射到全实数域,直接优化无约束的变换后参数,每次计算损失时再把无约束参数映射回原参数空间即可。
针对必须取正值的参数,常用两个映射函数:
sigma = torch.exp(s_raw):变换完全可逆,正好匹配正态分布NLL里的对数项,梯度计算顺滑,是最常用的选择sigma = torch.nn.functional.softplus(s_raw):映射曲线更平缓,不会因为无约束参数过大导致sigma数值爆炸,对大学习率的兼容性更好
注意:不推荐两种常见的低效方案:一是在损失里加违反约束的惩罚项,需要手动调惩罚权重,无法保证约束严格成立;二是优化步后直接把参数clamp到正数范围,会截断梯度,靠近参数边界时训练极易震荡、不收敛。
修改后的可运行代码
以下代码保留了你原本的初始值设置(初始sigma=5,初始mu=0.5),重参数化后可以用更大的学习率,收敛速度比原代码快很多:
##### 导入依赖 import numpy as np import torch ##### 生成模拟样本 np.random.seed(3) sample = np.random.normal(loc=5, scale=2, size=(1000, 1)) ##### 初始化参数张量 X = torch.tensor(sample, dtype=torch.float64, requires_grad=False) # 观测样本 mu_ = torch.tensor(np.array([0.5]), dtype=torch.float64, requires_grad=True) # 均值可取任意实数,直接优化 # 初始化无约束参数s_raw,exp(s_raw)=5对应初始sigma=5,和原代码初始值一致 s_raw_ = torch.tensor(np.array([np.log(5)]), dtype=torch.float64, requires_grad=True) ##### 配置优化器 learning_rate = 0.01 OPT_OBJ = torch.optim.SGD([mu_, s_raw_], lr = learning_rate) ##### 优化循环 for t in range(2000): # 无约束参数映射到正实数域得到sigma sigma = torch.exp(s_raw_) # 计算负对数似然 NLL = X.size()[0] * torch.log(sigma) + ((((X - mu_) / sigma).pow(2)) / 2).sum() OPT_OBJ.zero_grad() NLL.backward() if t % 200 == 0: print(f"负对数似然: {NLL.item():.2f}; 估计mu: {mu_.item():.3f}; 估计sigma: {sigma.item():.3f}") OPT_OBJ.step() print(f"mu、sigma真实值: 5, 2")
运行后参数会稳定收敛到真实值附近,sigma全程保持正数,不会出现跑到负数导致log计算报错的问题。
快速验证用的简易方案
如果你只是做简单测试不想写重参数化,可以在每次OPT_OBJ.step()之后加一行参数截断:
OPT_OBJ.step() s_.data.clamp_(min=1e-6) # 强制sigma不小于1e-6
这个方法实现成本极低,但训练稳定性差,只适合快速跑通逻辑的场景,正式训练还是推荐用重参数化方案。
内容的提问来源于stack exchange,提问作者henriquebsena
相关产品推荐
相关产品推荐

