欢迎光临
我们一直在努力

基于迁移学习实现宠物分类(oxford-iiit-pet数据集)任务

一、项目概述

1.1 项目背景

宠物分类是计算机视觉中的重要应用场景,旨在自动识别不同品种的猫狗。本项目使用Oxford-IIIT Pet数据集,通过迁移学习技术构建高效准确的宠物分类模型。

1.2 数据集介绍

Oxford-IIIT Pet数据集包含:

  • 37个宠物类别(12种猫+25种狗)

  • 7349张图片(训练集:3680,测试集:3669)

  • 每张图片包含:

    • 宠物主体分割掩码

    • 头部区域标注

    • 品种标签(如"Abyssinian", "Persian"等)

1.3 项目目标

  • 实现多类别宠物品种分类

  • 达到90%以上的分类准确率

  • 展示迁移学习在实际应用中的优势

  • 二、技术架构

    2.1 核心思想:迁移学习

    迁移学习的优势:
    1. 利用在大规模数据集(如ImageNet)上预训练的模型
    2. 只需要少量数据即可达到良好效果
    3. 减少训练时间和计算资源需求

    2.2 技术栈

    • 深度学习框架:PyTorch / TensorFlow

    • 预训练模型:

      • ResNet50/101

      • EfficientNet

      • VGG16

      • MobileNetV2(轻量化部署)

    • 数据增强:Albumentations或Torchvision Transforms

    • 评估指标:准确率、F1-score、混淆矩阵

    三、项目实施步骤

    Resnet预训练模型介绍

    Resnet是计算机视觉(CV)领域著名的预训练模型,ResNet50是计算机视觉领域最具影响力的深度学习架构之一,由何恺明等人在2015年提出。它解决了深度神经网络中的退化问题,使得训练极深的网络成为可能。下面详细介绍ResNet50的架构与设计思想.

    • 传统网络:直接学习目标映射 H(x)
    • 残差网络:学习残差映射 F(x) = H(x) – x,最终输出为 F(x) + x

    这种设计允许梯度直接流过恒等映射(identity mapping)路径,有效缓解了梯度消失问题,使得训练上百层的网络成为可能。

    ResNet50总共有50层(按权重层计算),由以下部分组成:

  • 初始层:7×7卷积 + 最大池化
  • 4个残差阶段(Stage),每个阶段包含多个残差块
  • 全局平均池化层
  • 1000类分类器(原始版本用于ImageNet)
  • ResNet50使用Bottleneck结构作为基本单元,每个Bottleneck包含3个卷积层:

  • 1×1卷积:降维,减少计算量
  • 3×3卷积:空间特征提取
  • 1×1卷积:升维,恢复通道数
  • 具体公式:y = F(x, {Wi}) + x

    • F(x)是残差函数
    • x是输入
    • y是输出
    • "+"操作通过元素相加实现,要求F(x)和x维度相同

    基于Resnet50和Resnet101实现宠物分类任务

    下面给出基于Resnet50实现cifar-10数据集分类任务的具体实现代码:

    1.导包与预训练模型的导入

    import torch
    import torch.nn as nn
    import torch.optim as optim
    import torchvision
    import torchvision.transforms as transforms
    from torch.utils.data import DataLoader, random_split
    import os
    from tqdm import tqdm
    import matplotlib.pyplot as plt
    import numpy as np
    from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
    import seaborn as sns
    import time

    # 设置随机种子以保证可重复性
    def set_seed(seed=42):
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False
    np.random.seed(seed)

    set_seed(42)

    # 检查是否有可用的GPU
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"使用设备: {device}")

    2.数据准备和增强

    # 1. 数据准备和增强
    def get_data_transforms():
    """
    定义训练和验证的数据转换
    """
    # 图像大小调整到适合预训练模型
    IMG_SIZE = 224

    train_transform = transforms.Compose([
    transforms.RandomResizedCrop(IMG_SIZE, scale=(0.8, 1.0)),
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.RandomRotation(15),
    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
    transforms.RandomAffine(degrees=0, translate=(0.1, 0.1)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])

    val_transform = transforms.Compose([
    transforms.Resize((IMG_SIZE, IMG_SIZE)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])

    return train_transform, val_transform

    # 2. 加载Oxford-IIIT Pet数据集
    def load_pet_dataset(batch_size=32):
    """
    加载和准备宠物数据集
    """
    train_transform, val_transform = get_data_transforms()

    # 下载并加载数据集
    train_val_dataset = torchvision.datasets.OxfordIIITPet(
    root='./data/oxford_pets',
    split='trainval',
    target_types='category',
    download=False,
    transform=train_transform # 暂时使用train_transform,后面会分割
    )

    test_dataset = torchvision.datasets.OxfordIIITPet(
    root='./data/oxford_pets',
    split='test',
    target_types='category',
    download=False,
    transform=val_transform
    )

    # 分割训练集和验证集
    dataset_size = len(train_val_dataset)
    train_size = int(0.85 * dataset_size) # 85%用于训练
    val_size = dataset_size – train_size

    train_dataset, val_dataset = random_split(train_val_dataset, [train_size, val_size])

    # 为训练集应用训练变换,验证集应用验证变换
    # 注意:我们需要手动设置变换,因为random_split会保留原变换
    train_dataset.dataset.transform = train_transform
    val_dataset.dataset.transform = val_transform

    print(f"训练集大小: {len(train_dataset)}")
    print(f"验证集大小: {len(val_dataset)}")
    print(f"测试集大小: {len(test_dataset)}")
    print(f"类别数量: {len(train_val_dataset.classes)}")

    # 创建数据加载器
    train_loader = DataLoader(
    train_dataset,
    batch_size=batch_size,
    shuffle=True,
    num_workers=4,
    pin_memory=True
    )

    val_loader = DataLoader(
    val_dataset,
    batch_size=batch_size,
    shuffle=False,
    num_workers=4,
    pin_memory=True
    )

    test_loader = DataLoader(
    test_dataset,
    batch_size=batch_size,
    shuffle=False,
    num_workers=4,
    pin_memory=True
    )

    return train_loader, val_loader, test_loader, train_val_dataset.classes

    3.构建网络模型

    # 3. 定义模型架构(使用预训练的ResNet50)
    class PetClassifier(nn.Module):
    def __init__(self, num_classes=37, dropout_rate=0.5):
    super(PetClassifier, self).__init__()

    # 使用预训练的ResNet50作为特征提取器
    self.backbone = torchvision.models.resnet50(pretrained=True)

    # 冻结前几层(可选,可根据需要调整)
    for param in list(self.backbone.parameters())[:100]:
    param.requires_grad = False

    # 获取ResNet的特征维度
    num_features = self.backbone.fc.in_features

    # 替换最后的全连接层
    self.backbone.fc = nn.Sequential(
    nn.Dropout(dropout_rate),
    nn.Linear(num_features, 512),
    nn.BatchNorm1d(512),
    nn.ReLU(),
    nn.Dropout(dropout_rate/2),
    nn.Linear(512, 256),
    nn.BatchNorm1d(256),
    nn.ReLU(),
    nn.Dropout(dropout_rate/3),
    nn.Linear(256, num_classes)
    )

    def forward(self, x):
    return self.backbone(x)

    4.编写训练函数和推理函数

    # 3. 定义模型架构(使用预训练的ResNet50)
    class PetClassifier(nn.Module):
    def __init__(self, num_classes=37, dropout_rate=0.5):
    super(PetClassifier, self).__init__()

    # 使用预训练的ResNet50作为特征提取器
    self.backbone = torchvision.models.resnet50(pretrained=True)

    # 冻结前几层(可选,可根据需要调整)
    for param in list(self.backbone.parameters())[:100]:
    param.requires_grad = False

    # 获取ResNet的特征维度
    num_features = self.backbone.fc.in_features

    # 替换最后的全连接层
    self.backbone.fc = nn.Sequential(
    nn.Dropout(dropout_rate),
    nn.Linear(num_features, 512),
    nn.BatchNorm1d(512),
    nn.ReLU(),
    nn.Dropout(dropout_rate/2),
    nn.Linear(512, 256),
    nn.BatchNorm1d(256),
    nn.ReLU(),
    nn.Dropout(dropout_rate/3),
    nn.Linear(256, num_classes)
    )

    def forward(self, x):
    return self.backbone(x)

    # 4. 训练和评估函数
    class PetTrainer:
    def __init__(self, model, device, num_classes=37):
    self.model = model.to(device)
    self.device = device
    self.num_classes = num_classes

    # 使用交叉熵损失
    self.criterion = nn.CrossEntropyLoss(label_smoothing=0.1) # 标签平滑防止过拟合

    # 使用AdamW优化器(比Adam更好)
    self.optimizer = optim.AdamW(
    filter(lambda p: p.requires_grad, self.model.parameters()),
    lr=0.0001,
    weight_decay=1e-4
    )

    # 使用余弦退火学习率调度器
    self.scheduler = optim.lr_scheduler.CosineAnnealingLR(
    self.optimizer,
    T_max=20, # 20个epoch后重置
    eta_min=1e-6
    )

    # 添加早停机制
    self.best_val_acc = 0.0
    self.patience = 10
    self.counter = 0

    def train_epoch(self, train_loader):
    self.model.train()
    running_loss = 0.0
    all_preds = []
    all_labels = []

    # 使用tqdm显示训练进度
    pbar = tqdm(train_loader, desc="训练中", leave=False)
    for batch_idx, (inputs, labels) in enumerate(pbar):
    inputs, labels = inputs.to(self.device), labels.to(self.device)

    # 梯度清零
    self.optimizer.zero_grad()

    # 前向传播
    outputs = self.model(inputs)
    loss = self.criterion(outputs, labels)

    # 反向传播和优化
    loss.backward()

    # 梯度裁剪防止梯度爆炸
    torch.nn.utils.clip_grad_norm_(self.model.parameters(), max_norm=1.0)

    self.optimizer.step()

    # 统计
    running_loss += loss.item()
    _, predicted = outputs.max(1)
    all_preds.extend(predicted.cpu().numpy())
    all_labels.extend(labels.cpu().numpy())

    # 更新进度条
    pbar.set_postfix({'loss': loss.item()})

    # 计算准确率
    train_acc = accuracy_score(all_labels, all_preds)
    train_loss = running_loss / len(train_loader)

    return train_loss, train_acc

    def validate(self, val_loader):
    self.model.eval()
    running_loss = 0.0
    all_preds = []
    all_labels = []

    with torch.no_grad():
    pbar = tqdm(val_loader, desc="验证中", leave=False)
    for inputs, labels in pbar:
    inputs, labels = inputs.to(self.device), labels.to(self.device)

    outputs = self.model(inputs)
    loss = self.criterion(outputs, labels)

    running_loss += loss.item()
    _, predicted = outputs.max(1)
    all_preds.extend(predicted.cpu().numpy())
    all_labels.extend(labels.cpu().numpy())

    val_acc = accuracy_score(all_labels, all_preds)
    val_loss = running_loss / len(val_loader)

    return val_loss, val_acc

    def test(self, test_loader):
    self.model.eval()
    all_preds = []
    all_labels = []
    all_probs = []

    with torch.no_grad():
    pbar = tqdm(test_loader, desc="测试中")
    for inputs, labels in pbar:
    inputs, labels = inputs.to(self.device), labels.to(self.device)

    outputs = self.model(inputs)
    probs = torch.nn.functional.softmax(outputs, dim=1)
    _, predicted = outputs.max(1)

    all_preds.extend(predicted.cpu().numpy())
    all_labels.extend(labels.cpu().numpy())
    all_probs.extend(probs.cpu().numpy())

    test_acc = accuracy_score(all_labels, all_preds)

    return test_acc, all_preds, all_labels, all_probs

    def train(self, train_loader, val_loader, num_epochs=30):
    history = {'train_loss': [], 'train_acc': [], 'val_loss': [], 'val_acc': []}

    print("开始训练…")
    start_time = time.time()

    for epoch in range(num_epochs):
    print(f"\\nEpoch {epoch+1}/{num_epochs}")
    print("-" * 50)

    # 训练一个epoch
    train_loss, train_acc = self.train_epoch(train_loader)

    # 验证
    val_loss, val_acc = self.validate(val_loader)

    # 更新学习率
    self.scheduler.step()

    # 保存历史记录
    history['train_loss'].append(train_loss)
    history['train_acc'].append(train_acc)
    history['val_loss'].append(val_loss)
    history['val_acc'].append(val_acc)

    # 打印结果
    current_lr = self.optimizer.param_groups[0]['lr']
    print(f"训练损失: {train_loss:.4f}, 训练准确率: {train_acc:.4f}")
    print(f"验证损失: {val_loss:.4f}, 验证准确率: {val_acc:.4f}")
    print(f"学习率: {current_lr:.6f}")

    # 早停检查和保存最佳模型
    if val_acc > self.best_val_acc:
    self.best_val_acc = val_acc
    self.counter = 0
    # 保存最佳模型
    torch.save({
    'epoch': epoch,
    'model_state_dict': self.model.state_dict(),
    'optimizer_state_dict': self.optimizer.state_dict(),
    'val_acc': val_acc,
    }, 'best_pet_classifier.pth')
    print(f"保存最佳模型,验证准确率: {val_acc:.4f}")
    else:
    self.counter += 1
    if self.counter >= self.patience:
    print(f"早停触发,在epoch {epoch+1}停止训练")
    break

    # 加载最佳模型
    checkpoint = torch.load('best_pet_classifier.pth')
    self.model.load_state_dict(checkpoint['model_state_dict'])

    training_time = time.time() – start_time
    print(f"\\n训练完成!总共用时: {training_time:.2f}秒")
    print(f"最佳验证准确率: {self.best_val_acc:.4f}")

    return history

    5.编写可视化函数

    # 5. 可视化函数
    def plot_training_history(history):
    """绘制训练历史曲线"""
    fig, axes = plt.subplots(1, 2, figsize=(15, 5))

    # 绘制损失曲线
    axes[0].plot(history['train_loss'], label='训练损失')
    axes[0].plot(history['val_loss'], label='验证损失')
    axes[0].set_xlabel('Epoch')
    axes[0].set_ylabel('损失')
    axes[0].set_title('训练和验证损失曲线')
    axes[0].legend()
    axes[0].grid(True)

    # 绘制准确率曲线
    axes[1].plot(history['train_acc'], label='训练准确率')
    axes[1].plot(history['val_acc'], label='验证准确率')
    axes[1].set_xlabel('Epoch')
    axes[1].set_ylabel('准确率')
    axes[1].set_title('训练和验证准确率曲线')
    axes[1].legend()
    axes[1].grid(True)

    plt.tight_layout()
    plt.savefig('training_history.png', dpi=100)
    plt.show()

    def plot_confusion_matrix(y_true, y_pred, class_names):
    """绘制混淆矩阵"""
    cm = confusion_matrix(y_true, y_pred)

    plt.figure(figsize=(12, 10))
    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
    xticklabels=class_names, yticklabels=class_names)
    plt.title('混淆矩阵')
    plt.xlabel('预测标签')
    plt.ylabel('真实标签')
    plt.xticks(rotation=45, ha='right')
    plt.tight_layout()
    plt.savefig('confusion_matrix.png', dpi=100)
    plt.show()

    def visualize_predictions(model, test_loader, class_names, device, num_samples=10):
    """可视化部分预测结果"""
    model.eval()
    images_shown = 0

    with torch.no_grad():
    for inputs, labels in test_loader:
    inputs, labels = inputs.to(device), labels.to(device)
    outputs = model(inputs)
    _, preds = torch.max(outputs, 1)
    probs = torch.nn.functional.softmax(outputs, dim=1)

    inputs = inputs.cpu()
    labels = labels.cpu()
    preds = preds.cpu()
    probs = probs.cpu()

    fig, axes = plt.subplots(2, 5, figsize=(15, 6))
    axes = axes.ravel()

    for idx in range(min(num_samples, len(inputs))):
    # 反标准化图像
    img = inputs[idx].numpy().transpose(1, 2, 0)
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    img = std * img + mean
    img = np.clip(img, 0, 1)

    axes[idx].imshow(img)
    true_label = class_names[labels[idx]]
    pred_label = class_names[preds[idx]]
    prob = probs[idx][preds[idx]].item()

    color = 'green' if labels[idx] == preds[idx] else 'red'
    axes[idx].set_title(f"真实: {true_label}\\n预测: {pred_label}\\n置信度: {prob:.2f}",
    color=color, fontsize=9)
    axes[idx].axis('off')

    images_shown += 1
    if images_shown >= num_samples:
    break

    plt.tight_layout()
    plt.savefig('sample_predictions.png', dpi=100)
    plt.show()
    break

    6.集成学习增强(可选)

    # 6. 集成学习增强(可选)
    class EnsembleModel:
    def __init__(self, model_paths, num_classes=37, device='cuda'):
    self.models = []
    self.device = device

    for path in model_paths:
    model = PetClassifier(num_classes=num_classes)
    checkpoint = torch.load(path, map_location=device)
    model.load_state_dict(checkpoint['model_state_dict'])
    model.to(device)
    model.eval()
    self.models.append(model)

    def predict(self, inputs):
    all_probs = []

    with torch.no_grad():
    for model in self.models:
    outputs = model(inputs)
    probs = torch.nn.functional.softmax(outputs, dim=1)
    all_probs.append(probs)

    # 平均所有模型的概率
    avg_probs = torch.stack(all_probs).mean(0)
    _, predictions = torch.max(avg_probs, 1)

    return predictions, avg_probs

    7.编写主函数

    # 7. 主程序
    def main():
    # 参数设置
    BATCH_SIZE = 32
    NUM_EPOCHS = 40
    NUM_CLASSES = 37

    print("=" * 60)
    print("Oxford-IIIT Pet 数据集分类任务")
    print("=" * 60)

    # 加载数据
    print("\\n1. 加载数据集…")
    train_loader, val_loader, test_loader, class_names = load_pet_dataset(BATCH_SIZE)

    # 创建模型
    print("\\n2. 创建模型…")
    model = PetClassifier(num_classes=NUM_CLASSES, dropout_rate=0.5)

    # 打印模型摘要
    total_params = sum(p.numel() for p in model.parameters())
    trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
    print(f"总参数: {total_params:,}")
    print(f"可训练参数: {trainable_params:,}")

    # 创建训练器并训练
    print("\\n3. 开始训练…")
    trainer = PetTrainer(model, device, NUM_CLASSES)
    history = trainer.train(train_loader, val_loader, NUM_EPOCHS)

    # 绘制训练历史
    print("\\n4. 绘制训练历史…")
    plot_training_history(history)

    # 在测试集上评估
    print("\\n5. 在测试集上评估模型…")
    test_acc, test_preds, test_labels, test_probs = trainer.test(test_loader)
    print(f"测试集准确率: {test_acc:.4f}")

    # 生成分类报告
    print("\\n6. 生成分类报告…")
    report = classification_report(test_labels, test_preds, target_names=class_names)
    print(report)

    # 绘制混淆矩阵
    print("\\n7. 绘制混淆矩阵…")
    plot_confusion_matrix(test_labels, test_preds, class_names)

    # 可视化一些预测结果
    print("\\n8. 可视化预测结果…")
    visualize_predictions(model, test_loader, class_names, device, num_samples=10)

    # 性能分析
    print("\\n9. 性能分析…")
    if test_acc >= 0.95:
    print(f"✅ 目标达成!测试准确率: {test_acc:.4f} (> 0.95)")
    else:
    print(f"⚠️ 未达到目标,测试准确率: {test_acc:.4f} (< 0.95)")

    # 保存最终模型
    torch.save({
    'model_state_dict': model.state_dict(),
    'class_names': class_names,
    'test_accuracy': test_acc
    }, 'final_pet_classifier.pth')
    print("\\n✅ 模型已保存为 'final_pet_classifier.pth'")

    8.优化版

    # 8. 进阶技巧:微调和数据增强增强版
    def train_with_advanced_techniques():
    """
    使用更高级的技术训练模型
    """
    print("\\n使用进阶技巧训练模型…")

    # 使用更大的批处理大小和更复杂的增强
    BATCH_SIZE = 48

    # 定义更复杂的数据增强
    IMG_SIZE = 256

    advanced_train_transform = transforms.Compose([
    transforms.RandomResizedCrop(IMG_SIZE, scale=(0.7, 1.0)),
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.RandomVerticalFlip(p=0.2),
    transforms.RandomRotation(20),
    transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3, hue=0.2),
    transforms.RandomAffine(degrees=0, translate=(0.15, 0.15), scale=(0.9, 1.1)),
    transforms.RandomPerspective(distortion_scale=0.2, p=0.3),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
    transforms.RandomErasing(p=0.2, scale=(0.02, 0.2), ratio=(0.3, 3.3))
    ])

    val_transform = transforms.Compose([
    transforms.Resize((IMG_SIZE, IMG_SIZE)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])

    # 加载数据
    train_val_dataset = torchvision.datasets.OxfordIIITPet(
    root='./data/oxford_pets',
    split='trainval',
    target_types='category',
    download=False,
    transform=advanced_train_transform
    )

    test_dataset = torchvision.datasets.OxfordIIITPet(
    root='./data/oxford_pets',
    split='test',
    target_types='category',
    download=False,
    transform=val_transform
    )

    # 分割数据集
    dataset_size = len(train_val_dataset)
    train_size = int(0.9 * dataset_size) # 90%用于训练
    val_size = dataset_size – train_size

    train_dataset, val_dataset = random_split(train_val_dataset, [train_size, val_size])
    val_dataset.dataset.transform = val_transform

    # 创建数据加载器
    train_loader = DataLoader(
    train_dataset,
    batch_size=BATCH_SIZE,
    shuffle=True,
    num_workers=6,
    pin_memory=True,
    drop_last=True
    )

    val_loader = DataLoader(
    val_dataset,
    batch_size=BATCH_SIZE,
    shuffle=False,
    num_workers=6,
    pin_memory=True
    )

    test_loader = DataLoader(
    test_dataset,
    batch_size=BATCH_SIZE,
    shuffle=False,
    num_workers=6,
    pin_memory=True
    )

    # 使用更大的模型(ResNet101)
    model = torchvision.models.resnet101(pretrained=True)
    num_features = model.fc.in_features

    # 更复杂的分类头
    model.fc = nn.Sequential(
    nn.Dropout(0.5),
    nn.Linear(num_features, 1024),
    nn.BatchNorm1d(1024),
    nn.ReLU(),
    nn.Dropout(0.4),
    nn.Linear(1024, 512),
    nn.BatchNorm1d(512),
    nn.ReLU(),
    nn.Dropout(0.3),
    nn.Linear(512, 256),
    nn.BatchNorm1d(256),
    nn.ReLU(),
    nn.Dropout(0.2),
    nn.Linear(256, 37)
    )

    model = model.to(device)

    # 使用SWA(随机权重平均)技术
    optimizer = optim.AdamW(model.parameters(), lr=0.0001, weight_decay=1e-4)
    scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(
    optimizer,
    T_0=10, # 初始周期长度
    T_mult=2, # 周期倍增因子
    eta_min=1e-6
    )

    # 训练循环(简化版)
    best_acc = 0.0
    for epoch in range(30):
    print(f"\\nEpoch {epoch+1}/30")

    # 训练阶段
    model.train()
    train_loss = 0.0
    train_correct = 0
    train_total = 0

    pbar = tqdm(train_loader, desc=f"训练 Epoch {epoch+1}")
    for inputs, labels in pbar:
    inputs, labels = inputs.to(device), labels.to(device)

    optimizer.zero_grad()
    outputs = model(inputs)
    loss = nn.CrossEntropyLoss()(outputs, labels)
    loss.backward()
    torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
    optimizer.step()

    train_loss += loss.item()
    _, predicted = outputs.max(1)
    train_total += labels.size(0)
    train_correct += predicted.eq(labels).sum().item()

    pbar.set_postfix({'loss': loss.item(), 'acc': train_correct/train_total})

    scheduler.step()

    # 验证阶段
    model.eval()
    val_correct = 0
    val_total = 0

    with torch.no_grad():
    for inputs, labels in val_loader:
    inputs, labels = inputs.to(device), labels.to(device)
    outputs = model(inputs)
    _, predicted = outputs.max(1)
    val_total += labels.size(0)
    val_correct += predicted.eq(labels).sum().item()

    val_acc = val_correct / val_total
    print(f"验证准确率: {val_acc:.4f}")

    # 保存最佳模型
    if val_acc > best_acc:
    best_acc = val_acc
    torch.save(model.state_dict(), 'advanced_model.pth')
    print(f"保存最佳模型,准确率: {val_acc:.4f}")

    return best_acc

    9.运行代码:

    if __name__ == "__main__":
    try:
    main()
    except Exception as e:
    print(f"发生错误: {e}")
    print("尝试使用进阶技巧…")

    # 如果主方法失败,尝试进阶方法
    try:
    acc = train_with_advanced_techniques()
    print(f"进阶方法训练完成,最佳准确率: {acc:.4f}")
    if acc >= 0.95:
    print("✅ 使用进阶技巧达到目标准确率!")
    except Exception as e2:
    print(f"进阶方法也失败: {e2}")
    print("请检查数据下载或安装必要的包")

    10.运行结果展示

                                                                                                                                                           

    mlstat) [haichao@node01 demo1]$ python demo4.py
    使用设备: cpu
    ============================================================
    Oxford-IIIT Pet 数据集分类任务
    ============================================================

    1. 加载数据集…
    训练集大小: 3128
    验证集大小: 552
    测试集大小: 3669
    类别数量: 37

    2. 创建模型…
    /home/haichao/anaconda/anaconda_install/envs/mlstat/lib/python3.11/site-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead.
      warnings.warn(
    /home/haichao/anaconda/anaconda_install/envs/mlstat/lib/python3.11/site-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet50_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet50_Weights.DEFAULT` to get the most up-to-date weights.
      warnings.warn(msg)
    Downloading: "https://download.pytorch.org/models/resnet50-0676ba61.pth" to /home/haichao/.cache/torch/hub/checkpoints/resnet50-0676ba61.pth
    100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 97.8M/97.8M [01:32<00:00, 1.11MB/s]
    总参数: 24,699,493
    可训练参数: 19,509,797

    3. 开始训练…
    开始训练…

    Epoch 1/40
    ————————————————–
    训练损失: 2.8773, 训练准确率: 0.4012                                                                                                                                                                                                               
    验证损失: 1.8949, 验证准确率: 0.7989
    学习率: 0.000099
    保存最佳模型,验证准确率: 0.7989

    Epoch 2/40
    ————————————————–
    训练损失: 1.7745, 训练准确率: 0.7874                                                                                                                                                                                                               
    验证损失: 1.3117, 验证准确率: 0.8859
    学习率: 0.000098
    保存最佳模型,验证准确率: 0.8859

    Epoch 3/40
    ————————————————–
    训练损失: 1.3021, 训练准确率: 0.8795                                                                                                                                                                                                               
    验证损失: 1.1134, 验证准确率: 0.8967
    学习率: 0.000095
    保存最佳模型,验证准确率: 0.8967

    Epoch 4/40
    ————————————————–
    训练损失: 1.0909, 训练准确率: 0.9105                                                                                                                                                                                                               
    验证损失: 1.0414, 验证准确率: 0.8841
    学习率: 0.000091

    Epoch 5/40
    ————————————————–
    训练损失: 0.9758, 训练准确率: 0.9313                                                                                                                                                                                                               
    验证损失: 1.0194, 验证准确率: 0.8877
    学习率: 0.000086

    Epoch 6/40
    ————————————————–
    训练损失: 0.8985, 训练准确率: 0.9511                                                                                                                                                                                                               
    验证损失: 1.1017, 验证准确率: 0.8533
    学习率: 0.000080

    Epoch 7/40
    ————————————————–
    训练损失: 0.8466, 训练准确率: 0.9655                                                                                                                                                                                                               
    验证损失: 1.0448, 验证准确率: 0.8895
    学习率: 0.000073

    Epoch 8/40
    ————————————————–
    训练损失: 0.8186, 训练准确率: 0.9696                                                                                                                                                                                                               
    验证损失: 0.9821, 验证准确率: 0.9076
    学习率: 0.000066
    保存最佳模型,验证准确率: 0.9076

    Epoch 9/40
    ————————————————–
    训练损失: 0.7915, 训练准确率: 0.9786                                                                                                                                                                                                               
    验证损失: 0.9865, 验证准确率: 0.8967
    学习率: 0.000058

    Epoch 10/40
    ————————————————–
    训练损失: 0.7790, 训练准确率: 0.9840                                                                                                                                                                                                               
    验证损失: 1.0045, 验证准确率: 0.8986
    学习率: 0.000051

    Epoch 11/40
    ————————————————–
    训练损失: 0.7580, 训练准确率: 0.9872                                                                                                                                                                                                               
    验证损失: 0.9778, 验证准确率: 0.9040
    学习率: 0.000043

    Epoch 12/40
    ————————————————–
    训练损失: 0.7432, 训练准确率: 0.9910                                                                                                                                                                                                               
    验证损失: 1.0484, 验证准确率: 0.9004
    学习率: 0.000035

    Epoch 13/40
    ————————————————–
    训练损失: 0.7395, 训练准确率: 0.9917                                                                                                                                                                                                               
    验证损失: 0.9999, 验证准确率: 0.9130
    学习率: 0.000028
    保存最佳模型,验证准确率: 0.9130

    Epoch 14/40
    ————————————————–
    训练损失: 0.7299, 训练准确率: 0.9923                                                                                                                                                                                                               
    验证损失: 1.0175, 验证准确率: 0.9130
    学习率: 0.000021

    Epoch 15/40
    ————————————————–
    训练损失: 0.7217, 训练准确率: 0.9958                                                                                                                                                                                                               
    验证损失: 0.9745, 验证准确率: 0.9312
    学习率: 0.000015
    保存最佳模型,验证准确率: 0.9312

    Epoch 16/40
    ————————————————–
    训练损失: 0.7198, 训练准确率: 0.9958                                                                                                                                                                                                               
    验证损失: 0.9395, 验证准确率: 0.9330
    学习率: 0.000010
    保存最佳模型,验证准确率: 0.9330

    Epoch 17/40
    ————————————————–
    训练损失: 0.7161, 训练准确率: 0.9974                                                                                                                                                                                                               
    验证损失: 0.9662, 验证准确率: 0.9185
    学习率: 0.000006

    Epoch 18/40
    ————————————————–
    训练损失: 0.7121, 训练准确率: 0.9984                                                                                                                                                                                                               
    验证损失: 0.9268, 验证准确率: 0.9239
    学习率: 0.000003

    Epoch 19/40
    ————————————————–
    训练损失: 0.7118, 训练准确率: 0.9981                                                                                                                                                                                                               
    验证损失: 0.9353, 验证准确率: 0.9221
    学习率: 0.000002

    Epoch 20/40
    ————————————————–
    训练损失: 0.7149, 训练准确率: 0.9968                                                                                                                                                                                                               
    验证损失: 0.9530, 验证准确率: 0.9203
    学习率: 0.000001

    Epoch 21/40
    ————————————————–
    训练损失: 0.7125, 训练准确率: 0.9984                                                                                                                                                                                                               
    验证损失: 1.0208, 验证准确率: 0.9130
    学习率: 0.000002

    Epoch 22/40
    ————————————————–
    训练损失: 0.7104, 训练准确率: 0.9984                                                                                                                                                                                                               
    验证损失: 0.9540, 验证准确率: 0.9221
    学习率: 0.000003

    Epoch 23/40
    ————————————————–
    训练损失: 0.7108, 训练准确率: 0.9984                                                                                                                                                                                                               
    验证损失: 0.9207, 验证准确率: 0.9348
    学习率: 0.000006
    保存最佳模型,验证准确率: 0.9348

    Epoch 24/40
    ————————————————–
    训练损失: 0.7081, 训练准确率: 0.9984                                                                                                                                                                                                               
    验证损失: 0.9419, 验证准确率: 0.9293
    学习率: 0.000010

    Epoch 25/40
    ————————————————–
    训练损失: 0.7099, 训练准确率: 0.9987                                                                                                                                                                                                               
    验证损失: 1.0030, 验证准确率: 0.9167
    学习率: 0.000015

    Epoch 26/40
    ————————————————–
    训练损失: 0.7163, 训练准确率: 0.9962                                                                                                                                                                                                               
    验证损失: 0.9952, 验证准确率: 0.9130
    学习率: 0.000021

    Epoch 27/40
    ————————————————–
    训练损失: 0.7101, 训练准确率: 0.9974                                                                                                                                                                                                               
    验证损失: 0.9511, 验证准确率: 0.9203
    学习率: 0.000028

    Epoch 28/40
    ————————————————–
    训练损失: 0.7103, 训练准确率: 0.9981                                                                                                                                                                                                               
    验证损失: 0.9734, 验证准确率: 0.9149
    学习率: 0.000035

    Epoch 29/40
    ————————————————–
    训练损失: 0.7149, 训练准确率: 0.9962                                                                                                                                                                                                               
    验证损失: 1.0185, 验证准确率: 0.9094
    学习率: 0.000043

    Epoch 30/40
    ————————————————–
    训练损失: 0.7138, 训练准确率: 0.9965                                                                                                                                                                                                               
    验证损失: 1.0045, 验证准确率: 0.9094
    学习率: 0.000050

    Epoch 31/40
    ————————————————–
    训练损失: 0.7162, 训练准确率: 0.9946                                                                                                                                                                                                               
    验证损失: 1.0169, 验证准确率: 0.9094
    学习率: 0.000058

    Epoch 32/40
    ————————————————–
    训练损失: 0.7264, 训练准确率: 0.9923                                                                                                                                                                                                               
    验证损失: 1.0629, 验证准确率: 0.9022
    学习率: 0.000066

    Epoch 33/40
    ————————————————–
    训练损失: 0.7261, 训练准确率: 0.9914                                                                                                                                                                                                               
    验证损失: 1.0436, 验证准确率: 0.8949
    学习率: 0.000073
    早停触发,在epoch 33停止训练
    发生错误: Weights only load failed. This file can still be loaded, to do so you have two options, do those steps only if you trust the source of the checkpoint. 
            (1) In PyTorch 2.6, we changed the default value of the `weights_only` argument in `torch.load` from `False` to `True`. Re-running `torch.load` with `weights_only` set to `False` will likely succeed, but it can result in arbitrary code execution. Do it only if you got the file from a trusted source.
            (2) Alternatively, to load with `weights_only=True` please check the recommended steps in the following error message.
            WeightsUnpickler error: Unsupported global: GLOBAL numpy.core.multiarray.scalar was not an allowed global by default. Please use `torch.serialization.add_safe_globals([scalar])` or the `torch.serialization.safe_globals([scalar])` context manager to allowlist this global if you trust this class/function.

    Check the documentation of torch.load to learn more about types accepted by default with weights_only https://pytorch.org/docs/stable/generated/torch.load.html.
    尝试使用进阶技巧…

    使用进阶技巧训练模型…
    /home/haichao/anaconda/anaconda_install/envs/mlstat/lib/python3.11/site-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead.
      warnings.warn(
    /home/haichao/anaconda/anaconda_install/envs/mlstat/lib/python3.11/site-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet101_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet101_Weights.DEFAULT` to get the most up-to-date weights.
      warnings.warn(msg)

    Epoch 1/30
    训练 Epoch 1: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:07<00:00,  3.59s/it, loss=3.08, acc=0.0848]
    验证准确率: 0.3804
    保存最佳模型,准确率: 0.3804

    Epoch 2/30
    训练 Epoch 2: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:08<00:00,  3.60s/it, loss=2.35, acc=0.391]
    验证准确率: 0.5435
    保存最佳模型,准确率: 0.5435

    Epoch 3/30
    训练 Epoch 3: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:09<00:00,  3.62s/it, loss=1.81, acc=0.617]
    验证准确率: 0.7473
    保存最佳模型,准确率: 0.7473

    Epoch 4/30
    训练 Epoch 4: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:08<00:00,  3.61s/it, loss=1.38, acc=0.761]
    验证准确率: 0.8179
    保存最佳模型,准确率: 0.8179

    Epoch 5/30
    训练 Epoch 5: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:09<00:00,  3.62s/it, loss=1.02, acc=0.821]
    验证准确率: 0.8397
    保存最佳模型,准确率: 0.8397

    Epoch 6/30
    训练 Epoch 6: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:10<00:00,  3.64s/it, loss=0.97, acc=0.864]
    验证准确率: 0.8370

    Epoch 7/30
    训练 Epoch 7: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:05<00:00,  3.56s/it, loss=0.817, acc=0.896]
    验证准确率: 0.8859
    保存最佳模型,准确率: 0.8859

    Epoch 8/30
    训练 Epoch 8: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:09<00:00,  3.62s/it, loss=0.659, acc=0.912]
    验证准确率: 0.8967
    保存最佳模型,准确率: 0.8967

    Epoch 9/30
    训练 Epoch 9: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:13<00:00,  3.68s/it, loss=0.583, acc=0.926]
    验证准确率: 0.9022
    保存最佳模型,准确率: 0.9022

    Epoch 10/30
    训练 Epoch 10: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:11<00:00,  3.64s/it, loss=0.633, acc=0.944]
    验证准确率: 0.9049
    保存最佳模型,准确率: 0.9049

    Epoch 11/30
    训练 Epoch 11: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:12<00:00,  3.66s/it, loss=0.698, acc=0.885]
    验证准确率: 0.8370

    Epoch 12/30
    训练 Epoch 12: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:06<00:00,  3.58s/it, loss=0.671, acc=0.886]
    验证准确率: 0.8261

    Epoch 13/30
    训练 Epoch 13: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:07<00:00,  3.59s/it, loss=0.6, acc=0.885]
    验证准确率: 0.8288

    Epoch 14/30
    训练 Epoch 14: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:10<00:00,  3.63s/it, loss=0.43, acc=0.901]
    验证准确率: 0.8424

    Epoch 15/30
    训练 Epoch 15: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:11<00:00,  3.65s/it, loss=0.441, acc=0.906]
    验证准确率: 0.8505

    Epoch 16/30
    训练 Epoch 16: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:07<00:00,  3.59s/it, loss=0.56, acc=0.911]
    验证准确率: 0.8614

    Epoch 17/30
    训练 Epoch 17: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:16<00:00,  3.72s/it, loss=0.484, acc=0.927]
    验证准确率: 0.8804

    Epoch 18/30
    训练 Epoch 18: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:32<00:00,  3.94s/it, loss=0.287, acc=0.929]
    验证准确率: 0.8370

    Epoch 19/30
    训练 Epoch 19: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:26<00:00,  3.86s/it, loss=0.158, acc=0.932]
    验证准确率: 0.8750

    Epoch 20/30
    训练 Epoch 20: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:48<00:00,  3.31s/it, loss=0.247, acc=0.949]
    验证准确率: 0.8723

    Epoch 21/30
    训练 Epoch 21: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:28<00:00,  3.02s/it, loss=0.27, acc=0.953]
    验证准确率: 0.8886

    Epoch 22/30
    训练 Epoch 22: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:28<00:00,  3.02s/it, loss=0.125, acc=0.958]
    验证准确率: 0.9076
    保存最佳模型,准确率: 0.9076

    Epoch 23/30
    训练 Epoch 23: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:26<00:00,  3.00s/it, loss=0.165, acc=0.963]
    验证准确率: 0.8913

    Epoch 24/30
    训练 Epoch 24: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:29<00:00,  3.04s/it, loss=0.111, acc=0.971]
    验证准确率: 0.8913

    Epoch 25/30
    训练 Epoch 25: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:25<00:00,  2.97s/it, loss=0.168, acc=0.971]
    验证准确率: 0.9266
    保存最佳模型,准确率: 0.9266

    Epoch 26/30
    训练 Epoch 26: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:26<00:00,  2.99s/it, loss=0.307, acc=0.98]
    验证准确率: 0.9293
    保存最佳模型,准确率: 0.9293

    Epoch 27/30
    训练 Epoch 27: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:27<00:00,  3.01s/it, loss=0.147, acc=0.978]
    验证准确率: 0.8967

    Epoch 28/30
    训练 Epoch 28: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:25<00:00,  2.98s/it, loss=0.179, acc=0.981]
    验证准确率: 0.9212

    Epoch 29/30
    训练 Epoch 29: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:35<00:00,  3.12s/it, loss=0.104, acc=0.982]
    验证准确率: 0.9130

    Epoch 30/30
    训练 Epoch 30: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:40<00:00,  3.19s/it, loss=0.1, acc=0.979]
    验证准确率: 0.9103
    进阶方法训练完成,最佳准确率: 0.9293
     

                                      

    这个代码还有继续优化的空间,感兴趣的友友可以在这个基础上进行模型微调及参数优化。

    最终模型的大小为244MB

    总用量 3.2G
    drwxrwxr-x  15 haichao haichao 4.0K 1月  22 15:13 .
    drwx——. 37 haichao haichao 4.0K 1月  22 18:15 ..
    -rw-rw-r–   1 haichao haichao 387K 1月  20 16:23 adaptive_rk_variable_stiffness.png
    -rw-rw-r–   1 haichao haichao 174M 1月  22 15:36 advanced_model.pth
    drwxrwxr-x   2 haichao haichao 4.0K 1月   5 13:51 atomsk
    -rw-rw-r–   1 haichao haichao 244M 1月  22 13:38 best_pet_classifier.pth
    drwxrwxr-x   3 haichao haichao 4.0K 1月  19 20:29 data
    -rw-rw-r–   1 haichao haichao 1.6G 1月  20 10:28 data.zip
    -rw-rw-r–   1 haichao haichao  20K 1月  20 16:23 demo10.py
    -rw-rw-r–   1 haichao haichao 106K 1月  20 15:49 demo1.png
    -rw-rw-r–   1 haichao haichao  251 11月 18 21:31 demo1.py
    -rw-rw-r–   1 haichao haichao  102 11月 28 15:19 demo2.py
    -rw-rw-r–   1 haichao haichao  18K 1月  19 20:29 demo3.py
    -rw-rw-r–   1 haichao haichao  24K 1月  22 13:09 demo4.py
     

    赞(0)
    未经允许不得转载:171主机测评 » 基于迁移学习实现宠物分类(oxford-iiit-pet数据集)任务
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址