CNN实战+OOP面向对象编程详解

本文适合已经了解CNN卷积基础原理的读者,将从代码实战角度深入讲解PyTorch中的CNN实现和面向对象编程应用。

在这里插入图片描述

📑 目录


第一部分:从简单开始 - 你的第一个CNN模块

让我们从最简单的CNN模块开始,这段代码定义了一个基础的卷积神经网络模块(Tudui(选自小土堆的代码):

🧱 类定义与继承

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)
    
    def forward(self, x):
        x = self.conv1(x)
        return x

核心要点:

  • Tudui 继承自 nn.Module,这是PyTorch中所有神经网络模块的基类
  • super() 调用父类构造函数,让PyTorch正确注册参数和模块结构
  • forward() 方法定义了数据的前向传播路径

📊 数据流转示例

输入 卷积层处理 输出
[B, 3, H, W] Conv2d(3→6) [B, 6, H-2, W-2](无padding时)

例如:输入 [1, 3, 32, 32] 的RGB图像 → 输出 [1, 6, 30, 30] 的特征图


第二部分:深入理解 - 卷积参数详解

🔍 Conv2d参数深度解析

self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)
参数 含义 影响
in_channels=3 输入通道数(RGB图像为3) 决定接受什么样的输入
out_channels=6 输出通道数(使用6个不同的卷积核) 决定提取多少种不同特征
kernel_size=3 卷积核大小(3×3) 感受野大小,影响特征提取范围
stride=1 步长(每次移动1个像素) 控制输出尺寸和计算密度
padding=0 边缘填充(不填充) 是否保持原图尺寸信息

📐 输出尺寸计算公式

掌握这个公式,你就能预测任何卷积层的输出尺寸:

output_size = floor((input_size - kernel_size + 2×padding) / stride) + 1

实际计算示例:

输入: (1, 1, 5, 5)
卷积核: 3×3, stride=1, padding=0

计算过程:
output_height = (5 - 3 + 0) / 1 + 1 = 3
output_width = (5 - 3 + 0) / 1 + 1 = 3

结果: (1, 1, 3, 3)

🛠️ padding的妙用

padding值 效果 使用场景
0 输出尺寸缩小 特征提取,降维
1 近似保持原尺寸 保留空间信息
‘same’ 输出与输入尺寸完全相同 需要精确尺寸保持时

第三部分:面向对象编程在PyTorch中的应用

🎯 OOP三大核心特性

特性 Python含义 PyTorch中的体现
封装 数据和方法打包到类中 网络结构封装在自定义类中
继承 子类自动获得父类的所有属性和方法 继承nn.Module获得训练相关功能
多态 同一接口在不同类中有不同的实现方式 不同网络类重写forward()方法

🔗 1. 封装:模块化设计

封装让代码更整洁、更易维护:

class MyNet(nn.Module):
    def __init__(self):
        super().__init__()
        # 数据(网络层)封装在类中
        self.conv = nn.Conv2d(3, 6, 3)
        self.relu = nn.ReLU()
    
    def forward(self, x):
        # 方法(前向传播逻辑)封装在类中
        x = self.conv(x)
        x = self.relu(x)
        return x

# 外部使用者只需要简单调用,无需了解内部细节
net = MyNet()
output = net(input_tensor)

🏗️ 2. 继承:站在巨人的肩膀上

继承nn.Module后,你的网络自动拥有:

  • ✅ 参数自动注册和管理
  • ✅ GPU/CPU设备转换:.cuda(), .cpu()
  • ✅ 训练/评估模式切换:.train(), .eval()
  • ✅ 参数访问:.parameters(), .named_parameters()
  • ✅ 模型保存/加载支持

关键代码:

super().__init__()  # 🚨 千万不能省略!

🎭 3. 多态:一个接口,多种实现

class LinearNet(nn.Module):
    def forward(self, x):
        return self.fc(x)  # 全连接网络的forward

class ConvNet(nn.Module):
    def forward(self, x):
        return self.conv(x)  # 卷积网络的forward

# 统一的调用方式,不同的内部实现
for model in [LinearNet(), ConvNet()]:
    output = model(input_data)  # 多态体现

第四部分:进阶实践 - 完整CNN模型构建

🏗️ 构建一个完整的图像分类网络

网络架构设计:

输入[B,3,32,32] → Conv1[6] → ReLU → MaxPool → Conv2[16] → ReLU → MaxPool → Flatten → FC → 分类[10]
import torch
import torch.nn as nn
import torch.nn.functional as F

class CompleteCNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        # 特征提取部分
        self.conv1 = nn.Conv2d(3, 6, kernel_size=5)    # [3,32,32] → [6,28,28]
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)  # [6,28,28] → [6,14,14]
        self.conv2 = nn.Conv2d(6, 16, kernel_size=5)   # [6,14,14] → [16,10,10]
        # 池化后: [16,5,5]
        
        # 分类器部分
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, num_classes)
        
    def forward(self, x):
        # 特征提取
        x = self.pool(F.relu(self.conv1(x)))  # 卷积→激活→池化
        x = self.pool(F.relu(self.conv2(x)))  # 卷积→激活→池化
        
        # 展平
        x = torch.flatten(x, 1)  # 保留batch维度,展平其他维度
        
        # 分类
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)  # 最后一层不加激活,交给损失函数处理
        return x

📦 模块容器:让网络更灵活

