具名張量¶
具名張量允許使用者為張量維度指定明確的名稱。在大多數情況下,採用維度參數的運算將接受維度名稱,避免需要按位置追蹤維度。此外,具名張量使用名稱在執行時自動檢查 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 存取張量的維度名稱,並使用 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() 分別展平和取消展平維度。這些方法比 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 目前以有限的方式支援命名張量: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 和張量操作的完整清單,請參閱 命名張量運算符涵蓋範圍。我們尚不支援連結中未涵蓋的以下內容
索引、進階索引。
對於 torch.nn.functional 運算符,我們支援以下內容
子系統¶
支援 Autograd,請參閱 Autograd 支援。由於梯度目前未命名,因此優化器可能會起作用,但尚未經過測試。
目前不支援 NN 模組。當使用命名張量輸入呼叫模組時,可能會導致以下情況
NN 模組參數未命名,因此輸出可能部分命名。
NN 模組正向傳遞的程式碼不支援命名張量,並且會適當出錯。
我們也不支援以下子系統,儘管有些子系統可能開箱即用
分佈
序列化(
torch.load()、torch.save())多重處理
JIT
分散式
ONNX
命名張量 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改善self的維度名稱。改善是重新命名的一種特殊情況,它「提升」未命名的維度。
None維度可以改善為具有任何名稱;命名維度只能改善為具有相同的名稱。由於命名張量可以與未命名張量共存,因此改善名稱提供了一種很好的方式來編寫可同時處理命名張量和未命名張量的命名張量感知程式碼。
names最多可以包含一個省略號(...)。省略號會貪婪地展開;它會就地展開以使用self.names中相應索引處的名稱將names填充到與self.dim()相同的長度。Python 2 不支援省略號,但可以使用字串字面量代替(
'...')。- 參數
names (iterable 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) 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最多可以包含一個省略號 (...)。省略號會展開為self中所有未在names中提及的維度名稱,順序與它們在self中出現的順序相同。Python 2 不支援省略號,但可以使用字串字面量代替(
'...')。- 參數
names (iterable of 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 處於實驗階段,可能會有所變更。