使用迁移学习来训练VGG模型

VGG可以看成是加深版的AlexNet,整个网络由卷积层和全连接层叠加而成,和AlexNet不同的是,VGG中使用的都是小尺寸的卷积核(3×3)

学习成果:

bash 复制代码
2026-02-23 10:37:59,766 - INFO - Using device: cuda
Downloading: "https://download.pytorch.org/models/vgg16-397923af.pth" to C:\Users\story/.cache\torch\hub\checkpoints\vgg16-397923af.pth
100%|██████████| 528M/528M [02:59<00:00, 3.08MB/s]
Epoch 1/5: 100%|██████████| 104/104 [00:21<00:00,  4.74it/s, loss=0.39]
2026-02-23 10:41:25,956 - INFO - Epoch 1/5, Loss: 0.5712, Test Acc: 0.8269
2026-02-23 10:41:26,540 - INFO - Saved best model with accuracy 0.8269
Epoch 2/5: 100%|██████████| 104/104 [00:21<00:00,  4.83it/s, loss=0.13]
2026-02-23 10:41:50,263 - INFO - Epoch 2/5, Loss: 0.3633, Test Acc: 0.8654
2026-02-23 10:41:50,879 - INFO - Saved best model with accuracy 0.8654
Epoch 3/5: 100%|██████████| 104/104 [00:21<00:00,  4.83it/s, loss=0.252]
2026-02-23 10:42:14,623 - INFO - Epoch 3/5, Loss: 0.3388, Test Acc: 0.8544
Epoch 4/5: 100%|██████████| 104/104 [00:21<00:00,  4.84it/s, loss=0.466]
2026-02-23 10:42:38,316 - INFO - Epoch 4/5, Loss: 0.2997, Test Acc: 0.8599
Epoch 5/5: 100%|██████████| 104/104 [00:21<00:00,  4.93it/s, loss=0.524]
2026-02-23 10:43:01,623 - INFO - Epoch 5/5, Loss: 0.3084, Test Acc: 0.8489

完整代码:

python 复制代码
import logging
import os
import torch
from torch import nn
from torchvision import transforms
from torch.utils.data import DataLoader
from torchvision.datasets import ImageFolder
from torchvision.models import vgg16, VGG16_Weights
from tqdm import tqdm  # 需要安装: pip install tqdm

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 使用设备
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
logging.info(f"Using device: {device}")


def main():
    # --- 1. 参数与路径定义 ---
    batch_size = 32
    epochs = 5
    lr = 0.001

    this_path = os.path.dirname(os.path.abspath(__file__))
    root_path = os.path.dirname(this_path)
    train_path = os.path.join(root_path, 'datas', 'flower_datas', 'train', '')
    test_path = os.path.join(root_path, 'datas', 'flower_datas', 'val', '')

    # --- 2. 数据预处理 (修正:增加归一化) ---
    # VGG16 必须使用 ImageNet 的均值和方差进行归一化
    transform_method = {
        "train": transforms.Compose([
            transforms.Resize((224, 224)),
            transforms.RandomHorizontalFlip(),  # 数据增强:随机翻转
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        ]),
        "val": transforms.Compose([
            transforms.Resize((224, 224)),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        ])
    }

    # --- 3. 数据集加载 (修正:放入 main 函数内避免多进程问题) ---
    try:
        train_data = ImageFolder(train_path, transform=transform_method["train"])
        test_data = ImageFolder(test_path, transform=transform_method["val"])
    except Exception as e:
        logging.error(f"Failed to load dataset: {e}")
        return

    # num_workers 在 Windows 上建议设为 0 或放入 main 保护块内
    train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True, pin_memory=True)
    test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False, pin_memory=True)

    # --- 4. 模型定义 (优化:使用预训练模型) ---
    # 加载预训练权重
    model = vgg16(weights=VGG16_Weights.IMAGENET1K_V1)

    # 冻结特征提取层参数 (迁移学习常用技巧)
    for param in model.features.parameters():
        param.requires_grad = False

    # 修改最后一层分类器
    model.classifier[6] = nn.Linear(4096, 5)
    model = model.to(device)

    # --- 5. 损失函数与优化器 ---
    criterion = nn.CrossEntropyLoss()
    # 只优化分类层的参数,加快训练速度
    optimizer = torch.optim.Adam(model.classifier[6].parameters(), lr=lr)
    # optimizer = torch.optim.Adam(model.parameters(), lr=lr) # 如果想训练全模型

    # 学习率调度器
    lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)

    # --- 6. 训练与评估函数 ---
    def evaluate(data_iter, model_input):
        total, correct = 0, 0
        model_input.eval()
        with torch.no_grad():
            for image, label in data_iter:
                image, label = image.to(device), label.to(device)
                output = model_input(image)
                _, predicted = torch.max(output.data, dim=1)
                total += label.size(0)
                correct += (predicted == label).sum().item()
        return correct / total if total > 0 else 0

    best_acc = 0.0
    for epoch in range(epochs):
        model.train()
        running_loss = 0.0
        # 使用 tqdm 显示进度
        train_bar = tqdm(train_loader, desc=f'Epoch {epoch + 1}/{epochs}')

        for image, label in train_bar:
            image, label = image.to(device), label.to(device)

            optimizer.zero_grad()
            outputs = model(image)
            loss_value = criterion(outputs, label)
            loss_value.backward()
            optimizer.step()

            running_loss += loss_value.item() * image.size(0)
            train_bar.set_postfix({'loss': loss_value.item()})

        lr_scheduler.step()

        # 计算平均损失
        epoch_loss = running_loss / len(train_data)
        test_acc = evaluate(test_loader, model)

        logging.info(f"Epoch {epoch + 1}/{epochs}, Loss: {epoch_loss:.4f}, Test Acc: {test_acc:.4f}")
        if test_acc > best_acc:
            best_acc = test_acc
            torch.save(model.state_dict(), 'best_model.pth')
            logging.info(f"Saved best model with accuracy {best_acc:.4f}")

