torch.nn.modules.lazy 的源代码
# mypy: 允许未类型化定义
导入 itertools
来自
打字
导入
任意,
可选,
协议
导入
火炬
来自
torch.nn 参数
导入 is_lazy
__all__ = [LazyModuleMixin]
类
懒协议(
协议):
这个类用于避免在混入(mixin)中的属性上使用 mypy 检查时出现错误。
https://mypy.readthedocs.io/en/latest/more_types.html#mixin-classes
"源代码"
def _register_load_state_dict_pre_hook(self, hook):
...
def 注册前向钩子(self, hook, *,
预先添加=
错误, with_kwargs=
错误):
...
def _lazy_load 钩子(
self,
state_dict,
前缀,
本地元数据,
严格的,
缺少键,
预期之外的键,
错误信息,
):
...
def _获取名称(self):
...
def 推断参数(self,
模块,
输入):
...
@property
def 参数(self):
...
@property
def _缓冲区(self):
...
@property
def _非持久缓冲区集合(self):
...
@property
def _加载钩子(self):
...
@property
def _初始化钩子(self):
...
[文档]
类
LazyModule 混入:
r混合模块,用于懒加载参数,也称为“懒模块”。
..警告:
懒模块是处于积极开发中的实验性新特性。
它们的 API 可能会发生变化。
懒加载参数的模块,或称为“懒模块”。
从第一个输入中推导出它们的参数形状
在第一次前向之前,它们包含
class:`torch.nn.UninitializedParameter`,不应访问或使用
之后,它们包含常规的 :class:`torch.nn.Parameter`
懒加载模块很方便,因为它们不需要计算一些模块参数,例如典型的 :class:`torch.nn.Linear` 的 :attr:`in_features` 参数。
模块参数,如典型的 :class:`torch.nn.Linear` 的 :attr:`in_features` 参数。
。
构造后,具有懒加载模块的网络应该首先
需要将其转换为所需的 dtype 并放置在预期的设备上。
这是因为惰性模块仅执行形状推断,所以通常的 dtype 和设备放置行为适用。
然后惰性模块应执行“干运行”,以初始化模块中的所有组件。
惰性模块应随后执行“干运行”,以初始化模块中的所有组件。
这些“干运行”将正确大小、dtype 和设备的输入通过
网络及其每个懒惰模块。之后,网络可以像往常一样使用。
>>> # xdoctest: +SKIP
>>> class LazyMLP(torch.nn.Module):
... def __init__(self) -> None:
... super().__init__()
... self.fc1 = torch.nn.LazyLinear(10)
... self.relu1 = torch.nn.ReLU()
... self.fc2 = torch.nn.LazyLinear(1)
... self.relu2 = torch.nn.ReLU()
...
... def forward(self, input):
... x = self.relu1(self.fc1(input))
... y = self.relu2(self.fc2(x))
... 返回 y
>>> 构建一个具有懒加载模块的网络
>>> lazy_mlp = LazyMLP()
>>> 转换网络的设备类型和数据类型
>>> # 注意:这些转换应在构建之后和任何“干运行”之前应用
>>> lazy_mlp = lazy_mlp.cuda().double()
>>> lazy_mlp
LazyMLP( (fc1): LazyLinear(in_features=0, out_features=10, bias=True)
(relu1): ReLU()
(fc2): LazyLinear(in_features=0, out_features=1, bias=True)
(relu2): ReLU()
)
>>> # 进行一次干运行以初始化网络的懒加载模块
>>> lazy_mlp(torch.ones(10,10).cuda())
>>> # 初始化后,LazyLinear 模块变为常规 Linear 模块
>>> lazy_mlp
LazyMLP(
(fc1): 线性(in_features=10, out_features=10, bias=True)
(relu1): ReLU()
(fc2): 线性(in_features=10, out_features=1, bias=True)
(relu2): ReLU()
)
>>> # 添加一个优化器,因为参数现在可以像往常一样使用
>>> optim = torch.optim.SGD(mlp.parameters(), lr=0.01)
使用懒加载模块时需要注意的一个最后警告是,由于懒加载模块总是在其他模块之后初始化,因此网络参数的初始化顺序可能会改变。
参数的初始化顺序可能会改变,因为懒加载模块总是在其他模块之后初始化。
例如,如果上面定义的 LazyMLP 类包含一个 :class:`torch.nn.LazyLinear` 模块
首先是 :class:`torch.nn.Linear` 模块,然后是常规模块
在构造时初始化,第一个模块将在第一次干燥运行时初始化
这可能导致使用延迟模块的网络参数初始化方式不同
与没有懒加载模块的网络参数初始化顺序相比,
这通常依赖于一个有状态的随机数生成器,是不同的。
查看更多详情::doc:`/notes/randomness`。
懒加载模块可以像其他模块一样使用状态字典进行序列化。例如:
>>> lazy_mlp = LazyMLP()
>>> # 状态字典显示了未初始化的参数
>>> lazy_mlp.state_dict()
OrderedDict([('fc1.weight', Uninitialized parameter),
('fc1.bias',)
张量([-1.8832e+25, 4.5636e-41, -1.8832e+25, 4.5636e-41, -6.1598e-30,])
4.5637e-41, -1.8788e+22, 4.5636e-41, -2.0042e-31, 4.5637e-41)))
('fc2.weight', 未初始化的参数)
('fc2.bias', 矩阵([0.0019]))])
懒加载模块可以加载常规 :class:`torch.nn.Parameter` (即您可以序列化/反序列化)
初始化的 LazyModules 将保持初始化状态)
>>> full_mlp = LazyMLP()
>>> # 干运行以初始化另一个模块
>>> full_mlp.forward(torch.ones(10, 1))
>>> # 将初始化的状态加载到懒加载模块中
>>> lazy_mlp.load_state_dict(full_mlp.state_dict())
>>> # 状态字典现在包含有效值
>>> lazy_mlp.state_dict()
OrderedDict([('fc1.weight',
tensor([[-0.3837],
[0.0907]
[0.6708]
[-0.5223]
[-0.9028]
[0.2851]
[-0.4537]
[0.6813]
[0.5766]
[-0.8678]]))
('fc1.bias',)
张量([-1.8832e+25, 4.5636e-41, -1.8832e+25, 4.5636e-41, -6.1598e-30,])
4.5637e-41, -1.8788e+22, 4.5636e-41, -2.0042e-31, 4.5637e-41)))
('fc2.weight',)
tensor([[0.1320, 0.2938, 0.0679, 0.2793, 0.1088, -0.1795, -0.2301, 0.2807,]]
0.2479, 0.1091]]))
('fc2.bias', 矩阵([0.0019]))])
注意,然而,在进行“dry run”时,加载的参数不会被替换,如果它们已经被初始化
当状态被加载时。这防止了在不同的上下文中使用已初始化的模块。
"源代码"
# 继承自该模块的模块将更改它们的 __class__ 为指定的
# 一旦它们完全初始化后
cls_to_become: 可选[
类型[
任意]] =
无
def 初始化(self:
懒协议, *
参数, **kwargs):
Mypy 不喜欢在混入(mixin)中的这个超级调用
超级().
初始化(*
参数, **kwargs)
# 类型:忽略[杂项]
self._load_hook = self._register_load_state_dict_pre_hook(self._lazy_load 钩子)
self._initialize_hook = self.注册前向钩子(
self.推断参数, with_kwargs=
真实
)
def 保存到状态字典(self:
懒协议,
目的地,
前缀,
保留变量):
这应该理想情况下作为一个钩子实现,
但是我们应该在 UninitializedParameter 中重写`detach`方法以返回自身
这并不干净
为
名称,
参数
在 self.
参数.
项目():
如果
参数
是 not
无:
如果 not (is_lazy(
参数)
或
保留变量):
参数 =
参数.detach()
目的地[
前缀 +
名称] =
参数
为
名称,
缓冲区
在 self.
_缓冲区.
项目():
如果
缓冲区
是 not
无
和
名称 not
在 self.
_非持久缓冲区集合:
如果 not (is_lazy(
缓冲区)
或
保留变量):
缓冲区 =
缓冲区.detach()
目的地[
前缀 +
名称] =
缓冲区
def _lazy_load 钩子(
self: 懒协议,
state_dict,
前缀,
本地元数据,
严格的,
缺少键,
预期之外的键,
错误信息,
):
懒加载缓冲区和参数的 `load_state_dict` 预钩子函数。
此钩子的目的是调整当前状态以及/或正在加载的 ``state_dict``,以便将序列化的模块实例反序列化到未初始化和已初始化的模块实例上。
``state_dict`` 正在加载,以便将序列化的模块实例反序列化到未初始化和已初始化的模块实例上。
无论是未初始化还是已初始化的状态,都可以反序列化到未初始化和已初始化的模块实例。
模块实例。
请查看 `torch.nn.Module._register_load_state_dict_pre_hook` 中的注释
了解钩子配置的详细信息。
"源代码"
为
名称,
参数
在 itertools.chain(
self.参数.
项目(), self.
_缓冲区.
项目()
):
key = 前缀 +
名称
如果 key
在
状态字典
和
参数
是 not
无:
输入参数 = state_dict[
键]
如果 is_lazy(
参数):
当前参数未初始化但正在加载的参数
基于未初始化的参数创建一个新的参数
如果 not is_lazy(
输入参数):
与
火炬.
不梯度():
参数.
实现化(
输入参数.shape)
[文档] def initialize_parameters(self: _LazyProtocol, *args, **kwargs):
r"""根据输入批次属性初始化参数。
这添加了一个接口,以隔离参数初始化。
进行参数形状推理时的前向传播。
"""
raise NotImplementedError(
f"initialize_parameters 未在 {self.__class__.__name__} 中实现"
)
[文档] def has_uninitialized_params(self: _LazyProtocol):
r"""检查模块是否有未初始化的参数。"""
# 这是为了避免 JIT 跟踪此参数并强制
将自定义模块的 __setstate__ 方法添加进去
params = self._parameters.values()
buffers = self._buffers.values()
for param in itertools.chain(params, buffers):
如果是 lazy(param):
返回 True
返回 False
# torchrec 测试代码的一致性,与以下代码
# fmt: off
def 推断参数(self:
懒协议,
模块,
参数, kwargs=
无):
r推断大小并根据提供的输入批次初始化参数。
给定一个包含已声明可推断参数的模块
使用 :class:`torch.nn.parameter.ParameterMode.Infer` 运行前向传播
在完整模块中使用提供的输入初始化所有参数
如有需要。
在运行前向传递之前,将模块设置为评估模式,以避免保存统计数据或计算梯度
以避免保存统计数据或计算梯度
"源代码"
kwargs = kwargs 如果 kwargs
否则 {}
模块.initialize_parameters(*
参数, **kwargs)
如果
模块.has_uninitialized_params():
提升
运行时错误(f
模块{self.
_获取名称()}
'尚未完全初始化')
模块.
_初始化钩子.
删除()
模块.
_加载钩子.
删除()
delattr(模块,
'_初始化钩子')
delattr(模块, '_load_hook')
如果
模块.cls_to_become
是 not
无:
模块.
类 =
模块.cls_to_become
# fmt: on
def _为数据并行复制(self:
懒协议):
提升
运行时错误(
"具有未初始化参数的模块不能与 `DataParallel` 一起使用。"
"运行一个虚拟的前向传递以正确初始化模块"
)