注意
跳转到末尾 以下载完整示例代码。
MaskedTensor 高级语义#
在开始本教程之前,请确保已阅读我们的 MaskedTensor 概述教程 <https://pytorch.ac.cn/tutorials/prototype/maskedtensor_overview.html>。
本教程的目的是帮助用户理解某些高级语义是如何工作的以及它们是如何产生的。我们将重点讨论以下两点:
*. MaskedTensor 与 NumPy 的 MaskedArray 之间的区别 *. 归约(Reduction)语义
准备工作#
# Disable prototype warnings and such
MaskedTensor vs NumPy 的 MaskedArray#
NumPy 的 MaskedArray 在几个基本语义上与 MaskedTensor 存在差异。
- *. 它们的工厂函数和基本定义在掩码(mask)上是相反的(类似于
torch.nn.MHA);也就是说,MaskedTensor 使用
True表示“已指定(specified)”且使用False表示“未指定(unspecified)”,或者说“有效(valid)”/“无效(invalid)”,而 NumPy 则恰恰相反。我们认为我们的掩码定义不仅更直观,而且与 PyTorch 整体的现有语义更加一致。- *. 交集语义。在 NumPy 中,如果两个元素中有一个被掩盖(masked out),结果元素也将
被掩盖——在实践中,它们应用了 logical_or 运算符。
与此同时,MaskedTensor 不支持掩码不匹配的加法或二元运算符——要了解原因,请参阅关于归约的部分。
然而,如果需要这种行为,MaskedTensor 通过提供对数据和掩码的访问,并使用 to_tensor() 将 MaskedTensor 方便地转换为填充了掩码值的 Tensor,从而支持这些语义。例如:
请注意,掩码是 mt0.get_mask() & mt1.get_mask(),因为 MaskedTensor 的掩码与 NumPy 的掩码相反。
归约语义#
回想在 MaskedTensor 概述教程 中,我们讨论了“实现缺失的 torch.nan* 操作”。这些就是归约的例子——即从 Tensor 中移除一个(或多个)维度并聚合结果的运算符。在本节中,我们将使用归约语义来阐明上述对掩码匹配的严格要求。
从根本上说,:class:`MaskedTensor` 在执行相同的归约操作时会忽略被掩盖的(未指定的)值。例如:
现在,尝试不同的归约(均在 dim=1 上):
值得注意的是,被掩盖元素下的值不保证具有任何特定值,特别是当整行或整列都被掩盖时(归一化操作也是如此)。有关掩码语义的更多细节,您可以参考此 RFC。
现在,我们可以重新审视这个问题:为什么我们要强制执行二元运算符的掩码必须匹配这一不变性?换句话说,为什么我们不使用与 np.ma.masked_array 相同的语义?请考虑以下示例:
现在,让我们尝试加法:
求和(Sum)和加法(addition)显然应该是结合的,但在 NumPy 的语义下,它们并不结合,这无疑会让用户感到困惑。
另一方面,MaskedTensor 则根本不允许这种操作,因为 mask0 != mask1。也就是说,如果用户希望这样做,可以通过其他方式实现(例如,如下所示,使用 to_tensor() 将 MaskedTensor 的未定义元素填充为 0 值),但用户现在必须更明确地表达其意图。
结论#
在本教程中,我们学习了 MaskedTensor 和 NumPy MaskedArray 背后的不同设计决策,以及归约语义。总的来说,MaskedTensor 旨在避免歧义和令人困惑的语义(例如,我们尝试在二元运算中保持结合律),这反过来可能需要用户在编写代码时有时更加谨慎,但我们认为这是更好的选择。如果您对此有任何想法,请告知我们!
# %%%%%%RUNNABLE_CODE_REMOVED%%%%%%
脚本总运行时间:(0 分 0.002 秒)