PyTorch 命名张量
原文: PyTorch 命名张量
命名张量旨在通过允许用户将显式名称与张量维度相关联来使张量更易于使用。 在大多数情况下,采用尺寸参数的操作将接受尺寸名称,从而无需按位置跟踪尺寸。 此外,命名张量使用名称来自动检查运行时是否正确使用了 API,从而提供了额外的安全性。 名称也可以用于重新排列尺寸,例如,支持“按名称广播”而不是“按位置广播”。
警告
命名的张量 API 是实验性的,随时可能更改。
创建命名张量
现在,工厂函数采用新的names
参数,该参数将名称与每个维度相关联。
>>> torch.zeros(2, 3, names=('N', 'C'))
tensor([[0., 0., 0.],
[0., 0., 0.]], names=('N', 'C'))
命名尺寸(与常规 Tensor 尺寸一样)已订购。 tensor.names[i]
是tensor
的尺寸i
的名称。
以下工厂函数支持命名张量:
torch.empty()
torch.rand()
torch.randn()
torch.ones()
torch.tensor()
torch.zeros()
命名尺寸
有关张量名称的限制,请参见 names
。
使用 names
访问张量的尺寸名称,并使用 rename()
重命名命名尺寸。
>>> imgs = torch.randn(1, 2, 2, 3 , names=('N', 'C', 'H', 'W'))
>>> imgs.names
('N', 'C', 'H', 'W')
>>> renamed_imgs = imgs.rename(H='height', W='width')
>>> renamed_imgs.names
('N', 'C', 'height', 'width)
命名张量可以与未命名张量共存; 命名张量是 torch.Tensor
的实例。 未命名的张量具有None
命名的尺寸。 命名张量不需要命名所有尺寸。
>>> imgs = torch.randn(1, 2, 2, 3 , names=(None, 'C', 'H', 'W'))
>>> imgs.names
(None, 'C', 'H', 'W')
名称传播语义
命名张量使用名称来自动检查在运行时是否正确调用了 API。 这在称为名称推断的过程中发生。 更正式地说,名称推断包括以下两个步骤:
- 检查名称:操作员可以在运行时执行自动检查,以检查某些尺寸名称是否必须匹配。
- 传播名称:名称推断会将名称传播到输出张量。
所有支持命名张量的操作都会传播名称。
>>> x = torch.randn(3, 3, names=('N', 'C'))
>>> x.abs().names
('N', 'C')
匹配语义
如果两个名称相等(字符串相等)或至少一个名称为None
则匹配。 从本质上讲,没有一个是特殊的“通配符”名称。
unify(A, B)
确定名称A
和B
中的哪个传播到输出。 如果两个名称匹配,它将返回更多特定的。 如果名称不匹配,则错误。
注意
在实践中,使用命名张量时,应避免使用未命名的维,因为它们的处理可能很复杂。 建议使用 refine_names()
将所有未命名的尺寸提升为已命名的尺寸。
基本名称推断规则
让我们看看在添加两个不广播的一维张量的情况下,如何在名称推断中使用match
和unify
。
x = torch.randn(3, names=('X',))
y = torch.randn(3)
z = torch.randn(3, names=('Z',))
检查名称:检查两个张量的名称是否与相匹配。
对于以下示例:
>>> # x + y # match('X', None) is True
>>> # x + z # match('X', 'Z') is False
>>> # x + x # match('X', 'X') is True
>>> x + z
Error when attempting to broadcast dims ['X'] and dims ['Z']: dim 'X' and dim 'Z' are at the same position from the right but do not match.
传播名称:统一的名称以选择要传播的名称。 在x + y
的情况下,unify('X', None) = 'X'
因为'X'
比None
更特异性。
>>> (x + y).names
('X',)
>>> (x + x).names
('X',)
有关名称推断规则的完整列表,请参见名为张量运算符的覆盖范围。 以下是两个可能有用的常见操作:
- 二进制算术运算:统一输入的名称
- 矩阵乘法操作:缩小暗淡
按名称明确对齐
使用 align_as()
或 align_to()
按名称将张量尺寸对齐到指定顺序。 这对于执行“按名称广播”很有用。
## This function is agnostic to the dimension ordering of `input`,
## as long as it has a `C` dimension somewhere.
def scale_channels(input, scale):
scale = scale.refine_names('C')
return input * scale.align_as(input)
>>> num_channels = 3
>>> scale = torch.randn(num_channels, names=('C',))
>>> imgs = torch.rand(3, 3, 3, num_channels, names=('N', 'H', 'W', 'C'))
>>> more_imgs = torch.rand(3, num_channels, 3, 3, names=('N', 'C', 'H', 'W'))
>>> videos = torch.randn(3, num_channels, 3, 3, 3, names=('N', 'C', 'H', 'W', 'D')
>>> scale_channels(imgs, scale)
>>> scale_channels(more_imgs, scale)
>>> scale_channels(videos, scale)
操纵尺寸
使用 align_to()
排列大量尺寸,而无需提及 permute()
要求的所有尺寸。
>>> tensor = torch.randn(2, 2, 2, 2, 2, 2)
>>> named_tensor = tensor.refine_names('A', 'B', 'C', 'D', 'E', 'F')
## Move the F (dim 5) and E dimension (dim 4) to the front while keeping
## the rest in the same order
>>> tensor.permute(5, 4, 0, 1, 2, 3)
>>> named_tensor.align_to('F', 'E', ...) # Use '...' instead in Python 2
使用 flatten()
和 unflatten()
分别展平和展平尺寸。 这些方法比 view()
和 reshape()
更冗长,但对于阅读代码的人来说,语义更有意义。
>>> imgs = torch.randn(32, 3, 128, 128)
>>> named_imgs = imgs.refine_names('N', 'C', 'H', 'W')
>>> flat_imgs = imgs.view(32, -1)
>>> named_flat_imgs = named_imgs.flatten(['C', 'H', 'W'], 'features')
>>> named_flat_imgs.names
('N', 'features')
>>> unflattened_imgs = imgs.view(32, 3, 128, 128)
>>> unflattened_named_imgs = named_flat_imgs.unflatten(
'features', [('C', 3), ('H', 128), ('W', 128)])
Autograd 支持
Autograd 当前以有限的方式支持命名张量:autograd 忽略所有张量上的名称。 梯度计算仍然是正确的,但是我们失去了名称赋予我们的安全性。
>>> x = torch.randn(3, names=('D',))
>>> weight = torch.randn(3, names=('D',), requires_grad=True)
>>> loss = (x - weight).abs()
>>> grad_loss = torch.randn(3)
>>> loss.backward(grad_loss)
>>> weight.grad # Unnamed for now. Will be named in the future
tensor([-1.8107, -0.6357, 0.0783])
>>> weight.grad.zero_()
>>> grad_loss = grad_loss.refine_names('C')
>>> loss = (x - weight).abs()
## Ideally we'd check that the names of loss and grad_loss match but we don't yet.
>>> loss.backward(grad_loss)
>>> weight.grad
tensor([-1.8107, -0.6357, 0.0783])
当前支持的操作和子系统
经营者
有关受支持的torch和张量操作的完整列表,请参见命名为 Tensors 的操作员范围。 我们尚不支持以下链接未涵盖的内容:
- 索引,高级索引。
对于torch.nn.functional
运算符,我们支持以下内容:
torch.nn.functional.relu()
torch.nn.functional.softmax()
torch.nn.functional.log_softmax()
torch.nn.functional.tanh()
torch.nn.functional.sigmoid()
torch.nn.functional.dropout()
子系统
支持 Autograd,请参见 Autograd support 。 由于当前未命名渐变,因此优化程序可能有效,但未经测试。
当前不支持 NN 模块。 调用具有命名张量输入的模块时,可能导致以下情况:
- NN 模块参数未命名,因此输出可以部分命名。
- NN 模块正向传递的代码不支持命名张量,并且会适当地出错。
我们也不支持以下子系统,尽管有些子系统可能是开箱即用的:
- 分布
- 序列化 (
torch.load()
,torch.save()
) - 并行处理
- JIT
- Distributed
- ONNX
如果其中任何一个可以帮助您解决用例,请搜索是否已提交问题,否则,请提交一个。
命名张量 API 参考
在本节中,请找到特定于特定张量的 API 的文档。 有关如何通过其他 PyTorch 运算符传播名称的全面参考,请参见名为 Tensors 运算符的覆盖范围。
class torch.Tensor
names¶
存储每个张量维度的名称。
names[idx]
对应于张量尺寸idx
的名称。 如果名称为维,则名称为字符串;如果名称为未命名,则名称为None
。
维度名称可以包含字符或下划线。 此外,维度名称必须是有效的 Python 变量名称(即,不能以下划线开头)。
张量可能没有两个具有相同名称的命名尺寸。
Warning
The named tensor API is experimental and subject to change.
rename(*names, **rename_map)¶
重命名self
的尺寸名称。
主要有两种用法:
self.rename(**rename_map)
返回张量的视图,该视图具有按映射rename_map
中的指定重命名的暗角。
self.rename(*names)
返回张量视图,并使用 names
重命名所有尺寸。 使用self.rename(None)
在张量上放置名称。
不能同时指定位置 args names
和关键字 args rename_map
。
例子:
>>> imgs = torch.rand(2, 3, 5, 7, names=('N', 'C', 'H', 'W'))
>>> renamed_imgs = imgs.rename(N='batch', C='channels')
>>> renamed_imgs.names
('batch', 'channels', 'H', 'W')
>>> renamed_imgs = imgs.rename(None)
>>> renamed_imgs.names
(None,)
>>> renamed_imgs = imgs.rename('batch', 'channel', 'height', 'width')
>>> renamed_imgs.names
('batch', 'channel', 'height', 'width')
Warning
The named tensor API is experimental and subject to change.
rename_(*names, **rename_map)¶
rename()
的就地版本。
refine_names(*names)¶
根据 names
细化self
的尺寸名称。
细化是重命名的特殊情况,可以“提升”未命名的尺寸。 可以将None
暗号细化为任何名称; 命名的 dim 只能精简为具有相同的名称。
由于命名张量可以与未命名张量共存,因此优化名称提供了一种编写可处理命名张量和未命名张量的命名张量感知代码的好方法。
names
最多可以包含一个省略号(...
)。 椭圆形贪婪地展开。 使用来自self.names
的相应索引的名称,将其就地扩展为将 names
填充为与self.dim()
相同的长度。
Python 2 不支持 Ellipsis,但是可以改用字符串文字('...'
)。
参数
名称(str 的可迭代)–输出张量的所需名称。 最多可以包含一个省略号。
Examples:
>>> imgs = torch.randn(32, 3, 128, 128)
>>> named_imgs = imgs.refine_names('N', 'C', 'H', 'W')
>>> named_imgs.names
('N', 'C', 'H', 'W')
>>> tensor = torch.randn(2, 3, 5, 7, 11)
>>> tensor = tensor.refine_names('A', ..., 'B', 'C')
>>> tensor.names
('A', None, None, 'B', 'C')
Warning
The named tensor API is experimental and subject to change.
align_as(other) → Tensor¶
排列self
张量的尺寸以匹配other
张量中的尺寸顺序,为任何新名称添加大小为 1 的变暗。
此操作对于按名称进行显式广播很有用(请参见示例)。
为了使用此方法,必须命名self
的所有暗色。 生成的张量是原始张量的视图。
self
的所有尺寸名称必须存在于other.names
中。 other
可能包含不在self.names
中的命名尺寸; 对于这些新名称中的每一个,输出张量都有一个一维的尺寸。
要将张量对齐到特定顺序,请使用 align_to()
。
Examples:
## Example 1: Applying a mask
>>> mask = torch.randint(2, [127, 128], dtype=torch.bool).refine_names('W', 'H')
>>> imgs = torch.randn(32, 128, 127, 3, names=('N', 'H', 'W', 'C'))
>>> imgs.masked_fill_(mask.align_as(imgs), 0)
## Example 2: Applying a per-channel-scale
def scale_channels(input, scale):
scale = scale.refine_names('C')
return input * scale.align_as(input)
>>> num_channels = 3
>>> scale = torch.randn(num_channels, names=('C',))
>>> imgs = torch.rand(32, 128, 128, num_channels, names=('N', 'H', 'W', 'C'))
>>> more_imgs = torch.rand(32, num_channels, 128, 128, names=('N', 'C', 'H', 'W'))
>>> videos = torch.randn(3, num_channels, 128, 128, 128, names=('N', 'C', 'H', 'W', 'D'))
## scale_channels is agnostic to the dimension order of the input
>>> scale_channels(imgs, scale)
>>> scale_channels(more_imgs, scale)
>>> scale_channels(videos, scale)
Warning
The named tensor API is experimental and subject to change.
align_to(*names)¶
排列self
张量的尺寸以匹配 names
中指定的顺序,为任何新名称添加大小为 1 的变暗。
All of the dims of self
must be named in order to use this method. The resulting tensor is a view on the original tensor.
self
的所有尺寸名称必须出现在 names
中。 names
可能包含self.names
中没有的其他名称; 对于这些新名称中的每一个,输出张量都有一个一维的尺寸。
names
最多可以包含一个省略号(...
)。 省略号按在self
中出现的顺序扩展为等于 names
中未提及的self
的所有尺寸名称。
Python 2 does not support Ellipsis but one may use a string literal instead ('...'
).
Parameters
名称(可迭代的)–输出张量的所需尺寸顺序。 最多可以包含一个省略号,扩展为self
的所有未提及的暗号。
Examples:
>>> tensor = torch.randn(2, 2, 2, 2, 2, 2)
>>> named_tensor = tensor.refine_names('A', 'B', 'C', 'D', 'E', 'F')
## Move the F and E dims to the front while keeping the rest in order
>>> named_tensor.align_to('F', 'E', ...)
Warning
The named tensor API is experimental and subject to change.
unflatten(dim, namedshape)¶
以namedshape
指定的形状将命名尺寸 dim
展平。
Parameters
命名为形状 –(可复制(name, size)
元组)。
Examples:
>>> flat_imgs = torch.rand(32, 3 * 128 * 128, names=('N', 'features'))
>>> imgs = flat_imgs.unflatten('features', (('C', 3), ('H', 128), ('W', 128)))
>>> imgs.names, images.shape
(('N', 'C', 'H', 'W'), torch.Size([32, 3, 128, 128]))
Warning
The named tensor API is experimental and subject to change.
flatten(dims, out_dim) → Tensor
将dims
展平为名称为out_dim
的单个维度。
所有<cite>变暗</cite>必须在self
张量中按顺序连续,但在内存中不必是连续的。
Examples:
>>> imgs = torch.randn(32, 3, 128, 128, names=('N', 'C', 'H', 'W'))
>>> flat_imgs = imgs.flatten(['C', 'H', 'W'], 'features')
>>> flat_imgs.names, flat_imgs.shape
(('N', 'features'), torch.Size([32, 49152]))
Warning
The named tensor API is experimental and subject to change.