DAY44 PYTHON 预训练模型

预训练模型

知识点回顾

  1. 预训练的概念
  2. 常见的分类预训练模型
  3. 图像预训练模型的发展史
  4. 预训练的策略
  5. 预训练代码实战:resnet18

作业

  1. 尝试在cifar10对比如下其他的预训练模型,观察差异,尽可能和他人选择的不同
  2. 尝试通过ctrl进入resnet的内部,观察残差究竟是什么

知识点 1:预训练(Pre-training)的概念

预训练是深度学习中的一种迁移学习策略,核心逻辑是 "先通用学习,再特定任务适配",具体定义和价值如下:

核心定义:在大规模通用数据集(如 ImageNet)上,先训练一个基础模型,让模型学习到通用的特征提取能力(如边缘、纹理、物体部件等);之后将这个 "预训练好的模型" 作为起点,在小规模特定任务数据集(如自己的分类任务)上进行微调(Fine-tuning),以快速适配目标任务。

本质区别:与 "从零训练(Training from Scratch)" 相比,预训练相当于让模型 "站在巨人的肩膀上"------ 无需从原始像素开始学习基础特征,直接复用通用特征,再优化任务相关的特定特征。

核心价值:

解决小数据问题:当目标任务数据量较小时,从零训练易过拟合,预训练的通用特征可提供有效初始化,提升模型泛化能力;

降低计算成本:大规模数据集预训练需大量算力,但一次预训练可多次复用(微调成本远低于从头训练);

提升模型性能:通用数据集包含更丰富的特征模式,预训练模型的初始参数更优,微调后易达到更高精度。

python 复制代码
| **模型**       | **年份** | **提出团队**       | **关键创新点**                                                                 | **层数** | **参数量** | **ImageNet Top-5错误率** | **典型应用场景**               | **预训练权重可用性**         |
|----------------|----------|--------------------|--------------------------------------------------------------------------------|----------|------------|--------------------------|------------------------------|------------------------------|
| **LeNet-5**    | 1998     | Yann LeCun等       | 首个CNN架构,卷积层+池化层+全连接层,Sigmoid激活函数                          | 7        | ~60K       | N/A                      | 手写数字识别(MNIST)         | 无(历史模型)               |
| **AlexNet**    | 2012     | Alex Krizhevsky等  | ReLU激活函数、Dropout、数据增强、GPU训练                                        | 8        | 60M        | 15.3%                    | 大规模图像分类               | PyTorch/TensorFlow官方支持   |
| **VGGNet**     | 2014     | Oxford VGG团队     | 统一3×3卷积核、多尺度特征提取、结构简洁                                         | 16/19    | 138M/144M  | 7.3%/7.0%                | 图像分类、目标检测基础骨干网络 | PyTorch/TensorFlow官方支持   |
| **GoogLeNet**  | 2014     | Google             | Inception模块(多分支并行卷积)、1×1卷积降维、全局平均池化                     | 22       | 5M         | 6.7%                     | 大规模图像分类               | PyTorch/TensorFlow官方支持   |
| **ResNet**     | 2015     | 何恺明等           | 残差连接(解决梯度消失)、Batch Normalization                                   | 18/50/152| 11M/25M/60M | 3.57%/3.63%/3.58%        | 图像/视频分类、检测、分割     | PyTorch/TensorFlow官方支持   |
| **DenseNet**   | 2017     | Gao Huang等        | 密集连接(每层与后续所有层相连)、特征复用、参数效率高                         | 121/169  | 8M/14M     | 2.80%                    | 小数据集、医学图像处理        | PyTorch/TensorFlow官方支持   |
| **MobileNet**  | 2017     | Google             | 深度可分离卷积(减少75%计算量)、轻量级设计                                     | 28       | 4.2M       | 7.4%                     | 移动端图像分类/检测           | PyTorch/TensorFlow官方支持   |
| **EfficientNet** | 2019   | Google             | 复合缩放(同时优化深度、宽度、分辨率)、NAS搜索最佳配置                         | B0-B7    | 5.3M-66M   | 2.6% (B7)                | 高精度图像分类(资源受限场景)| PyTorch/TensorFlow官方支持   |

