返回

PyTorch模型输入大小不匹配问题解决:训练与测试数据集维度差异

Ai

PyTorch 模型输入样本大小不匹配问题:训练与测试数据集维度差异

最近接触了 PyTorch 和 AI 领域的一些东西,我找到了一个关于自动调制分类(AMC)的开源项目(https://github.com/kacperbednarz1997/AMC_nets)。

作者在项目中比较了多种用于 AMC 的模型。我下载了他用到的所有数据集 (RadioML2016.10a、RadioML2016.10b、RadioML2018.01a 和 MIGOU)。

比如说,我用了 RadioML2016.10a 数据集训练了一个 AWN 模型,每个输入样本是 IQ 信号,也就是 2x128 个值。训练完成后,验证集和测试集上的结果看起来还不错。

我想用 RadioML2018.01a 这个数据集来测试一下已经训练好的模型,看看它在从未见过的数据上的表现如何。但是,这个数据集的输入样本大小是 2x1024,而不是 2x128。这就麻烦了,我该怎么办呢?

我想知道:

  1. PyTorch 够不够灵活?
    a) 它能不能自动把一个 2x1024 的输入样本分成 8 个 2x128 的子样本,然后把标签复制 8 份?
    b) 还是说它只取前 128 个值,忽略剩下的?
  2. 我是不是得在进行推理(测试)之前,自己手动把数据集按照 (a) 的方式切分好?

如果反过来,训练集是 2x1024,测试集是 2x128,又该怎么办?

问题原因分析

这个问题的根本原因在于,训练模型时使用的输入数据维度,和测试(或推理)时使用的输入数据维度不一致 。PyTorch 模型,特别是卷积神经网络(CNN),通常对输入数据的形状(shape)有特定要求。

这种不匹配会导致两个主要问题:

  1. 维度错误(Dimension Error) :大多数 PyTorch 模型在定义时就已经确定了输入层的大小。如果输入数据的维度和模型期望的不一样,就会直接报错,模型无法运行。
  2. 性能下降 :即使模型能够勉强处理不同维度的数据(比如某些全连接层网络,做了相应的修改能应对变长输入),模型的性能通常也会受到影响。因为模型是基于特定的输入维度进行训练的,改变输入维度可能会破坏模型学习到的特征表示。

解决方案

针对这个问题,我们需要根据具体情况选择不同的解决办法。下面我分别针对几种情况给出建议:

情况一:训练集 2x128,测试集 2x1024

1. 数据预处理:分割大样本

这是最直接、也是最推荐的解决办法。我们需要把 2x1024 的测试样本分割成多个 2x128 的子样本。

  • 原理: 因为模型是在 2x128 数据上训练的,分割后每个子样本都符合模型的输入要求。
  • 代码示例(Python, PyTorch):
import torch

def split_samples(samples, input_size=128):
    """
    将样本分割成指定大小的子样本。

    Args:
        samples: 输入样本,形状为 (batch_size, 2, 1024)
        input_size: 目标子样本大小 (128)

    Returns:
        分割后的子样本,形状为 (batch_size * num_splits, 2, input_size)
    """
    batch_size, _, original_size = samples.shape
    num_splits = original_size // input_size
    reshaped_samples = samples.reshape(batch_size * num_splits, 2, input_size)
    return reshaped_samples

# 假设 test_data 是你的 2x1024 测试数据集
test_data = torch.randn(10, 2, 1024)  # 示例数据

# 分割样本
split_test_data = split_samples(test_data)

# 现在 split_test_data 可以直接用于测试模型了
print(split_test_data.shape)  # 输出: torch.Size([80, 2, 128])
# 注意:标签也需要相应地复制 num_splits 份.
*   **安全建议** :这种数据预处理方式是安全的,数据没有任何丢失或者扭曲, 只是形式上的切割。

2. 修改模型(不推荐,除非你非常清楚自己在做什么)

如果你对模型结构有深入的理解,也可以尝试修改模型,让它能够接受 2x1024 的输入。但这种方法通常比较复杂,容易出错,而且可能导致模型性能下降。所以一般不建议直接上手。

