如何在奇数示例大小的批次中训练神经网络?

1 投票
2 回答
55 浏览
提问于 2025-04-14 17:45

我刚开始接触神经网络(NN)这个领域,正在用pytorch进行一些训练。
我决定做一个简单的基础神经网络。
我使用了一个个人的数据集,这个数据集有2377个数值特征和6277个样本。

我第一次尝试让神经网络预测每一个样本,所以伪代码大概是这样的:

for i in range(...):
    X = ... # features
    y = ... # outcome
    y_pred = model(X[i])
    loss = criterion(y_pred, y)

    y_pred.size # [1,1]
    y.size # [1,1]

这个过程每个周期大约需要10秒,我决定通过使用小批量来提高效率。

所以我在开始时定义了批量大小,而在pytorch中,神经网络是这样定义的:

batch_size = 30
n_inputs = X.size[1] #2377

## 2 hidden layers
model = nn.Sequential(
    nn.Linear(n_inputs, 1024),
    nn.ReLU(),
    nn.Linear(1024, 512),
    nn.ReLU(),
    nn.Linear(512, 356),
    nn.ReLU(),
    nn.Linear(356, batch_size),
    nn.ReLU(),
)

然后我就开始按批次进行训练:

for epoch in range(5):
    totalloss = 0  
    permutation = torch.randperm(X.size()[0])
    for i in range(0, X.size()[0], batch_size):
        optimizer.zero_grad()
        indices = permutation[i:i+batch_size]
        batch_x, batch_y = x[indices], y[indices]

        ypred = model(batch_x)
        loss = criterion(ypred, batch_y) 
        totalloss += loss.item()
        
        ## update the weights
        loss.backward()
        optimizer.step()

现在的问题是,我的神经网络总是输出100个值,但是最后一个批次的大小可能会变化。
实际上,如果我选择100作为批量大小,那么最后一个批次就会由77个样本组成(6277%100)。

我相信有办法解决这个问题,而且我的结构中肯定有错误,但我看不出来。

你能帮我把批量训练的过程通用化,让它能适应任何数量的样本和批量大小吗?

2 个回答

1

我建议你使用 Pytorch 的 DataLoader 来批量加载数据,而不是手动加载。这样做会更简单。PyTorch 提供了一个很方便的解决方案,就是在 DataLoader 中使用 drop_last 这个参数。当你把它设置为 True 时,它会丢掉最后一个不完整的批次,确保除了最后一个批次外,所有批次的大小都是一致的。Dataloader 是对 torch 的 Dataset 的一个封装,你可以在 这里 找到更多信息。

import torch
import torch.nn as nn
from torch.utils.data import DataLoader

X = torch.Tensor(...)  # your features
y = torch.Tensor(...)  # your labels

dataset = 

# Create a DataLoader with drop_last=True
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, drop_last=True)

model = ...

optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.MSELoss()

# Training loop
num_epochs = 5
for epoch in range(num_epochs):
    total_loss = 0
    for batch_x, batch_y in dataloader:
        optimizer.zero_grad()
        y_pred = model(batch_x)
        loss = criterion(y_pred, batch_y)
        total_loss += loss.item()
        loss.backward()
        optimizer.step()

    average_loss = total_loss / len(dataloader)
    print(f'Epoch {epoch + 1}/{num_epochs}, Average Loss: {average_loss}')
1

我不明白为什么其他的回答被接受了,因为在你提问时构建模型的方式上有一个根本性的误解!而且其他的回答没有考虑到这一点。
当你定义一个模型以及它的输入和输出大小时,其实你只是在考虑一个样本。你并没有使用 batch_size 来调整输出。当你把一批输入数据放入模型时,PyTorch 会在内部处理这批数据,模型会同时对每个样本进行评估。

你可以看看官方的 PyTorch 教程,他们在这个教程中为 Fashion MNIST 数据集构建了一个模型。这个数据集中的每张图片是 (28x28x1) 像素(灰度图),并且有10个不同的类别需要预测。注意一下模型的第一层和最后一层:

nn.Linear(28*28, 512)
....
nn.Linear(512, 10)

这里的输入是图片的像素 28*28,输出是 10 个数字,代表10个类别。然后你可以使用 SoftMaxcategorical_crossentropy 来进行预测。模型本身并没有关于批量大小的信息,因为模型并不需要这些。

大多数情况下,最后一批数据的大小比其他批次小一点也没问题。如果你的批量大小是 32,但最后一批只有 15 个样本,模型会只处理这15个样本和标签,进行预测,并将这15个结果与最后一批的15个标签进行比较。
如果出于某种原因你需要所有批次的大小完全相同(例如,对于有状态的 LSTM),那么你可以使用 DataLoader 并设置 drop_last=True。但大多数情况下,这并不是必要的,如果你使用它,模型只会忽略掉一些数据。

使用 DataLoader 仍然是个好主意,因为它们可以高效地在CPU上加载你的数据,而模型则可以在GPU上进行训练。

撰写回答