You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyTorch自定义类VGG模型仅batch_size=1可用,其他值报尺寸不匹配错误

问题:Batch Size≠1时模型训练报错

使用PyTorch实现类VGG19的神经网络模型,当设置batch_size为1以外的值时,抛出错误:

ValueError: Expected input batch_size (1) to match target batch_size (16).

仅当batch_size=1时模型能正常训练,希望修改batch_size且不触发错误,以下是模型代码及完整错误回溯:

模型代码

import os
import glob

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt

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

from torchvision import datasets, transforms

class ICModel(nn.Module):

    def __init__(self):
        super().__init__()
        # CNN
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=2)
        self.conv2 = nn.Conv2d(64, 128, kernel_size=3, stride=2)
        self.conv3 = nn.Conv2d(128, 256, kernel_size=3, stride=2)
        self.conv4 = nn.Conv2d(256, 512, kernel_size=3, stride=2)

        # FULLY CONNECTED LAYERS
        self.fc1 = nn.Linear(61952, 256)
        self.fc2 = nn.Linear(256, 64)
        self.out = nn.Linear(64, 2)
    def forward(self, x):
        # CONV - 1
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, kernel_size=3, stride=1)
        # CONV - 2
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, kernel_size=3, stride=1)
        # CONV - 3
        x = F.relu(self.conv3(x))
        x = F.max_pool2d(x, kernel_size=3, stride=1)
        # CONV - 4
        x = F.relu(self.conv4(x))

        flattened_size = x.shape[0] * x.shape[1] * x.shape[2] * x.shape[3]

        x = x.view(-1, flattened_size)
        # FULLY CONNECTED LAYERS
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.out(x)

        return F.log_softmax(x, dim=1)

    def train(self, dataset_dir='', epochs=5, batch_size=16, seed=35, learning_rate=0.001, model_weights_path=''):
        if dataset_dir == '':
            raise Exception("Please enter a valid dataset directory path!")

        train_correct = []
        train_losses = []

        torch.manual_seed(seed)

        # CRITERION AND OPTIMIZER SETUP
        criterion = nn.CrossEntropyLoss()
        optimizer = torch.optim.Adam(self.parameters(), lr=learning_rate)

        optim_width, optim_height = 224, 224

        data_transforms = transforms.Compose([
            transforms.Resize((optim_width, optim_height)),  # Resize images to average dimensions
            transforms.ToTensor(),  # Convert images to PyTorch tensors
            transforms.Normalize(mean=[0.456, 0.456, 0.456], std=[0.456, 0.456, 0.456])  # Normalize images
        ])

        dataset = datasets.ImageFolder(root=dataset_dir, transform=data_transforms)
        train_loader = DataLoader(dataset=dataset, batch_size=batch_size, shuffle=True)

        for epoch in range(epochs):
            trn_corr = 0

            for b, (X_train, y_train) in enumerate(train_loader):
                b += 1
                y_pred = self(X_train)
                loss = criterion(y_pred, y_train)

                predicted = torch.max(y_pred, dim=1)[1]
                batch_corr = (predicted == y_train).sum()

                trn_corr += batch_corr.item()

                optimizer.zero_grad()
                loss.backward()
                optimizer.step()

                if b % 4 == 0:
                    print(f'Epoch: {epoch}  Batch: {b}  Loss: {loss.item()}')

        train_losses.append(loss)
        train_correct.append(trn_corr)

        if (model_weights_path != '') & os.path.exists(model_weights_path) & os.path.isdir(model_weights_path):
            torch.save({
                'model_state_dict': self.state_dict(),
                'optimizer_state_dict': optimizer.state_dict()
            }, model_weights_path)

    def test(self, dataset_dir='', batch_size=16):
        if dataset_dir == '':
            raise Exception("Please enter a valid dataset directory path!")

        optim_width, optim_height = 224, 224
        test_losses = []
        tst_crr = 0

        criterion = nn.CrossEntropyLoss()

        data_transforms = transforms.Compose([
            transforms.Resize((optim_width, optim_height)),  # Resize images to average dimensions
            transforms.Grayscale(),
            transforms.ToTensor(),  # Convert images to PyTorch tensors
            transforms.Normalize(mean=[0.456], std=[0.456])  # Normalize images
        ])

        dataset = datasets.ImageFolder(root=dataset_dir, transform=data_transforms)
        test_loader = DataLoader(dataset=dataset, batch_size=batch_size, shuffle=True)

        with torch.no_grad():
            for b, (X_test, y_test) in enumerate(test_loader):
                y_val = self(X_test)
                predicted = torch.max(y_val.data, dim=1)[1]
                tst_crr += (predicted == y_test).sum()

            loss = criterion(y_val, y_test)
            test_losses.append(loss.item())

            test_results = {
                'true_positive': tst_crr,
                'false_positive': len(dataset.imgs) - tst_crr
            }

        return test_results, test_losses

