命名张量¶
命名张量允许用户为张量维度赋予显式名称。 在大多数情况下,接受维度参数的操作将接受维度名称,从而避免通过位置跟踪维度的需要。 此外,命名张量使用名称在运行时自动检查API的正确使用,提供额外的安全性。名称还可以用于重新排列维度,例如,支持“按名称广播”而不是“按位置广播”。
警告
命名张量 API 是一个原型功能,可能会发生变化。
创建命名张量¶
工厂函数现在接受一个新的 names 参数,用于将名称与每个维度关联起来。
>>> torch.zeros(2, 3, names=('N', 'C'))
张量([[0., 0., 0.],
[0., 0., 0.]], names=('N', 'C'))
命名维度,像常规的张量维度一样,是有序的。
tensor.names[i] 是 tensor 的第 i 维度的名称。
以下工厂函数支持命名张量:
命名维度¶
请参阅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。
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) 为 True
>>> # x + z # match('X', 'Z') 为 False
>>> # x + x # match('X', 'X') 为 True
>>> x + z
尝试广播维度 ['X'] 和维度 ['Z'] 时出错:维度 'X' 和维度 'Z' 从右侧的同一位置开始,但它们不匹配。
传播名称:统一名称以选择要传播的名称。
在 x + y 的情况下,unify('X', None) = 'X' 因为 'X' 比 None 更具体。
>>> (x + y).names
('X',)
>>> (x + x).names
('X',)
有关名称推断规则的综合列表,请参阅命名张量操作符覆盖范围。 以下是两个可能有用且常见的操作:
按名称显式对齐¶
使用 align_as() 或 align_to() 按名称对齐张量维度到指定的顺序。这对于执行“按名称广播”非常有用。
# 此函数对`input`的维度顺序不敏感,
# 只要它在某个地方有一个`C`维度。
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')
# 将 F(维度 5)和 E 维度(维度 4)移动到前面,同时保持
# 其余维度顺序不变
>>> tensor.permute(5, 4, 0, 1, 2, 3)
>>> named_tensor.align_to('F', 'E', ...)
使用 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_named_imgs = named_flat_imgs.unflatten('features', [('C', 3), ('H', 128), ('W', 128)])
>>> unflattened_named_imgs.names
('N', 'C', 'H', 'W')
自动微分支持¶
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 # 目前未命名。未来将会命名
tensor([-1.8107, -0.6357, 0.0783])
>>> weight.grad.zero_()
>>> grad_loss = grad_loss.refine_names('C')
>>> loss = (x - weight).abs()
# 理想情况下,我们会检查损失和梯度损失的名称是否匹配,但我们还没有这样做。
>>> loss.backward(grad_loss)
>>> weight.grad
tensor([-1.8107, -0.6357, 0.0783])
当前支持的操作和子系统¶
运算符¶
请参阅命名张量操作符覆盖范围以获取支持的torch和 张量操作的完整列表。我们尚未支持以下未在链接中涵盖的内容:
索引,高级索引。
对于 torch.nn.functional 操作符,我们支持以下内容:
子系统¶
自动求导功能已支持,请参阅自动求导支持。 由于梯度目前没有命名,优化器可能可以工作,但尚未经过测试。
NN 模块目前不受支持。在调用带有命名张量输入的模块时,可能会导致以下情况:
NN 模块参数是无名称的,因此输出可能部分有名称。
NN 模块的前向传播包含不支持命名张量的代码,并将适当地报错。
我们也不支持以下子系统,尽管其中一些可能可以直接使用:
分布
序列化 (
torch.load(),torch.save())多进程
即时编译
分布式
ONNX
如果这些对你的用例有帮助,请 搜索是否已有相关问题 如果没有,请提交一个。
命名张量 API 参考¶
在本节中,请找到命名张量特定API的文档。 有关名称如何在其他PyTorch操作符中传播的综合参考,请参阅命名张量操作符覆盖范围。
- class torch.Tensor
- names¶
存储此张量每个维度的名称。
names[idx]对应于张量维度idx的名称。 名称要么是一个字符串(如果维度有名称),要么是None(如果维度没有名称)。维度名称可以包含字符或下划线。此外,维度名称必须是有效的Python变量名称(即,不以下划线开头)。
张量可能不会有两个名称相同的命名维度。
警告
命名张量 API 是实验性的,可能会发生变化。
- rename(*names, **rename_map)[源代码]¶
重命名
self的维度名称。有两种主要用途:
self.rename(**rename_map)返回一个张量的视图,其维度已按照映射rename_map中指定的名称重命名。self.rename(*names)返回张量的视图,使用names按位置重命名所有维度。 使用self.rename(None)删除张量上的名称。不能同时指定位置参数
names和关键字参数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, None, None, None) >>> renamed_imgs = imgs.rename('batch', 'channel', 'height', 'width') >>> renamed_imgs.names ('batch', 'channel', 'height', 'width')
警告
命名张量 API 是实验性的,可能会发生变化。
- refine_names(*names)[源代码]¶
根据
names精炼self的维度名称。精炼是一种特殊的重命名操作,它“提升”了未命名的维度。 一个
None维度可以被精炼为任意名称;一个已命名的维度只能被精炼为相同的名称。因为命名张量可以与未命名张量共存,细化名称提供了一种很好的方式来编写既适用于命名张量又适用于未命名张量的代码。
names可能包含最多一个省略号 (...)。 省略号会贪婪地扩展;它在原地扩展以填充names到与self.dim()相同的长度,使用来自self.names对应索引的名称。Python 2 不支持省略号,但可以使用字符串字面量代替(
'...')。- Parameters
names (可迭代对象 of str) – 输出张量所需的名称。最多可以包含一个省略号。
示例:
>>> 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')
警告
命名张量 API 是实验性的,可能会发生变化。
- align_as(other) 张量¶
将
self张量的维度重新排列以匹配other张量的维度顺序,并为任何新名称添加大小为一的维度。此操作对于按名称显式广播非常有用(参见示例)。
为了使用此方法,
self的所有维度都必须命名。 生成的张量是原始张量的视图。self的所有维度名称必须存在于other.names中。other可能包含不在self.names中的命名维度; 输出张量对于每个新名称都有一个大小为一的维度。要将张量对齐到特定顺序,请使用
align_to()。示例:
# 示例 1: 应用掩码 >>> 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) # 示例 2: 应用逐通道缩放 >>> 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 对输入的维度顺序不敏感 >>> scale_channels(imgs, scale) >>> scale_channels(more_imgs, scale) >>> scale_channels(videos, scale)
警告
命名张量 API 是实验性的,可能会发生变化。
- align_to(*names)[源代码]¶
将
self张量的维度重新排列以匹配names中指定的顺序,并为任何新名称添加大小为一的维度。为了使用此方法,
self的所有维度都必须命名。 生成的张量是原始张量的视图。self的所有维度名称必须存在于names中。names可能包含不在self.names中的额外名称; 输出张量对于每个新名称都有一个大小为一的维度。names最多可以包含一个省略号 (...)。 省略号被扩展为等于self中未在names中提到的所有维度名称,按它们在self中出现的顺序排列。Python 2 不支持省略号,但可以使用字符串字面量代替(
'...')。- Parameters
names (iterable of str) – 输出张量所需的维度顺序。最多可以包含一个省略号(Ellipsis),该省略号会扩展到
self中所有未提及的维度名称。
示例:
>>> tensor = torch.randn(2, 2, 2, 2, 2, 2) >>> named_tensor = tensor.refine_names('A', 'B', 'C', 'D', 'E', 'F') # 将F和E维度移动到前面,同时保持其余维度的顺序 >>> named_tensor.align_to('F', 'E', ...)
警告
命名张量 API 是实验性的,可能会发生变化。
- flatten(dims, out_dim) 张量
将
dims展平为一个名为out_dim的单一维度。所有维度必须在
self张量中按顺序连续排列,但不必在内存中连续。示例:
>>> 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]))
警告
命名张量 API 是实验性的,可能会发生变化。