神经网络框架
经典神经网络架构
AlexNet
AlexNet是由Alex Krizhevsky、Ilya Sutskever和Geoffrey Hinton在2012年提出的深度卷积神经网络,在ImageNet大规模视觉识别挑战赛(ILSVRC)中取得了突破性成果,标志着深度学习在计算机视觉领域的崛起。
主要特点:
- 深度结构:包含5个卷积层和3个全连接层
- ReLU激活函数:首次在大型CNN中广泛使用ReLU代替传统的tanh或sigmoid函数
- 局部响应归一化(LRN):提高泛化能力
- 重叠池化:减少过拟合并提高特征提取能力
- 数据增强与Dropout:采用多种方法防止过拟合
网络结构:
- 输入:227×227×3的RGB图像
- 第一卷积层:96个11×11的卷积核,步长为4
- 最大池化层:3×3,步长为2
- 第二卷积层:256个5×5的卷积核,填充为2
- 最大池化层:3×3,步长为2
- 第三卷积层:384个3×3的卷积核,填充为1
- 第四卷积层:384个3×3的卷积核,填充为1
- 第五卷积层:256个3×3的卷积核,填充为1
- 最大池化层:3×3,步长为2
- 全连接层:4096个神经元
- 全连接层:4096个神经元
- 输出层:1000个类别的softmax分类器
ResNet (残差网络)
ResNet由微软研究院的Kaiming He等人在2015年提出,通过引入残差连接解决了深度网络训练困难的问题,赢得了ILSVRC 2015的冠军。ResNet的出现使得训练更深的神经网络成为可能。
主要特点:
- 残差块:核心创新是残差连接(skip connection),允许梯度直接流过多层网络
- 批量归一化:每个卷积层后应用批量归一化
- 深度灵活:有多个变体,如ResNet-18、ResNet-34、ResNet-50、ResNet-101和ResNet-152等
- 瓶颈设计:较深的版本使用"瓶颈"设计降低计算复杂度
残差块结构: 基本残差块包含两个3×3的卷积层,每层后跟批量归一化和ReLU激活函数。跳跃连接将输入直接添加到这两个卷积层的输出上,形成:
输出 = F(x) + x
其中F(x)是卷积层的输出,x是输入。
瓶颈残差块: 在更深的ResNet变体中,采用1×1 -> 3×3 -> 1×1的卷积序列,其中1×1卷积用于降维和升维,减少计算量。
VGG
VGG网络由牛津大学Visual Geometry Group在2014年提出,以其简洁统一的架构著称。
主要特点:
- 统一结构:使用小尺寸卷积核(3×3)构建网络
- 深层设计:有多个变体,如VGG-16(16层)和VGG-19(19层)
- 固定池化窗口:所有最大池化层均采用2×2窗口,步长为2
- 全连接层统一:三个全连接层,前两个各有4096个神经元
网络结构(VGG-16):
- 输入:224×224×3的RGB图像
- 两个3×3卷积层(64个滤波器),最大池化
- 两个3×3卷积层(128个滤波器),最大池化
- 三个3×3卷积层(256个滤波器),最大池化
- 三个3×3卷积层(512个滤波器),最大池化
- 三个3×3卷积层(512个滤波器),最大池化
- 三个全连接层(4096-4096-1000)
GoogLeNet (Inception)
GoogLeNet由Google团队在2014年提出,引入了Inception模块,在保持计算效率的同时增加网络宽度和深度。
主要特点:
- Inception模块:并行使用不同大小的卷积核(1×1, 3×3, 5×5)和池化操作
- 1×1卷积:用于降维,减少计算量
- 辅助分类器:在中间层添加辅助分类器帮助训练
- 全局平均池化:替代全连接层减少参数数量
Inception模块结构: 同时进行四个并行操作,然后在深度方向上拼接结果:
- 1×1卷积
- 1×1卷积降维 -> 3×3卷积
- 1×1卷积降维 -> 5×5卷积
- 3×3最大池化 -> 1×1卷积
MobileNet
MobileNet由Google团队开发,专为移动和嵌入式设备优化,保持较高准确率的同时显著减少参数量和计算复杂度。
主要特点:
- 深度可分离卷积:将标准卷积分解为深度卷积(depthwise)和逐点卷积(pointwise)
- 宽度乘数:控制每层滤波器数量
- 分辨率乘数:控制输入图像分辨率
- 轻量级设计:比传统CNN模型小得多,计算效率高
深度可分离卷积:
- 深度卷积:对输入的每个通道单独应用单一滤波器
- 逐点卷积:使用1×1卷积组合深度卷积的输出
MobileNet V2进一步引入了倒置残差结构和线性瓶颈层,提升了性能。
PyTorch实现示例
import cv2
import os
import torch
from torchvision import transforms, datasets, models
from tqdm import tqdm
from torchvision.models import ResNet18_Weights
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.optim as optim
# 保存模型的文件
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 指定设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
#数据增强
train_transform = transforms.Compose([
transforms.RandomResizedCrop((224, 224)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]
)
# 构建数据集
train_data = datasets.ImageFolder(root='train_balanced',transform=train_transform)
# 构建数据加载器
train_loader = DataLoader(train_data, batch_size=32, shuffle=True)
# --------------------------------------------------------------------
# 预加载模型
model = models.resnet18(weights=ResNet18_Weights.IMAGENET1K_V1).to(device)
# 修改全连接层
out_features = model.fc.in_features
model.fc = nn.Linear(out_features, 7)
model = model.to(device)
# 构建损失函数与优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# --------------------------------------------------------------------
model.train()
# 训练
epochs = 15
best_acc = 0.0
for epoch in range(epochs):
totle_loss = 0
totle_cur = 0
# 进度条
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)
# 前向传播
outpout = model(image)
loss = criterion(outpout, label)
totle_loss += loss.item()
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 计算准确率
_, predict = torch.max(outpout, 1)
totle_cur += torch.sum(predict==label.data).item()
train_loss = totle_loss / len(train_loader.dataset)
train_acc = float(totle_cur) / len(train_loader.dataset)
if train_acc > best_acc:
best_acc = train_acc
if not os.path.exists("./models"):
os.makedirs("./models")
torch.save(model.state_dict(), "./models/best_mask_model.pth")
print(f'最新保存的模型准确率为: {100 * train_acc:.2f}%')
print(f"Best Test Accuracy: {100 * best_acc:.4f}")