目录

引言

一、项目背景与数据集介绍

1.1 项目背景

1.2 数据集结构详解

二、完整代码实现(附逐行注释)

2.1 环境准备与依赖导入

2.2 自动生成train.txt和test.txt文件

代码解析:

2.3 自定义数据集类(继承Dataset)

代码解析:

2.4 数据预处理与加载(DataLoader)

代码解析:

2.5 模型构建(CNN网络定义)

代码解析:

2.6 模型训练与测试

代码解析:

2.7 模型初始化与训练执行

代码解析:

三、代码分段深度解析

3.1 数据准备:为什么选择“按类别分文件夹”?

3.2 自定义Dataset类的核心方法

3.3 CNN模型的设计逻辑

3.4 训练过程中的关键细节

四、实验结果与优化建议

4.1 预期结果

4.2 常见问题与优化建议

五、总结


引言

在人工智能领域,图像分类是计算机视觉的核心任务之一,其应用场景覆盖医疗影像诊断、安防监控、智能推荐等多个领域。卷积神经网络(Convolutional Neural Network, CNN)凭借其局部感知和权值共享的特性,成为图像分类任务的“黄金模型”。本文将以“食物分类”为具体任务,手把手教你如何基于PyTorch框架,利用本地自定义图像数据集搭建卷积神经网络,完成从数据准备、模型构建到训练测试的全流程实战。

本文的代码和数据集结构均基于用户提供的实际项目素材(如图1-4所示),包含详细的注释和分步解析,适合PyTorch初学者及计算机视觉爱好者参考。

                           图1                                                                   图2

                         图3                                                                     图4


一、项目背景与数据集介绍

1.1 项目背景

食物分类是图像分类任务的一个垂直细分场景,其核心目标是通过模型识别图像中的食物种类(如薯条、八宝粥、骨肉相连等)。与通用图像分类(如ImageNet)不同,食物分类的数据集通常规模较小但类别更聚焦,因此需要更精细的数据组织和模型调优。

本项目的数据集来源于用户的本地文件夹(如图2所示),结构清晰且符合PyTorch的Dataset加载规范:

  • 主文件夹:食物分类/food_dataset
  • 子文件夹:train(训练集)和test(测试集),分别存储训练和验证用的图像数据。
  • 类别子文件夹:traintest下各有多个以食物命名的子文件夹(如“薯条”“八宝粥”),每个子文件夹内存储对应类别的图像(如图3、图4所示)。

这种“按类别分文件夹”的组织方式是PyTorch官方推荐的​​自定义数据集标准结构​​,便于通过代码自动读取图像路径和标签。

1.2 数据集结构详解

通过图2-4的文件管理器界面,我们可以直观看到数据集的层级关系:

  • 根路径:PycharmProjects/PythonProject/lanzhi/深度学习资料/2、卷积神经网络/食物分类/food_dataset
  • 训练集:train文件夹下包含“八宝粥”“巴旦木”“白萝卜”等20个食物类别子文件夹(如图3),每个子文件夹内有若干JPEG格式的图像(如图4的“八宝粥”文件夹包含6张图像)。
  • 测试集:test文件夹结构与train完全一致,用于验证模型的泛化能力。

这种结构下,标签可以通过“子文件夹名称”自动生成(如“薯条”文件夹的索引为n,则该文件夹下所有图像的标签为n),无需额外标注文件(如CSV或JSON),极大简化了数据预处理流程。


二、完整代码实现(附逐行注释)

2.1 环境准备与依赖导入

首先需要安装必要的库,包括PyTorch、Pillow(图像处理)、matplotlib(可视化)等。本文假设已配置好PyTorch环境(支持CUDA或MPS加速)。

# 导入基础库:用于文件操作、数值计算等
import os
import numpy as np

# 导入PyTorch核心库及数据加载工具
import torch
from torch.utils.data import Dataset, DataLoader  # Dataset定义数据集,DataLoader批量加载数据

# 导入图像处理库:PIL用于打开、保存、显示图像
from PIL import Image

# 导入PyTorch的图像预处理工具(缩放、转Tensor等)
from torchvision import transforms

2.2 自动生成train.txt和test.txt文件

在PyTorch中,Dataset类通常需要读取一个包含图像路径和标签的文本文件(如train.txt)。本节通过遍历文件夹结构,自动生成这两个文件。

