欠拟合单个批次:不会导致自动编码器过度拟合一维数据的多样本批次。如何调试?
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 问题。解决方案是使用更好的激活函数,如 LeakyReLU、ELU、MISH 等
线性与转化
在你的例子中,你在单个批次上过度拟合。由于线性层将比卷积层具有更多参数,也许它们是给定小数据轻松记忆
批量大小
由于您在单个批次上过拟合,小批次数据将使 memorise 另一方面,对于 large batch,每批次 Update 网络(在过度拟合期间)使网络学习 广义 抽象特征。 (如果有更多批次且数据种类繁多,这种方法效果更好)
我尝试使用简单的 Gaussian 数据重现您的问题。只需使用具有适当学习率的 LeakyReLU 代替 ReLU 即可解决问题。
使用了您提供的相同架构。
超参数:
batch_size = 16
纪元 = 100
lr = 1e-3
优化器 = Adam
损失(用 ReLU 训练后)= 0.27265918254852295
损失(在使用 LeakyReLU 训练后)= 0.0004763789474964142
用Relu
使用 Leaky Relu
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 问题。解决方案是使用更好的激活函数,如 LeakyReLU、ELU、MISH 等
线性与转化
在你的例子中,你在单个批次上过度拟合。由于线性层将比卷积层具有更多参数,也许它们是给定小数据轻松记忆
批量大小
由于您在单个批次上过拟合,小批次数据将使 memorise 另一方面,对于 large batch,每批次 Update 网络(在过度拟合期间)使网络学习 广义 抽象特征。 (如果有更多批次且数据种类繁多,这种方法效果更好)
我尝试使用简单的 Gaussian 数据重现您的问题。只需使用具有适当学习率的 LeakyReLU 代替 ReLU 即可解决问题。 使用了您提供的相同架构。
超参数:
batch_size = 16
纪元 = 100
lr = 1e-3
优化器 = Adam
损失(用 ReLU 训练后)= 0.27265918254852295
损失(在使用 LeakyReLU 训练后)= 0.0004763789474964142