# mypy: 允许未类型化定义
from 打字
导入
角色,
可选,
联合
导入
火炬
from 火炬
导入
张量
from .优化器
导入 (
_默认融合或 foreach,
_融合时设备数据类型检查,
_可微文档,
_遍历文档,
_获取标量数据类型,
_获取值,
_maximize_doc,
_params_doc,
_use_grad_for_differentiable,
_view_as_real,
优化器,
参数 T,
)
全部 = ["Adagrad",
adagrad]
[文档]
类 Adagrad(
优化器):
def __init__(
我,
参数:
参数 T,
lr: 并集[float,
张量] =
0.01,
lr_decay: 浮点数 = 0,
weight_decay: 浮点数 = 0,
初始累加器值:
浮点数 = 0,
eps: 浮点数 = 1e-10,
foreach: 可选[
布尔] =
无,
*,
最大化:
布尔值 =
错误,
可微分的:
布尔值 =
错误,
融合的:
可选[
布尔] =
无,
):
如果 isinstance(lr,
张量)
和 lr.
元素数量() != 1:
提升 ValueError(
"张量 lr 必须是 1 个元素")
如果
不 0.0 <= lr:
提升 ValueError(f
"无效的学习率:"{lr}")
如果
不 0.0 <= lr_decay:
提升 ValueError(f
"无效的 lr_decay 值:"{
学习率衰减}")
如果
不 0.0 <=
权重衰减:
提升 ValueError(f
"无效的权重衰减值:"{
权重衰减}")
如果
不 0.0 <=
初始累加器值:
提升 ValueError(
f"无效的初始累加器值:"{
初始累加器值}"
)
如果
不 0.0 <= eps:
提升 ValueError(f
"无效的 epsilon 值:"{eps}")
默认 =
字典(
学习率=
学习率,
学习率衰减=
学习率衰减,
eps=eps,
权重衰减=
权重衰减,
初始累积值=
初始累积值,
foreach=foreach,
最大化=
最大化,
可微分的=
可微分的,
融合=
融合,
)
超级().__init__(
参数,
默认值)
如果
融合:
如果
可微分的:
提升
运行时错误(
"融合不支持可微分")
如果 foreach:
提升
运行时错误(
"融合和 foreach 不能同时为 True。")
我.
需要为融合进行设备数据类型检查 =
真实
为
群组
在
我.
参数组:
为 p
在
群组[
参数]:
状态 =
我.
状态[p]
状态[
步骤] = (
火炬.
零值(
(),
数据类型=
_获取标量数据类型(
是否融合=
群组[
融合
)]
设备=p.
设备,
)
如果
群组[
融合]
否则
火炬.
张量(0.0,
数据类型=
_获取标量数据类型())
)
初始化值 = (
复杂(
初始累加器值,
初始累加器值)
如果
火炬.
是复杂的(p)
否则
初始累加器值
)
状态[
"求和"] =
火炬.
完全一样(
p, 初始化值,
内存格式=
火炬.
保留格式
)
def __setstate__(我,
状态):
超级().__setstate__(
状态)
# 为 "fused" 定义
# MYPY 错误:名称 "fused" 可能未定义
fused = 无
为
群组
在
我.
参数组:
群组.setdefault(
foreach,
无)
群组.setdefault(
最大化,
错误)
群组.setdefault(
可微分的,
错误)
融合 =
群组.setdefault(
"融合",
无)
状态值 =
列表(
我.
状态.
值())
step_is_tensor = (长度(
状态值) != 0)
和
火炬.is_tensor(
状态值[0
]
[步骤]
)
如果
不
步骤是张量:
为 s
在
状态值:
s[步骤] =
火炬.
张量(
float(s[步骤
)]
数据类型=
_获取标量数据类型(
混合=
混合的)
)
def 共享内存(
我):
为
群组
在
我.
参数组:
为 p
在
群组[
参数]:
状态 =
我.
状态[p]
状态[
"求和"].
共享内存_()
def _初始化组(
我,
群组,
带梯度的参数,
梯度,
状态和,
状态步骤):
有稀疏梯度,
有复杂 =
错误,
假
为 p
在
群组[
参数]:
如果 p.
梯度 is
不
无:
如果
群组[
融合]
和 getattr(
我,
_需要检查融合的设备数据类型,
是,
):
_检查融合的设备数据类型(p,
cuda 不支持=
是)
我.
需要检查设备数据类型是否支持融合 =
假
有稀疏梯度 |= p.
研究生.is_sparse
有复数 |=
火炬.
是复杂的(p)
有梯度参数.append(p)
梯度.append(p.
研究生)
状态 =
我.
状态[p]
状态求和.append(
状态[
"求和"])
状态步骤.append(
状态[
步骤])
返回
有稀疏梯度,
复杂的
[文档] @使用梯度进行可微分
def step(self, closure=None):
"""执行单个优化步骤。
Args:
闭包(Callable,可选):一个重新评估模型并返回损失的闭包。
。
"""
loss = None
if closure is not None:
with torch.enable_grad():
loss = closure()
for group in self.param_groups:
params_with_grad: list[Tensor] = []
grads: list[Tensor] = []
state_sums: list[Tensor] = []
state_steps: 列表[Tensor] = []
has_sparse_grad, has_complex = self._init_group(
group, params_with_grad, grads, state_sums, state_steps
)
adagrad(
带梯度的参数,
梯度,
状态和,
状态步骤,
学习率=group["lr"],
权重衰减=group["weight_decay"],
学习率衰减=group["lr_decay"],
eps=group["eps"]
has_sparse_grad=has_sparse_grad
foreach=group["foreach"]
maximize=group["maximize"]
differentiable=group["differentiable"],
has_complex=has_complex,
fused=group["fused"],
grad_scale=getattr(self, "grad_scale", None),
found_inf = getattr(self, "found_inf", None)
)
返回损失
Adagrad.__doc__ = (
r实现 Adagrad 算法。
.. math::
\begin{aligned}
&\rule{110mm}{0.4pt} \\
&\textbf{输入} : \gamma \text{ (lr)}, \: \theta_0 \text{ (params)}, \: f(\theta)
目标函数,λ(权重衰减)
&\hspace{12mm} τ(初始累加器值),:η(学习率衰减)\\
初始化:state_sum_0 ← τ
&\rule{110mm}{0.4pt} \\
for t=1 到 ... do
g_t ← ∇_θ f_t (θ_{t-1}) \\
&\hspace{5mm} ~γ ← γ / (1 + (t-1)η) \\
如果 λ 不等于 0
g_t ← g_t + λθ_{t-1}
状态和_t ← 状态和_{t-1} + g^2_t
&\hspace{5mm}θ_t ←
\theta_{t-1}- \tilde{\gamma} \frac{g_t}{\sqrt{state\_sum_t}+\epsilon} \\
\theta_{t-1} - \tilde{\gamma} \frac{g_t}{\sqrt{state\_sum_t}+\epsilon} \\
&\rule{110mm}{0.4pt} \\[-1.ex]
&\bf{return} \: \theta_t \\[-1.ex]
&\rule{110mm}{0.4pt} \\[-1.ex]
\end{aligned}
关于算法的更多细节,请参阅《自适应梯度下降法在线学习和随机优化》_
和
"文档"
+ rf""
参数:
{_params_doc}
lr (float, Tensor, 可选): 学习率(默认:1e-2)
lr_decay (float, 可选): 学习率衰减(默认:0)
weight_decay (float, 可选): 权重衰减(L2 惩罚)(默认:0)
initial_accumulator_value (float, 可选): 梯度平方和的初始值(默认:0)
sum of squares of gradients (float, 可选): 梯度平方和(默认:0)
eps(浮点数,可选):添加到分母的项,以提高数值稳定性(默认:1e-10)
数值稳定性(默认:1e-10)
{_foreach_doc}
{_maximize_doc}
{_可微文档}
混合(bool,可选):是否使用混合实现(仅 CPU)。
目前,支持 `torch.float64`、`torch.float32`、`torch.float16` 和 `torch.bfloat16`
(默认:None)。请注意,混合实现不
支持稀疏或复杂梯度。
.. _自适应梯度下降法在线学习和随机优化
优化:http://jmlr.org/papers/v12/duchi11a.html
"文档"
)
def adagrad(
参数:
列表[
张量
]
梯度:
列表[
张量
]
状态求和:
列表[
张量
]
状态步长:
列表[
张量
]
融合:
可选[
布尔] =
无,
梯度缩放:
可选[
张量] =
无,
found_inf: 可选[
张量] =
无,
# torchscript 编译的函数不支持只写参数默认值的问题 #70627
# 目前将这些设置为 kwargs,因为功能 API 是由 torch/distributed/optim 编译的
has_sparse_grad: 布尔值 =
错误,
foreach: 可选[
布尔] =
无,
可微分的:
布尔值 =
错误,
具有复杂的:
布尔值 =
错误,
*,
学习率: float,
权重衰减: float,
学习率衰减: float,
eps: float,
最大化:
布尔,
):
r功能性 API,执行 Adagrad 算法计算。
详细信息请参阅 :class:`~torch.optim.Adagrad`。
"文档"
如果
不
所有(isinstance(t,
火炬.
张量)
为 t
在
状态步数):
提升
运行时错误(
"API 已更改,`state_steps`参数必须包含单例张量列表。"
)
当用户为 foreach 或 fused 输入 False/True 时,请尊重。我们只想更改
默认值,当两者均未指定时。注意,我们默认为 foreach
并将 False 传递给 use_fused。这不是错误——我们希望在将其设置为默认值之前
烘焙一段时间,即使它通常更快。
如果
融合 is
无
和
遍历 is
无:
_, 遍历 =
默认为融合或遍历(
参数,
可微分的,
使用融合的=
假
)
如果
融合 is
无:
融合 =
假
如果 foreach is
无:
foreach = 假
如果 foreach
和
火炬.
算子.
是否正在脚本化():
提升
运行时错误(
"torch.jit.script 不支持与 foreach 优化器一起使用")
如果
熔合
和
火炬.
算子.
是否正在脚本化():
提升
运行时错误(
"torch.jit.script 不支持与熔合优化器一起使用")
如果
融合
和
不
火炬.
算子.
是否正在脚本化():
函数 = _fused_adagrad
如果...否则
遍历
和
不
火炬.
算子.
是否正在脚本化():
函数 =
多张量 Adagrad
else:
函数 =
单张量 Adagrad
函数(
参数,
梯度,
状态和,
状态步数,
lr=lr,
权重衰减=
权重衰减,
学习率衰减=
学习率衰减,
eps=eps,
是否有稀疏梯度=
是否有稀疏梯度,
最大化=
最大化,
可微分的=
可微分的,
包含复杂=
复杂的,
梯度尺度=
梯度缩放,
发现无穷大=
发现无穷大,
)
def _制作稀疏(
研究生,
梯度索引,
值):
大小 =
研究生.
尺寸()
返回
火炬.
稀疏 Coo 张量(
梯度索引,
值,
尺寸)
def _单张张量 Adagrad(
参数:
列表[
张量
]
梯度:
列表[
张量
]
状态总和:
列表[
张量
]
状态步骤:
列表[
张量
]
学习率梯度缩放:
可选[
张量
]
发现无穷大:
可选[
张量
]
*,
学习率: float,
权重衰减: float,
学习率衰减: float,
eps: float,
有稀疏梯度:
布尔,
最大化:
布尔,
可微分的:
布尔,
具有复杂的:
布尔,
):
断言
梯度缩放 is
无
和
发现无穷大 is
无
为
参数,
研究生,
状态总和,
步长_t
在 zip(
参数,
梯度,
状态总和,
状态步骤):
# 更新步骤
步骤_t += 1
步骤 =
_获取值(
步骤_t)
梯度 =
梯度
如果
不
最大化
否则 -
梯度
如果
权重衰减 != 0:
如果
研究生.is_sparse:
提升
运行时错误(
"权重衰减选项与稀疏梯度不兼容"
)
梯度 =
研究生.
添加(
参数,
阿尔法=
权重衰减)
清除率 = lr / (1 + (
步骤 - 1) *
lr 衰减)
如果
研究生.is_sparse:
梯度 =
研究生.
合并()
# 更新是非线性的,因此索引必须是唯一的
累加索引 =
研究生.
索引()
累加值 =
研究生.
值()
状态总和.
加_(
_创建稀疏(
研究生,
梯度索引,
梯度值.pow(2)))
std = 状态和.sparse_mask(
研究生)
标准值 =
标准的.
值().sqrt_().
加_(eps)
参数.
加_(
构建稀疏(
研究生,
梯度索引,
梯度值 /
标准值),
阿尔法=-
清除
)
else:
是否为复数 =
火炬.
是复杂的(
参数)
如果
是复杂的:
梯度 =
火炬.
真实查看(
研究生)
状态总和 =
火炬.
真实查看(
状态总和)
参数 =
火炬.
真实查看(
参数)
状态和.
加减乘_(
研究生,
研究生,
值=1)
如果
可微分的:
std = 状态和.
平方根() + eps
else:
std = 状态总和.
平方根().
加_(eps)
参数.addcdiv_(
研究生,
标准的,
值=-clr)
如果
是复杂的:
参数 =
火炬.
以复数视图查看(
参数)
state_sum = 火炬.
以复数视图查看(
状态和)
def _多张量 Adagrad(
参数:
列表[
张量
]
梯度:
列表[
张量
]
状态总和:
列表[
张量
]
状态步数:
列表[
张量
]
梯度缩放:
可选[
张量
]
发现无穷大:
可选[
张量
]
*,
学习率: float,
权重衰减: float,
学习率衰减: float,
eps: float,
has_sparse_grad: 布尔,
maximize: 布尔,
differentiable: 布尔,
has_complex: 布尔,
):
断言
不
可微分的,
"_foreach 操作不支持自动求导"
断言
梯度缩放 is
无
和 found_inf is
无
# 每当给空列表提供 foreach 函数时,将会抛出错误
如果
长度(
参数) == 0:
返回
grouped_tensorlists = 优化器._group_tensors_by_device_and_dtype(
[参数,
梯度,
状态求和,
状态步长] # type: ignore[list-item]
)
为 (
设备参数_,
device_grads_,
device_state_sums_,
device_state_steps_,
), _ 在 grouped_tensorlists.
值():
设备参数 =
角色(
列表[
张量
]
设备参数_)
设备梯度 =
角色(
列表[
张量
] device_grads_)
device_state_sums = 角色(
列表[
张量
] device_state_sums_)
device_state_steps = 角色(
列表[
张量
]
设备状态步骤_)
设备是否有稀疏梯度 =
有稀疏梯度
和
任何(
研究生.is_sparse
为
梯度
在
设备梯度
)
如果
设备具有稀疏梯度:
单个张量 Adagrad(
设备参数,
设备梯度,
设备状态和,
设备状态步骤,
学习率=
学习率,
权重衰减=
权重衰减,
学习率衰减=
学习率衰减,
eps=eps,
是否有稀疏梯度=
是,
最大化=
最大化,
可微分的=
可微分的,
具有复杂的=
具有复杂的,
梯度缩放=
梯度缩放,
发现无穷大=
发现无穷大,
)
continue
处理复杂参数
如果
具有复杂:
_以真实视图(
设备参数,
设备梯度,
设备状态总和)
如果
最大化:
设备梯度 =
火炬._foreach_neg(
设备梯度)
# 类型:忽略[赋值]
# 更新步骤
# 如果步骤在 CPU 上,foreach 将回退到慢速路径,即通过循环调用 t.add(1)
#然后 1 会被反复包裹成 Tensor,这比只包裹一次要慢。
#alpha 是必须的,以确保我们进入正确的重载。
如果
不
火炬.
编译器.
编译中()
和
设备状态步骤[0].
是 CPU:
火炬._foreach_add_(
设备状态步骤,
火炬.
张量(1.0,
设备="cpu"),
阿尔法=1.0
)
else:
火炬._foreach_add_(
设备状态步骤, 1)
如果
权重衰减 != 0:
重复使用已分配的中间内存(device_grads)以最大化
如果
最大化:
火炬._foreach_add_(
设备梯度,
设备参数,
阿尔法=
权重衰减)
else:
设备梯度 =
火炬.
每次添加(
# 类型:忽略[赋值]
设备梯度,
设备参数,
阿尔法=
权重衰减
)
减色 = [
-左右 / (1 + (
_获取值(
步长) - 1) *
学习率衰减)
为
步长
在
设备状态步数
]
火炬._foreach_addcmul_(
设备状态总和,
设备梯度,
设备梯度,
值=1)
std = 火炬._foreach_sqrt(
设备状态总和)
火炬._foreach_add_(
标准的, eps)
如果
权重衰减 != 0
或者
最大化:
再次,重用已分配的中间内存(device_grads)
火炬._foreach_mul_(device_grads, minus_clr)
分子 =
设备梯度
else:
分子 =
火炬._foreach_mul(
设备梯度,
减色)
# 类型:忽略[赋值]
火炬._foreach_addcdiv_(
设备参数,
分子,
标准的)
def _融合 Adam(
参数:
列表[
张量
]
梯度:
列表[
张量
]
状态和:
列表[
张量
]
状态步数:
列表[
张量
]
梯度缩放:
可选[
张量
]
发现无穷大:
可选[
张量
]
*,
学习率: float,
权重衰减: float,
学习率衰减: float,
eps: float,
有稀疏梯度:
布尔,
最大化:
布尔,
可微的:
布尔,
具有复杂结构:
布尔,
) 翻译
无:
如果
不
参数:
返回
如果
具有稀疏梯度
或者
具有复杂结构:
提升
运行时错误(
"融合不支持稀疏梯度或复杂参数")
如果
可微分的:
提升
运行时错误(
"adagrad with fused=True 不支持 differentiable=True"
)
grad_scale_dict = (
{grad_scale.设备:
梯度缩放}
如果
梯度缩放 is
不
无
否则
无
)
找到无穷字典 = {
找到无穷.
设备:
发现信息}
如果
发现信息 is
不
无
否则
无
分组张量 =
优化器.
按设备类型和 dtype 分组张量(
[参数,
梯度,
状态总和,
状态步数]
忽略列表项
)
为 (
设备, _), (
(
device_params_,
device_grads_,
device_state_sums_,
设备状态步骤,
),
_,
) 在
分组张量.
项目():
设备参数 =
角色(
列表[
张量
]
设备参数_)
设备梯度 =
角色(
列表[
张量
] device_grads_)
device_state_sums = 角色(
列表[
张量
] device_state_sums_)
device_state_steps = 角色(
列表[
张量
]
设备状态步骤_)
设备梯度缩放,
设备发现无穷 =
无,
无
如果
梯度缩放 is
不
无
和
梯度缩放字典 is
不
无:
如果
设备
不
在
梯度缩放字典:
梯度缩放字典[
设备] =
梯度缩放.
到(
设备,
非阻塞=
是)
忽略索引
设备梯度缩放 =
梯度缩放字典[
设备]
忽略索引
如果
发现无穷大 is
不
无
和
发现无穷大字典 is
不
无:
如果
发现_信息
不
在
发现_信息字典:
发现_信息字典[
设备] =
发现_信息.
到(
设备,
非阻塞=
是)
忽略索引
设备发现信息 =
发现信息字典[
设备]
忽略索引
火炬._foreach_add_(
设备状态步骤, 1)
火炬._fused_adagrad_(
设备参数,
设备梯度,
设备状态和,
设备状态步骤,
学习率=
学习率,
学习率衰减=
学习率衰减,
权重衰减=
权重衰减,
eps=eps,
最大化=
最大化,
学习率梯度=
设备梯度学习率,
发现无穷大值=
设备已找到,
)
如果
设备已找到 is
不
无:
火炬._foreach_sub_(
设备状态步骤, [
设备已找到] *
长度(
设备状态步骤)
)