上图的层数,代表该模型不同的版本resnet有resnet18、resnet50、resnet152;efficientnet有efficientnet-b0、efficientnet-b1、efficientnet-b2、efficientnet-b3、efficientnet-b4等


其中ImageNet Top - 5 准确率是图像分类任务里的一种评估指标 ,用于衡量模型在 ImageNet 数据集上的分类性能,模型对图像进行分类预测,输出所有类别(共 1000 类 )的概率,取概率排名前五的类别,只要这五个类别里包含人工标注的正确类别,就算预测正确。

模型架构演进关键点总结

1. **深度突破**:从LeNet的7层到ResNet152的152层,残差连接解决了深度网络的训练难题。  ----没上过我复试班cv部分的自行去了解下什么叫做残差连接,很重要!
2. **计算效率**:GoogLeNet(Inception)和MobileNet通过结构优化,在保持精度的同时大幅降低参数量。  
3. **特征复用**:DenseNet的密集连接设计使模型能更好地利用浅层特征,适合小数据集。  
4. **自动化设计**:EfficientNet使用神经架构搜索(NAS)自动寻找最优网络配置,开创了AutoML在CNN中的应用。  


预训练模型使用建议

| **任务需求**               | **推荐模型**       | **理由**                                                                 |
|----------------------------|--------------------|--------------------------------------------------------------------------|
| 快速原型开发               | ResNet50/18        | 结构平衡,预训练权重稳定,社区支持完善                                   |
| 移动端部署                 | MobileNetV3        | 参数量小,计算高效,专为移动设备优化                                     |
| 高精度分类(资源充足)     | EfficientNet-B7    | 目前ImageNet准确率领先,适合GPU/TPU环境                                 |
| 小数据集或特征复用需求     | DenseNet           | 密集连接设计减少过拟合,特征复用能力强                                   |
| 多尺度特征提取             | Inception-ResNet   | 结合Inception多分支和ResNet残差连接,适合复杂场景                        |

这些模型的预训练权重均可通过主流框架(如PyTorch的`torchvision.models`、Keras的`applications`模块)直接加载,便于快速迁移到新任务。

预训练的效果依赖于 "数据、任务、模型、训练" 四大维度的策略设计,核心策略如下:

数据策略:决定预训练的 "特征通用性"

数据集选择:优先选择 "规模大、分布广、与目标任务相似" 的数据集(如通用任务用 ImageNet-1K/21K,医学图像用 CheXpert,遥感图像用 NWPU-RESISC45);

**数据增强:**通过随机翻转、裁剪、旋转、颜色抖动、MixUp/CutMix 等增强,扩大数据多样性,避免预训练模型过拟合(如 ImageNet 预训练常用 "随机水平翻转 + 随机裁剪");

数据清洗: 剔除数据集中的标注错误(如 ImageNet 存在约 5% 标注错误),避免错误信息影响预训练特征质量。

任务策略:决定预训练的 "特征导向"

**监督预训练(传统):**以 "图像分类" 为预训练任务(如给模型输入图像,输出 1000 类标签),学习 "从图像到类别" 的判别性特征,适合后续分类、检测等判别性任务;

自监督预训练(当前主流): 无需人工标注,让模型从无标签数据中自学习特征,更适合数据稀缺场景,常见任务包括:

对比学习(如 MoCo、SimCLR):通过 "同一图像的不同增强版本为正样本,其他图像为负样本",让模型学习相似图像的特征聚集;

掩码建模(如 MAE、BEiT):随机掩码部分图像 patch(如掩码 75%),让模型预测掩码区域的像素或语义,学习全局特征关联;

对比学习适合细粒度特征,掩码建模适合全局特征,当前大型预训练模型(如 ViT-L/16)多采用 "自监督预训练 + 监督微调" 的两步策略。

