# mypy: 允许未类型化定义
rRMSprop 算法的实现
from 打字
导入
角色,
可选,
联合
导入
火炬
from 火炬
导入
张量
from .优化器
导入 (
_可捕获文档,
默认为融合或遍历,
_可区分文档,
_如果不受支持则禁用 Dynamo,
_foreach_doc,
_获取可捕获的设备,
_获取标量数据类型,
_maximize_doc,
_params_doc,
_use_grad_for_differentiable,
真实查看,
优化器,
参数 T,
)
全部 = [
RMSprop,
rmsprop]
[文档]
类 RMSprop(
优化器): # noqa: D101
定义 __init__(
我,
参数:
参数 T,
学习率:
并集[float,
张量] =
0.01,
阿尔法:
浮点数 = 0.99,
eps: 浮点数 = 1e-8,
权重衰减:
浮点数 = 0,
动量:
浮点数 = 0,
居中:
布尔值 =
错误,
可捕捉的:
布尔值 =
错误,
foreach: 可选[
布尔] =
无,
最大化:
布尔值 =
错误,
可微分的:
布尔值 =
错误,
): # 无需注意:D107
如果 isinstance(
学习率,
张量)
和
学习率.
元素数量() != 1:
提升 ValueError(
"Tensor lr 必须是 1 个元素")
如果
不 0.0 <=
学习率:
提升 ValueError(f
"无效的学习率:"{
学习率}")
如果
不 0.0 <= eps:
提升 ValueError(f
"无效的 epsilon 值:"{eps}")
如果
不 0.0 <=
动量:
提升 ValueError(f
"无效的动量值:"{
动量}")
如果
不 0.0 <=
权重衰减:
提升 ValueError(f
"无效的 weight_decay 值:"{
权重衰减}")
如果
不 0.0 <=
阿尔法:
提升 ValueError(f
"无效的 alpha 值:"{
阿尔法}")
默认 =
字典(
学习率=
学习率,
动量=
动量,
阿尔法=
阿尔法,
eps=eps,
居中=
居中,
权重衰减=
权重衰减,
可捕捉的=
可捕捉的,
foreach=foreach,
最大化=
最大化,
可微分的=
可微分的,
)
超级().__init__(
参数,
默认值)
定义 __setstate__(
我,
状态):
# 无需注意:D105
超级().__setstate__(
状态)
for 组
在
我.
参数组:
群组.setdefault(
动量, 0)
群组.setdefault(
居中,
错误)
群组.setdefault(
foreach,
无)
群组.setdefault(
最大化,
错误)
群组.setdefault(
可微分的,
错误)
群组.setdefault(
"可捕获的",
错误)
for p 在
群组[
参数]:
状态 =
我.
状态.
获取(p,
[]
如果
长度(
状态) != 0
和
不 torch.is_tensor(
状态[
步骤
)]
步骤值 = float(
状态[
步骤])
状态[
步骤] = (
torch.张量(
步值,
数据类型=
_获取标量数据类型(),
设备=p.
设备
)
如果
群组[
"可捕获的"]
否则 torch.
张量(
步值,
数据类型=
_获取标量数据类型())
)
定义
初始化组(
我,
群组,
带梯度的参数,
梯度,
平方平均值,
动量缓冲区列表,
梯度平均值,
状态步数,
):
复杂的参数 =
假
for p 在
群组[
参数]:
如果 p.
梯度
是
无:
continue
复杂的参数 |= torch.
是复杂的(p)
带梯度的参数.append(p)
如果 p.
研究生.is_sparse:
提升
运行时错误(
RMSprop 不支持稀疏梯度)
梯度.append(p.
研究生)
状态 =
我.
状态[p]
# 状态初始化
如果
长度(
状态) == 0:
状态[
步骤] = (
torch.零值((),
数据类型=
_获取标量数据类型(),
设备=p.
设备)
如果
群组[
"可捕获的"]
否则 torch.
零值((),
数据类型=
_获取标量数据类型())
)
状态[
平方平均] = torch.
等于零的(
p, 内存格式=torch.
保留格式
)
如果
群组[
动量] > 0:
状态[
动量缓冲区] = torch.
等于零的(
p, 内存格式=torch.
保留格式
)
如果
群组[
居中]:
状态[
梯度平均值] = torch.
等于零的(
p, 内存格式=torch.
保留格式
)
平方平均值.append(
状态[
平方平均])
状态步数.append(
状态[
步骤])
如果
群组[
动量] > 0:
动量缓冲区列表.append(
状态[
动量缓冲区])
如果
群组[
居中]:
梯度平均值.append(
状态[
梯度平均])
返回
复杂的参数
[文档] @_use_grad_for_differentiable
定义
步长(
我,
闭包=
无):
执行单个优化步骤。
参数:
闭包(Callable,可选):一个重新评估模型并返回损失的闭包
和返回损失。
"文档"
我._cuda_graph_capture_health_check()
损失 =
无
如果
闭包
是
不
无:
与 torch.
启用梯度():
损失 =
闭包()
for 组
在
我.
参数组:
带梯度的参数:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
梯度:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
平方平均值:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
梯度平均值:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
动量缓冲区列表:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
状态步数:
列表[
张量] =
输入文本为空,请提供需要翻译的文本
复杂的参数 =
我.
初始化组(
群组,
带梯度的参数,
梯度,
平方平均值,
动量缓冲区列表,
梯度平均值,
状态步数,
)
rmsprop(
带梯度的参数,
梯度,
平方平均值,
grad_avgs,
momentum_buffer_list,
状态步数,
学习率=
群组["lr"
]
阿尔法=
群组["alpha"
]
eps=群组[
eps
]
权重衰减=
群组[
权重衰减
]
动量=
群组[
动量
]
centered=群组[
居中
]
foreach=群组[
foreach
]
最大化=
群组[
最大化
]
可微分的=
群组[
可微分的
]
可捕捉的=
群组[
"可捕获的"
]
有复杂的=
有复杂的,
)
返回
损失
RMSprop.__doc__ = (
r实现 RMSprop 算法。
.. math::
\begin{aligned}
&\rule{110mm}{0.4pt} \\
输入 &\textbf{input} : \alpha \text{ (alpha)}, \: \gamma \text{ (lr)},
θ_0 (参数), f(θ) (目标函数) \\
&\hspace{13mm} λ(权重衰减),μ(动量),
居中,ε(epsilon)
初始化:v_0 ← 0(平方平均值),
\textbf{b}_0 \leftarrow 0 \text{(缓冲区)}, \: g^{ave}_0 \leftarrow 0
&\rule{110mm}{0.4pt} \\
for t=1 到 ... do
g_t ← ∇_θ f_t (θ_{t-1}) \\
若 \(\lambda \neq 0\)
\(g_t \leftarrow g_t + \lambda \theta_{t-1}\)
v_t ← αv_{t-1} + (1 - α)g^2_t
\hspace{8mm} \\
空格 8 毫米 \\
&\hspace{5mm} ˜v_t ← v_t \\
&\hspace{5mm}如果居中
g^{ave}_t ← g^{ave}_{t-1} α + (1-α) g_t
&\hspace{10mm} \tilde{v_t} \leftarrow \tilde{v_t} - \big(g^{ave}_{t} \big)^2
\\
翻译:&\hspace{10mm} \tilde{v_t} \leftarrow \tilde{v_t} - \big(g^{ave}_{t} \big)^2
\\
如果 μ > 0
&\hspace{10mm} \textbf{b}_t \leftarrow \mu \textbf{b}_{t-1} +
g_t/ \big(\sqrt{\tilde{v_t}} + \epsilon \big) \\
g_t/(\sqrt{\tilde{v_t}} + \epsilon ) \\
&\hspace{10mm} θ_t ← θ_{t-1} - γb_t
否则 \\
θ_t ← θ_{t-1} -
γ g_t/ (√˜v_t + ε) \hspace{3mm} \\
&\规则{110 毫米}{0.4 点} \\[-1.ex]
&\bf{return} \: \theta_t \\[-1.ex]
&\规则{110 毫米}{0.4 点} \\[-1.ex]
\end{aligned}
有关算法的更多详细信息,请参阅
G. Hinton 的《讲座笔记 `_
并居中的版本《生成序列
使用循环神经网络 `_。
这里实现的是在计算梯度平均值之前取其平方根
添加 epsilon(请注意,TensorFlow 交换了这两个操作)。有效
学习率因此为::math:`\gamma/(\sqrt{v} + \epsilon)` 其中 :math:`\gamma`
预定的学习率,其中:math:`v` 是加权移动平均
平方梯度的值。
"文档"
+ rf""
参数:
{_params_doc}
lr (float, Tensor, 可选): 学习率(默认:1e-2)
alpha (float, 可选): 平滑常数(默认:0.99)
eps (float, 可选): 添加到分母中的项,以改善
数值稳定性(默认:1e-8)
权重衰减(float,可选):权重衰减(L2 惩罚)(默认:0)
momentum (float, 可选): 动量因子(默认:0)
中心化(bool,可选):如果为 ``True``,则计算中心化 RMSProp,
梯度通过其方差的估计进行归一化
{_可捕获文档}
{_foreach_doc}
{_maximize_doc}
{_可区分文档}
"文档"
)
定义 _single_tensor_rmsprop(
参数:
列表[
张量
]
梯度:
列表[
张量
]
平方平均值:
列表[
张量
]
grad_avgs: 列表[
张量
]
动量缓冲区列表:
列表[
张量
]
状态步数:
列表[
张量
]
*,
学习率: float,
阿尔法: float,
eps: float,
权重衰减: float,
动量: float,
居中:
布尔,
最大化:
布尔,
可微分的:
布尔,
可捕捉的:
布尔,
有复杂的:
布尔,
):
for i, 参数
在
列举(
参数):
步骤 =
状态步数[i]
如果编译,编译器将处理 cudagraph 检查,参见注释[torch.compile x capturable]
如果
不 torch.
编译器.is_compiling()
和
可捕捉的:
可捕获支持的设备 =
_获取可捕获的设备()
断言 (
参数.
设备.
类型 ==
步长.
设备.
类型
和
参数.
设备.
类型
在
可捕获支持的设备
), f如果 capturable=True,则 params 和 state_steps 必须在支持的设备上:{
支持捕获的设备}
。
梯度 =
梯度[i]
梯度 =
梯度
如果
不
最大化
否则 -
梯度
平方平均值 =
平方平均值[i]
步骤 += 1
如果
权重衰减 != 0:
梯度 =
研究生.
添加(
参数,
阿尔法=
权重衰减)
是否为复杂参数 = torch.
是复杂的(
参数)
如果
是否为复杂参数:
参数 = torch.
真实查看(
参数)
梯度 = torch.
真实查看(
研究生)
平方平均值 = torch.
真实查看(
平方平均值)
平方平均值.mul_(
阿尔法).addcmul_(
研究生,
研究生,
值=1 -
阿尔法)
如果
居中:
平均成绩 =
平均成绩列表[i]
如果
是否为复杂参数:
平均毕业成绩 = torch.
真实查看(
平均毕业成绩)
平均毕业成绩.
线性插值 _(
研究生, 1 -
阿尔法)
平均值 =
平方平均值.
添加乘法(
毕业平均,
毕业平均,
值=-1).sqrt_()
else:
平均值 =
平方平均值.
平方根()
如果
可微分的:
平均 =
平均.
添加(eps)
else:
平均 =
平均.
加_(eps)
如果
动量 > 0:
缓冲区 =
动量缓冲列表[i]
如果
是否为复杂参数:
缓冲区 = torch.
真实查看(
缓冲区)
缓冲区.mul_(
动量).addcdiv_(
研究生,
平均)
参数.
加_(
缓冲区,
阿尔法=-
学习率)
else:
参数.addcdiv_(
研究生,
平均,
值=-
学习率)
定义
_多张量 RMSprop(
参数:
列表[
张量
]
梯度:
列表[
张量
]
平方平均值:
列表[
张量
]
梯度平均值:
列表[
张量
]
动量缓冲区列表:
列表[
张量
]
状态步数:
列表[
张量
]
*,
学习率: float,
阿尔法: float,
eps: float,
权重衰减: float,
动量: float,
居中:
布尔,
最大化:
布尔,
可微分的:
布尔,
可捕捉的:
布尔,
有复杂的:
布尔,
):
如果
长度(
参数) == 0:
返回
断言
不
可微分的,
"_foreach 操作不支持自动求导"
如果编译,编译器将处理 cudagraph 检查,参见注释[torch.compile x capturable]
如果
不 torch.
编译器.is_compiling()
和
可捕捉的:
可捕获支持的设备 =
_获取可捕获的设备()
断言
所有(
p.设备.
类型 ==
步长.
设备.
类型
和 p.
设备.
类型
在
可捕获支持的设备
for p, 步骤
在 zip(
参数,
状态步数)
), f如果 capturable=True,则 params 和 state_steps 必须在支持的设备上:{
支持捕获的设备}
。
分组张量 =
优化器.
按设备类型和数据类型分组张量(
[参数,
梯度,
平方平均值,
平均梯度,
动量缓冲区列表,
状态步数] # type: ignore[list-item]
)
for (
(
分组参数_,
分组梯度_,
分组平方平均,
分组梯度平均,
分组动量缓冲区列表_,
分组状态步骤_,
)
), _ 在
分组张量.
值():
分组参数 =
角色(
列表[
张量
]
分组参数_)
分组梯度 =
角色(
列表[
张量
]
分组梯度_)
分组平方平均值 =
角色(
列表[
张量
]
分组平方平均值_)
分组状态步骤 =
角色(
列表[
张量
]
分组状态步骤_)
如果
有复杂的:
状态和梯度 = [
分组梯度,
分组平方平均值]
如果
动量 > 0:
分组动量缓冲列表 =
角色(
列表[
张量
]
分组动量缓冲列表_
)
状态与梯度.append(
分组动量缓冲区列表)
如果
居中:
分组梯度平均值 =
角色(
列表[
张量
]
分组梯度平均值_)
状态与梯度.append(
分组梯度平均值)
真实查看(
分组参数, *
状态与梯度)
如果
最大化:
分组梯度 = torch._foreach_neg(
分组梯度)
# 类型:忽略[赋值]
# 更新步骤
# 如果步骤在 CPU 上,foreach 将回退到慢速路径,即通过循环调用 t.add(1)
#然后 1 会被反复包裹成 Tensor,这比只包裹一次要慢。
#alpha 是必须的,以确保我们进入正确的重载。
如果
不 torch.
编译器.is_compiling()
和
分组状态步骤[0].
是 CPU:
torch._foreach_add_(
分组状态步骤, torch.
张量(1.0,
设备="cpu"),
阿尔法=1.0
)
else:
torch._foreach_add_(分组状态步骤, 1)
如果
权重衰减 != 0:
重复使用已分配给最大化操作的中间内存(grouped_grads)
如果
最大化:
torch._foreach_add_(分组梯度,
分组参数,
阿尔法=
权重衰减)
else:
分组梯度 = torch.
_foreach_add_(
# 类型:忽略[赋值]
分组梯度,
分组参数,
阿尔法=
权重衰减
)
torch._foreach_mul_(分组平方平均值,
阿尔法)
torch._foreach_addcmul_(
分组平方平均值,
分组梯度,
分组梯度,
值=1 - alpha
)
如果
居中:
分组梯度平均值 =
角色(
列表[
张量
]
分组梯度平均值_)
torch._foreach_lerp_(分组梯度平均值,
分组梯度, 1 -
阿尔法)
平均值 = torch._foreach_addcmul(
分组平方平均值,
分组梯度平均值,
分组梯度平均值,
值=-1
)
torch._foreach_sqrt__(
平均值)
torch._foreach_add_(平均, eps)
else:
平均 = torch.
_foreach 平方根(
分组平均平方)
torch._foreach_add_(平均, eps)
如果
动量 > 0:
分组动量缓冲区列表 =
角色(
列表[
张量
]
分组动量缓冲区列表_
)
torch._foreach_mul_(分组动量缓冲区列表,
动量)
torch._foreach_addcdiv_(分组动量缓冲区列表,
分组梯度,
平均值)
如果 LR 是一个张量,else 分支将内部调用 item()
这将导致在捕获时出现无声的错误
如果
可捕获的
和 isinstance(
学习率, torch.
张量):
动量学习率 = torch._foreach_mul(
分组动量缓冲区列表, -
学习率)
torch._foreach_add_(分组参数,
动量学习率)
else:
torch._foreach_add_(
分组参数,
分组动量缓冲区列表,
阿尔法=-
左右
)
else:
如果 LR 是一个张量,else 分支将内部调用 item()
这将导致在捕获时出现无声的错误
如果
可捕获的
和 isinstance(
学习率, torch.
张量):
torch._foreach_div_(平均值, -
学习率)
torch._foreach_addcdiv_(分组参数,
分组梯度,
平均)
else:
torch._foreach_addcdiv_(分组参数,
分组梯度,
平均,
值=-
学习率)
@_disable_dynamo_if_unsupported(单个张量函数=
_单张张量_rmsprop)
定义 rmsprop(
参数:
列表[
张量
]
梯度:
列表[
张量
]
平方平均值:
列表[
张量
]
平均梯度:
列表[
张量
]
动量缓冲区列表:
列表[
张量
]
状态步数:
列表[
张量
]
使用 torchscript 编译的函数不支持带默认值的只写关键字参数问题 #70627
现由 torch/distributed/optim 编译的函数 API 参数
foreach: 可选[
布尔] =
无,
最大化:
布尔值 =
错误,
可微分的:
布尔值 =
错误,
可捕捉的:
布尔值 =
错误,
有复杂的:
布尔值 =
错误,
*,
学习率: float,
阿尔法: float,
eps: float,
权重衰减: float,
动量: float,
居中:
布尔,
):
r执行 rmsprop 算法计算的函数式 API。
查看 :class:`~torch.optim.RMSProp` 以获取详细信息。
"文档"
此检查在编译期间较慢,因此我们跳过它。
如果确实需要,我们可以在 dynamo 中添加此检查。
如果
不 torch.
编译器.is_compiling()
和
不
所有(
isinstance(t, torch.张量) for t
在
状态步骤
):
提升
运行时错误(
"API 已更改,`state_steps`参数必须包含一个单例张量列表"
)
如果 foreach
是
无:
_, foreach = 默认为融合或遍历(
参数,
可微分的,
使用融合的=
假
)
如果 foreach
和 torch.
算子.
是否正在脚本化():
提升
运行时错误(
"torch.jit.script 不支持 foreach 优化器")
如果 foreach
和
不 torch.
算子.
是否正在脚本化():
函数 =
多张量 RMSprop
else:
函数 =
单张量 RMSprop
函数(
参数,
梯度,
平方平均值,
梯度平均值,
动量缓冲区列表,
状态步数,
学习率=
学习率,
阿尔法=
阿尔法,
eps=eps,
权重衰减=
权重衰减,
动量=
动量,
居中=
居中,
最大化=
最大化,
可捕捉的=
可捕捉的,
可微分的=
可微分的,
有复杂的=
有复杂的,
)