【问题标题】:Masking and Instance Normalization in PyTorchPyTorch 中的屏蔽和实例规范化
【发布时间】:2020-02-10 03:37:35
【问题描述】:

假设我有一个 PyTorch 张量,排列为 [N, C, L] 形状,其中 N 是批量大小,C 是通道数或特征数,L 是长度。在这种情况下,如果希望执行实例规范化,可以执行以下操作:

N = 20
C = 100
L = 40
m = nn.InstanceNorm1d(C, affine=True)
input = torch.randn(N, C, L)
output = m(input) 

这将为每个 N*C = 2000 个数据切片在 L 维维度上执行归一化,减去 2000 个均值,按 2000 个标准差缩放,并按 100 个可学习的权重和偏差参数重新缩放(每个通道一个)。这里不言而喻的假设是所有这些值都存在并且是有意义的。

但我有一种情况,对于切片 N=1,我想排除(例如)L=35 之后的所有数据。对于切片 N=2(比如说),所有数据都是有效的。对于切片 N=3,排除 L=30 之后的所有数据等。这模拟了具有多个特征但长度不同的一维时间序列的数据。

如何在 PyTorch 中对此类数据执行实例规范、获取正确的统计数据并维护可微性/AutoGrad 信息?

更新:在保持 GPU 性能的同时,或者至少不会杀死它。

我不能……

  1. ...使用零值的掩码,因为这会破坏计算机均值和方差,从而导致错误结果
  2. ...使用 np.nan 或 np.inf 进行掩码,因为 PyTorch 张量不会忽略这些值,而是将它们视为错误。它们很粘,并导致垃圾结果。 PyTorch 目前缺少 np.nanmean 和 np.nanvar 的等效项。
  3. ...置换或转置为合适的数据排列;没有这样的方法可以满足我的需要
  4. ...使用 pack_padded_sequence;实例规范化不适用于该数据结构,据我所知,无法将数据导入该结构。此外,数据重新排列仍然是必要的,见上文 3。

我是否错过了一种可以满足我需要的方法?或者也许我错过了一种允许上述 3 或 4 工作的数据重新排列方法?

这是循环神经网络一直面临的问题,因此 pack_padded_sequence 功能,但在这里不太适用。

【问题讨论】:

  • 我是否理解正确,如果您指定例如排除最后 35 个条目,您不想更新 InstanceNorms 最后 35 个权重(和偏差)?
  • 你如何处理其他模块中的最后 35 个值? (比如Conv1dLinear 之类的?)
  • @flawr 并不是我不想更新它们(尽管从技术上讲它们不应该更新。)这是归一化依赖于均值和方差的计算,我希望该计算忽略掩码值。例如,[1, 2, 3, 4, 5, ___, ___, ___, ___, ___] 应该基于 3 的平均值和 2 的总体方差进行归一化,而不管 ___ 表示的掩码值如何。
  • @flawr 计算后,它们将从损失函数中被屏蔽。如有必要,重新屏蔽为零也是一种选择。
  • Sot huis 意味着对于您通过修改后的InstanceNorm 的每一批次,您还传递了一个索引列表/张量(大小为NxC),这些索引定义了哪些值是相关的,哪个应该是蒙面,对吧?

标签: pytorch normalization


【解决方案1】:

我认为这不能直接使用现有的InstanceNorm1d 来实现,最简单的方法可能是自己从头开始实现。我做了一个应该可以工作的快速实现。为了让它更通用一点,这个模块需要一个布尔掩码(一个与输入大小相同的布尔张量),它指定在通过实例规范时应该考虑哪些元素。

import torch


class MaskedInstanceNorm1d(torch.nn.Module):
    def __init__(self, num_features, eps=1e-6, momentum=0.1, affine=True, track_running_stats=False):
        super().__init__()
        self.num_features = num_features
        self.eps = eps
        self.momentum = momentum
        self.affine = affine
        self.track_running_stats = track_running_stats
        self.gamma = None
        self.beta = None
        if self.affine:
            self.gamma = torch.nn.Parameter(torch.ones((1, self.num_features, 1), requires_grad=True))
            self.beta = torch.nn.Parameter(torch.zeros((1, self.num_features, 1), requires_grad=True))

        self.running_mean = None
        self.running_variance = None
        if self.affine:
            self.running_mean = torch.zeros((1, self.num_features, 1), requires_grad=True)
            self.running_variance = torch.zeros((1, self.num_features, 1), requires_grad=True)

    def forward(self, x, mask):
        mean = torch.zeros((1, self.num_features, 1), requires_grad=False)
        variance = torch.ones((1, self.num_features, 1), requires_grad=False)

        # compute masked mean and variance of batch
        for c in range(self.num_features):
            if mask[:, c, :].any():
                mean[0, c, 0] = x[:, c, :][mask[:, c, :]].mean()
                variance[0, c, 0] = (x[:, c, :][mask[:, c, :]] - mean[0, c, 0]).pow(2).mean()

        # update running mean and variance
        if self.training and self.track_running_stats:
            for c in range(self.num_features):
                if mask[:, c, :].any():
                    self.running_mean[0, c, 0] = (1-self.momentum) * self.running_mean[0, c, 0] \
                                                 + self.momentum * mean[0, c, 0]
                    self.running_variance[0, c, 0] = (1-self.momentum) * self.running_variance[0, c, 0] \
                                                     + self.momentum * variance[0, c, 0]

        # compute output
        x = (x - mean)/(self.eps + variance).sqrt()

        if self.affine:
            x = x * self.gamma + self.beta

        return x

【讨论】:

  • 我得出了同样的结论,即如果不实施或重新实施某些东西就无法完成。我怀疑将 nanmean 和 nanvar 添加到 torch 中,并有条不紊地将它们扩展到规范化例程中,这是最干净的方法,但这超出了我的时间投资范围。
  • 此外,它的语义(尤其是循环索引)不太正确,但很容易修补它们以提供正确的行为。我会对此进行思考和基准测试,同时希望其他人能看到我错过的东西。
  • @Novak 王到底是什么?我测试了它,它的工作原理就像我们讨论的那样,在更新中只考虑被屏蔽的条目,而未被屏蔽的条目基本上通过一个身份。
  • 统计方向不对,分组不太对;幼稚的解释需要在某些位置嵌套循环。制作一个形状为 [3, 2, 4] 的随机张量,并针对 InstanceNorm1d() 的默认实现对其进行测试。如果掩码张量设置为 all True,您应该会看到相同的结果,但不会。
  • 另外——这也是我真正害怕的——GPU 设备上的性能会因为这些循环而下降。
猜你喜欢
  • 2021-02-10
  • 2020-08-27
  • 2021-11-28
  • 2021-05-23
  • 2018-01-09
  • 1970-01-01
  • 2017-07-09
  • 2015-12-09
  • 2013-12-19
相关资源
最近更新 更多