模型策略:决定预训练的 "特征提取能力"

模型初始化:优先选择已验证的基础架构(如 ResNet18/50、ViT-B),避免从零设计复杂模型(风险高、成本大);

正则化策略:加入 Dropout(随机失活)、Weight Decay(权重衰减)、Label Smoothing(标签平滑),防止预训练模型过拟合;

模型适配:根据数据集分辨率调整模型输入尺寸(如 ImageNet 预训练输入 224x224,高分辨率任务预训练输入 384x384)。

训练策略:决定预训练的 "收敛效率与稳定性"

优化器选择:常用 SGD(带动量,如 momentum=0.9)或 AdamW(适合 Transformer,权重衰减集成在优化器中);

学习率调度:采用 "余弦退火" 或 "阶梯衰减"(如 ImageNet 预训练用 "初始学习率 0.1,每 30 轮衰减为 1/10"),避免学习率过高导致不收敛或过低导致收敛慢;

批次大小(Batch Size):尽可能大(如 ImageNet 预训练用 256/512 batch),配合梯度累积(Gradient Accumulation)解决显存不足问题,提升训练稳定性。

ResNet18 的预训练完整代码(以 "CIFAR-10 数据集" 为例,模拟小规模通用数据集预训练,后续可微调至其他任务):
步骤 1:环境准备与导入库

python 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from torchvision.models import resnet18  # PyTorch内置ResNet18
import matplotlib.pyplot as plt

# 设备配置(GPU优先)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(f"使用设备:{device}")

步骤 2:数据加载与增强(CIFAR-10 数据集,10 类,5 万训练图)

python 复制代码
# 数据增强:训练集用强增强,验证集仅Resize和归一化
train_transform = transforms.Compose([
    transforms.RandomResizedCrop(224),  # 随机裁剪到224x224(ResNet输入尺寸)
    transforms.RandomHorizontalFlip(p=0.5),  # 随机水平翻转
    transforms.ColorJitter(brightness=0.2, contrast=0.2),  # 颜色抖动
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],  # ImageNet均值(通用归一化)
                         std=[0.229, 0.224, 0.225])
])

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

# 加载数据集
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=train_transform)
val_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=val_transform)

# 数据加载器
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=4)
val_loader = DataLoader(val_dataset, batch_size=64, shuffle=False, num_workers=4)

步骤 3:定义 ResNet18 模型(修改输出层适配 CIFAR-10)

PyTorch 内置的 ResNet18 默认输出 1000 类(适配 ImageNet),需修改最后全连接层为 10 类(CIFAR-10):

python 复制代码
def build_resnet18(num_classes=10):
    # 加载预训练权重(可选:若已有预训练权重,可加载后微调;此处为"从零预训练",故pretrained=False)
    model = resnet18(pretrained=False)
    # 修改最后一层全连接层(输入维度512,输出维度num_classes)
    in_features = model.fc.in_features
    model.fc = nn.Linear(in_features, num_classes)
    return model.to(device)

# 初始化模型
model = build_resnet18(num_classes=10)

步骤 4:配置预训练参数(损失函数、优化器、学习率调度)

python 复制代码
# 1. 损失函数:分类任务用交叉熵损失
criterion = nn.CrossEntropyLoss()

# 2. 优化器:SGD+动量+权重衰减
optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4)

# 3. 学习率调度:每15轮衰减为原来的0.1
lr_scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=15, gamma=0.1)

步骤 5:预训练循环(训练 + 验证)

