欠拟合单个批次:不会导致自动编码器过度拟合一维数据的多样本批次。如何调试?

Underfitting a single batch: Can't cause autoencoder to overfit multi-sample batches of 1d data. How to debug?

TL;DR

我无法使用自动编码器对具有多个样本的批次进行过拟合。

全连接解码器每批似乎比 conv 解码器处理更多样本,但当样本数量增加时也会失败。 为什么会这样,如何调试?


深入

我正在尝试对大小为 (n, 1, 1024) 的一维数据点使用自动编码器,其中 n 是批处理中的样本数。

我正在尝试过拟合那一个批次。

使用卷积解码器,我只能拟合单个样本 (n=1),当 n>1 我无法将损失 (MSE) 降至 0.2 以下。

蓝色:预期输出(=输入),橙色:重构。

单个样本,单个批次:

多样本,单批次,损失不会下降:

使用多个样本,我们可以看到网络学习了输入(=输出)信号的一般形状,但大大错过了偏差。


使用完全连接的解码器确实可以重建多个样本的批次:


相关代码:

class Conv1DBlock(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size):
        super().__init__()
        self._in_channels = in_channels
        self._out_channels = out_channels
        self._kernel_size = kernel_size

        self._block = nn.Sequential(
                nn.Conv1d(
                        in_channels=self._in_channels,
                        out_channels=self._out_channels,
                        kernel_size=self._kernel_size,
                        stride=1,
                        padding=(self._kernel_size - 1) // 2,
                ),
                # nn.BatchNorm1d(num_features=out_channels),
                nn.ReLU(True),
                nn.MaxPool1d(kernel_size=2, stride=2),
        )

    def forward(self, x):
        for layer in self._block:
            x = layer(x)
        return x


class Upsample1DBlock(nn.Module):
    def __init__(self, in_channels, out_channels, factor):
        super().__init__()
        self._in_channels = in_channels
        self._out_channels = out_channels
        self._factor = factor

        self._block = nn.Sequential(
                nn.Conv1d(
                        in_channels=self._in_channels,
                        out_channels=self._out_channels,
                        kernel_size=3,
                        stride=1,
                        padding=1
                ),  # 'same'
                nn.ReLU(True),
                nn.Upsample(scale_factor=self._factor, mode='linear', align_corners=True),
        )

    def forward(self, x):
        x_tag = x
        for layer in self._block:
            x_tag = layer(x_tag)
        # interpolated = F.interpolate(x, scale_factor=0.5, mode='linear') # resnet idea
        return x_tag

编码器:

self._encoder = nn.Sequential(
            # n, 1024
            nn.Unflatten(dim=1, unflattened_size=(1, 1024)),
            # n, 1, 1024
            Conv1DBlock(in_channels=1, out_channels=8, kernel_size=15),
            # n, 8, 512
            Conv1DBlock(in_channels=8, out_channels=16, kernel_size=11),
            # n, 16, 256
            Conv1DBlock(in_channels=16, out_channels=32, kernel_size=7),
            # n, 32, 128
            Conv1DBlock(in_channels=32, out_channels=64, kernel_size=5),
            # n, 64, 64
            Conv1DBlock(in_channels=64, out_channels=128, kernel_size=3),
            # n, 128, 32
            nn.Conv1d(in_channels=128, out_channels=128, kernel_size=32, stride=1, padding=0),  # FC
            # n, 128, 1
            nn.Flatten(start_dim=1, end_dim=-1),
            # n, 128
        )

转换解码器:

self._decoder = nn.Sequential(
    nn.Unflatten(dim=1, unflattened_size=(128, 1)),  # 1
    Upsample1DBlock(in_channels=128, out_channels=64, factor=4),  # 4
    Upsample1DBlock(in_channels=64, out_channels=32, factor=4),  # 16
    Upsample1DBlock(in_channels=32, out_channels=16, factor=4),  # 64
    Upsample1DBlock(in_channels=16, out_channels=8, factor=4),  # 256
    Upsample1DBlock(in_channels=8, out_channels=1, factor=4),  # 1024
    nn.ReLU(True),
    nn.Conv1d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1),
    nn.ReLU(True),
    nn.Flatten(start_dim=1, end_dim=-1),
    nn.Linear(1024, 1024)
)

FC解码器:

self._decoder = nn.Sequential(
    nn.Linear(128, 256),
    nn.ReLU(True),
    nn.Linear(256, 512),
    nn.ReLU(True),
    nn.Linear(512, 1024),
    nn.ReLU(True),
    nn.Flatten(start_dim=1, end_dim=-1),
    nn.Linear(1024, 1024)
)

另一个观察是,当批量大小增加更多时,比如 16,FC 解码器也开始失败。

在图像中,我试图过拟合 16 个样本批次中的 4 个样本


conv 解码器可能出了什么问题?

如何调试或使 conv 解码器正常工作?

Dying ReLU

  • 我认为 欠拟合 的主要原因是 Dying Relu problem. Your network is simple Autoencoder with no skip/residual 连接。所以瓶颈中的 Code 应该编码数据中关于 bias 的足够信息,以便 Decoder 学习。
  • 所以如果使用ReLU激活函数Negative Biased数据信息可以丢失由于到 Dying ReLU 问题。解决方案是使用更好的激活函数,如 LeakyReLUELUMISH

线性与转化

在你的例子中,你在单个批次上过度拟合。由于线性层将比卷积层具有更多参数,也许它们是给定小数据轻松记忆

批量大小

由于您在单个批次上过拟合小批次数据将使 memorise 另一方面,对于 large batch,每批次 Update 网络(在过度拟合期间)使网络学习 广义 抽象特征。 (如果有更多批次且数据种类繁多,这种方法效果更好)

我尝试使用简单的 Gaussian 数据重现您的问题。只需使用具有适当学习率的 LeakyReLU 代替 ReLU 即可解决问题。 使用了您提供的相同架构。

超参数:

batch_size = 16

纪元 = 100

lr = 1e-3

优化器 = Adam

损失(用 ReLU 训练后)= 0.27265918254852295

损失(在使用 LeakyReLU 训练后)= 0.0004763789474964142

用Relu

使用 Leaky Relu