快捷键

PyTorch 优化器 Adam 的源代码

# mypy: 允许未类型化定义
from 打字 导入 角色, 可选, 联合

导入 火炬
from 火炬 导入 张量

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


全部 = ["Adam", 亚当]


[文档] 亚当(优化器): def __init__( , 参数: 参数 T, lr: 并集[float, 张量] = 0.001, 测试版: 元组[并集[float, 张量] 并集[float, 张量]] = (0.9, 0.999), eps: 浮点数 = 1e-8, 权重衰减: 浮点数 = 0, amsgrad: 布尔值 = 错误, *, foreach: 可选[布尔] = , 最大化: 布尔值 = 错误, 可捕捉的: 布尔值 = 错误, 可微: 布尔值 = 错误, 融合: 可选[布尔] = , 解耦权重衰减: 布尔值 = 错误, ): 如果 isinstance(学习率, 张量): 如果 foreach 可捕捉的: 提升 ValueError( "不支持在 capturable=False 和 foreach=True 的情况下将 lr 作为 Tensor" ) 如果 学习率.元素数量() != 1: 提升 ValueError("Tensor lr 必须是 1 个元素") 如果 0.0 <= 学习率: 提升 ValueError(f"无效的学习率:"{学习率}") 如果 0.0 <= eps: 提升 ValueError(f"无效的 epsilon 值:"{eps}") 如果 0.0 <= β值[0] < 1.0: 提升 ValueError(f"在索引 0 处的无效 beta 参数:"{β值[0]}") 如果 0.0 <= betas[1] < 1.0: 提升 ValueError(f"无效的 beta 参数,索引为 1:"{betas[1]}") 如果 0.0 <= 权重衰减: 提升 ValueError(f"无效的 weight_decay 值:"{权重衰减}") 如果 ( (isinstance(贝塔[0] float) isinstance(贝塔[1] float)) 或者 (isinstance(贝塔[0] 张量) isinstance(贝塔[1] 张量)) ): 提升 ValueError("beta 必须同时为浮点数或同时为张量") 如果 isinstance(betas[0] 张量): 如果 可捕获的 foreach: 提升 ValueError( "当 capturable=False 且 foreach=True 时,betas[0]作为张量不支持" ) 如果 betas[0].元素数量() != 1: 提升 ValueError("张量 betas[0]必须是一维的") 如果 isinstance(betas[1] 张量): 如果 可捕获的 foreach: 提升 ValueError( "betas[1]作为一个张量不支持 capturable=False 和 foreach=True" ) 如果 betas[1].元素数量() != 1: 提升 ValueError("Tensor betas[1] 必须是 1 个元素") 默认 = 字典( 学习率=学习率, beta 参数=β测试, eps=eps, 权重衰减=权重衰减, amsgrad=amsgrad, 最大化=最大化, foreach=foreach, 可捕获的=可捕捉的, 可区分的=可区分的, 融合的=融合, 解耦权重衰减=解耦权重衰减, ) 超级().__init__(参数, 默认值) 如果 融合: 如果 可微分的: 提升 运行时错误("融合"不支持"可微分的") ._支持_step_supports_amp_scaling = 真实 # TODO(crcrpar): [低精度参数及其高精度副本] 支持使用 FP16/BF16 模型参数的 AMP,这些参数需要 将参数以更高的精度复制以进行更新数学运算,从而 减轻信息丢失。 如果 foreach: 提升 运行时错误("fused"和"foreach"不能同时为"True"。) def __setstate__(, 状态): 超级().__setstate__(状态) 群组 .参数组: 群组.setdefault("amsgrad", 错误) 群组.setdefault(最大化, 错误) 群组.setdefault("foreach", ) 群组.setdefault("可捕获的", 错误) 群组.setdefault("可区分的", 错误) 群组.setdefault("解耦权重衰减", 错误) 融合的 = 群组.setdefault(融合, ) p 群组[参数]: 状态 = .状态.获取(p, [] 如果 长度(状态) != 0 火炬.is_tensor(状态[步骤)] 步骤值 = float(状态[步骤]) 状态[步骤] = ( 火炬.张量( 步长值, 数据类型=_获取标量数据类型(是否融合=融合), 设备=p.设备, ) 如果 群组["可捕获的"] 或者 群组["融合的"] 否则 火炬.张量(步值, 数据类型=_获取标量数据类型()) ) def 初始化组( , 群组, 带梯度的参数, 梯度, 实验平均值, 实验平均值的平方, 最大期望平均平方, 状态步数, ): 是否复杂 = p 群组[参数]: 如果 p.梯度 is : 复杂的参数 |= 火炬.是复杂的(p) 带梯度的参数.append(p) 如果 p.研究生.is_sparse: 提升 运行时错误( "Adam 不支持稀疏梯度,请考虑使用 SparseAdam" ) 梯度.append(p.研究生) 状态 = .状态[p] 懒加载状态初始化 如果 长度(状态) == 0: 如果 群组[混合]: _设备类型检查混合(p) # note(crcrpar): [为步骤提供特殊设备托管] # 故意将 `step` 托管在 CPU 上,如果可捕获和混合都关闭。 因为在 CUDA 和 XLA 上内核启动代价高昂。 状态[步骤] = ( 火炬.零值( (), 数据类型=_获取标量数据类型(是否已融合=群组[融合)] 设备=p.设备, ) 如果 群组[可捕获的] 或者 群组[融合] 否则 火炬.张量(0.0, 数据类型=_获取标量数据类型()) ) 指数移动平均的梯度值 状态["exp_avg"] = 火炬.等于零的( p, 内存格式=火炬.保留格式 ) 平方梯度值的指数移动平均 状态["exp_avg_sq"] = 火炬.等于零的( p, 内存格式=火炬.保留格式 ) 如果 群组["amsgrad"]: # 维持所有平方梯度值的指数移动平均的最大值 状态["max_exp_avg_sq"] = 火炬.等于零的( p, 内存格式=火炬.保留格式 ) 平均值.append(状态[平均值]) 平均值平方.append(状态["exp_avg_sq"]) 如果 群组["amsgrad"]: max_exp_avg_sqs.append(状态["max_exp_avg_sq"]) 如果 群组["可微分的"] 状态["步"].需要梯度: 提升 运行时错误( "在可微分模式下,`requires_grad` 对 `step` 不支持" ) 不带捕获的 foreach 不支持 tensor lr 如果 ( 群组["foreach"] 火炬.is_tensor(群组["学习率 (lr)"]) 群组["可捕获的"] ): 提升 运行时错误( "当 capturable=False 且 foreach=True 时,lr 作为 Tensor 不受支持" ) 状态步数.append(状态["步骤"]) 返回 具有复杂性
[文档] @_use_grad_for_differentiable def 步长(, 闭包=): 执行单次优化步骤。 参数: 闭包(Callable,可选):一个重新评估模型并返回损失的闭包。 并返回损失。 "文档" .CUDA 图捕获健康检查() 损失 = 如果 闭包 is : 火炬.启用梯度(): 损失 = 闭包() 群组 .参数组: 带梯度的参数: 列表[张量] = 输入文本为空,请提供需要翻译的文本 梯度: 列表[张量] = 输入文本为空,请提供需要翻译的文本 实验平均值: 列表[张量] = 输入文本为空,请提供需要翻译的文本 实验平均值的平方: 列表[张量] = 输入文本为空,请提供需要翻译的文本 最大经验平均平方: 列表[张量] = 输入文本为空,请提供需要翻译的文本 状态步数: 列表[张量] = 输入文本为空,请提供需要翻译的文本 beta1, beta2 = 群组[betas] has_complex = ._init_group( 群组, params_with_grad, 梯度, 实验平均值, 实验平均平方值, 最大实验平均平方值, 状态步数, ) 亚当( 带梯度的参数, 梯度, 指数平均值, 指数平均值的平方, 最大期望平均平方, 状态步数, amsgrad=群组["amsgrad"] 复杂的=复杂的, beta1=beta1, beta2=beta2, lr=群组["lr"] 权重衰减=群组["权重衰减"] eps=群组["eps"] 最大化=群组[最大化] foreach=群组[foreach] 可捕获的=群组["可捕获的"] 可区分的=群组["可区分的"] 融合的=群组[混合] 梯度缩放=getattr(, 梯度缩放, ), 找到无穷大=getattr(, "发现感染", ), 解耦权重衰减=群组["解耦权重衰减"] ) 返回 损失
Adam.__doc__ = ( r实现 Adam 算法。 .. math:: \begin{aligned} &\rule{110mm}{0.4pt} \\ &\textbf{输入} : \gamma \text{ (学习率)}, \beta_1, \beta_2 \text{ (beta 值)},\theta_0 \text{ (参数)},f(\theta) \text{ (目标函数)} \\ &\hspace{13mm} λ(权重衰减), \: \textit{amsgrad} 最大化,ε(epsilon) 初始化:m_0 ← 0(第一矩) v_0←0(二阶矩),v_0^max←0 &\rule{110mm}{0.4pt} \\ &\textbf{for} \: t=1 \: \textbf{to} \: \ldots \: \textbf{do} \\ &\hspace{5mm}\textbf{if} \: \textit{maximize}: \\ &\hspace{10mm}g_t \leftarrow -\nabla_{\theta} f_t (\theta_{t-1}) \\ &\hspace{5mm}\textbf{else} \\ &\hspace{10mm}g_t \leftarrow \nabla_{\theta} f_t (\theta_{t-1}) \\ &\hspace{5mm}\textbf{if} \: \lambda \neq 0 \\ &\hspace{10mm} g_t \leftarrow g_t + \lambda \theta_{t-1} \\ m_t ← β_1 m_{t-1} + (1 - β_1) g_t \\ v_t ← β_2 v_{t-1} + (1-β_2) g^2_t \\ &\hspace{5mm}\widehat{m_t} \leftarrow m_t/\big(1-\beta_1^t \big) \\ 翻译:&\hspace{5mm}\widehat{m_t} \leftarrow m_t/\big(1-\beta_1^t \big) \\ 如果 amsgrad v_t^{max} ← max(v_{t-1}^{max},v_t) &\hspace{10mm}\widehat{v_t} \leftarrow \frac{v_t^{max}}{1-\beta_2^t} \\ 否则 &\hspace{10mm}\widehat{v_t} \leftarrow v_t/\big(1-\beta_2^t \big) \\ 翻译:&\hspace{10mm}\widehat{v_t} \leftarrow v_t/(1-β_2^t) \\ &\hspace{5mm}θ_t ← θ_{t-1} - γ \widehat{m_t}/ \big(√{\widehat{v_t}} + ε \big) \\ &\rule{110mm}{0.4pt} \\[-1.ex] &\bf{return} \: θ_t \\[-1.ex] &\规则{110 毫米}{0.4 点} \\[-1.ex] \end{aligned} 关于算法的更多详细信息,请参阅《Adam:随机优化的方法》_。 "文档" + rf"" 参数: {_params_doc} lr (float, Tensor, 可选): 学习率(默认:1e-3)。一个张量 LR 目前不支持我们所有实现。请使用浮点数 LR 如果您也没有指定 fused=True 或 capturable=True。 betas (Tuple[float, float], 可选): 用于计算系数 运行平均梯度及其平方(默认:(0.9, 0.999)) eps (float, 可选): 添加到分母中的项,以改善 数值稳定性(默认:1e-8) 权重衰减(float,可选):权重衰减(L2 惩罚)(默认:0) decoupled_weight_decay (布尔值,可选): 如果为 True,则此优化器等价于 AdamW,并且算法不会累积动量衰减和方差。 (默认:False) equivalent to AdamW and the algorithm will not accumulate weight decay in the momentum nor variance. (default: False) amsgrad (布尔值,可选): 是否使用此算法的 AMSGrad 变体 amsgrad (bool, optional): 是否使用该优化器的 AMSGrad 变体 论文《Adam 及其超越的收敛》中的算法 (默认:False) {_foreach_doc} {_maximize_doc} {_可捕获文档} {_可区分文档} {_融合文档} .. 注意:: Adam 和 AdamW 的原型实现支持 MPS,并使用`torch.float32`和`torch.float16`。 .. _Adam:随机优化的方法: https://arxiv.org/abs/1412.6980 .. _关于 Adam 收敛性及其超越: https://openreview.net/forum?id=ryQu7f-RZ "文档" ) def _single_tensor_adam( 参数: 列表[张量] 梯度: 列表[张量] 实验平均值: 列表[张量] 实验平均平方值: 列表[张量] 最大实验平均平方值: 列表[张量] 状态步数: 列表[张量] 梯度缩放: 可选[张量] 找到无穷大: 可选[张量] *, amsgrad: 布尔, 复杂的: 布尔, beta1: 并集[float, 张量] beta2: 并集[float, 张量] 学习率: 并集[float, 张量] 权重衰减: float, eps: float, 最大化: 布尔, 可捕获的: 布尔, 可微分的: 布尔, 解耦权重衰减: 布尔, ): 断言 梯度缩放 is 找到无穷大 is 如果 火炬.算子.是否正在脚本化(): 此断言是因为即时编译器愚蠢,没有意识到下面的操作 # 具有处理浮点数和 Tensor lrs 的重载,所以我们只需断言它是 # 一个浮点数,因为大多数使用即时编译(JIT)的人都在使用浮点数 断言 isinstance(lr, float) 断言 isinstance(beta1, float) 断言 isinstance(beta2, float) # 我们只在 beta 是张量时才对其进行打乱,否则,我们更倾向于将其视为标量。 # 将其视为标量。 # 注意:确保在条件检查 isinstance 下声明类型 # 否则 torchscript 会对 DeviceDict 类型感到烦躁。 如果 isinstance(beta1, 张量): beta1_dict Beta1 字典: 可选[设备数据类型字典] = {(beta1.设备, beta1.数据类型): beta1} else: beta1_字典 = i, 参数 列举(参数): 梯度 = 梯度[i] 如果 最大化 否则 -梯度[i] 指数平均值 = 指数平均值列表[i] 指数平均平方 = exp_avg_sqs[i] step_t = 状态步骤[i] 如果编译,编译器将处理 cudagraph 检查,参见注释[torch.compile x capturable] 如果 火炬.编译器.编译中() 可捕捉的: 支持捕获的设备 = _获取支持捕获的设备() 断言 ( 参数.设备.类型 == step_t.设备.类型 参数.设备.类型 支持捕获的设备 ), f如果 capturable=True,则 params 和 state_steps 必须在支持的设备上:{支持捕获的设备} # 更新步骤 step_t += 1 如果 权重衰减 != 0: 如果 解耦权重衰减: 执行步长权重衰减 参数.mul_(1 - 学习率 * 权重衰减) else: # 嵌套 if 是必要的以绕过 jitscript 规则 如果 可微分的 isinstance(权重衰减, 张量): 如果 权重衰减.需要梯度: 梯度 = 研究生.addcmul_(参数.克隆(), 权重衰减) else: 梯度 = 研究生.添加(参数, 阿尔法=权重衰减) else: 梯度 = 研究生.添加(参数, 阿尔法=权重衰减) 如果 火炬.是复杂的(参数): 梯度 = 火炬.真实查看(研究生) 指数平均值 = 火炬.真实查看(指数平均值) 指数平均值的平方 = 火炬.真实查看(指数平均值的平方) 如果 amsgrad: 最大指数平均平方[i] = 火炬.真实查看(最大指数平均平方[i]) 参数 = 火炬.真实查看(参数) 设备 = 参数.设备 如果 beta1_dict is : dtype = 参数.dtype # 类型:忽略[联合属性] # 转换以解决 https://github.com/pytorch/pytorch/issues/140601 key = (设备, 数据类型) 如果 key beta1 字典: beta1 字典[] = beta1.(设备=设备, 数据类型=数据类型, 非阻塞=) # 类型:忽略[联合属性] 设备_beta1: 并集[float, 张量] = beta1 字典[] else: 设备_beta1 = beta1 衰减第一个和第二个矩度的运行平均值系数 指数平均.线性插值_(研究生, 1 - 设备_beta1) # 需要嵌套 if 以绕过 jitscript 规则 如果 可微分的 isinstance(beta2, 张量): 如果 beta2.需要梯度: 因为 addcmul 的值不能是一个张量,所以使用 lerp 只使用 2 个操作 展示可微路径与不可微路径的等价性 expavg * b2 + grad^2 * (1-b2) add expavg * (1-b2) - expavg * (1-b2) = 0 # expavg * b2 + expavg * (1-b2) - expavg * (1-b2) + 梯度平方 * (1-b2) # expavg - expavg * (1-b2) + 梯度平方 * (1-b2) # expavg + (梯度平方 - expavg) * (1-b2) # expavg.lerp(梯度平方, 1-beta2) 指数平均平方.线性插值 _(火炬.平方(研究生), 重量=1 - beta2) else: 指数平均平方.mul_(beta2).addcmul_(研究生, 研究生, =1 - beta2) else: exp_avg_sq.mul_(beta2).addcmul_(研究生, 研究生, =1 - beta2) 如果 可捕获的 或者 differentiable: 步骤 = 步骤_t # 嵌套 if 是必要的,以绕过 jitscript 规则 如果 可微分的 isinstance(beta1, 张量): 如果 beta1.需要梯度: 偏差校正 1 = 1 - beta1 ** 步长.克隆() else: 偏差校正 1 = 1 - beta1**步骤 else: 偏差校正 1 = 1 - beta1**步骤 嵌套 if 语句是绕过 jitscript 规则的必要条件 如果 可微分的 isinstance(beta2, 张量): 如果 beta2.需要梯度: 偏差校正 2 = 1 - beta2 ** 步长.克隆() else: 偏差校正 2 = 1 - beta2**步骤 else: 偏差校正 2 = 1 - beta2**步骤 步长 = 学习率 / 偏差校正 1 步长负值 = 步长.否定() 偏差校正 2 开方 = 偏差校正 2.平方根() 如果 amsgrad: # 维持到目前为止所有 2 次方运行平均值的最大值 如果 differentiable: 最大指数平均平方 = 最大指数平均平方[i].克隆() else: 最大指数平均平方根 = 最大指数平均平方[i] 最大指数平均平方[i].复制_(火炬.最大值(最大期望平均平方, 期望平均平方)) # 使用最大值来归一化梯度的运行平均值 # 为了避免额外的参数集大小的读取和写入,这里折叠了(确实很丑陋的)1-elem 步长大小的数学计算 因为 addcdiv_需要值是数字,而不是张量,所以不能将其折叠到下面的 addcdiv_中 分母 = ( 最大指数平均平方[i].平方根() / (偏差校正 2 的平方根 * 步长负) ).加_(eps / 步长负) else: 币值 = ( 指数平均平方.平方根() / (偏差校正 2 的平方根 * 步长负值) ).加_(eps / 步长负) 如果 differentiable: 参数.addcdiv_(指数平均.克隆(), 分母) else: 参数.addcdiv_(exp_avg, denom) else: 步骤 = _get_value(步骤_t) 偏差校正 1 = 1 - beta1**步骤 偏差校正 2 = 1 - beta2**步骤 步长 = 学习率 / 偏差校正 1 偏差校正 2 的平方根 = 偏差校正 2**0.5 如果 amsgrad: # 维持到目前为止所有 2 次矩运行平均值的最大值 火炬.最大值(最大指数平均平方[i] 指数平均平方, 输出=最大指数平均平方[i]) 使用最大值来归一化梯度的运行平均 分母 = (最大指数平均平方[i].平方根() / 偏差校正 2 的平方根).加_(eps) else: 分母 = (指数平均平方.平方根() / 偏差校正 2 的平方根).加_(eps) 参数.addcdiv_(exp_avg, denom, =-步长) 最后,切换回复杂视图 如果 amsgrad 火炬.是复杂的(参数[i)] 最大指数平均平方[i] = 火炬.以复数视图查看(最大期望平均平方[i]) def _多张量 Adam( 参数: 列表[张量] 梯度: 列表[张量] 期望平均值: 列表[张量] 指数平均平方: 列表[张量] 最大指数平均平方: 列表[张量] 状态步长: 列表[张量] 梯度缩放: 可选[张量] 发现_信息: 可选[张量] *, amsgrad: 布尔, 有复杂的: 布尔, beta1: 并集[float, 张量] beta2: 并集[float, 张量] 学习率: 并集[float, 张量] 权重衰减: float, eps: float, 最大化: 布尔, 可捕获的: 布尔, differentiable: 布尔, 解耦权重衰减: 布尔, ): 如果 长度(参数) == 0: 返回 如果 isinstance(学习率, 张量) 可捕获的: 提升 运行时错误( "lr 作为一个 Tensor 不支持 capturable=False 和 foreach=True" ) 如果 isinstance(beta1, 张量): 如果 可捕获的: 提升 ValueError( "beta1 作为 Tensor 不支持 capturable=False 和 foreach=True" ) 如果 beta1.元素数量() != 1: 提升 ValueError("Tensor beta1 必须是 1 个元素") 如果 isinstance(beta2, 张量): 如果 可捕获的: 提升 ValueError( "beta2 作为张量不支持 capturable=False 和 foreach=True" ) 如果 beta2.元素数量() != 1: 提升 ValueError(张量 beta2 必须是 1 元素) # 如果正在编译,编译器将处理 cudagraph 检查,参见注解[torch.compile x capturable] 如果 火炬.编译器.正在编译() 可捕获的: 支持的设备 = _获取支持的设备( 支持 XLA= ) 断言 所有( p.设备.类型 == 步长.设备.类型 p.设备.类型 支持的设备 p, 步骤 zip(参数, 状态步数) ), f如果 capturable=True,则 params 和 state_steps 必须在支持的设备上:{capturable 支持的设备} 断言 梯度缩放 is 找到无穷大 is 断言 differentiable, "_foreach 操作不支持自动微分" 分组张量 = 优化器.按设备类型和数据类型分组张量( [参数, 梯度, 实验平均值, 实验平均平方值, 最大实验平均平方值, 状态步数] # type: ignore[list-item] ) 仅当 beta 是张量且在 CUDA 上时才对其进行打乱,否则我们更倾向于将其视为标量。 将其视为标量。 beta1_dict: 可选[DeviceDict] = ( # 类型: 忽略[attr-defined] {beta1.设备: beta1} 如果 isinstance(beta1, 张量) 字符串(beta1.设备) != cpu 否则 ) ( 设备参数_, device_grads_, 设备经验平均值_, 设备_exp_avg_sqs 平均值_, 设备_max_exp_avg_sqs 最大值_, 设备状态步骤_, ), _ 分组张量.(): 设备参数 = 角色(列表[张量] 设备参数_) 设备梯度 = 角色(列表[张量] device_grads_) 设备实验平均值 = 角色(列表[张量] 设备实验平均值_) 设备经验平均平方根 = 角色(列表[张量] 设备经验平均平方根_) 设备状态步骤 = 角色(列表[张量] 设备状态步骤_) 设备 = 设备参数[0].设备 如果 beta1_dict Beta1 字典 is 设备 beta1_dict: beta1_dict[设备] = beta1.(设备=设备, 非阻塞=) # type: ignore[union-attr, attr-defined] 设备_beta1 = beta1 字典[设备] 如果 beta1_dict Beta1 字典 否则 beta1 处理复杂参数 如果 复杂的: 如果 amsgrad: 设备最大指数平均平方 = 角色(列表[张量] 设备最大指数平均平方_) _视图作为真实( 设备参数, 设备梯度, 设备经验平均值, 设备经验平均平方, 设备最大经验平均平方, ) else: 真实查看( 设备参数, 设备梯度, 设备实验平均值, 设备实验平均平方 ) 如果 最大化: 设备梯度 = 火炬._foreach_neg(设备梯度) # 类型:忽略[赋值] # 更新步骤 # 如果步骤在 CPU 上,foreach 将回退到慢速路径,即通过循环调用 t.add(1) #然后 1 会被反复包裹成 Tensor,这比只包裹一次要慢。 #alpha 是必须的,以确保我们进入正确的重载。 如果 火炬.编译器.正在编译() device_state_steps[0].是 CPU: 火炬._foreach_add_( device_state_steps, 火炬.张量(1.0, 设备="cpu"), 阿尔法=1.0 ) else: 火炬._foreach_add_(设备状态步骤, 1) 如果 权重衰减 != 0: 如果 解耦权重衰减: 执行步长权重衰减 火炬._foreach_mul_(设备参数, 1 - lr * 权重衰减) else: 重新使用为最大化已分配的中间内存(device_grads) 如果 最大化: 火炬._foreach_add_(device_grads, 设备参数, 阿尔法=权重衰减) else: 设备梯度 = 火炬._foreach_add_( # 类型:忽略[赋值] device_grads, 设备参数, 阿尔法=权重衰减 ) # 衰减第一和第二矩运行平均系数 使用设备 beta1,如果 beta1 是一个张量以确保所有 张量位于同一设备上 火炬._foreach_lerp_(设备经验平均值, 设备梯度, 1 - device_beta1) 火炬._foreach_mul_(device_exp_avg_sqs, beta2) 由于_foreach_addcmul API 的严格性,我们不能有一个单独的 # 张量标量作为标量参数(仅支持 Python 数字) 因此,将值 mul 单独提取出来 # 已提交 https://github.com/pytorch/pytorch/issues/139795 如果 isinstance(beta2, 火炬.张量): 扩展设备梯度 = 火炬._foreach_mul(设备梯度, 1 - beta2) # 类型:忽略[赋值] = 1.0 else: 扩放设备梯度 = 设备梯度 # 类型:忽略[赋值] = 1 - beta2 火炬._foreach_addcmul_( 设备指数平均平方, 扩放设备梯度, 设备梯度, ) # 删除本地中间文件(因为它们将不再使用,以节省峰值内存) 删除 设备梯度 删除 扩放设备梯度 偏差校正 1: 并集[元组[张量, ...] 列表[张量]] 偏差校正 2: 并集[元组[张量, ...] 列表[张量]] 偏差校正 2 的平方根: 并集[元组[张量, ...] 列表[张量]] 如果 可捕获的: 偏差校正 1 = 火炬._foreach_pow(beta1, device_state_steps) # type: ignore[arg-type] 偏差校正 2 = 火炬._foreach_pow(beta2, device_state_steps) # type: ignore[arg-type] # foreach_sub 不允许将标量作为第一个参数 火炬._foreach_sub_(偏差校正 1, 1) 火炬._foreach_sub_(偏差校正 2, 1) # 我们不否定偏差校正 1,因为它稍后还需要被否定 火炬._foreach_neg_(偏差校正 2) # foreach_div 不允许将标量作为第一个参数 火炬._foreach_div_(偏差校正 1, 学习率) 火炬._foreach_reciprocal_(偏差校正 1) 火炬._foreach_sqrt__(偏差校正 2) 为了清晰起见重新分配:我们将保持最少的中间变量:我们将有 步长 = - lr / (1 - beta1 ^ t) 其中 t = num_steps 偏差校正 2 的平方根 = sqrt(1 - beta2 ^ t) 步长 = 偏差校正 1 偏差校正 2 开平方 = 偏差校正 2 如果 amsgrad: 设备最大经验平均平方 = 角色(列表[张量] device_max_exp_avg_sqs_) # 维护到目前为止所有二阶矩运行平均的最大值 火炬._foreach_maximum_(device_max_exp_avg_sqs, device_exp_avg_sqs) # 类型:忽略[赋值] 将中间值设置为最大值,用于在 amsgrad 时归一化梯度的运行平均值 exp_avg_sq_sqrt = 火炬._foreach_sqrt(设备最大期望平均平方和) else: 期望平均平方根 = 火炬._foreach 平方根(设备期望平均平方和) 火炬._foreach_div_(exp_avg_sq_sqrt, 偏差校正 2 平方根) 火炬._foreach_add_(exp_avg_sq_sqrt, eps) 火炬._foreach_div_(指数平均平方根, 步长) # 在这一点上,exp_avg_sq_sqrt = - (1 - beta^t) * [sqrt(exp_avg_sq / (1 - beta2^t)) + eps] / lr 火炬._foreach_addcdiv_(设备参数, 设备经验平均值, 经验平均值平方根) else: 偏差校正 1 = [ 1 - beta1 ** _get_value(步长) 步长 设备状态步数 ] bias_correction2 = [ 1 - beta2 ** 获取值(步长) 步长 设备状态步数 ] 步长 = _编译时堆栈([(lr / bc) * -1 bc 偏差校正 1]) 偏差校正 2 平方根 = [bc**0.5 bc 偏差校正 2] # type: ignore[arg-type] 如果 amsgrad: device_max_exp_avg_sqs = 角色(列表[张量] device_max_exp_avg_sqs_) # 维护到目前为止所有二阶矩运行平均的最大值 火炬._foreach_maximum_(device_max_exp_avg_sqs, device_exp_avg_sqs) 使用最大值进行梯度运行平均值的归一化 exp_avg_sq_sqrt = 火炬._foreach_sqrt(设备最大期望平均平方和) else: 期望平均平方根 = 火炬._foreach 平方根(设备期望平均平方和) 火炬._foreach_div_(指数平均平方根, 偏差校正 2 平方根) 火炬._foreach_add_(指数平均平方根, eps) 火炬._foreach_addcdiv_( 设备参数, 设备经验平均值, 经验平均值平方根, 步长大小 # type: ignore[arg-type] ) def _fused_adam( 参数: 列表[张量] 梯度: 列表[张量] 实验平均值: 列表[张量] 实验平均平方值: 列表[张量] 最大实验平均平方值: 列表[张量] 状态步数: 列表[张量] 梯度缩放: 可选[张量] 发现无穷大: 可选[张量] *, amsgrad: 布尔, 具有复杂的: 布尔, # Needed for consistency. beta1: float, beta2: float, lr: 并集[float, 张量] 权重衰减: float, eps: float, 最大化: 布尔, 可捕获的: 布尔, # 需要保持一致性。 可区分的: 布尔, 解耦权重衰减: 布尔, ) 翻译 : 如果 参数: 返回 如果 可区分的: 提升 运行时错误("Adam 参数 fused=True 时,不支持 differentiable=True") 梯度缩放字典: 设备字典 = ( {梯度缩放.设备: 梯度缩放} 如果 梯度缩放 is 否则 {} ) 找到的 inf 字典: 设备字典 = ( {发现无穷大.设备: 发现无穷大} 如果 找到无穷大 is 否则 {} ) # 仅当 lr 是张量且在 CUDA 上时才对其进行随机排列,否则,我们更愿意将其视为标量。 # 处理它作为标量。 lr 字典: 可选[设备字典] = ( {学习率.设备: 学习率} 如果 isinstance(学习率, 张量) 字符串(学习率.设备) != cpu 否则 ) 分组张量 = 优化器.按设备类型和 dtype 分组张量( [参数, 梯度, 实验平均值, 实验平均值的平方, 最大期望平均平方, 状态步数] # 类型:忽略[列表项] ) (设备, _), ( ( 设备参数_, device_grads_, 设备经验平均值_, 设备经验平均平方值_, 设备最大经验平均平方值, 设备状态步数_, ), _, ) 分组张量.项目(): 设备参数 = 角色(列表[张量] 设备参数_) 设备梯度 = 角色(列表[张量] device_grads_) 设备经验平均值 = 角色(列表[张量] 设备经验平均值_) 设备经验平均值平方 = 角色(列表[张量] 设备经验平均平方) 设备状态步数 = 角色(列表[张量] 设备状态步骤_) 如果 设备.类型 == 国会议员: # 类型:忽略[联合属性] 断言 找到无穷大 is 梯度缩放 is 设备梯度缩放, 设备已找到 = , 如果 梯度缩放 is : 设备梯度缩放 = 梯度缩放字典.setdefault( 设备, 梯度缩放.(设备, 非阻塞=) ) 如果 找到无穷大 is : 设备已找到 = found_inf_dict.setdefault( 设备, 发现无穷大.(设备, 非阻塞=) ) 如果 lr_dict is 设备 lr_dict: lr_dict[设备] = 学习率.(设备=设备, 非阻塞=) # 类型:忽略[联合属性] lr = lr_dict[设备] 火炬._foreach_add_(设备状态步骤, 1) 函数 = 火炬._fused_adam_ 如果 解耦权重衰减 否则 火炬._fused_adamw_ 函数( 设备参数, 设备梯度, 设备经验平均值, 设备经验平均平方, 设备最大经验平均平方, # type: ignore[arg-type] 设备状态步骤, amsgrad=amsgrad, 学习率=学习率, # type: ignore[arg-type] beta1=beta1, beta2=beta2, 权重衰减=权重衰减, eps=eps, 最大化=最大化, 梯度缩放=设备梯度缩放, 发现无穷大=设备已找到, ) 如果 设备已找到 is : 火炬._foreach_sub_( 设备状态步骤, [设备已找到] * 长度(设备状态步骤) ) @_disable_dynamo_if_unsupported(单个张量函数=单个_tensor_adam) def adam( 参数: 列表[张量] 梯度: 列表[张量] 指数平均值列表: 列表[张量] 实验平均值的平方: 列表[张量] 最大指数平均平方: 列表[张量] 状态步骤: 列表[张量] # torchscript 编译的函数不支持只写参数默认值的问题 #70627 # 现在将其设置为 kwarg,因为功能 API 由 torch/distributed/optim 编译 foreach: 可选[布尔] = , 可捕获的: 布尔值 = 错误, 可微分的: 布尔值 = 错误, 融合的: 可选[布尔] = , 梯度缩放: 可选[张量] = , 发现无穷大: 可选[张量] = , 是否复杂: 布尔值 = 错误, 解耦权重衰减: 布尔值 = 错误, *, amsgrad: 布尔, beta1: float, beta2: float, lr: 并集[float, 张量] weight_decay: float, eps: float, maximize: 布尔, ): r执行 Adam 算法计算的函数式 API。 详细信息请参阅 :class:`~torch.optim.Adam`。 "文档" 当用户输入 False/True 用于 foreach 或 fused 时,请尊重。我们只想更改 # 默认值,当两个都没有被指定时。注意,我们默认为 foreach # 并传递 False 以使用_fused。这不是错误——我们希望提供融合实现 # 在将其作为默认值之前先进行内嵌时间,即使它通常更快。 如果 融合 is foreach is : _, foreach = _默认融合或 foreach( 参数, 可微分的, 使用融合= ) 不要在 lr 是 Tensor 且 capturable=False 的情况下开启 foreach。 如果 foreach isinstance(学习率, 张量) 可捕获的: foreach = 如果 融合 is : 融合 = 如果 foreach is : foreach = 此检查在编译期间较慢,因此我们跳过它。 如果确实需要,我们可以在 dynamo 中添加此检查。 如果 火炬.编译器.正在编译() 所有( isinstance(t, 火炬.张量) t 状态步骤 ): 提升 运行时错误( "API 已更改,`state_steps`参数必须包含一个单例张量列表" ) 如果 foreach 火炬.算子.是否正在脚本化(): 提升 运行时错误("torch.jit.script 不支持与 foreach 优化器一起使用") 如果 融合 火炬.算子.是否正在脚本化(): 提升 运行时错误("torch.jit.script 不支持与融合优化器一起使用") 如果 融合 火炬.算子.是否正在脚本化(): 函数 = _fused_adam 如果...否则 foreach 火炬.算子.是否正在脚本化(): 函数 = _multi_tensor_adam else: 函数 = _single_tensor_adam 函数( 参数, 梯度, 指数平均值列表, 指数平均平方, 最大指数平均平方, 状态步骤, amsgrad=amsgrad, 具有复杂的=具有复杂的, beta1=beta1, beta2=beta2, lr=lr, 权重衰减=权重衰减, eps=eps, maximize=maximize, 可捕获的=可捕获的, 可微分的=可微分的, 梯度缩放=梯度缩放, 发现_信息=发现_信息, 解耦权重衰减=解耦权重衰减, )

© 版权所有 PyTorch 贡献者。

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

文档

查看 PyTorch 的全面开发者文档

查看文档

教程

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

查看教程

资源

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

查看资源