python 复制代码
def train_one_epoch(model, train_loader, criterion, optimizer, device):
    model.train()  # 训练模式(启用Dropout等)
    total_loss = 0.0
    total_correct = 0
    total_samples = 0
    
    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device)
        
        # 前向传播
        outputs = model(images)
        loss = criterion(outputs, labels)
        
        # 反向传播与优化
        optimizer.zero_grad()  # 清空梯度
        loss.backward()        # 计算梯度
        optimizer.step()       # 更新参数
        
        # 统计指标
        total_loss += loss.item() * images.size(0)
        _, predicted = torch.max(outputs, 1)  # 取预测概率最大的类别
        total_correct += (predicted == labels).sum().item()
        total_samples += images.size(0)
    
    # 计算 epoch 级指标
    avg_loss = total_loss / total_samples
    accuracy = total_correct / total_samples
    return avg_loss, accuracy

def validate(model, val_loader, criterion, device):
    model.eval()  # 验证模式(关闭Dropout等)
    total_loss = 0.0
    total_correct = 0
    total_samples = 0
    
    with torch.no_grad():  # 禁用梯度计算,节省显存和时间
        for images, labels in val_loader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            loss = criterion(outputs, labels)
            
            # 统计指标
            total_loss += loss.item() * images.size(0)
            _, predicted = torch.max(outputs, 1)
            total_correct += (predicted == labels).sum().item()
            total_samples += images.size(0)
    
    avg_loss = total_loss / total_samples
    accuracy = total_correct / total_samples
    return avg_loss, accuracy

# 开始预训练(共50轮)
num_epochs = 50
train_losses, train_accs = [], []
val_losses, val_accs = [], []

for epoch in range(num_epochs):
    print(f"Epoch [{epoch+1}/{num_epochs}]")
    
    # 训练
    train_loss, train_acc = train_one_epoch(model, train_loader, criterion, optimizer, device)
    train_losses.append(train_loss)
    train_accs.append(train_acc)
    print(f"Train - Loss: {train_loss:.4f}, Acc: {train_acc:.4f}")
    
    # 验证
    val_loss, val_acc = validate(model, val_loader, criterion, device)
    val_losses.append(val_loss)
    val_accs.append(val_acc)
    print(f"Val - Loss: {val_loss:.4f}, Acc: {val_acc:.4f}")
    
    # 更新学习率
    lr_scheduler.step()

# 保存预训练模型(后续可用于微调)
torch.save(model.state_dict(), "resnet18_pretrained_cifar10.pth")
print("预训练模型已保存!")

# 绘制训练曲线(可选,直观查看训练过程)
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(train_losses, label="Train Loss")
plt.plot(val_losses, label="Val Loss")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(train_accs, label="Train Acc")
plt.plot(val_accs, label="Val Acc")
plt.xlabel("Epoch")
plt.ylabel("Accuracy")
plt.legend()
plt.show()

作业

python 复制代码
from torchvision.models import resnet18
import torch

# 加载ResNet-18模型
model = resnet18()
# 打印网络结构(观察layer1~layer4由残差块组成)
print(model)
相关推荐
yuzhuanhei3 小时前
机器学习算法常用算法
人工智能·算法·机器学习
子不语1803 小时前
深度学习——Logistic回归中的梯度下降法
深度学习
2401_841495643 小时前
自然语言处理实战——英法机器翻译
人工智能·pytorch·python·深度学习·自然语言处理·transformer·机器翻译
大千AI助手3 小时前
Frobenius范数:矩阵分析的万能度量尺
人工智能·神经网络·线性代数·矩阵·矩阵分解·l2范数·frobenius范数
优软轻创-拓客私域4 小时前
数字权益市场爆发:如何通过权益数卡选对优质货源
大数据·人工智能
EllenLiu4 小时前
从 Transformer 理论到文本分类:BERT 微调实战总结
人工智能
B站计算机毕业设计之家4 小时前
计算机视觉python口罩实时检测识别系统 YOLOv8模型 PyTorch 和PySide6界面 opencv (建议收藏)✅
python·深度学习·opencv·计算机视觉·cnn·1024程序员节
绿算技术4 小时前
绿算GP Spark引爆关注,成为AI工厂存储利器
大数据·人工智能·spark
树欲静而风不止864 小时前
全星QMS:破解高端制造质量难题的一体化数字平台
人工智能