情况二:训练集 2x1024,测试集 2x128

1. 数据预处理: 填充或截取

处理的办法比前面简单点, 可以考虑将2x128 的数据补成 2x1024,或者从原模型训练集的2x1024采样数据.

  • 原理: 通过补零或其它数据增强方法,让每个样本在长度上与模型接受尺寸匹配,这使得数据符合训练集数据的维度分布.

  • 代码示例(Python, PyTorch):

import torch
import torch.nn.functional as F

def pad_samples(samples, target_size=1024):
    """
    将样本填充到指定大小。

    Args:
        samples: 输入样本,形状为 (batch_size, 2, 128)
        target_size: 目标大小 (1024)

    Returns:
        填充后的样本,形状为 (batch_size, 2, target_size)
    """
    batch_size, _, original_size = samples.shape
    padding_size = target_size - original_size
    # 在最后一个维度上进行填充,左侧填充 0,右侧填充 padding_size
    padded_samples = F.pad(samples, (0, padding_size), "constant", 0)  # 使用 0 填充
    return padded_samples

# 假设 test_data_small 是你的 2x128 测试数据集
test_data_small = torch.randn(10, 2, 128)  # 示例数据

# 填充样本
padded_test_data = pad_samples(test_data_small)

# 现在 padded_test_data 可以用于测试模型了
print(padded_test_data.shape)
  • 另一种做法,直接从原始的训练集获取一部分数据 ,如果实在缺少原始2x1024尺寸的数据的话, 作为新的训练集合.

2. 使用Global Average Pooling(全局平均池化)-进阶技巧

  • 原理 :假设在定义网络结构的时候,加入一个Global Average Pooling 层,可以将任意大小的feature map转换成一个固定长度的向量。 这样就一定程度可以对输入的尺寸不那么敏感。
import torch
import torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()
        #假设你的其他层结构保持原样,我仅展示重要的部分。
        self.conv1 = nn.Conv1d(in_channels=2, out_channels=64, kernel_size=3)
        #假设conv1的输出结果是[批次,64,某个可变长度L].
        self.global_avg_pool = nn.AdaptiveAvgPool1d(1) #输出[批次,64,1].
        self.fc1=nn.Linear(64,10)# 全连接层的输入数量为global avg pooling后特征数量
        # ... 其余层...

    def forward(self,x):
      x= self.conv1(x)
      x = self.global_avg_pool(x) #输出形状变为 [Batch, 64, 1]
      x = torch.flatten(x, 1)      #输出形状变成 [Batch,64]
      x = self.fc1(x)
      return x

通过添加AdaptiveAvgPool1d(1),无论self.conv1产生的特征映射长度L为多少, 都会被转换成为[Batch,64,1]大小.随后展平成[Batch,64].
使用注意事项:
虽然全局平均池化层在处理不同输入尺寸时,具有一定的弹性, 然而不保证效果相同, 它有可能损失某些长度独有的信息, 使用它只是缓解这种尴尬尺寸输入的一个技巧。

  • 全局平均池化适合用在特征图尺寸较大,需要减少参数和计算量时. 它也可以一定程度提高模型抗过拟合性。

通用建议与注意事项

  • 数据一致性: 最好的做法是在整个项目中(训练、验证、测试)使用相同大小的输入数据。这样可以避免很多麻烦。
  • 数据增强: 在数据预处理阶段,除了分割和填充,还可以考虑使用一些数据增强技巧,如随机裁剪、添加噪声等。
  • 模型理解: 在修改模型之前,一定要仔细理解模型的结构和原理。避免盲目修改。

总结一下,处理 PyTorch 模型输入大小不匹配问题,最核心的思想就是保证训练和测试数据的维度一致性 。在做不到这一点的,可以通过对数据的合理预处理(切分或填充), 在大部分情况下就能顺利让模型成功运行。 而加入全局池化等操作,是作为锦上添花式的调参出现.