命名張量#
建立日期:2019 年 10 月 8 日 | 最後更新日期:2025 年 6 月 14 日
命名張量允許使用者為張量維度指定明確的名稱。在大多數情況下,接受維度引數的操作也會接受維度名稱,從而無需按位置跟蹤維度。此外,命名張量使用名稱自動檢查 API 在執行時是否被正確使用,提供額外的安全性。名稱還可用於重新排列維度,例如,支援“按名稱廣播”而不是“按位置廣播”。
警告
The named tensor API is a prototype feature and subject to change.
建立命名張量#
工廠函式現在接受一個名為 names 的新引數,該引數將名稱與每個維度關聯起來。
>>> torch.zeros(2, 3, names=('N', 'C'))
tensor([[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) 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', ...)
使用 flatten() 和 unflatten() 分別展平(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')
自動微分支援#
自動微分目前僅以有限的方式支援命名張量:自動微分會忽略張量上的所有名稱。梯度計算仍然正確,但我們失去了名稱提供的安全性。
>>> 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 和 tensor 操作的完整列表,請參閱 命名張量運算子覆蓋範圍。我們尚未支援以下連結中未涵蓋的內容:
索引、高階索引。
對於 torch.nn.functional 運算子,我們支援以下內容:
子系統#
支援自動微分,請參閱 自動微分支援。由於梯度目前是未命名的,最佳化器可能可以工作,但未經測試。
NN 模組目前不受支援。在使用具有命名張量輸入的模組時,這可能導致以下情況:
NN 模組引數是未命名的,因此輸出可能部分命名。
NN 模組的轉發(forward)傳遞包含不支援命名張量的程式碼,並將適當地引發錯誤。
我們也不支援以下子系統,儘管其中一些可能開箱即用:
分佈(distributions)
序列化(
torch.load(),torch.save())多程序(multiprocessing)
JIT
分散式
ONNX
如果其中任何一個對您的用例有幫助,請搜尋是否已有相關 issue,如果沒有,請提交一個 issue。
命名張量 API 參考#
在本節中,請查詢命名張量特定 API 的文件。有關名稱如何透過其他 PyTorch 運算子傳播的全面參考,請參閱 命名張量運算子覆蓋範圍。
- class torch.Tensor
- names#
儲存此張量每個維度的名稱。
names[idx]對應於張量第idx個維度的名稱。名稱可以是字串(如果維度已命名)或None(如果維度未命名)。維度名稱可以包含字元或下劃線。此外,維度名稱必須是有效的 Python 變數名(即,不能以下劃線開頭)。
張量不能有兩個同名的命名維度。
警告
命名張量 API 是實驗性的,可能會發生更改。
- rename(*names, **rename_map)[source]#
重新命名
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)[source]#
根據
names精煉(refine)self的維度名稱。精煉是重新命名的一種特殊情況,它“提升”了未命名的維度。一個
None維度可以被精煉成任何名稱;一個已命名的維度只能被精煉成相同的名稱。由於命名張量可以與未命名張量共存,因此細化名稱提供了一種很好的方式來編寫既適用於命名張量又適用於未命名張量的命名張量感知程式碼。
names最多可以包含一個省略號(...)。省略號是貪婪地展開的;它會就地展開,以填充names,使其長度與self.dim()相同,並使用self.names的相應索引中的名稱。Python 2 不支援省略號,但可以使用字串字面量代替(
'...')。- 引數
names(可迭代的 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) Tensor#
置換
self張量的維度,使其與other張量中的維度順序匹配,併為任何新名稱新增大小為一的維度。此操作對於按名稱進行顯式廣播(參見示例)非常有用。
為了使用此方法,
self的所有維度都必須命名。結果張量是原始張量的檢視。self的所有維度名稱必須存在於other.names中。other可能包含self.names中不存在的命名維度;輸出張量為每個新名稱包含一個大小為一的維度。要將張量對齊到特定順序,請使用
align_to()。示例
# 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)
警告
命名張量 API 是實驗性的,可能會發生更改。
- align_to(*names)[source]#
置換
self張量的維度,使其與names中指定的順序匹配,併為任何新名稱新增大小為一的維度。為了使用此方法,
self的所有維度都必須命名。結果張量是原始張量的檢視。self的所有維度名稱必須存在於names中。names可能包含self.names中不存在的額外名稱;輸出張量為每個新名稱包含一個大小為一的維度。names最多可以包含一個省略號(...)。省略號被展開,以等於names中未提及的self的所有維度名稱,按它們在self中出現的順序。Python 2 不支援省略號,但可以使用字串字面量代替(
'...')。- 引數
names(可迭代的 str)– 輸出張量的期望維度順序。可以包含最多一個省略號,該省略號將被展開為
self中所有未提及的維度名稱。
示例
>>> 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', ...)
警告
命名張量 API 是實驗性的,可能會發生更改。
- flatten(dims, out_dim) Tensor
將
dims展平為一個名為out_dim的單一維度。dims 的所有維度必須在
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 是實驗性的,可能會發生更改。