快捷键

torch.optim.asgd 的源代码

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

导入 火炬
from 火炬 导入 张量

from .优化器 导入 (
    _capturable_doc,
    默认为融合或遍历,
    _可微文档,
    _如果不受支持则禁用 Dynamo,
    _foreach_doc,
    _获取可捕获的设备,
    获取标量数据类型,
    _获取值,
    _maximize_doc,
    _params_doc,
    _use_grad_for_differentiable,
    真实查看,
    优化器,
    参数 T,
)


全部 = ["ASGD", asgd]


[文档] ASGD(优化器): 定义 __init__( 自身, 参数: 参数 T, 学习率: 并集[float, 张量] = 0.01, 拉姆达: 浮点数 = 0.0001, 阿尔法: 浮点数 = 0.75, t0: 浮点数 = 10 万, 权重衰减: 浮点数 = 0, foreach: 可选[布尔] = , 最大化: 布尔值 = 错误, 可微: 布尔值 = 错误, 可捕捉的: 布尔值 = 错误, ): 如果 isinstance(学习率, 张量) 学习率.元素数量() != 1: 提升 ValueError("Tensor lr 必须是 1 个元素") 如果 0.0 <= 学习率: 提升 ValueError(f"无效的学习率:"{学习率}") 如果 0.0 <= 权重衰减: 提升 ValueError(f"无效的 weight_decay 值:"{权重衰减}") 默认 = 字典( 学习率=学习率, 拉姆达=拉姆达, 阿尔法=阿尔法, t0=t0, 权重衰减=权重衰减, foreach=foreach, 最大化=最大化, 可微=可微, 可捕捉的=可捕捉的, ) 超级().__init__(参数, 默认值) 定义 __setstate__(自身, 状态): 超级().__setstate__(状态) 群组 自身.参数组: 群组.setdefault("foreach", ) 群组.setdefault(最大化, 错误) 群组.setdefault("可区分的", 错误) 群组.setdefault("可捕获的", 错误) p 群组[参数]: 状态 = 自身.状态.获取(p, [] 如果 长度(状态) != 0: 如果 火炬.is_tensor(状态[步骤)] 步骤值 = float(状态[步骤]) 状态[步骤] = 火炬.张量( 步值, 数据类型=获取标量数据类型(), 设备=p.设备 ) 如果 火炬.is_tensor(状态["eta")] 状态["eta"] = 火炬.张量( 状态["eta"] 数据类型=获取标量数据类型(), 设备=p.设备 ) 如果 火炬.is_tensor(状态["mu")] 状态[“穆”] = 火炬.张量( 状态[“穆”] 数据类型=获取标量数据类型(), 设备=p.设备 ) 定义 初始化组(自身, 群组, 带梯度的参数, 梯度, , “axs”, “etas”, state_steps): 复杂的参数 = p 群组[参数]: 如果 p.梯度 is : 复杂的参数 |= 火炬.是复杂的(p) 带梯度的参数.append(p) 如果 p.研究生.is_sparse: 提升 运行时错误("ASGD 不支持稀疏梯度") 梯度.append(p.研究生) 状态 = 自身.状态[p] # 状态初始化 如果 长度(状态) == 0: 状态[步骤] = 火炬.零值( (), 设备=p.设备, 数据类型=获取标量数据类型() ) 状态["η"] = ( 火炬.as_tensor( 群组["lr"] 设备=p.设备, 数据类型=获取标量数据类型() ) .克隆() .detach() ) 状态["mu"] = 火炬.( (), 设备=p.设备, 数据类型=获取标量数据类型() ) 状态["ax"] = 火炬.等于零的( p, 内存格式=火炬.保留格式 ) .append(状态["mu"]) axs.append(状态["ax"]) etas.append(状态["eta"]) state_steps.append(状态[步骤]) 返回 复杂的参数
[文档] @_use_grad_for_differentiable def step(self, closure=None): """执行单个优化步骤。 Args: closure (Callable, 可选): 一个重新评估模型的闭包 返回损失。 """ self._cuda_graph_capture_health_check() 损失 = None 如果闭包不为空: 使用 torch.enable_grad(): 损失 = 闭包() 对于 self.param_groups 中的每个组: params_with_grad: 列表[Tensor] = [] grads: 列表[Tensor] = [] mus: 列表[Tensor] = [] axs: 列表[Tensor] = [] etas: 列[Tensor] = [] state_steps: 列[Tensor] = [] has_complex = self._init_group( group, params_with_grad, grads, mus, axs, etas, state_steps ) asgd( 带梯度的参数, 梯度, axs, mus, etas, state_steps, lambd=group["lambd"] lr=group["lr"] t0=group["t0"] alpha=group["alpha"] weight_decay=group["weight_decay"], foreach=group["foreach"], maximize=group["maximize"], differentiable=group["differentiable"], capturable=group["capturable"] has_complex=has_complex ) 返回损耗
ASGD.__doc__ = rf实现平均随机梯度下降。 它在《通过平均加速随机逼近》中提出。 _。 参数: {_params_doc} lr (float, Tensor, 可选): 学习率(默认:1e-2) lambd (float, 可选): 衰减项(默认:1e-4) alpha (float, 可选): eta 更新时的幂(默认:0.75) t0 (float, 可选): 开始平均的点(默认:1e6) weight_decay (float, 可选): 权重衰减(L2 惩罚)(默认:0) {_foreach_doc} {_maximize_doc} {_可微文档} {_capturable_doc} .. _加速随机逼近的平均法: https://dl.acm.org/citation.cfm?id=131098 "文档" 定义 单个张量 ASGD( 参数: 列表[张量] 梯度: 列表[张量] axs: 列表[张量] : 列表[张量] etas: 列表[张量] state_steps: 列表[张量] *, 拉姆达: float, 学习率: float, t0: float, 阿尔法: float, 权重衰减: float, 最大化: 布尔, 可微: 布尔, 可捕捉的: 布尔, 有复杂的: 布尔, ): i, 参数 列举(参数): 梯度 = 梯度[i] 梯度 = 梯度 如果 最大化 否则 -梯度 mu = [i] ax = axs[i] 时代 = 时代们[i] 步骤_t = state_steps[i] 如果编译,编译器将处理 cudagraph 检查,参见注释[torch.compile x capturable] 如果 火炬.编译器.is_compiling() 可捕捉的: 可捕获支持的设备 = _获取可捕获的设备() 断言 ( 参数.设备.类型 == mu.设备.类型 == 埃塔.设备.类型 == 步骤_t.设备.类型 参数.设备.类型 可捕获支持的设备 ), ( f"如果 capturable=True,则 params、mus、etas 和 state_steps 必须在 " f"支持设备上:"{支持捕获的设备} ) 如果 火炬.是复杂的(参数): 梯度 = 火炬.真实查看(研究生) 参数 = 火炬.真实查看(参数) ax = 火炬.真实查看(ax) # 更新步骤 步骤_t += 1 如果 权重衰减 != 0: 梯度 = 研究生.添加(参数, 阿尔法=权重衰减) 如果 可捕捉的: 参数.mul_(1 - lambd * eta) 参数.addcmul_(研究生, 埃塔, =-1) 更新参数 else: 估计值 = _获取值(估计) 参数.mul_(1 - 拉姆达 * 期望值) # 衰减项 参数.加_(研究生, 阿尔法=-期望值) # 更新参数 平均 如果 可捕获的 或者 .项目() != 1: ax.加_(参数.(ax).mul_(mu)) else: ax.复制_(参数) 如果 可捕捉的: 埃塔.复制_(左右 / ((1 + λ * 左右 * 步骤_t) ** 阿尔法)) μ.复制_(1 / 火炬.最大值(步骤_t - t0, 火炬.喜欢的(步骤_t))) else: 步骤 = _获取值(步骤_t) 新ητα = 火炬.as_tensor(左右 / ((1 + λ * 左右 * 步长) ** 阿尔法)) 估计时间.复制_(新的估计时间) 新的μ = 火炬.as_tensor(1 / 最大值(1, 步骤 - t0)) mu.复制_(新_mu) 定义 _多张量_ASGD( 参数: 列表[张量] 梯度: 列表[张量] axs: 列表[张量] : 列表[张量] 埃塔斯: 列表[张量] state_steps: 列表[张量] *, 拉姆达: float, 学习率: float, t0: float, 阿尔法: float, 权重衰减: float, 最大化: 布尔, 可微: 布尔, 可捕捉的: 布尔, 有复杂的: 布尔, ): 如果 长度(参数) == 0: 返回 断言 可微, "_foreach 操作不支持自动求导" 如果编译,编译器将处理 cudagraph 检查,参见注释[torch.compile x capturable] 如果 火炬.编译器.is_compiling() 可捕捉的: 可捕获支持的设备 = _获取可捕获的设备( 支持 XLA= ) 断言 所有( p.设备.类型 == .设备.类型 == 俄塔.设备.类型 == 步长.设备.类型 p.设备.类型 可捕获支持的设备 p, , 俄塔, 步骤 zip(参数, , etas, state_steps) ), f如果 capturable=True,则 params、mus、etas 和 state_steps 必须在支持的设备上:{支持捕获的设备} 分组张量 = 优化器.按设备类型和数据类型分组张量( [参数, 梯度, axs, , etas, state_steps] # type: ignore[list-item] ) (设备, _), ( ( 分组参数_, 分组梯度_, 分组_axs_, 分组_mus_, 分组_etas_, 分组状态步骤_, ), _, ) 分组张量.项目(): 分组参数 = 角色(列表[张量] 分组参数_) 分组梯度 = 角色(列表[张量] 分组梯度_) 分组_axs = 角色(列表[张量] 分组_axs_) 分组_mus = 角色(列表[张量] 分组_mus_) 分组_etas = 角色(列表[张量] 分组_etas_) 分组状态步骤 = 角色(列表[张量] 分组状态步骤_) 如果 有复杂的: 真实查看(分组参数, 分组梯度, 分组_axs) 如果 最大化: 分组梯度 = 火炬._foreach_neg(分组梯度) # 类型:忽略[赋值] # 更新步骤 # 如果步骤在 CPU 上,foreach 将回退到慢速路径,即通过循环调用 t.add(1) #然后 1 会被反复包裹成 Tensor,这比只包裹一次要慢。 #alpha 是必须的,以确保我们进入正确的重载。 如果 火炬.编译器.is_compiling() 分组状态步骤[0].是 CPU: 火炬._foreach_add_( 分组状态步骤, 火炬.张量(1.0, 设备="cpu"), 阿尔法=1.0 ) else: 火炬._foreach_add_(分组状态步骤, 1) # 中间值 = 梯度 + 参数 * 拉姆达 intermediate: 并集[元组[张量, ...] 列表[张量]] 如果 权重衰减 != 0: 如果 最大化: 火炬._foreach_add_(分组梯度, 分组参数, 阿尔法=权重衰减) intermediate = 分组梯度 else: 中级 = 火炬._foreach_add_( 分组梯度, 分组参数, 阿尔法=权重衰减 ) 火炬._foreach_add_(中级, 分组参数, 阿尔法=拉姆达) else: 中级 = 火炬._foreach_add_( 分组梯度, 分组参数, 阿尔法=羊驼 ) 更新参数 # param * (1 - lambd * eta) - eta * grad # => 参数 - 参数 * lambd * eta - eta * grad # => 参数 - eta * 中间值 火炬._foreach_addcmul_(分组参数, 中级, 分组_etas, =-1) 删除 中级 # 更新分组_axs 平均值:ax = ax + mu * (参数 - ax) 注释(mlazos):在这里我们不能使用 lerp,因为它需要权重为 float64 并且我们的分组代码要求组内所有张量的数据类型匹配(它应该如此,因为我们 在其他地方使用了 mus) # 所有数据类型需要匹配,因此我们可以在循环中引入类型转换 # 但这只会增加一个额外的内核启动,看起来这是一个更简洁的 # 并且更快的解决方案 中间 = 火炬.遍历子项(分组参数, 分组轴) 火炬._foreach_addcmul_(分组轴, 中间状态, 分组音乐) 删除 中间阶段 新的 etas: 并集[元组[张量, ...] 列表[张量]] 新音乐: 并集[元组[张量, ...] 列表[张量]] 如果 可捕捉的: 更新分组音乐 新音乐 = 火炬.遍历子项(分组状态步骤, t0) 火炬._foreach_maximum_(新音乐, 1.0) 火炬._foreach_reciprocal_(新音乐) 火炬._foreach_copy_(分组音乐, 新音乐) 删除 新音乐 # 更新 eta = lr / ((1 + lambd * lr * step)^alpha) 新 eta 值 = 火炬._foreach_mul(分组状态步骤, 拉姆达) 火炬._foreach_mul_(新的 etas, 学习率) 火炬._foreach_add_(新的 etas, 1) 火炬._foreach_pow_(新的 etas, 阿尔法) 火炬._foreach_reciprocal_(新的 etas) 火炬._foreach_mul_(新的 etas, 学习率) 火炬._foreach_copy_(分组 etas, 新的 etas) else: 新的 etas = [ 火炬.as_tensor(左右 / ((1 + lambd * 左右 * 步长) ** 阿尔法), 设备=设备) 步骤 分组状态步骤 ] 新的 mus = [ 火炬.as_tensor(1 / 最大值(1, _获取值(步长) - t0), 设备=设备) 步骤 分组状态步骤 ] 火炬._foreach_copy_(分组_etas, 新_etas) 火炬._foreach_copy_(分组音乐, 新音乐) @_disable_dynamo_if_unsupported(单个张量函数=单个张量 ASGD) 定义 asgd( 参数: 列表[张量] 梯度: 列表[张量] axs: 列表[张量] : 列表[张量] etas: 列表[张量] state_steps: 列表[张量] 使用 torchscript 编译的函数不支持带默认值的只写关键字参数问题 #70627 现由 torch/distributed/optim 编译的函数 API 参数 foreach: 可选[布尔] = , 最大化: 布尔值 = 错误, 可微: 布尔值 = 错误, 可捕捉的: 布尔值 = 错误, 有复杂的: 布尔值 = 错误, *, 拉姆达: float, 学习率: float, t0: float, 阿尔法: float, 权重衰减: float, ): r执行 asgd 算法计算的函数式 API。 详细信息请参阅 :class:`~torch.optim.ASGD`。 "文档" 如果 foreach is : _, foreach = 默认为融合或遍历( 参数, 可微, 使用融合的= ) 如果 foreach 火炬.算子.是否正在脚本化(): 提升 运行时错误("torch.jit.script 不支持 foreach 优化器") 如果 foreach 火炬.算子.是否正在脚本化(): 函数 = _multi_tensor_asgd else: 函数 = _single_tensor_asgd 函数( 参数, 梯度, axs, , etas, state_steps, 拉姆达=拉姆达, 学习率=学习率, t0=t0, 阿尔法=阿尔法, 权重衰减=权重衰减, 最大化=最大化, 可微=可微, 可捕捉的=可捕捉的, 有复杂的=有复杂的, )

© 版权所有 PyTorch 贡献者。

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

文档

查看 PyTorch 的全面开发者文档

查看文档

教程

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

查看教程

资源

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

查看资源