错误回溯

Traceback (most recent call last):
  File "/Users/eaidy/Repos/ML/inclination-classification-pytorch/src/main.py", line 12, in <module>
    ic_model.train(dataset_dir=train_dataset_absolute_path, epochs=1, batch_size=16, learning_rate=1e-5)
  File "/Users/eaidy/Repos/ML/inclination-classification-pytorch/src/models/cnn_model.py", line 94, in train
    loss = criterion(y_pred, y_train)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/eaidy/Repos/ML/inclination-classification-pytorch/.venv/lib/python3.11/site-packages/torch/nn/modules/module.py", line 1511, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/eaidy/Repos/ML/inclination-classification-pytorch/.venv/lib/python3.11/site-packages/torch/nn/modules/module.py", line 1520, in _call_impl
    return forward_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/eaidy/Repos/ML/inclination-classification-pytorch/.venv/lib/python3.11/site-packages/torch/nn/modules/loss.py", line 1179, in forward
    return F.cross_entropy(input, target, weight=self.weight,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/eaidy/Repos/ML/inclination-classification-pytorch/.venv/lib/python3.11/site-packages/torch/nn/functional.py", line 3059, in cross_entropy
    return torch._C._nn.cross_entropy_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index, label_smoothing)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: Expected input batch_size (1) to match target batch_size (16).
解决方案

错误根源在forward函数的特征展平步骤:

flattened_size = x.shape[0] * x.shape[1] * x.shape[2] * x.shape[3]
x = x.view(-1, flattened_size)

这里的x.shape[0]是batch大小,计算flattened_size时把batch维度也乘进去,导致每个batch被压缩成1个样本(总元素数为batch_size通道数高*宽,view(-1, flattened_size)就会将整个batch变成1行,对应batch_size=1),和输入的target batch_size(比如16)不匹配,因此报错。

修改方法有两种,任选其一即可:

方法1:修正flattened_size的计算

排除batch维度,只计算单样本的特征维度:

def forward(self, x):
    # CONV - 1
    x = F.relu(self.conv1(x))
    x = F.max_pool2d(x, kernel_size=3, stride=1)
    # CONV - 2
    x = F.relu(self.conv2(x))
    x = F.max_pool2d(x, kernel_size=3, stride=1)
    # CONV - 3
    x = F.relu(self.conv3(x))
    x = F.max_pool2d(x, kernel_size=3, stride=1)
    # CONV - 4
    x = F.relu(self.conv4(x))

    # 只计算单样本的特征维度,排除batch维度x.shape[0]
    flattened_size = x.shape[1] * x.shape[2] * x.shape[3]
    x = x.view(-1, flattened_size)
    # FULLY CONNECTED LAYERS
    x = F.relu(self.fc1(x))
    x = F.relu(self.fc2(x))
    x = self.out(x)

    return F.log_softmax(x, dim=1)

方法2:使用更简洁的展平方式

直接保留batch维度,展平后续所有维度,无需手动计算尺寸:

def forward(self, x):
    # CONV - 1
    x = F.relu(self.conv1(x))
    x = F.max_pool2d(x, kernel_size=3, stride=1)
    # CONV - 2
    x = F.relu(self.conv2(x))
    x = F.max_pool2d(x, kernel_size=3, stride=1)
    # CONV - 3
    x = F.relu(self.conv3(x))
    x = F.max_pool2d(x, kernel_size=3, stride=1)
    # CONV - 4
    x = F.relu(self.conv4(x))

    # 保留batch维度(dim=0),展平从dim=1开始的所有维度
    x = x.flatten(1)
    # 或者等价写法:x = x.view(x.size(0), -1)
    # FULLY CONNECTED LAYERS
    x = F.relu(self.fc1(x))
    x = F.relu(self.fc2(x))
    x = self.out(x)

    return F.log_softmax(x, dim=1)

修改后,模型输出的batch_size会和输入一致,即可正常使用任意batch_size训练。

另外注意:测试函数test中存在两个潜在问题:

  1. 测试时转换用了transforms.Grayscale(),将3通道转为单通道,但模型输入是3通道(conv1的in_channels=3),会导致输入维度不匹配,需删除该转换;
  2. 损失计算放在循环外,只会计算最后一个batch的损失,需移到循环内或修改逻辑。

内容的提问来源于stack exchange,提问作者eaidy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 16:17:03