容器类型 特点 适用场景
Sequential 顺序执行,自动连接 线性网络结构
ModuleList 列表管理,需手动控制执行顺序 动态深度、循环结构
ModuleDict 字典管理,支持名称索引 条件分支、模块动态选择

Sequential示例:

self.feature_extractor = nn.Sequential(
    nn.Conv2d(3, 6, 3),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(6, 16, 3),
    nn.ReLU(),
    nn.MaxPool2d(2)
)

ModuleList示例:

self.layers = nn.ModuleList([
    nn.Linear(10, 10) for _ in range(5)  # 动态创建5层
])

def forward(self, x):
    for layer in self.layers:
        x = F.relu(layer(x))
    return x

第五部分:实战演练 - 完整训练流程

🎯 训练流程全览

数据准备
模型定义
损失函数
优化器
训练循环
模型评估
保存模型

📊 1. 数据准备(CIFAR-10示例)

import torch
import torchvision
import torchvision.transforms as transforms

# 数据预处理管道
transform = transforms.Compose([
    transforms.ToTensor(),                          # PIL → Tensor
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 标准化到[-1,1]
])

# 训练集
trainset = torchvision.datasets.CIFAR10(
    root='./data', train=True, download=True, transform=transform
)
trainloader = torch.utils.data.DataLoader(
    trainset, batch_size=64, shuffle=True, num_workers=2
)

# 测试集
testset = torchvision.datasets.CIFAR10(
    root='./data', train=False, download=True, transform=transform
)
testloader = torch.utils.data.DataLoader(
    testset, batch_size=64, shuffle=False, num_workers=2
)

🏗️ 2. 模型、损失函数和优化器配置

import torch.optim as optim

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

# 模型实例化
model = CompleteCNN(num_classes=10).to(device)

# 损失函数(多分类交叉熵)
criterion = nn.CrossEntropyLoss()

# 优化器(SGD + 动量)
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

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

🔄 3. 训练循环

def train_model(model, trainloader, criterion, optimizer, num_epochs=5):
    model.train()  # 设置为训练模式
    
    for epoch in range(num_epochs):
        running_loss = 0.0
        correct_predictions = 0
        total_samples = 0
        
        for batch_idx, (inputs, labels) in enumerate(trainloader):
            # 数据移到设备
            inputs, labels = inputs.to(device), labels.to(device)
            
            # 前向传播
            optimizer.zero_grad()        # 清零梯度
            outputs = model(inputs)      # 模型预测
            loss = criterion(outputs, labels)  # 计算损失
            
            # 反向传播
            loss.backward()              # 计算梯度
            optimizer.step()             # 更新参数
            
            # 统计信息
            running_loss += loss.item()
            _, predicted = torch.max(outputs.data, 1)
            total_samples += labels.size(0)
            correct_predictions += (predicted == labels).sum().item()
            
            # 打印进度
            if batch_idx % 100 == 99:
                avg_loss = running_loss / 100
                accuracy = 100 * correct_predictions / total_samples
                print(f'Epoch [{epoch+1}/{num_epochs}], '
                      f'Batch [{batch_idx+1}], '
                      f'Loss: {avg_loss:.4f}, '
                      f'Accuracy: {accuracy:.2f}%')
                running_loss = 0.0
        
        # 可选:更新学习率
        scheduler.step()

# 开始训练
train_model(model, trainloader, criterion, optimizer, num_epochs=10)

📊 4. 模型评估

def evaluate_model(model, testloader):
    model.eval()  # 设置为评估模式
    correct = 0
    total = 0
    
    with torch.no_grad():  # 关闭梯度计算,节省内存
        for inputs, labels in testloader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            _, predicted = torch.max(outputs, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    
    accuracy = 100 * correct / total
    print(f'测试集准确率: {accuracy:.2f}%')
    return accuracy

# 评估模型
final_accuracy = evaluate_model(model, testloader)

💾 5. 模型保存和加载

# 保存模型(推荐方式:只保存参数)
torch.save(model.state_dict(), 'best_model.pth')

# 保存完整信息
checkpoint = {
    'epoch': epoch,
    'model_state_dict': model.state_dict(),
    'optimizer_state_dict': optimizer.state_dict(),
    'accuracy': final_accuracy,
}
torch.save(checkpoint, 'checkpoint.pth')

# 加载模型
def load_model(model_path, model_class, num_classes=10):
    model = model_class(num_classes=num_classes)
    model.load_state_dict(torch.load(model_path, map_location=device))
    model.to(device)
    model.eval()
    return model

# 使用示例
loaded_model = load_model('best_model.pth', CompleteCNN)

🎓 总结与要点回顾

核心知识点梳理

阶段 关键技术点 核心代码
模型设计 继承nn.Module,重写forward class MyNet(nn.Module)
参数配置 卷积层参数、输出尺寸计算 Conv2d(in_ch, out_ch, kernel_size)
数据处理 DataLoader、数据预处理 transforms.Compose([...])
训练过程 前向传播、反向传播、参数更新 loss.backward(), optimizer.step()
模型评估 准确率计算、模型状态切换 model.eval(), torch.no_grad()

💡 最佳实践提示

  • ✅ 始终使用super().__init__()初始化父类
  • ✅ 合理设置batch_size和学习率
  • ✅ 训练时使用.train(),推理时使用.eval()
  • ✅ 使用torch.no_grad()节省推理时的内存
  • ✅ 定期保存模型checkpoints

通过本文的学习,你应该已经掌握了PyTorch中CNN的完整实现流程和面向对象编程的核心概念。现在可以开始你的深度学习实战之旅了!🎉

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