PyTorch中的屏蔽和实例规范化

时间:2019-10-13 06:18:37

标签: pytorch normalization

假设我有一个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功能,但在这里并不十分适用。

1 个答案:

答案 0 :(得分: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