# mypy: 允许未类型化定义
from 打字
导入
角色,
可选,
联合
导入
火炬
from 火炬
导入
张量
from .优化器
导入 (
_capturable_doc,
默认为融合或遍历,
_可微分文档,
_如果不受支持则禁用 Dynamo,
_foreach 文档,
_获取可捕获支持的设备,
_获取标量数据类型,
_获取值,
_maximize_doc,
_params_doc,
_use_grad_for_differentiable,
_以真实视图,
优化器,
ParamsT,
)
全部 = ["Adamax", "adamax"]
[文档]
类 Adamax(
优化器):
def __init__(
我,
参数: ParamsT,
学习率:
并集[float,
张量] =
0.002,
beta 参数:
元组[float, float] = (0.9, 0.999),
eps: 浮点数 = 1e-8,
权重衰减:
浮点数 = 0,
foreach: 可选[
布尔] =
无,
*,
最大化:
布尔值 =
错误,
可微分的:
布尔值 =
错误,
可捕获的:
布尔值 =
错误,
):
如果 isinstance(
学习率,
张量)
和
学习率.
元素数量() != 1:
提升 ValueError(
"张量学习率必须为 1 个元素")
如果
不 0.0 <=
学习率:
提升 ValueError(f
"无效的学习率:"{
学习率}")
如果
不 0.0 <= eps:
提升 ValueError(f
"无效的 epsilon 值:"{eps}")
如果
不 0.0 <=
β测试[0] < 1.0:
提升 ValueError(f
"在索引 0 处的无效 beta 参数:"{
β测试[0]}")
如果
不 0.0 <=
β测试[1] < 1.0:
提升 ValueError(f
"在索引 1 处的无效 beta 参数:"{
β测试[1]}")
如果
不 0.0 <=
权重衰减:
提升 ValueError(f
"无效的权重衰减值:"{
权重衰减}")
默认 =
字典(
学习率=
学习率,
β测试=
β测试,
eps=eps,
权重衰减=
权重衰减,
foreach=foreach,
最大化=
最大化,
可微分的=
可微分的,
可捕获的=
可捕获的,
)
超级().__init__(
参数,
默认值)
def __setstate__(我,
状态):
超级().__setstate__(
状态)
为
群组
在
我.
参数组:
群组.setdefault(
foreach,
无)
群组.setdefault(
最大化,
错误)
群组.setdefault(
"可区分的",
错误)
群组.setdefault(
"可捕获的",
错误)
为 p
在
群组[
参数]:
p 状态 =
我.
状态.
获取(p,
[]
如果
长度(
状态) != 0
和
不
火炬.is_tensor(
状态[
步骤
)]
步骤值 = float(
状态[
步骤])
状态[
步骤] = (
火炬.
张量(
步长值,
数据类型=
_获取标量数据类型(),
设备=p.
设备
)
如果
群组[
"可捕获的"]
否则
火炬.
张量(
步长值,
数据类型=
_获取标量数据类型())
)
def _init_group(
我,
群组, params_with_grad,
梯度,
实验平均值,
演练信息,
状态步骤
):
具有复杂 =
假
为 p
在
群组[
参数]:
如果 p.
梯度 is
无:
continue
具有复杂 |=
火炬.
是复杂的(p)
params_with_grad.append(p)
如果 p.
研究生.is_sparse:
提升
运行时错误(
Adamax 不支持稀疏梯度)
梯度.append(p.
研究生)
状态 =
自身.
状态[p]
# 状态初始化
如果
长度(
状态) == 0:
状态[
"步骤"] = (
火炬.
零值((),
数据类型=
获取标量数据类型(),
设备=p.
设备)
如果
群组[
"可捕获的"]
否则
火炬.
张量(0.0,
数据类型=
获取标量数据类型())
)
状态["exp_avg"] =
火炬.
等于零的(
p, 内存格式=
火炬.
保留格式
)
状态[
"指数无穷"] =
火炬.
等于零的(
p, 内存格式=
火炬.
保留格式
)
期望平均值.append(
状态["exp_avg"])
指数无穷 s.append(
状态[
"指数无穷"])
状态步数.append(
状态[
步骤])
返回
具有复杂性
[文档] @_use_grad_for_differentiable
def 步长(
我,
闭包=
无):
执行单次优化步骤。
参数:
闭包(Callable,可选):一个重新评估模型并返回损失的闭包。
并返回损失。
"文档"
我.
CUDA 图捕获健康检查()
损失 =
无
如果
闭包 is
不
无:
与
火炬.
启用梯度():
损失 =
闭包()
为
群组
在
我.
参数组:
带梯度的参数:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
梯度:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
期望平均值:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
指数:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
状态步骤:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
beta1, beta2 = 群组[
贝塔]
eps = 群组["eps"]
lr = 群组["lr"]
权重衰减 =
群组[
"权重衰减"]
遍历 =
群组[
foreach]
最大化 =
群组[
最大化]
可微分的 =
群组[
"可区分的"]
可捕获的 =
群组[
"可捕获的"]
具有复杂性 =
我.
初始化组(
群组,
带梯度的参数,
梯度,
期望平均值,
指数衰减,
状态步骤
)
adamax(
带梯度的参数,
梯度,
期望平均值,
指数衰减,
状态步数,
eps=eps,
beta1=beta1,
beta2=beta2,
lr=lr,
权重衰减=
权重衰减,
foreach=foreach,
最大化=
最大化,
可微分的=
可微分的,
可捕获的=
可捕获的,
具有复杂的=
具有复杂的,
)
返回
损失
Adamax.__doc__ = (
r实现 Adamax 算法(基于无穷范数的 Adam 变体)。
.. math::
\begin{aligned}
&\rule{110mm}{0.4pt} \\
&\textbf{输入} : \gamma \text{ (学习率)}, \beta_1, \beta_2
\text{(beta)},\theta_0 \text{(参数)},f(\theta) \text{(目标函数)},
\: \lambda \text{(权重衰减)} \\
&\hspace{13mm} \epsilon \text{(epsilon)} \\
&\textbf{initialize} : m_0 \leftarrow 0 \text{ ( 首矩)}
u_0 \leftarrow 0 \text{ ( 无穷范数)} \\[-1.ex]
&\rule{110mm}{0.4pt} \\
&\textbf{for} \: t=1 \: \textbf{to} \: \ldots \: \textbf{do} \\
&\hspace{5mm}g_t \leftarrow \nabla_{\theta} f_t (\theta_{t-1}) \\
若 \(\lambda \neq 0\)
\(g_t \leftarrow g_t + \lambda \theta_{t-1}\)
\(m_t \leftarrow \beta_1 m_{t-1} + (1 - \beta_1) g_t\)
\(u_t \leftarrow \mathrm{max}(\beta_2 u_{t-1}, |g_{t}|+\epsilon)\)
&\hspace{5mm}\theta_t \leftarrow \theta_{t-1} - \frac{\gamma m_t}{(1-\beta^t_1) u_t} \\
&\rule{110mm}{0.4pt} \\[-1.ex]
&\bf{return} \: \theta_t \\[-1.ex]
&\rule{110mm}{0.4pt} \\[-1.ex]
\end{aligned}
有关算法的更多详细信息,请参阅《Adam:一种随机优化方法》_。
"文档"
+ rf""
参数:
{参数文档}
lr (float, Tensor, 可选): 学习率(默认:2e-3)
betas (Tuple[float, float], 可选): 用于计算系数
梯度和其平方的运行平均值
eps(浮点数,可选):添加到分母中的项,以提高数值稳定性(默认:1e-6)。
数值稳定性(默认:1e-8)
weight_decay (浮点数,可选): 权重衰减(L2 惩罚)(默认:0)
{_foreach 文档}
{最大化文档}
{_可微分文档}
{_capturable_doc}
.. _Adam:一种随机优化的方法:
https://arxiv.org/abs/1412.6980
"文档"
)
def _single_tensor_adamax(
参数:
列表[
张量
]
梯度:
列表[
张量
]
期望平均值:
列表[
张量
]
exp_infs: 列表[
张量
]
状态步数:
列表[
张量
]
*,
eps: float,
beta1: float,
beta2: float,
lr: float,
权重衰减: float,
最大化:
布尔,
可微分的:
布尔,
可捕获的:
布尔,
具有复杂的:
布尔,
):
为 i,
参数
在
列举(
参数):
梯度 =
梯度[i]
梯度 =
梯度
如果
不
最大化
否则 -
梯度
指数平均值 =
期望平均值[i]
指数 =
指数们[i]
step_t = 状态步骤[i]
如果编译,编译器将处理 cudagraph 检查,参见注释[torch.compile x capturable]
如果
不
火炬.
编译器.
编译中()
和
可捕获的:
可捕获支持的设备 =
_获取可捕获支持的设备()
断言 (
参数.
设备.
类型 == step_t.
设备.
类型
和
参数.
设备.
类型
在
可捕获支持的设备
), f如果 capturable=True,则 params 和 state_steps 必须在支持的设备上:{
支持捕获的设备}
。
# 更新步骤
step_t += 1
如果
权重衰减 != 0:
梯度 =
研究生.
添加(
参数,
阿尔法=
权重衰减)
如果
火炬.
是复杂的(
参数):
参数 =
火炬.
真实查看(
参数)
梯度 =
火炬.
真实查看(
研究生)
指数平均值 =
火炬.
真实查看(
指数平均)
指数无穷 =
火炬.
真实查看(
指数无穷)
更新偏置的第一矩估计。
指数平均.
线性插值 _(
研究生, 1 - beta1)
更新指数加权无穷范数。
如果
不
可微分的:
火炬.
最大值(
指数无穷.mul_(beta2),
研究生.
绝对值().
加_(eps),
输出=
指数无穷,
)
else:
正态缓冲区 =
火炬.
猫(
[指数无穷.mul_(beta2).
展平(0),
研究生.
绝对值().
加_(eps).
unsqueeze_ (未翻译,因为这不是一个完整的英文单词或短语)(0
)],
0,
)
指数函数.
复制_(
火炬.amax(
正态缓冲区, 0,
保持维度=
错误))
如果
可捕获的:
# 为什么要跳过额外的步骤并取消偏置校正?查看 #121238
# 修复后,我们应该使用带有 addcdiv 值为 -1 的偏置校正以提高可读性
负偏置校正 = beta1**step_t - 1
负偏差校正.div_(
学习率)
分母 =
指数无穷 *
负偏差校正
参数.addcdiv_(
指数平均,
分母)
else:
偏差校正 = 1 - beta1 **
获取值(step_t)
清除 = lr /
偏差校正
参数.addcdiv_(
指数平均,
指数无穷,
值=-clr)
def _多张量 Adamax(
参数:
列表[
张量
]
梯度:
列表[
张量
]
实验平均值:
列表[
张量
]
指数无穷大:
列表[
张量
]
状态步骤:
列表[
张量
]
*,
eps: float,
beta1: float,
beta2: float,
学习率: float,
权重衰减: float,
最大化:
布尔,
可微分的:
布尔,
可捕获的:
布尔,
具有复杂的:
布尔,
):
断言
不
可微分的,
"_foreach 操作不支持自动求导"
如果
长度(
参数) == 0:
返回
如果编译,编译器将处理 cudagraph 检查,参见注释[torch.compile x capturable]
如果
不
火炬.
编译器.
编译中()
和
可捕获的:
可捕获支持的设备 =
_获取可捕获支持的设备(
支持 XLA=
假
)
断言
所有(
p.设备.
类型 ==
步长.
设备.
类型
和 p.
设备.
类型
在
可捕获支持的设备
为 p,
步骤
在 zip(
参数,
状态步骤)
), f如果 capturable=True,则 params 和 state_steps 必须在支持的设备上:{
支持捕获的设备}
。
分组张量 =
优化器.
按设备类型和数据类型分组张量(
[参数,
梯度,
实验平均值,
指数,
状态步骤] # type: ignore[list-item]
)
为 (
分组参数_,
分组梯度_,
分组指数平均值_,
分组经验信息_,
分组状态步骤_,
), _ 在
分组张量.
值():
分组参数 =
角色(
列表[
张量
]
分组参数_)
分组梯度 =
角色(
列表[
张量
]
分组梯度_)
分组实验平均值 =
角色(
列表[
张量
]
分组实验平均值_)
分组实验下限 =
角色(
列表[
张量
]
分组实验下限_)
分组状态步骤 =
角色(
列表[
张量
]
分组状态步骤_)
如果
具有复杂:
_以真实视图(
分组参数,
分组梯度,
分组实验平均值,
分组实验下限
)
如果
最大化:
分组梯度 =
火炬._foreach_neg(
分组梯度)
# 类型:忽略[赋值]
# 更新步骤
# 如果步骤在 CPU 上,foreach 将回退到慢速路径,即通过循环调用 t.add(1)
#然后 1 会被反复包裹成 Tensor,这比只包裹一次要慢。
#alpha 是必须的,以确保我们进入正确的重载。
如果
不
火炬.
编译器.
编译中()
和
分组状态步数[0].
是 CPU:
火炬._foreach_add_(
分组状态步数,
火炬.
张量(1.0,
设备="cpu"),
阿尔法=1.0
)
else:
火炬._foreach_add_(
分组状态步骤, 1)
如果
权重衰减 != 0:
如果
最大化:
重复使用已分配给最大化操作的中间内存(grouped_grads)
火炬._foreach_add_(grouped_grads, grouped_params,
阿尔法=
权重衰减)
else:
分组梯度 =
火炬.
_foreach_add_(
# 类型:忽略[赋值]
分组梯度,
分组参数,
阿尔法=
权重衰减
)
更新偏置的第一矩估计。
火炬._foreach_lerp_(
分组平均,
分组梯度, 1 - beta1)
更新指数加权无穷范数。
火炬._foreach_mul_(grouped_exp_infs, beta2)
在这种情况下,我们需要引入 grads 的一个副本
因为之前还没有引入
如果
不
最大化
和
权重衰减 == 0:
分组梯度 =
火炬._foreach_abs(
分组梯度)
# 类型:忽略[赋值]
else:
火炬._foreach_abs_(
分组梯度)
火炬._foreach_add_(
分组梯度, eps)
火炬._foreach_maximum_(
分组经验信息,
分组梯度)
偏差校正:
并集[
元组[
张量, ...
]
列表[
张量]]
如果
可捕获的:
偏差校正 =
火炬._foreach_pow(beta1,
分组状态步骤)
不允许将标量作为 foreach_sub 的第一个参数
火炬._foreach_sub_(
偏差校正, 1)
火炬._foreach_div_(
偏差校正,
学习率)
分母 =
火炬._foreach_mul(
分组经验无穷,
偏差校正)
火炬._foreach_addcdiv_(
分组参数,
分组经验平均值,
分母)
else:
偏差校正 = [
1 - beta1 ** _获取值(
步长)
为
步骤
在
分组状态步骤
]
步长大小 =
[
_获取值(
学习率) / bc) * -1
为 bc
在
偏差校正]
火炬._foreach_addcdiv_(
分组参数,
分组实验平均值,
分组实验下限,
步长大小
)
@_disable_dynamo_if_unsupported(单个张量函数=
_单个张量 Adamax)
def 自适应动量(
参数:
列表[
张量
]
梯度:
列表[
张量
]
实验平均值:
列表[
张量
]
exp_infs: 列表[
张量
]
状态步数:
列表[
张量
]
使用 torchscript 编译的函数不支持带默认值的只写关键字参数问题 #70627
# 现在将其设置为 kwarg,因为功能 API 由 torch/distributed/optim 编译
foreach: 可选[
布尔] =
无,
最大化:
布尔值 =
错误,
可微分的:
布尔值 =
错误,
可捕获的:
布尔值 =
错误,
具有复杂结构:
布尔值 =
错误,
*,
eps: float,
beta1: float,
beta2: float,
学习率: float,
权重衰减: float,
):
r功能性 API,执行 adamax 算法计算。
详细信息请参阅 :class:`~torch.optim.Adamax`。
"文档"
如果
不
火炬.
编译器.
编译中()
和
不
所有(
isinstance(t, 火炬.
张量)
为 t
在
状态步骤
):
提升
运行时错误(
"API 已更改,`state_steps`参数必须包含一个单例张量列表"
)
如果
遍历 is
无:
_, 遍历 =
默认为融合或遍历(
参数,
可微分的,
使用融合=
假
)
如果
遍历
和
火炬.
算子.
是否正在脚本化():
提升
运行时错误(
"torch.jit.script 不支持与 foreach 优化器一起使用")
如果
遍历
和
不
火炬.
算子.
是否正在脚本化():
函数 =
多张张量 Adamax
else:
函数 =
单张张量 Adamax
函数(
参数,
梯度,
实验平均值,
指数无穷,
状态步数,
eps=eps,
beta1=beta1,
beta2=beta2,
学习率=
学习率,
权重衰减=
权重衰减,
最大化=
最大化,
可微分的=
可微分的,
具有复杂结构=
具有复杂结构,
可捕获的=
可捕获的,
)