def generate_txt_files(root_dir, subset_dir, output_file):
    """
    生成训练集或测试集的路径-标签列表文件(.txt)
    
    参数:
        root_dir (str): 数据集根目录(如'./食物分类/food_dataset')
        subset_dir (str): 子集名称('train'或'test')
        output_file (str): 输出文件路径(如'./train.txt')
    """
    # 拼接子集的完整路径(如'./食物分类/food_dataset/train')
    subset_path = os.path.join(root_dir, subset_dir)
    # 打开输出文件(写入模式)
    with open(output_file, 'w', encoding='utf-8') as f:
        # 遍历子集路径下的所有目录和文件(os.walk递归遍历)
        for root, dirs, files in os.walk(subset_path):
            # 如果当前目录下有子目录(说明是类别文件夹的父级)
            if dirs:
                # 记录当前层级的所有类别名称(如['八宝粥', '巴旦木', ...])
                class_names = dirs
            else:
                # 当前目录是类别文件夹(无嵌套子目录),获取类别名称(父目录的最后一级)
                current_class = os.path.basename(root)
                # 计算当前类别对应的标签(类别名称在class_names中的索引)
                label = class_names.index(current_class)
                # 遍历当前类别文件夹下的所有图像文件
                for img_file in files:
                    # 拼接图像的完整路径(如'./食物分类/food_dataset/train/八宝粥/img_八宝粥罐_22.jpeg')
                    img_path = os.path.join(root, img_file)
                    # 将路径和标签写入txt文件(格式:"路径 标签")
                    f.write(f"{img_path} {label}
")
    print(f"成功生成{subset_dir}集列表文件:{output_file}")

# 配置参数
dataset_root = r'.\食物分类\food_dataset'  # 数据集根目录(根据实际路径修改)
train_subset = 'train'                     # 训练集子集名称
test_subset = 'test'                       # 测试集子集名称

# 生成训练集和测试集的txt文件
generate_txt_files(dataset_root, train_subset, 'train.txt')
generate_txt_files(dataset_root, test_subset, 'test.txt')
代码解析:
  • ​函数功能​​:generate_txt_files通过os.walk递归遍历数据集目录,自动识别类别文件夹(如“八宝粥”),并将每个图像的路径与其对应的类别索引(标签)写入txt文件。
  • ​关键逻辑​​:
    • os.walk返回三元组(root, dirs, files),其中root是当前遍历的目录路径,dirs是当前目录下的子目录名列表,files是当前目录下的文件名列表。
    • dirs非空时,说明当前目录是类别文件夹的父级(如train),此时记录所有子目录名(类别名称);当dirs为空时,说明当前目录是类别文件夹(如train/八宝粥),通过os.path.basename(root)获取类别名称,并查找其在class_names中的索引作为标签。
    • 最终生成的train.txttest.txt格式示例如下:
      ./食物分类/food_dataset/train/八宝粥/img_八宝粥罐_22.jpeg 0
      ./食物分类/food_dataset/train/八宝粥/img_八宝粥罐_29.jpeg 0
      ./食物分类/food_dataset/train/巴旦木/img_巴旦木_01.jpeg 1
      ...

2.3 自定义数据集类(继承Dataset)

PyTorch的Dataset类是一个抽象类,需要重写__len__(数据集大小)和__getitem__(按索引获取数据)方法。本节定义FoodDataset类,用于加载图像和标签。

class FoodDataset(Dataset):
    def __init__(self, txt_path, transform=None):
        """
        初始化食物分类数据集
        
        参数:
            txt_path (str): 图像路径-标签列表文件的路径(如'train.txt')
            transform (callable, optional): 图像预处理变换(如缩放、转Tensor)
        """
        self.txt_path = txt_path
        self.transform = transform
        # 存储图像路径和标签的列表
        self.img_paths = []
        self.labels = []
        
        # 读取txt文件并解析数据
        with open(txt_path, 'r', encoding='utf-8') as f:
            # 逐行读取,每行格式为"图像路径 标签"
            for line in f.readlines():
                # 去除行首尾空白,按空格分割(注意:路径可能包含空格,需谨慎处理)
                parts = line.strip().split(' ')
                if len(parts) == 2:
                    img_path = parts[0]
                    label = int(parts[1])  # 标签转为整数
                    self.img_paths.append(img_path)
                    self.labels.append(label)
    
    def __len__(self):
        """返回数据集的大小(图像数量)"""
        return len(self.img_paths)
    
    def __getitem__(self, idx):
        """
        按索引获取图像和标签
        
        参数:
            idx (int): 数据索引
            
        返回:
            tuple: (图像Tensor, 标签Tensor)
        """
        # 获取图像路径和标签
        img_path = self.img_paths[idx]
        label = self.labels[idx]
        
        # 用PIL打开图像(支持JPEG、PNG等格式)
        image = Image.open(img_path).convert('RGB')  # 确保图像为3通道(RGB)
        
        # 应用预处理变换(如缩放、转Tensor)
        if self.transform:
            image = self.transform(image)
        
        # 标签转为Tensor(PyTorch的损失函数需要Tensor类型)
        label = torch.tensor(label, dtype=torch.long)
        
        return image, label
代码解析:
  • ​初始化方法(__init__)​​:读取txt文件,将图像路径和标签分别存储到img_pathslabels列表中。注意处理路径中的空格问题(实际项目中建议避免路径包含空格)。
  • __len__方法​​:返回数据集的大小,即图像的总数,通过len(self.img_paths)获取。
  • __getitem__方法​​:
    • 根据索引idx获取对应的图像路径和标签。
    • PIL.Image.open打开图像,并通过.convert('RGB')确保图像为3通道(避免灰度图导致的通道数不一致问题)。
    • 应用预处理变换(如缩放、转Tensor),这部分在后续的data_transforms中定义。
    • 将标签转为long类型的Tensor(PyTorch的交叉熵损失函数CrossEntropyLoss要求标签为long类型)。

2.4 数据预处理与加载(DataLoader)

本节定义训练集和测试集的预处理变换,并通过DataLoader批量加载数据。

# 定义训练集和测试集的预处理变换
data_transforms = {
    'train': transforms.Compose([
        transforms.Resize((256, 256)),  # 调整图像大小为256x256(统一尺寸)
        transforms.RandomHorizontalFlip(),  # 随机水平翻转(数据增强,防止过拟合)
        transforms.ToTensor(),  # 转换为Tensor(自动归一化到[0,1])
        transforms.Normalize(     # 标准化(根据ImageNet的均值和标准差)
            mean=[0.485, 0.456, 0.406], 
            std=[0.229, 0.224, 0.225]
        )
    ]),
    'test': transforms.Compose([
        transforms.Resize((256, 256)),
        transforms.ToTensor(),
        transforms.Normalize(
            mean=[0.485, 0.456, 0.406], 
            std=[0.229, 0.224, 0.225]
        )
    ])
}

# 加载训练集和测试集
train_dataset = FoodDataset(
    txt_path='./train.txt',          # 训练集的txt文件路径
    transform=data_transforms['train']  # 应用训练集的预处理
)
test_dataset = FoodDataset(
    txt_path='./test.txt',           # 测试集的txt文件路径
    transform=data_transforms['test']   # 应用测试集的预处理
)

# 创建数据加载器(DataLoader)
train_loader = DataLoader(
    dataset=train_dataset,    # 训练集数据集
    batch_size=32,            # 每批加载32张图像
    shuffle=True,             # 训练集打乱顺序(重要!防止模型记忆顺序)
    num_workers=4             # 多线程加载数据(根据CPU核心数调整)
)
test_loader = DataLoader(
    dataset=test_dataset,     
    batch_size=32,            
    shuffle=False,            # 测试集不打乱顺序(便于结果分析)
    num_workers=4             
)
代码解析:
  • ​预处理变换(data_transforms)​​:
    • 训练集比测试集多了RandomHorizontalFlip(随机水平翻转),这是一种数据增强手段,通过对训练数据进行随机变换,增加数据的多样性,防止模型过拟合。
    • transforms.ToTensor()将PIL图像转换为PyTorch的Tensor,并自动将像素值从[0, 255]归一化到[0, 1]
    • transforms.Normalize对Tensor进行标准化,使用ImageNet数据集的均值([0.485, 0.456, 0.406])和标准差([0.229, 0.224, 0.225]),这是因为预训练模型(如ResNet)通常基于ImageNet训练,标准化后能更快收敛。
  • ​DataLoader​​:
    • batch_size:每批加载的样本数,影响训练速度和内存占用(通常取32、64、128等)。
    • shuffle:训练集需要打乱顺序,避免模型因数据顺序固定而学习到无关模式;测试集不需要打乱,便于按顺序评估。
    • num_workers:多线程加载数据的线程数,加快数据读取速度(Windows系统需注意多线程可能导致的问题,可设为0)。

2.5 模型构建(CNN网络定义)

本节定义一个简单的卷积神经网络(CNN),包含3个卷积层和2个全连接层,适用于食物分类任务。

import torch.nn as nn
import torch.nn.functional as F

class FoodCNN(nn.Module):
    def __init__(self, num_classes=20):
        """
        定义食物分类的CNN模型
        
        参数:
            num_classes (int): 食物类别数量(根据数据集调整,默认20类)
        """
        super(FoodCNN, self).__init__()
        # 卷积层1:输入3通道(RGB),输出16通道,卷积核5x5,步长1,填充2
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 16, kernel_size=5, stride=1, padding=2),
            nn.ReLU(inplace=True),  # ReLU激活函数(引入非线性)
            nn.MaxPool2d(kernel_size=2, stride=2)  # 最大池化(降维,减少计算量)
        )
        # 卷积层2:输入16通道,输出32通道,卷积核5x5,步长1,填充2
        self.conv2 = nn.Sequential(
            nn.Conv2d(16, 32, kernel_size=5, stride=1, padding=2),
            nn.ReLU(inplace=True),
            nn.Conv2d(32, 32, kernel_size=5, stride=1, padding=2),  # 堆叠卷积层提取更复杂特征
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )
        # 卷积层3:输入32通道,输出128通道,卷积核5x5,步长1,填充2
        self.conv3 = nn.Sequential(
            nn.Conv2d(32, 128, kernel_size=5, stride=1, padding=2),
            nn.ReLU(inplace=True)
        )
        # 全连接层:输入128 * 64 * 64(卷积后的特征图尺寸),输出num_classes(类别数)
        self.fc = nn.Linear(128 * 64 * 64, num_classes)
    
    def forward(self, x):
        """
        前向传播
        
        参数:
            x (Tensor): 输入图像(batch_size, 3, 256, 256)
            
        返回:
            Tensor: 预测概率分布(batch_size, num_classes)
        """
        # 卷积层1 → 输出尺寸:(batch_size, 16, 256, 256) → 池化后→(batch_size, 16, 128, 128)
        x = self.conv1(x)
        # 卷积层2 → 输出尺寸:(batch_size, 32, 128, 128) → 池化后→(batch_size, 32, 64, 64)
        x = self.conv2(x)
        # 卷积层3 → 输出尺寸:(batch_size, 128, 64, 64)(无池化)
        x = self.conv3(x)
        # 展平特征图:(batch_size, 128 * 64 * 64)
        x = x.view(x.size(0), -1)
        # 全连接层输出预测结果
        x = self.fc(x)
        return x
