# 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,
可捕获的=
可捕获的,
可微分的=
可微分的,
梯度缩放=
梯度缩放,
发现_信息=
发现_信息,
解耦权重衰减=
解耦权重衰减,
)