注意
點擊 這裡 以下載完整的範例程式碼
(原型) MaskedTensor 高級語義¶
建立於:2022 年 10 月 28 日 | 最後更新:2022 年 10 月 28 日 | 最後驗證:未驗證
在開始本教學之前,請確保查看我們的 MaskedTensor 概述教學 <https://pytorch.dev.org.tw/tutorials/prototype/maskedtensor_overview.html>。
本教學的目的旨在幫助使用者了解一些高級語義如何運作以及它們是如何產生的。我們將重點關注兩個特定的方面
*。 MaskedTensor 和 NumPy 的 MaskedArray 之間的差異 *。 簡化語義
準備¶
import torch
from torch.masked import masked_tensor
import numpy as np
import warnings
# Disable prototype warnings and such
warnings.filterwarnings(action='ignore', category=UserWarning)
MaskedTensor 與 NumPy 的 MaskedArray¶
NumPy 的 MaskedArray
與 MaskedTensor 存在一些基本的語義差異。
- *。 它們的工廠函數和基本定義會反轉遮罩(類似於
torch.nn.MHA
);也就是說,MaskedTensor 使用
True
表示「指定」,使用False
表示「未指定」,或「有效」/「無效」,而 NumPy 則相反。 我們認為我們的遮罩定義不僅更直觀,而且也更符合 PyTorch 整體中現有的語義。- *。 交集語義。 在 NumPy 中,如果兩個元素中的一個被遮罩,則產生的元素也將被
遮罩 - 在實踐中,它們 應用 logical_or 運算符。
data = torch.arange(5.)
mask = torch.tensor([True, True, False, True, False])
npm0 = np.ma.masked_array(data.numpy(), (~mask).numpy())
npm1 = np.ma.masked_array(data.numpy(), (mask).numpy())
print("npm0:\n", npm0)
print("npm1:\n", npm1)
print("npm0 + npm1:\n", npm0 + npm1)
npm0:
[0.0 1.0 -- 3.0 --]
npm1:
[-- -- 2.0 -- 4.0]
npm0 + npm1:
[-- -- -- -- --]
同時,MaskedTensor 不支援遮罩不匹配的加法或二元運算符 - 要了解原因,請查看 關於簡化的章節。
mt0:
MaskedTensor(
[ 0.0000, 1.0000, --, 3.0000, --]
)
mt1:
MaskedTensor(
[ --, --, 2.0000, --, 4.0000]
)
mt0 + mt1 failed. Error: Input masks must match. If you need support for this, please open an issue on Github.
但是,如果需要此行為,MaskedTensor 確實透過提供對數據和遮罩的存取權,以及使用 to_tensor()
方便地將 MaskedTensor 轉換為具有填入遮罩值的張量來支援這些語義。 例如
t0:
tensor([0., 1., 0., 3., 0.])
t1:
tensor([0., 0., 2., 0., 4.])
mt2 (t0 + t1):
MaskedTensor(
[ --, --, --, --, --]
)
請注意,遮罩是 mt0.get_mask() & mt1.get_mask(),因為 MaskedTensor
的遮罩與 NumPy 的遮罩相反。
簡化語義¶
回想一下,在 MaskedTensor 的概述教學 中,我們討論了「實作缺少的 torch.nan* 運算」。 這些是簡化的範例 - 從張量中刪除一個(或多個)維度,然後聚合結果的運算符。 在本節中,我們將使用簡化語義來激勵我們對上述匹配遮罩的嚴格要求。
從根本上講,:class:`MaskedTensor`s 執行相同的簡化運算,同時忽略遮罩的(未指定)值。 例如
data = torch.arange(12, dtype=torch.float).reshape(3, 4)
mask = torch.randint(2, (3, 4), dtype=torch.bool)
mt = masked_tensor(data, mask)
print("data:\n", data)
print("mask:\n", mask)
print("mt:\n", mt)
data:
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
mask:
tensor([[False, True, False, False],
[False, True, False, False],
[False, True, False, False]])
mt:
MaskedTensor(
[
[ --, 1.0000, --, --],
[ --, 5.0000, --, --],
[ --, 9.0000, --, --]
]
)
現在,不同的簡化(全部在 dim=1 上)
print("torch.sum:\n", torch.sum(mt, 1))
print("torch.mean:\n", torch.mean(mt, 1))
print("torch.prod:\n", torch.prod(mt, 1))
print("torch.amin:\n", torch.amin(mt, 1))
print("torch.amax:\n", torch.amax(mt, 1))
torch.sum:
MaskedTensor(
[ 1.0000, 5.0000, 9.0000]
)
torch.mean:
MaskedTensor(
[ 1.0000, 5.0000, 9.0000]
)
torch.prod:
MaskedTensor(
[ 1.0000, 5.0000, 9.0000]
)
torch.amin:
MaskedTensor(
[ 1.0000, 5.0000, 9.0000]
)
torch.amax:
MaskedTensor(
[ 1.0000, 5.0000, 9.0000]
)
值得注意的是,遮罩元素下的值不保證具有任何特定值,尤其是當行或列完全被遮罩時(法線也是如此)。 有關遮罩語義的更多詳細資訊,您可以在此 RFC 中找到。
現在,我們可以重新審視這個問題:為什麼我們要強制執行二元運算符的遮罩必須匹配的不變性? 換句話說,為什麼我們不使用與 np.ma.masked_array
相同的語義? 考慮以下範例
data0 = torch.arange(10.).reshape(2, 5)
data1 = torch.arange(10.).reshape(2, 5) + 10
mask0 = torch.tensor([[True, True, False, False, False], [False, False, False, True, True]])
mask1 = torch.tensor([[False, False, False, True, True], [True, True, False, False, False]])
npm0 = np.ma.masked_array(data0.numpy(), (mask0).numpy())
npm1 = np.ma.masked_array(data1.numpy(), (mask1).numpy())
print("npm0:", npm0)
print("npm1:", npm1)
npm0: [[-- -- 2.0 3.0 4.0]
[5.0 6.0 7.0 -- --]]
npm1: [[10.0 11.0 12.0 -- --]
[-- -- 17.0 18.0 19.0]]
現在,讓我們嘗試加法
print("(npm0 + npm1).sum(0):\n", (npm0 + npm1).sum(0))
print("npm0.sum(0) + npm1.sum(0):\n", npm0.sum(0) + npm1.sum(0))
(npm0 + npm1).sum(0):
[-- -- 38.0 -- --]
npm0.sum(0) + npm1.sum(0):
[15.0 17.0 38.0 21.0 23.0]
加總和加法理應具備結合律,但使用 NumPy 的語意時卻不然,這肯定會讓使用者感到困惑。
另一方面,由於 mask0 != mask1,MaskedTensor
會直接禁止此操作。話雖如此,如果使用者願意,還是有方法可以解決這個問題(例如,使用 to_tensor()
將 MaskedTensor 未定義的元素填入 0 值,如下所示),但使用者現在必須更明確地表達他們的意圖。
tensor([15., 17., 38., 21., 23.])