Keras实战:从LeNet到ResNeXt,10种经典CNN模型代码实现与性能对比
作为一名长期在计算机视觉领域摸爬滚打的开发者,我常常被问到这样一个问题:“面对这么多经典的卷积神经网络(CNN)架构,我到底该从哪个学起?哪个最适合我的项目?” 这确实是个好问题。从1998年Yann LeCun的LeNet-5,到2012年引爆AI热潮的AlexNet,再到后来VGG、Inception、ResNet等家族的百花齐放,每一代模型都不仅仅是参数量的堆砌,其背后是研究者们对网络结构、计算效率和特征表达能力的深刻思考与精巧设计。对于已经掌握了Keras基础,希望深入理解不同CNN架构精髓,并能亲手复现、对比其性能的开发者来说,仅仅知道理论是远远不够的。你需要看到代码,运行它,感受不同结构在训练时的收敛曲线,比较它们在标准数据集上的准确率、参数量和推理速度。这正是本文想要带你做的事情:我们将用Keras,从零开始,一步步搭建从LeNet到ResNeXt的10个里程碑式模型,并在CIFAR-10和MNIST这样的经典数据集上进行实战演练与横向对比。你会发现,亲手敲下这些代码,远比阅读十篇论文摘要更能让你理解深度学习的演进脉络。
1. 环境准备与基础框架搭建
在开始构建任何模型之前,一个稳定、可复现的开发环境是重中之重。我个人的习惯是使用conda来管理Python环境,这能有效避免不同项目间的依赖冲突。对于深度学习项目,我强烈建议单独创建一个环境。
首先,我们创建并激活一个名为keras_cnn的虚拟环境:
conda create -n keras_cnn python=3.8
conda activate keras_cnn
接下来,安装核心的深度学习库。TensorFlow 2.x 已经将Keras作为其官方高级API,因此我们直接安装TensorFlow即可。为了后续的可视化分析,我们还需要matplotlib和seaborn。
pip install tensorflow==2.10.0 matplotlib seaborn pandas scikit-learn
注意:TensorFlow的版本选择需与你的CUDA/cuDNN版本匹配,以启用GPU加速。如果仅使用CPU,安装上述命令即可。
环境就绪后,让我们建立一个统一的项目结构。我将创建一个主脚本文件,用于集中管理数据加载、模型定义、训练和评估流程。同时,为每个模型单独创建模块文件,保持代码的清晰和可维护性。一个建议的目录结构如下:
keras_cnn_project/
│
├── models/ # 存放所有模型定义
│ ├── __init__.py
│ ├── lenet.py
│ ├── alexnet.py
│ └── ... (其他模型)
│
├── utils/ # 工具函数(数据加载、可视化等)
│ ├── __init__.py
│ └── data_loader.py
│
├── config.py # 超参数配置
├── train.py # 统一训练脚本
└── evaluate.py # 统一评估与对比脚本
在config.py中,我们可以定义一些全局参数,方便后续调整和对比实验:
# config.py
class Config:
# 数据相关
IMG_HEIGHT = 32 # CIFAR-10 图像尺寸
IMG_WIDTH = 32
NUM_CLASSES = 10
BATCH_SIZE = 64
EPOCHS = 50
VALIDATION_SPLIT = 0.1
# 训练相关
LEARNING_RATE = 1e-3
PATIENCE = 10 # 用于Early Stopping
# 路径相关
LOG_DIR = './logs'
MODEL_SAVE_DIR = './saved_models'
这种模块化的设计,使得我们后续增加新模型或调整实验设置变得非常轻松。准备工作完成后,我们就可以正式踏入经典CNN的代码世界了。
2. 奠基者:LeNet-5与AlexNet的代码复现与解析
让我们从起点开始。LeNet-5虽然结构简单,但它确立了CNN的基本范式:卷积层提取特征,池化层降维,全连接层进行分类。在Keras中实现它,是理解这一范式的绝佳练习。
2.1 LeNet-5:卷积神经网络的雏形
LeNet-5最初用于手写数字识别,其输入是32x32的灰度图像。在我们的实现中,为了适配CIFAR-10的32x32x3彩色图像,只需将输入通道数改为3。以下是完整的Keras Sequential模型实现:
# models/lenet.py
from tensorflow.keras import layers, models
def build_lenet(input_shape=(32, 32, 3), num_classes=10):
model = models.Sequential([
# 第一卷积块
layers.Conv2D(6, (5, 5), activation='relu', padding='same', input_shape=input_shape),
layers.AveragePooling2D((2, 2), strides=2),
# 第二卷积块
layers.Conv2D(16, (5, 5), activation='relu'),
layers.AveragePooling2D((2, 2), strides=2),
# 展平后接全连接层
layers.Flatten(),
layers.Dense(120, activation='relu'),
layers.Dense(84, activation='relu'),
layers.Dense(num_classes, activation='softmax')
])
return model
这个模型只有约6万个参数,训练速度极快。你可以用model.summary()查看其详细结构。值得注意的是,原始LeNet-5使用的是tanh激活函数和平均池化,这里我们遵循现代惯例使用了ReLU。在CIFAR-10上,这个简单模型经过50轮训练,准确率大约能达到65%-70%。虽然不高,但作为一个基线非常有价值。
2.2 AlexNet:深度学习的“大爆炸”
AlexNet在结构上是LeNet的深化版,但其意义远不止于此。它首次成功应用了ReLU激活函数、Dropout正则化和数据增强,并在ImageNet大赛中以巨大优势夺冠,正式开启了深度学习时代。其结构更为复杂,包含5个卷积层和3个全连接层。
由于原始AlexNet输入是224x224x3,而我们的实验数据是32x32,我们需要进行一些适配。同时,为了节省计算资源,我适当减少了第一全连接层的神经元数量。
# models/alexnet.py
from tensorflow.keras import layers, models, regularizers
def build_alexnet(input_shape=(32, 32, 3), num_classes=10):
model = models.Sequential([
# 第一卷积块:大卷积核捕捉特征
layers.Conv2D(96, (11, 11), strides=4, activation='relu', input_shape=input_shape, padding='same'),
layers.BatchNormalization(),
layers.MaxPooling2D((3, 3), strides=2),
# 第二卷积块
layers.Conv2D(256, (5, 5), activation='relu', padding='same'),
layers.BatchNormalization(),
layers.MaxPooling2D((3, 3), strides=2),
# 第三至第五卷积块:连续3x3卷积
layers.Conv2D(384, (3, 3), activation='relu', padding='same'),
layers.Conv2D(384, (3, 3), activation='relu', padding='same'),
layers.Conv2D(256, (3, 3), activation='relu', padding='same'),
layers.MaxPooling2D((3, 3), strides=2),
# 全连接层,加入Dropout防止过拟合
layers.Flatten(),
la


1031

被折叠的 条评论
为什么被折叠?



