快捷键

torch.optim.nadam 的源代码

# mypy: 允许未类型化定义
rNAdam 算法的实现
from 打字 导入 角色, 可选, 联合

导入 火炬
from 火炬 导入 张量

from .优化器 导入 (
    _可捕获文档,
    默认为融合或遍历,
    _可区分文档,
    _如果不受支持则禁用 Dynamo,
    _foreach_doc,
    _获取可捕获的设备,
    _获取标量数据类型,
    _获取值,
    _maximize_doc,
    _params_doc,
    _stack_if_compiling,
    _use_grad_for_differentiable,
    真实查看,
    优化器,
    参数 T,
)


全部 = ["NAdam", nadam]


[文档] NAdam(优化器): # noqa: D101 定义 __init__( 自身, 参数: 参数 T, 学习率: 联盟[float, 张量] = 0.002, betas: 元组[float, float] = (0.9, 0.999), eps: 浮点数 = 1e-8, 权重衰减: 浮点数 = 0, 动量衰减: 浮点数 = 0.004, 解耦权重衰减: 布尔值 = 错误, *, foreach: 可选[布尔] = , 最大化: 布尔值 = 错误, 可捕捉的: 布尔值 = 错误, 可微分的: 布尔值 = 错误, ): # 无需注意:D107 如果 isinstance(学习率, 张量) 学习率.元素数量() != 1: 提升 ValueError("Tensor lr 必须是 1 个元素") 如果 0.0 <= 学习率: 提升 ValueError(f"无效的学习率:"{学习率}") 如果 0.0 <= eps: 提升 ValueError(f"无效的 epsilon 值:"{eps}") 如果 0.0 <= betas[0] < 1.0: 提升 ValueError(f"在索引 0 处的无效 beta 参数:"{betas[0]}") 如果 0.0 <= betas[1] < 1.0: 提升 ValueError(f"无效的 beta 参数,索引为 1:"{betas[1]}") 如果 0.0 weight_decay: raise ValueError(f"无效的 weight_decay 值:"{权重衰减}") 如果 0.0 <= 动量衰减: 提升 ValueError(f"无效的动量衰减值:"{动量衰减}") 默认 = 字典( 学习率=学习率, betas=betas, eps=eps, 权重衰减=权重衰减, 动量衰减=动量衰减, 解耦权重衰减=解耦权重衰减, 最大化=最大化, foreach=foreach, 可捕捉的=可捕捉的, 可微分的=可微分的, ) 超级().__init__(参数, 默认值) 定义 __setstate__(自身, 状态): # 无需注意:D105 超级().__setstate__(状态) for 群组 自身.参数组: 群组.setdefault(最大化, 错误) 群组.setdefault("foreach", ) 群组.setdefault("可捕获的", 错误) 群组.setdefault("可区分的", 错误) 群组.setdefault("解耦权重衰减", 错误) for p 群组[参数]: 状态 = 自身.状态.获取(p, [] 如果 长度(状态) != 0: 如果 火炬.is_tensor(状态[步骤)] 步骤值 = float(状态[步骤]) 状态[步骤] = ( 火炬.张量( 步值, 数据类型=_获取标量数据类型(), 设备=p.设备 ) 如果 群组["可捕获的"] 否则 火炬.张量(步值, 数据类型=_获取标量数据类型()) ) 如果 火炬.is_tensor(状态["mu 产品")] mu_prod 值 = 状态["mu 产品"] 状态["mu 产品"] = ( 火炬.张量( mu_prod_val, 数据类型=_获取标量数据类型(), 设备=p.设备 ) 如果 群组["可捕获的"] 否则 火炬.张量(mu_prod_val, 数据类型=_获取标量数据类型()) ) 定义 初始化组( 自身, 群组, 带梯度的参数, 梯度, 实验平均值, 实验平均平方值, mu_products, 状态步数, ): 复杂的参数 = for p 群组[参数]: 如果 p.梯度 : 复杂的参数 |= 火炬.是复杂的(p) 带梯度的参数.append(p) 如果 p.研究生.is_sparse: 提升 运行时错误("NAdam 不支持稀疏梯度") 梯度.append(p.研究生) 状态 = 自身.状态[p] 懒惰状态初始化 如果 长度(状态) == 0: # note(crcrpar): [为步骤提供特殊设备托管] # 故意将`step`和`mu_product`在`capturable`为 False 时放在 CPU 上。 因为在 CUDA 和 XLA 上内核启动代价高昂。 状态[步骤] = ( 火炬.零值((), 数据类型=_获取标量数据类型(), 设备=p.设备) 如果 群组["可捕获的"] 否则 火炬.张量(0.0, 数据类型=_获取标量数据类型()) ) 状态["mu_product"] = ( 火炬.((), 数据类型=_获取标量数据类型(), 设备=p.设备) 如果 群组["可捕获的"] 否则 火炬.张量(1.0, 数据类型=_获取标量数据类型()) ) 指数移动平均的梯度值 状态[平均值] = 火炬.等于零的( p, 内存格式=火炬.保留格式 ) 平方梯度值的指数移动平均 状态["exp_avg_sq"] = 火炬.等于零的( p, 内存格式=火炬.保留格式 ) 实验平均值.append(状态[平均值]) 实验平均平方值.append(状态["exp_avg_sq"]) 積木产品.append(状态[積木产品]) 状态步数.append(状态[步骤]) 返回 复杂的参数
[文档] @_use_grad_for_differentiable 定义 步长(自身, 闭包=): 执行单个优化步骤。 参数: 闭包(Callable,可选):一个重新评估模型并返回损失的闭包 和返回损失。 "文档" 自身._cuda_graph_capture_health_check() 损失 = 如果 闭包 : 火炬.启用梯度(): 损失 = 闭包() for 群组 自身.参数组: 带梯度的参数: 列表[张量] = 输入文本为空,请提供需要翻译的文本 梯度: 列表[张量] = 输入文本为空,请提供需要翻译的文本 实验平均值: 列表[张量] = 输入文本为空,请提供需要翻译的文本 实验平均平方值: 列表[张量] = 输入文本为空,请提供需要翻译的文本 mu_products: 列表[张量] = 输入文本为空,请提供需要翻译的文本 状态步数: 列表[张量] = 输入文本为空,请提供需要翻译的文本 beta1, beta2 = 角色(元组[float, float] 群组[贝塔]) 复杂的参数 = .初始化组( 群组, 带梯度的参数, 梯度, 实验平均值, 实验平均平方值, 穆产品, 状态步数, ) 纳达姆( 带梯度的参数, 梯度, 实验平均值, 实验平均平方值, 穆产品, 状态步数, beta1=beta1, beta2=beta2, 学习率=群组["lr"] 权重衰减=群组[权重衰减] 动量衰减=群组["动量衰减"] eps=群组[eps] 最大化=群组[最大化] 解耦权重衰减=群组["解耦权重衰减"] foreach=群组["foreach"] 可捕捉的=群组["可捕获的"] 可微分的=群组["可区分的"] 有复杂的=有复杂的, ) 返回 损失
NAdam.__doc__ = ( r实现 NAdam 算法。 .. math:: \begin{aligned} &\rule{110mm}{0.4pt} \\ &textbf{输入} : γ_t (学习率), β_1,β_2 (beta 参数), θ_0 (参数), f(θ) (目标函数) \\ &\hspace{13mm} \: λ (权重衰减), \:\psi (动量衰减) \\ &\hspace{13mm} 解耦权重衰减,\:\textit{最大化} \\ 初始化:m_0 ← 0(第一矩) v_0 \leftarrow 0 \text{ (第二矩)} \\[-1.ex] &\rule{110mm}{0.4pt} \\ for t=1 到 ... do &\hspace{5mm}\textbf{if} \: \textit{maximize}: \\ &\hspace{10mm}g_t \leftarrow -\nabla_{\theta} f_t (\theta_{t-1}) \\ 否则 &\hspace{10mm}g_t \leftarrow \nabla_{\theta} f_t (\theta_{t-1}) \\ &\hspace{5mm} \theta_t \leftarrow \theta_{t-1} \\ 如果 λ 不等于 0 &\hspace{10mm}\textbf{if} \: \textit{decoupled\_weight\_decay} \\ &\hspace{15mm} \theta_t \leftarrow \theta_{t-1} - \gamma \lambda \theta_{t-1} \\ &\hspace{10mm}\textbf{else} \\ &\hspace{15mm} g_t \leftarrow g_t + \lambda \theta_{t-1} \\ &\hspace{5mm} \mu_t \leftarrow \beta_1 \big(1 - \frac{1}{2} 0.96^{t \psi} \big) \\ μ_{t+1} ← β_1(1 - \frac{1}{2} 0.96^{(t+1)ψ}) m_t ← β_1 m_{t-1} + (1 - β_1) g_t \\ v_t ← β_2 v_{t-1} + (1-β_2) g^2_t \\ \widehat{m_t} ← μ_{t+1} m_t/(1-\prod_{i=1}^{t+1}\mu_i)\\[-1.ex] + (1-μ_t) g_t /(1-\prod_{i=1}^{t} \mu_{i}) \widehat{v_t} ← v_t/(1-\beta_2^t \big) &\hspace{5mm}\theta_t \leftarrow \theta_t - \gamma \widehat{m_t}/ \big(√{\widehat{v_t}} + ε \big) \\ &\规则{110 毫米}{0.4 点} \\[-1.ex] &\bf{return} \: \theta_t \\[-1.ex] &\规则{110 毫米}{0.4 点} \\[-1.ex] \end{aligned} 关于算法的更多细节,请参阅《将 Nesterov 动量引入 Adam》_。 "文档" + rf"" 参数: {_params_doc} lr (float, Tensor, 可选): 学习率(默认:2e-3) betas (Tuple[float, float], 可选): 用于计算系数 运行平均梯度及其平方(默认:(0.9, 0.999)) eps (float, 可选): 添加到分母中的项,以改善 数值稳定性(默认:1e-8) 权重衰减(float,可选):权重衰减(L2 惩罚)(默认:0) momentum_decay (浮点数,可选): 动量衰减率 momentum_decay (默认: 4e-3) decoupled_weight_decay (布尔值,可选): 是否解耦权重 衰减如 AdamW 以获得 NAdamW。如果为 True,则算法不 累积动量和无方差中的权重衰减。(默认:False) {_foreach_doc} {_maximize_doc} {_可捕获文档} {_可区分文档} 将 Nesterov 动量融入 Adam https://openreview.net/forum?id=OM0jvwB8jIp57ZJjtNEZ .. _解耦权重衰减正则化 https://arxiv.org/abs/1711.05101 "文档" ) 定义 单个 Tensor_Nadam( 参数: 列表[张量] 梯度: 列表[张量] 实验平均值: 列表[张量] 实验平均平方值: 列表[张量] 慕产品: 列表[张量] 状态步数: 列表[张量] *, beta1: float, beta2: float, 学习率: float, 权重衰减: float, 动量衰减: float, eps: float, 解耦权重衰减: 布尔, 最大化: 布尔, 可捕捉的: 布尔, 可微分的: 布尔, 有复杂的: 布尔, ): for i, 参数 列举(参数): 梯度 = 梯度[i] 如果 最大化 否则 -梯度[i] 指数平均值 = 实验平均值[i] 指数平均值的平方 = 实验平均平方值[i] 穆产品 = mu_products[i] 步骤_t = 状态步数[i] 如果 火炬.是复杂的(参数): 参数 = 火炬.真实查看(参数) 梯度 = 火炬.真实查看(研究生) 指数平均值 = 火炬.真实查看(exp_avg) 指数平均值的平方 = 火炬.真实查看(指数平均平方) 如果编译,编译器将处理 cudagraph 检查,参见注释[torch.compile x capturable] 如果 火炬.编译器.is_compiling() 可捕捉的: 可捕获支持的设备 = _获取可捕获的设备() 断言 ( 参数.设备.类型 == mu_product.设备.类型 == 步骤_t.设备.类型 参数.设备.类型 可捕获支持的设备 ), ( f如果 capturable=True,params、mu_products 和 state_steps 必须是 f"支持设备上:"{支持捕获的设备} ) # 更新步骤 步骤_t += 1 如果 可捕捉的: 步骤 = 步骤_t else: 步骤 = _获取值(步骤_t) 偏差校正 2 = 1 - beta2**步骤 如果 权重衰减 != 0: 如果 解耦权重衰减: 执行步长权重衰减 参数.mul_(1 - 左右 * 权重衰减) else: 梯度 = 研究生.添加(参数, 阿尔法=权重衰减) 计算动量缓存 \mu^{t} 和 \mu^{t+1} mu = beta1 * (1.0 - 0.5 * (0.96 ** (步骤 * 动量衰减))) mu_next = beta1 * (1.0 - 0.5 * (0.96 ** ((步骤 + 1) * 动量衰减))) 更新 mu_product mu_product *= mu # 衰减第一个和第二个矩度的运行平均值系数 exp_avg.线性插值 _(研究生, 1 - beta1) 指数平均平方.mul_(beta2).addcmul_(研究生, 研究生, =1 - beta2) 分母 = 指数平均平方.除法(偏差校正 2).平方根() 如果 可微分的 或者 可捕捉的: 分母 = denom.添加(eps) # 让自动微分跟踪操作 通过直接更新 grad 和 exp_avg 而不是使用 # scalar "value" 参数的 addcdiv。 mu_product_next = mu 产品 * mu 下一个 梯度 = 梯度 * (-左右 * (1.0 - ) / (1.0 - mu_product)) 指数平均值 = 指数平均值 * (-左右 * mu_next / (1.0 - mu_product_next)) 参数.addcdiv_(研究生, denom) 参数.addcdiv_(exp_avg, denom) else: mu_product_next = _获取值(mu_product) * mu_next denom.加_(eps) 参数.addcdiv_( 研究生, denom, =(-左右 * (1.0 - ) / (1.0 - _获取值(mu 产品))) ) 参数.addcdiv_( exp_avg, denom, =(-左右 * mu 下一个) / (1.0 - mu 产品下一个) ) 定义 多张量 Nadam( 参数: 列表[张量] 梯度: 列表[张量] 实验平均值: 列表[张量] 实验平均平方值: 列表[张量] 穆产品: 列表[张量] 状态步数: 列表[张量] *, beta1: float, beta2: float, 学习率: float, 权重衰减: float, 动量衰减: float, eps: float, 解耦权重衰减: 布尔, 最大化: 布尔, 可捕捉的: 布尔, 可微分的: 布尔, 有复杂的: 布尔, ): 如果 长度(参数) == 0: 返回 断言 可微分的, "_foreach 操作不支持自动求导" 如果编译,编译器将处理 cudagraph 检查,参见注释[torch.compile x capturable] 如果 火炬.编译器.is_compiling() 可捕捉的: 可捕获支持的设备 = _获取可捕获的设备( 支持 XLA= ) 断言 所有( p.设备.类型 == mp.设备.类型 == 步长.设备.类型 p.设备.类型 可捕获支持的设备 for p, mp, 步骤 zip(参数, 積木产品, 状态步数) ), f如果 capturable=True,params、mu_products 和 state_steps 必须在支持的设备上:{支持捕获的设备} 分组张量 = 优化器.按设备类型和数据类型分组张量( [参数, 梯度, 实验平均值, 实验平均平方值, mu 产品, 状态步数] # type: ignore[list-item] ) for ( 分组参数_, 分组梯度_, 分组实验平均值_, 分组期望平均平方, 分组 mu 产品, 分组状态步骤_, ), _ 分组张量.(): 分组参数 = 角色(列表[张量] 分组参数_) 分组梯度 = 角色(列表[张量] 分组梯度_) 分组实验平均值 = 角色(列表[张量] 分组实验平均值_) 分组期望平均平方 = 角色(列表[张量] 分组经验平均平方) 分组μ乘积 = 角色(列表[张量] 分组μ乘积_) 分组状态步骤 = 角色(列表[张量] 分组状态步骤_) 处理复杂情况 如果 有复杂的: 真实查看( 分组参数, 分组梯度, 分组实验平均值, 分组实验平均平方 ) 如果 最大化: 分组梯度 = 火炬._foreach_neg(分组梯度) # 类型:忽略[赋值] # 更新步骤 # 如果步骤在 CPU 上,foreach 将回退到慢速路径,即通过循环调用 t.add(1) #然后 1 会被反复包裹成 Tensor,这比只包裹一次要慢。 #alpha 是必须的,以确保我们进入正确的重载。 如果 火炬.编译器.is_compiling() 分组状态步骤[0].是 CPU: 火炬._foreach_add_( 分组状态步骤, 火炬.张量(1.0, 设备="cpu"), 阿尔法=1.0 ) else: 火炬._foreach_add_(分组状态步骤, 1) 如果 权重衰减 != 0: 如果 解耦权重衰减: 执行步长权重衰减 火炬._foreach_mul_(分组参数, 1 - 左右 * 权重衰减) else: 重复使用已分配给最大化操作的中间内存(grouped_grads) 如果 最大化: 火炬._foreach_add_( 分组梯度, 分组参数, 阿尔法=权重衰减 ) else: 分组梯度 = 火炬._foreach_add_( # 类型:忽略[赋值] 分组梯度, 分组参数, 阿尔法=权重衰减 ) 衰减第一个和第二个矩度的运行平均值系数 火炬._foreach_lerp_(分组实验平均值, 分组梯度, 1 - beta1) 火炬._foreach_mul_(分组经验平均平方, beta2) 火炬._foreach_addcmul_( 分组经验平均平方, 分组梯度, 分组梯度, 1 - beta2 ) 期望平均平方根 = 火炬._foreach 平方根(分组经验平均平方) 偏差校正开平方: 并集[元组[张量, ...] 列表[张量]] : 并集[元组[张量, ...] 列表[张量]] 下一个μ值: 并集[元组[张量, ...] 列表[张量]] 如果 可捕捉的: # mus 将是 beta1 * (1 - 0.5 * 0.96 ** (步数 * 动量衰减)) 指数 = 火炬._foreach_mul(分组状态步骤, 动量衰减) = 火炬._foreach_pow(0.96, 指数) 火炬._foreach_mul_(, -0.5) 火炬._foreach_add_(, 1.0) 火炬._foreach_mul_(, beta1) # mu_nexts 将被 beta1 * (1 - 0.5 * 0.96 ** ((step + 1) * 动量衰减率)) 火炬._foreach_add_(指数, 动量衰减率) mu_nexts = torch._foreach_pow(0.96, 指数) 火炬._foreach_mul_(mu_nexts, -0.5) 火炬._foreach_add_(mu_nexts, 1.0) 火炬._foreach_mul_(mu_nexts, beta1) # 保存峰值内存,因为我们不再需要指数 删除 指数 偏差校正平方根 = 火炬._foreach_pow(beta2, 分组状态步骤) 不允许将标量作为 foreach_sub 的第一个参数 火炬._foreach_sub_(偏差校正平方根, 1.0) 火炬._foreach_neg_(偏差校正平方根) 火炬._foreach_sqrt__(偏差校正平方根) else: 偏差校正平方根 = [ (1 - beta2 ** _获取值(步长)) ** 0.5 for 步骤 分组状态步骤 ] = [ beta1 * (1.0 - 0.5 * (0.96 ** (_获取值(步长) * 动量衰减))) for 步骤 分组状态步骤 ] 下一个μ值 = [ beta1 * (1.0 - 0.5 * (0.96 ** ((_获取值(步长) + 1) * 动量衰减))) for 步骤 分组状态步骤 ] # 更新 mu_products 火炬._foreach_mul_(分组 mu_products, ) 火炬._foreach_div_(指数平均平方根, 偏差校正平方根) 火炬._foreach_add_(指数平均平方根, eps) # 显式删除 bias_correction 引用以节省内存 删除 bias_correction_sqrt 如果 可捕捉的: # 为 grad 构建步长乘数,重用 mus 的内存 火炬._foreach_sub_(, 1.0) 火炬._foreach_mul_(, 学习率) 不允许将标量作为 foreach_sub 的第一个参数 分母 = 火炬.遍历子项(分组_mu 产品, 1.0) 火炬._foreach_neg_(denom) 火炬._foreach_div_(, denom) # - lr * (1 - mu) / (1 - mu_product) 步长梯度 = 明确删除 denom 以节省内存 删除 分母 为 exp_avg 构建步长乘数,重用 mu_nexts 的内存 分母 = 火炬._foreach_mul(分组 mu 乘积, mu_nexts) torch._foreach_mul_(mu_nexts, 学习率) # foreach_sub 不允许将标量作为第一个参数,但没关系 # 我们无论如何都需要这里有一个负号 火炬._foreach_sub_(denom, 1.0) 火炬._foreach_div_(mu_nexts, denom) # - lr * mu_next / (1 - mu_product * mu_next) step_size_expavg = mu_nexts # 明确删除 denom 以节省内存 删除 分母 # 我们不能原地修改 step_size_grads 因为它是一个 ScalarTensors 列表 # 并且与 grouped_grads 相乘将得到一个更大的 Tensors 列表 分子 = 火炬._foreach_mul(步长梯度, 分组梯度) 火炬._foreach_addcmul_(分子, 步长指数移动平均, 分组实验平均值) # 最后,更新参数 火炬._foreach_addcdiv_(分组参数, 分子, 指数平均平方根) else: 步长梯度 = _stack_if_compiling( [ (_获取值(学习率) * (1.0 - ) / (1.0 - _获取值(mu 产品))) * -1 for mu 产品, mu zip(分组 mu 产品, ) ] ) 步长指数移动平均 = _stack_if_compiling( [ ( _获取值(学习率) * mu_next / (1.0 - _获取值(mu_product) * mu_next) ) * -1 for mu_product, mu_next zip(分组 mu 产品, mu_nexts) ] ) 火炬._foreach_addcdiv_( 分组参数, 分组梯度, 指数平均平方根, 步长梯度 # type: ignore[arg-type] ) 火炬._foreach_addcdiv_( 分组参数, 分组实验平均值, 指数平均平方根, 步长指数加权平均 # type: ignore[arg-type] ) @_disable_dynamo_if_unsupported(单个张量函数=_单张张量 Nadam) 定义 nadam( 参数: 列表[张量] 梯度: 列表[张量] 实验平均值: 列表[张量] 实验平均平方值: 列表[张量] 積木产品: 列表[张量] 状态步数: 列表[张量] 使用 torchscript 编译的函数不支持带默认值的只写关键字参数问题 #70627 现由 torch/distributed/optim 编译的函数 API 参数 解耦权重衰减: 布尔值 = 错误, foreach: 可选[布尔] = , 可捕捉的: 布尔值 = 错误, 可微分的: 布尔值 = 错误, 有复杂的: 布尔值 = 错误, 最大化: 布尔值 = 错误, *, beta1: float, beta2: float, 学习率: float, 权重衰减: float, 动量衰减: float, eps: float, ): r执行 NAdam 算法计算的函数 API。 详细信息请参阅 :class:`~torch.optim.NAdam`。 "文档" 如果 所有(isinstance(t, 火炬.张量) for t 状态步数): 提升 运行时错误( "API 已更改,`state_steps`参数必须包含一个单例张量列表" ) 如果 所有(isinstance(t, 火炬.张量) for t 積木产品): 提升 运行时错误( "API 已更改,`mu_products`参数必须包含一个单例张量列表" ) 如果 foreach : _, foreach = 默认为融合或遍历( 参数, 可微分的, 使用融合的= ) 如果 foreach 火炬.算子.是否正在脚本化(): 提升 运行时错误("torch.jit.script 不支持 foreach 优化器") 如果 foreach 火炬.算子.是否正在脚本化(): 函数 = _multi_tensor_nadam else: 函数 = _single_tensor_nadam 函数( 参数, 梯度, 实验平均值, 实验平均平方值, 積木产品, 状态步数, beta1=beta1, beta2=beta2, 学习率=学习率, 权重衰减=权重衰减, 动量衰减=动量衰减, 最大化=最大化, 解耦权重衰减=解耦权重衰减, eps=eps, 可捕捉的=可捕捉的, 可微分的=可微分的, 有复杂的=有复杂的, )

© 版权所有 PyTorch 贡献者。

使用 Sphinx 构建,并使用 Read the Docs 提供的主题。

文档

查看 PyTorch 的全面开发者文档

查看文档

教程

深入了解初学者和高级开发者的教程

查看教程

资源

查找开发资源,获取您的疑问解答

查看资源