代码解析:
  • ​卷积层设计​​:
    • 每个卷积层后接ReLU激活函数,引入非线性,使模型能拟合更复杂的特征。
    • 最大池化层(MaxPool2d)通过取局部区域的最大值,降低特征图的空间尺寸(宽、高减半),减少计算量的同时保留主要特征。
    • 填充(padding=2)确保卷积后的特征图尺寸与输入一致(当kernel_size=5, stride=1时,padding=(kernel_size-1)/2可保持尺寸不变)。
  • ​全连接层​​:
    • 卷积层提取的特征图需要展平为一维向量后输入全连接层。例如,输入图像尺寸为256x256,经过3次卷积和2次池化后,特征图尺寸为64x64(计算过程:256→池化→128→池化→64),通道数为128,因此展平后的长度为128 * 64 * 64
    • 全连接层的输出维度为类别数(num_classes=20),对应每个类别的得分(logits)。

2.6 模型训练与测试

本节定义训练函数和测试函数,完成模型的优化和性能评估。

def train_model(model, train_loader, criterion, optimizer, epochs, device):
    """
    训练模型
    
    参数:
        model (nn.Module): 待训练的模型
        train_loader (DataLoader): 训练集数据加载器
        criterion (nn.Module): 损失函数(如交叉熵)
        optimizer (optim.Optimizer): 优化器(如Adam)
        epochs (int): 训练轮数
        device (torch.device): 计算设备(CPU/GPU)
    """
    model.to(device)  # 将模型移动到目标设备
    model.train()     # 开启训练模式(启用Dropout、BatchNorm等)
    
    for epoch in range(epochs):
        running_loss = 0.0
        correct = 0
        total = 0
        
        for batch_idx, (images, labels) in enumerate(train_loader):
            # 将数据和标签移动到目标设备
            images = images.to(device)
            labels = labels.to(device)
            
            # 前向传播:计算预测值
            outputs = model(images)
            loss = criterion(outputs, labels)
            
            # 反向传播:优化参数
            optimizer.zero_grad()  # 清空梯度
            loss.backward()        # 计算梯度
            optimizer.step()       # 更新参数
            
            # 统计指标
            running_loss += loss.item()
            _, predicted = torch.max(outputs.data, 1)  # 获取预测类别(得分最高的类别)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
            
            # 每10个batch打印一次训练进度
            if (batch_idx + 1) % 10 == 0:
                print(f'Epoch [{epoch+1}/{epochs}], Batch [{batch_idx+1}/{len(train_loader)}], '
                      f'Loss: {running_loss/10:.4f}, Acc: {100*correct/total:.2f}%')
                running_loss = 0.0
        
        # 每轮结束后打印平均损失和准确率
        epoch_loss = running_loss / len(train_loader)
        epoch_acc = 100 * correct / total
        print(f'Epoch [{epoch+1}/{epochs}] End, Train Loss: {epoch_loss:.4f}, Train Acc: {epoch_acc:.2f}%
')

def test_model(model, test_loader, criterion, device):
    """
    测试模型性能
    
    参数:
        model (nn.Module): 待测试的模型
        test_loader (DataLoader): 测试集数据加载器
        criterion (nn.Module): 损失函数
        device (torch.device): 计算设备
    """
    model.to(device)
    model.eval()  # 开启测试模式(禁用Dropout、BatchNorm等)
    test_loss = 0.0
    correct = 0
    total = 0
    
    with torch.no_grad():  # 关闭梯度计算(节省内存)
        for images, labels in test_loader:
            images = images.to(device)
            labels = labels.to(device)
            
            outputs = model(images)
            loss = criterion(outputs, labels)
            
            test_loss += loss.item()
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    
    # 计算平均损失和准确率
    avg_loss = test_loss / len(test_loader)
    avg_acc = 100 * correct / total
    print(f'Test Result: Loss: {avg_loss:.4f}, Accuracy: {avg_acc:.2f}%')
代码解析:
  • ​训练函数(train_model)​​:
    • model.train():开启训练模式,启用Dropout层(随机失活神经元)和BatchNorm层(计算当前批次的均值和方差)。
    • 遍历train_loader获取每个批次的图像和标签,将数据和标签移动到目标设备(GPU/CPU)。
    • 前向传播计算预测值outputs,通过损失函数criterion计算预测值与真实标签的损失loss
    • 反向传播loss.backward()计算梯度,优化器optimizer.step()更新模型参数。
    • 统计每个批次的损失和准确率,每10个批次打印一次进度,每轮结束后打印平均指标。
  • ​测试函数(test_model)​​:
    • model.eval():开启测试模式,禁用Dropout和BatchNorm的随机操作(使用训练阶段统计的均值和方差)。
    • with torch.no_grad():关闭自动梯度计算,减少内存消耗。
    • 遍历测试集,计算整体损失和准确率,评估模型的泛化能力。

2.7 模型初始化与训练执行

最后,初始化模型、损失函数和优化器,启动训练流程。

if __name__ == '__main__':
    # 配置参数
    num_classes = 20       # 食物类别数量(根据数据集调整)
    learning_rate = 0.001  # 学习率(控制参数更新步长)
    epochs = 10            # 训练轮数
    device = torch.device('cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu')
    # 自动选择计算设备(优先GPU,其次Apple MPS,最后CPU)
    
    # 初始化模型、损失函数和优化器
    model = FoodCNN(num_classes=num_classes)
    criterion = nn.CrossEntropyLoss()  # 交叉熵损失函数(适用于多分类)
    optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)  # Adam优化器
    
    # 启动训练
    print("开始训练...")
    train_model(model, train_loader, criterion, optimizer, epochs, device)
    
    # 测试训练后的模型
    print("
训练结束,开始测试...")
    test_model(model, test_loader, criterion, device)
代码解析:
  • ​设备选择​​:通过torch.device自动检测可用的计算设备(GPU优先),提升训练速度。
  • ​损失函数​​:nn.CrossEntropyLoss结合了Softmax激活函数和交叉熵损失,适用于多分类任务(输出为类别得分,无需手动计算概率)。
  • ​优化器​​:torch.optim.Adam是一种自适应学习率的优化器,通常比传统的SGD收敛更快。

三、代码分段深度解析

3.1 数据准备:为什么选择“按类别分文件夹”?

PyTorch的Dataset加载机制高度依赖数据组织方式。本文采用“train/test目录下按类别分子目录”的结构,主要原因如下:

  • ​自动化标签生成​​:通过遍历子目录名(如“薯条”),可以自动为每个图像分配标签(子目录索引),无需手动标注。
  • ​兼容数据增强​​:在Dataset__getitem__方法中应用数据增强(如随机翻转),可以灵活地对每个批次的图像进行变换,避免重复存储增强后的图像。
  • ​符合官方最佳实践​​:PyTorch官方教程(如ImageFolder)推荐此结构,社区支持广泛,便于后续扩展(如使用预训练模型的ImageFolder加载方式)。

3.2 自定义Dataset类的核心方法

FoodDataset类继承自nn.ModuleDataset,其核心是__len____getitem__

  • __len__:告诉PyTorch数据集有多大,DataLoader通过此方法确定需要生成多少个批次。
  • __getitem__:定义如何获取单个样本。这里不仅返回图像和标签,还应用了预处理变换(如缩放、转Tensor),确保数据在输入模型前格式正确。

​注意​​:如果图像尺寸不一致,Resize变换是必须的(否则无法组成批量Tensor);如果数据量极大,可使用num_workers>0加速数据加载(但需注意Windows系统的多线程问题)。

3.3 CNN模型的设计逻辑

本文的FoodCNN是一个典型的“卷积-池化-全连接”结构,设计逻辑如下:

  • ​浅层卷积(conv1)​​:提取边缘、纹理等低级特征(如食物的轮廓、颜色块)。
  • ​中层卷积(conv2)​​:组合低级特征形成中级特征(如食物的局部结构,如薯条的条状、八宝粥的颗粒感)。
  • ​深层卷积(conv3)​​:提取高级语义特征(如食物的整体形状、细节,如骨肉相连的骨头和肉的纹理)。
  • ​全连接层​​:将高级特征映射到类别空间,输出每个类别的得分。

​改进方向​​:实际项目中可使用更深的网络(如ResNet、VGG),或引入残差连接、注意力机制(如SE Block)提升性能。

3.4 训练过程中的关键细节

  • ​数据归一化​​:Normalize变换将像素值从[0, 1]转换为均值为0、标准差为1的分布,有助于优化器更快收敛(类似标准化数据)。
  • ​学习率调整​​:本文使用固定学习率(0.001),实际中可使用torch.optim.lr_scheduler动态调整(如余弦退火、阶梯衰减)。
  • ​过拟合处理​​:除了数据增强(RandomHorizontalFlip),还可添加Dropout层、L2正则化(weight_decay参数)等方法。

四、实验结果与优化建议

4.1 预期结果

假设数据集包含20类食物,每类约100张图像(训练集80%,测试集20%),使用上述代码训练10轮后,预期测试准确率可达70%-85%(具体取决于数据质量和类别复杂度)。

4.2 常见问题与优化建议

  • ​问题1:训练损失不下降​

    • 可能原因:学习率过大(模型无法收敛)或过小(收敛过慢)、数据预处理错误(如标签错误)、模型容量不足(网络太浅)。
    • 解决方法:调整学习率(如从0.001降至0.0001)、检查txt文件中的标签是否正确、增加卷积层或全连接层的神经元数量。
  • ​问题2:测试准确率远低于训练准确率​

    • 可能原因:过拟合(模型过度记忆训练数据)。
    • 解决方法:增加数据增强(如随机裁剪、颜色抖动)、添加Dropout层(如在conv1后加nn.Dropout2d(0.5))、使用早停法(Early Stopping)。
  • ​问题3:GPU内存不足​

    • 可能原因:批量大小(batch_size)过大、图像尺寸过大。
    • 解决方法:减小batch_size(如从32降至16)、降低图像尺寸(如从256x256降至128x128)、使用混合精度训练(torch.cuda.amp)。

五、总结

本文通过完整的代码示例和详细解析,演示了如何基于PyTorch框架和本地自定义数据集实现食物分类任务。核心步骤包括:

  1. 数据准备:按类别分文件夹组织数据,自动生成train.txttest.txt
  2. 自定义数据集:继承Dataset类,实现__len____getitem__方法。
  3. 模型构建:设计卷积神经网络,提取图像特征并映射到类别空间。
  4. 训练与测试:定义损失函数和优化器,通过DataLoader批量加载数据,迭代训练并评估模型性能。

通过本案例,读者可以掌握PyTorch处理自定义数据集的核心流程,并为后续的图像分类、目标检测等任务打下坚实基础。建议在实际项目中尝试不同的网络结构(如ResNet)、数据增强方法和超参数调优,以进一步提升模型性能。


​附:完整代码仓库​
本文所有代码已整理至GitHub仓库(),包含数据集示例和详细注释,欢迎克隆学习!

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