具名張量¶
具名張量允許使用者為張量維度指定明確的名稱。 在大多數情況下,採用維度參數的運算將接受維度名稱,從而避免了按位置追蹤維度的需要。 此外,具名張量使用名稱來自動檢查 API 是否在運行時被正確使用,從而提供額外的安全性。 名稱也可以用於重新排列維度,例如,為了支援「按名稱廣播」而不是「按位置廣播」。
警告
具名張量 API 是一個原型功能,可能會發生變更。
建立具名張量¶
工廠函數現在採用一個新的 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
來存取 tensor 的維度名稱,並使用 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)
具名 tensors 可以與不具名 tensors 並存;具名 tensors 是 torch.Tensor
的實例。不具名 tensors 具有 None
作為維度名稱。具名 tensors 並不要求所有維度都必須具名。
>>> imgs = torch.randn(1, 2, 2, 3 , names=(None, 'C', 'H', 'W'))
>>> imgs.names
(None, 'C', 'H', 'W')
名稱傳播語意¶
具名 tensors 使用名稱來自動檢查 API 在執行時是否被正確調用。這個過程稱為名稱推斷。更正式地說,名稱推斷包含以下兩個步驟:
檢查名稱:運算子可能會在執行時執行自動檢查,以檢查某些維度名稱是否必須匹配。
傳播名稱:名稱推斷會將名稱傳播到輸出 tensors。
所有支援具名 tensors 的操作都會傳播名稱。
>>> x = torch.randn(3, 3, names=('N', 'C'))
>>> x.abs().names
('N', 'C')
匹配語意¶
兩個名稱,如果它們相等(字串相等)或至少其中一個為 None
,則認為是匹配。Nones 本質上是一個特殊的“萬用字元”名稱。
unify(A, B)
決定將哪個名稱 A
和 B
傳播到輸出。如果它們匹配,則它會回傳兩個名稱中更具體的那個。如果名稱不匹配,則會發生錯誤。
注意
實際上,在使用具名 tensors 時,應避免使用不具名的維度,因為它們的處理可能很複雜。建議使用 refine_names()
將所有不具名的維度提升為具名的維度。
基本名稱推斷規則¶
讓我們看看在沒有廣播的情況下,對於添加兩個一維 tensors,match
和 unify
是如何在名稱推斷中使用的。
x = torch.randn(3, names=('X',))
y = torch.randn(3)
z = torch.randn(3, names=('Z',))
檢查名稱:檢查兩個 tensors 的名稱是否匹配。
對於以下範例:
>>> # 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',)
有關名稱推斷規則的完整列表,請參閱 具名 Tensors 運算子覆蓋範圍。以下是兩個常見的操作,可能對您有所幫助:
通過名稱顯式對齊¶
使用 align_as()
或 align_to()
,通過名稱將 tensor 維度對齊到指定的順序。這對於執行“通過名稱廣播”很有用。
# 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()
分別展平和解除展平維度。這些方法比 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 目前以有限的方式支援具名 tensors:autograd 忽略所有 tensors 上的名稱。梯度計算仍然正確,但我們失去了名稱給我們的安全性。
>>> 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 運算的完整列表,請參閱 具名 Tensors 運算子覆蓋範圍。我們目前尚不支援連結中未包含的以下內容:
索引,進階索引。
對於 torch.nn.functional
運算子,我們支援以下內容:
子系統¶
支援 Autograd,請參閱 Autograd 支援。因為梯度目前是不具名的,所以最佳化器可能會起作用,但尚未經過測試。
目前不支援 NN 模組。當使用具名張量輸入呼叫模組時,可能會導致以下情況:
NN 模組參數未命名,因此輸出可能只有部分命名。
NN 模組的前向傳遞 (forward pass) 中有程式碼不支援具名張量,並會適當地產生錯誤。
我們也不支援以下子系統,儘管有些可能可以直接運作:
distributions (分佈)
序列化 (
torch.load()
、torch.save()
)多進程 (multiprocessing)
JIT (即時編譯)
分散式 (distributed)
ONNX
如果這些功能對您的使用案例有幫助,請搜尋是否已提交 issue,如果沒有,請提交一個。
具名張量 API 參考¶
在本節中,您可以找到具名張量特定 API 的文件。 有關名稱如何在其他 PyTorch 運算子中傳播的完整參考,請參閱具名張量運算子覆蓋範圍。
- class torch.Tensor
- names¶
儲存此張量每個維度的名稱。
names[idx]
對應於張量維度idx
的名稱。如果維度已命名,則名稱為字串;如果維度未命名,則名稱為None
。維度名稱可以包含字元或底線。 此外,維度名稱必須是有效的 Python 變數名稱 (即,不能以底線開頭)。
張量不能有兩個具有相同名稱的命名維度。
警告
具名張量 API 是實驗性的,可能會更改。
- rename(*names, **rename_map)[source][source]¶
重新命名
self
的維度名稱。有兩種主要用法
self.rename(**rename_map)
傳回張量上的視圖 (view),該視圖的維度已按照映射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][source]¶
根據
names
優化self
的維度名稱。優化是重新命名的特殊情況,可以“提升”未命名的維度。
None
維度可以優化為具有任何名稱;命名的維度只能優化為具有相同的名稱。由於具名張量可以與未命名的張量共存,因此優化名稱提供了一種很好的方式來編寫具名張量感知程式碼,該程式碼可以與具名和未命名的張量一起使用。
names
最多可以包含一個 Ellipsis (...
)。 Ellipsis 會貪婪地擴展;它會就地擴展,以使用來自self.names
對應索引的名稱,將names
填滿到與self.dim()
相同的長度。Python 2 不支援 Ellipsis,但可以使用字串文字代替 (
'...'
)。- 參數
names (iterable of str) – 輸出張量所需的名稱。 最多可以包含一個 Ellipsis。
範例
>>> 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
張量中的維度順序,並為任何新的名稱添加大小為 1 的維度。此操作對於通過名稱進行顯式廣播非常有用(請參閱範例)。
必須命名
self
的所有維度才能使用此方法。結果張量是原始張量的一個視圖。self
的所有維度名稱必須存在於other.names
中。other
可能包含不在self.names
中的已命名維度;輸出張量會為每個新名稱都有一個大小為 1 的維度。要將張量對齊到特定順序,請使用
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)[原始碼][原始碼]¶
排列
self
張量的維度,使其符合names
中指定的順序,並為任何新的名稱添加大小為 1 的維度。必須命名
self
的所有維度才能使用此方法。結果張量是原始張量的一個視圖。self
的所有維度名稱必須存在於names
中。names
可能包含不在self.names
中的其他名稱;輸出張量會為每個新名稱都有一個大小為 1 的維度。names
最多可以包含一個 Ellipsis(...
)。Ellipsis 會展開成等於self
中所有未在names
中提及的維度名稱,並按照它們在self
中出現的順序排列。Python 2 不支援 Ellipsis,但可以使用字串文字代替 (
'...'
)。- 參數
names (字串的 iterable) – 輸出張量所需的維度順序。最多可以包含一個 Ellipsis,該 Ellipsis 會展開為
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
。在
self
張量中,所有 dims 必須是連續的,但不一定要在記憶體中連續。範例
>>> 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 是實驗性的,可能會更改。