Day44

1. 预训练概念:在大规模数据上训练模型学习通用知识,再迁移到下游任务微调

2. 常见模型:图像有AlexNet、ResNet、ViT;NLP有BERT、GPT

3. 图像模型发展:从手工特征到深度学习,从CNN到Transformer、多模态

4. 预训练策略:数据增强、自监督/监督训练、模型微调、多模态学习

 


作业1.

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms, models
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import time
import warnings

# 依赖库加载与配置
%pip install tqdm  # 进度条工具
from tqdm import tqdm
warnings.filterwarnings("ignore")  # 忽略警告信息


### 数据处理模块 ###
def get_cifar10_data(batch=128, img_size=32):
    """
    构建CIFAR-10数据集加载器
    :param batch: 批量大小
    :param img_size: 图像缩放尺寸
    :return: 训练/测试数据加载器
    """
    print(f"[数据准备] 图像尺寸调整为 {img_size}x{img_size}")
    
    # 训练集增强策略
    train_trans = transforms.Compose([
        transforms.Resize(img_size),        # 尺寸统一
        transforms.RandomCrop(img_size, padding=4),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
    ])
    
    # 测试集标准化处理
    test_trans = transforms.Compose([
        transforms.Resize(img_size),
        transforms.ToTensor(),
        transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
    ])
    
    # 数据集实例化
    train_data = datasets.CIFAR10(root='./data', train=True, download=True, transform=train_trans)
    test_data = datasets.CIFAR10(root='./data', train=False, transform=test_trans)
    
    # 数据加载器配置
    train_loader = DataLoader(train_data, batch_size=batch, shuffle=True, num_workers=2, pin_memory=True)
    test_loader = DataLoader(test_data, batch_size=batch, shuffle=False, num_workers=2, pin_memory=True)
    
    print("[数据] 加载完成")
    return train_loader, test_loader


### 模型构建模块 ###
def build_resnet18(pretrain=True, class_num=10):
    """构建ResNet18模型(带迁移学习)"""
    # 加载预训练模型
    model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1 if pretrain else None)
    # 替换分类层
    last_dim = model.fc.in_features
    model.fc = nn.Linear(last_dim, class_num)
    return model

def build_mobilenet(pretrain=True, class_num=10):
    """构建MobileNetV2轻量级模型"""
    model = models.mobilenet_v2(weights=models.MobileNet_V2_Weights.IMAGENET1K_V1 if pretrain else None)
    # 调整分类器输出维度
    last_dim = model.classifier[1].in_features
    model.classifier[1] = nn.Linear(last_dim, class_num)
    return model


### 训练与评估模块 ###
def run_training(model_tag, model_builder, device, total_epochs, freeze_epochs):
    """
    执行完整训练流程
    :param model_tag: 模型标识名称
    :param model_builder: 模型构建函数
    :param device: 计算设备(CPU/GPU)
    :param total_epochs: 总训练轮次
    :param freeze_epochs: 冻结预训练层的轮次
    """
    print(f"\n{'='*30} 开始实验: {model_tag} {'='*30}")
    
    # 1. 准备数据与模型
    train_loader, test_loader = get_cifar10_data()
    model = model_builder(pretrained=True, class_num=10).to(device)
    
    # 统计模型参数量
    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 / 1e6:.2f}M")
    
    # 2. 迁移学习参数冻结控制
    def set_param_freeze(freeze_flag):
        """冻结/解冻模型参数"""
        action = "冻结" if freeze_flag else "解冻"
        print(f"[参数控制] {action} 特征提取层")
        for name, param in model.named_parameters():
            # 分类层始终可训练
            if 'fc' not in name and 'classifier' not in name:
                param.requires_grad = not freeze_flag
    
    set_param_freeze(freeze=True)  # 初始冻结特征提取层
    
    # 优化器配置
    optimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-3)
    loss_fn = nn.CrossEntropyLoss()
    lr_scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.2, patience=2, verbose=True)
    
    # 3. 训练主循环
    start_time = time.time()
    for epoch in range(1, total_epochs + 1):
        # 解冻控制(在指定轮次后解冻全部参数)
        if epoch == freeze_epochs + 1:
            set_param_freeze(freeze=False)
            # 解冻后使用更小学习率
            optimizer = optim.Adam(model.parameters(), lr=1e-4)
            print("[优化器] 已更新为全局微调模式")
        
        # 训练阶段
        model.train()
        train_loop = tqdm(train_loader, desc=f"[训练] Epoch {epoch}/{total_epochs}", leave=False)
        for inputs, targets in train_loop:
            inputs, targets = inputs.to(device), targets.to(device)
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = loss_fn(outputs, targets)
            loss.backward()
            optimizer.step()
            train_loop.set_postfix(loss=loss.item())
        train_loop.close()
        
        # 评估阶段
        model.eval()
        test_loss = 0
        correct = 0
        with torch.no_grad():
            for inputs, targets in test_loader:
                inputs, targets = inputs.to(device), targets.to(device)
                outputs = model(inputs)
                test_loss += loss_fn(outputs, targets).item() * inputs.size(0)
                preds = outputs.argmax(dim=1)
                correct += preds.eq(targets).sum().item()
        
        # 结果统计
        avg_loss = test_loss / len(test_loader.dataset)
        accuracy = 100. * correct / len(test_loader.dataset)
        print(f"[评估] Epoch {epoch} | 测试损失: {avg_loss:.4f} | 准确率: {accuracy:.2f}%")
        
        # 学习率调度
        lr_scheduler.step(avg_loss)
    
    # 实验完成统计
    end_time = time.time()
    print(f"[实验] {model_tag} 完成,总耗时: {end_time - start_time:.2f} 秒")
    print(f"{'='*70}\n")


### 主程序执行 ###
if __name__ == "__main__":
    # 设备配置
    DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"[系统] 使用设备: {DEVICE}")
    
    # 训练参数
    TOTAL_EPOCHS = 10
    FREEZE_EPOCHS = 3  # 前3轮冻结特征提取层
    
    # 运行ResNet18实验
    run_training("ResNet18实验", build_resnet18, DEVICE, TOTAL_EPOCHS, FREEZE_EPOCHS)
    
    # 运行MobileNetV2实验
    run_training("MobileNetV2实验", build_mobilenet, DEVICE, TOTAL_EPOCHS, FREEZE_EPOCHS)

你可能感兴趣的:(Day44)