if __name__ == '__main__':
    main()

1. 环境初始化与配置

  • 日志配置 :使用 logging 模块记录训练过程(时间、等级、信息),便于调试和复盘。
  • 设备选择 :自动检测是否有 GPU (cuda),若无则回退到 CPU,确保代码在不同硬件环境下的兼容性。

2. 数据预处理与加载

  • 路径管理 :使用 os.path 动态构建训练集和验证集的路径,提高了代码的可移植性。
  • 数据增强与归一化
    • 训练集 :包含 RandomHorizontalFlip(随机水平翻转)进行数据增强,增加模型泛化能力。
    • 归一化 :关键点在于使用了 ImageNet 的均值 [0.485, 0.456, 0.406] 和方差 [0.229, 0.224, 0.225]。这是使用预训练模型的必要步骤,确保输入数据分布与模型预训练时一致。
  • 数据加载器 :使用 ImageFolder 自动根据文件夹结构分类,DataLoader 负责批次加载和打乱数据。

3. 模型构建与迁移学习策略

这是代码的核心部分:

  • 加载预训练权重VGG16_Weights.IMAGENET1K_V1 自动下载并加载 ImageNet 的官方权重。
  • 冻结参数 :通过 param.requires_grad = False 冻结了 VGG16 的特征提取层。这大大减少了计算量,防止在大规模预训练权重上进行微调导致破坏特征。
  • 修改分类头:将 VGG16 最后一层全连接层输出从 1000 改为 5,适配花卉 5 分类任务。

4. 损失函数与优化器

  • 损失函数CrossEntropyLoss,多分类任务的标准选择。
  • 优化器 :仅将 model.classifier[6].parameters() 传入优化器,意味着只更新最后一层的权重,这是迁移学习中 "Feature Extraction" 模式的典型做法。
  • 学习率调度 :使用 StepLR,每 7 个 Epoch 学习率衰减为原来的 0.1 倍(当前 Epochs=5 未触发,但结构完整)。

5. 训练与评估流程

  • 评估函数 (evaluate) :在 torch.no_grad() 模式下计算模型在验证集上的准确率,避免计算梯度节省显存。
  • 训练循环
    • 标准的 PyTorch 训练步骤:zero_grad -> forward -> backward -> step
    • 模型保存 :采用 "Best Model" 策略,只有当验证集准确率超过历史最高时才保存模型权重 (best_model.pth)。
相关推荐
LaughingZhu2 小时前
Product Hunt 每日热榜 | 2026-02-23
人工智能·经验分享·深度学习·神经网络·产品运营
qq_24218863322 小时前
AI内容审核系统(简化版实现)
人工智能·深度学习·目标检测
啊阿狸不会拉杆2 小时前
《计算机视觉:模型、学习和推理》第 9 章-分类模型
人工智能·python·学习·算法·机器学习·计算机视觉·分类
陈天伟教授13 小时前
人工智能应用- 材料微观:06.GAN 三维重构
人工智能·神经网络·算法·机器学习·重构·推荐算法
DeepModel14 小时前
第14章 注意力机制与Transformer
人工智能·深度学习·transformer
Figo_Cheung14 小时前
Figo《量子几何学:从希尔伯特空间到全息时空的统一理论体系》(二)
算法·机器学习·几何学·量子计算
ppppppatrick14 小时前
【深度学习基础篇03】激活函数详解:从生物突触到非线性注入
人工智能·深度学习
Figo_Cheung14 小时前
Figo《量子几何学:从希尔伯特空间到全息时空的统一理论体系》(三)
人工智能·深度学习·几何学