作为深度学习领域的从业者,我们都知道 Keras 以其简洁性和用户友好性而广受欢迎。而在 Keras 的众多组件中,Sequential 类(顺序模型)可以说是初学者和专家构建原型的首选工具。你是否曾想过如何快速构建一个从输入到输出层层递进的神经网络?Sequential 类正是为此而生。在本文中,我们将深入探讨 Sequential 类的内部机制、使用场景以及如何利用它高效地构建模型,同时我们也会讨论它的局限性以及何时应该考虑更复杂的模型架构。
目录
目录
- Keras 和 Sequential 类简介
- 理解 Sequential 模型的结构
- 实战一:使用 Sequential 构建简单的前馈网络
- 实战二:构建卷积神经网络 (CNN)
- Sequential 类的局限性及解决方案
- 最佳实践与常见错误
Keras 和 Sequential 类简介
在开始编码之前,让我们先明确什么是 Sequential 类。它是 Keras 库的核心组件之一,专为线性堆叠的神经网络设计。这意味着模型中的每一层都恰好只有一个输入张量和一个输出张量,并且层的连接是按顺序进行的。
你可以把 Sequential 模型想象成一个“三明治”或者一条“流水线”。数据从一端进入,经过每一层的加工处理,最后从另一端输出结果。这种结构非常适合大多数常见的深度学习任务,比如简单的图像分类、回归预测等。
为什么选择 Sequential 类?
在我们深入研究代码之前,了解一下它的主要特性是有帮助的:
- 极简主义:它极大地减少了样板代码。你不需要去定义复杂的图结构,只需要告诉模型你想添加哪一层即可。
- 线性流程:对于层与层之间没有共享分支、没有多重输入或输出的情况,这是最直观的实现方式。
- 快速原型:当我们有一个新想法想快速验证时,Sequential 模型能让我们在几分钟内搭建好可训练的网络。
理解 Sequential 模型的结构
Sequential 模型的核心在于“堆叠”。在内部,Keras 维护了一个层的列表,当数据流过模型时,它会按顺序将每一层的输出作为下一层的输入。
定义模型的方法
通常,我们有两种主要的方式来定义一个 Sequential 模型:
- 通过
add()方法:先创建一个空的模型实例,然后像搭积木一样一层一层地加上去。 - 通过层列表:在创建模型实例时,直接传入一个包含所有层的列表。
输入形状的重要性
这是一个初学者经常遇到的问题:Keras 需要知道输入数据的形状。 在深度学习中,形状信息至关重要。对于 Sequential 模型,第一层必须接收关于输入形状的信息。这可以通过 INLINECODEa655154b 参数、INLINECODE5f3d08d5 参数,或者更现代的做法——使用 Input 对象来实现。
让我们看一个最基础的结构示例来理解这一点:
from keras.models import Sequential
from keras.layers import Dense, Input
# 方法 1:使用 add() 方法,这是最灵活的方式
model = Sequential()
# 注意:这里我们使用 Input 对象来明确指定输入形状
# 这是一种最佳实践,可以避免关于输入形状的警告
model.add(Input(shape=(16,))) # 假设我们有 16 个特征
model.add(Dense(units=32, activation=‘relu‘)) # 第一层隐含层
model.add(Dense(units=10, activation=‘softmax‘)) # 输出层
# 方法 2:通过列表直接构建
model_alternative = Sequential([
Input(shape=(16,)),
Dense(32, activation=‘relu‘),
Dense(10, activation=‘softmax‘)
])
在这个例子中,我们构建了一个全连接网络(MLP)。第一层 INLINECODEe0878722 将输入的 16 个特征映射到 32 个神经元,通过 INLINECODE6c42f3bc 告诉 Keras 每个样本是一个长度为 16 的向量。最后一层使用 softmax 激活函数,常用于多分类任务。
实战一:使用 Sequential 构建简单的前馈网络
让我们通过一个具体的例子——MNIST 手写数字识别——来看看如何一步步构建和训练模型。这是一个经典的“Hello World”级别的深度学习任务。
在开始之前,我们需要处理数据。虽然 MNIST 原本是 28×28 的灰度图,但为了模拟更复杂的输入情况(或者为了适应某些预定义的架构),我们在下面的代码中演示如何对数据进行预处理,包括调整大小和归一化。
import keras
from keras.models import Sequential
from keras.layers import Dense, Flatten
from keras.datasets import mnist
from keras.utils import to_categorical
import tensorflow as tf
# 1. 加载 MNIST 数据集
# MNIST 包含 60,000 个训练样本和 10,000 个测试样本
print("正在加载数据...")
(train_X, train_y), (test_X, test_y) = mnist.load_data()
# 2. 数据预处理
# 这里我们将图像调整为 64x64 大小,并转换为 RGB 3通道格式
# 这主要是为了演示如何处理非标准输入,实际 MNIST 任务通常不需要这一步
print("正在预处理数据...")
train_X_resized = tf.image.resize(train_X.reshape(-1, 28, 28, 1), (64, 64))
train_X_rgb = tf.image.grayscale_to_rgb(train_X_resized)
test_X_resized = tf.image.resize(test_X.reshape(-1, 28, 28, 1), (64, 64))
test_X_rgb = tf.image.grayscale_to_rgb(test_X_resized)
# 归一化像素值到 0-1 之间,这有助于模型收敛
train_X_rgb = train_X_rgb / 255.0
test_X_rgb = test_X_rgb / 255.0
# 将标签转换为 One-Hot 编码
# 例如,数字 5 变成 [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
train_y = to_categorical(train_y, 10)
test_y = to_categorical(test_y, 10)
print(f"训练集形状: {train_X_rgb.shape}")
输出说明:
运行上述代码后,你会看到数据被成功下载和转换。注意 INLINECODEa2cd5930 的形状变成了 INLINECODE0af5d1d3,表示有 60,000 张图片,每张是 64×64 像素的 RGB 图像。
添加层并构建模型
现在数据已经准备好了,让我们构建模型。为了处理图像数据,我们首先需要将多维的图像数据展平成一维向量,以便输入到全连接层(Dense)。
# 3. 构建 Sequential 模型
model = Sequential()
# 第一步:展平层
# 将输入的 (64, 64, 3) 图像展平为长度为 64*64*3 = 12288 的向量
# 注意:我们在第一层使用 input_shape 指定输入数据的维度
model.add(Flatten(input_shape=(64, 64, 3)))
# 第二步:全连接隐藏层
# 128 个神经元,使用 ReLU 激活函数增加非线性能力
model.add(Dense(units=128, activation=‘relu‘))
# 输出层
# 10 个神经元对应 10 个数字类别,Softmax 用于输出概率分布
model.add(Dense(units=10, activation=‘softmax‘))
# 4. 编译模型
# 配置优化器 和损失函数
model.compile(optimizer=‘adam‘,
loss=‘categorical_crossentropy‘,
metrics=[‘accuracy‘])
# 打印模型概要,查看每一层的形状和参数数量
model.summary()
代码解析:
- INLINECODE7a2bdf33:这是处理图像数据的关键第一步。它不包含任何可学习的参数,只是对数据进行重塑。如果不使用这一层,我们就无法将 2D 图像数据直接传入 INLINECODE26b0f18b 层。
-
Dense(128, activation=‘relu‘):这是模型的“大脑”。ReLU 是目前最流行的激活函数,因为它能有效解决梯度消失问题。 - INLINECODEf32bfdc5:这一步告诉 Keras 我们要“如何”训练模型。INLINECODE90697717 优化器通常是一个不错的默认选择。
实战二:构建卷积神经网络 (CNN)
虽然全连接网络(MLP)可以处理图像,但它们并不是最高效的。在图像识别领域,卷积神经网络 (CNN) 才是王者。Sequential 模型同样可以轻松构建 CNN,只需要将 INLINECODEfb23d122 层替换为 INLINECODE52df2b06 和 MaxPooling2D 层。
让我们构建一个更专业的图像分类器:
from keras.layers import Conv2D, MaxPooling2D, Dropout
# 创建一个新的 Sequential 模型
cnn_model = Sequential()
# 第一卷积块
# 32 个滤波器,每个大小为 3x3。
# padding=‘same‘ 意味着输出图像的大小与输入相同(通过填充边缘)
cnn_model.add(Conv2D(32, (3, 3), padding=‘same‘, activation=‘relu‘, input_shape=(64, 64, 3)))
cnn_model.add(MaxPooling2D(pool_size=(2, 2))) # 下采样,减少计算量
# 第二卷积块
cnn_model.add(Conv2D(64, (3, 3), padding=‘same‘, activation=‘relu‘))
cnn_model.add(MaxPooling2D(pool_size=(2, 2)))
# 防止过拟合的技巧
# Dropout 会在训练过程中随机“丢弃”一部分神经元,迫使模型学习更鲁棒的特征
cnn_model.add(Dropout(0.25))
# 将特征图展平以便输入到全连接层
cnn_model.add(Flatten())
# 全连接层进行分类
cnn_model.add(Dense(128, activation=‘relu‘))
cnn_model.add(Dropout(0.5)) # 再次使用 Dropout
cnn_model.add(Dense(10, activation=‘softmax‘))
# 编译模型
cnn_model.compile(loss=‘categorical_crossentropy‘, optimizer=‘adam‘, metrics=[‘accuracy‘])
cnn_model.summary()
实战见解:
在这个例子中,你可以看到 Sequential 模型是如何优雅地处理复杂的 CNN 结构的。我们交替使用卷积层和池化层,这是提取图像特征的标准模式。通过 model.summary(),你会观察到随着层数的加深,图像的高度和宽度在减小(由于池化),而深度(滤波器数量)在增加。
Sequential 类的局限性及解决方案
虽然 Sequential 模型非常方便,但它并不适合所有情况。作为开发者,了解它的局限性至关重要。那么,什么时候不应该使用 Sequential 类呢?
- 多输入或多输出模型:如果你需要将图像数据(视觉输入)和元数据(表格输入)同时输入模型,Sequential 类就无能为力了。
- 非线性拓扑结构:如果你需要实现残差连接,即某一层的输出需要跳过中间层直接与更后面的层相加,这种非线性的路径无法用 Sequential 实现。
- 共享层:如果你想让两个不同的分支共享同一层的权重(例如,处理两个不同的文本输入并使用同一个嵌入层),Sequential 类无法处理。
解决方案:Keras 函数式 API
当你遇到上述情况时,不要强行使用 Sequential 类。Keras 提供了函数式 API,它允许你定义复杂的层与层之间的有向无环图(DAG)。虽然这不在本文的重点范围内,但你需要知道这是进阶学习的下一步。
最佳实践与常见错误
在我们结束之前,让我们总结一些在使用 Sequential 类时的最佳实践和需要注意的陷阱。
1. 始终明确输入形状
最常见的错误之一就是忘记定义输入形状。
# 错误示范
model = Sequential()
model.add(Dense(10, activation=‘softmax‘)) # Keras 不知道输入是什么样子的!
虽然 Keras 会在你第一次调用 INLINECODE02a9d31a 时尝试推断形状,但这通常会导致警告或不明确的行为。最佳实践是始终在第一层使用 INLINECODE31c1793e 显式定义输入。
2. 注意输出的激活函数
选择正确的激活函数对于模型性能至关重要。
- 回归问题(预测房价):通常在输出层不使用激活函数(即线性激活
linear)。 - 二分类问题:输出层使用
sigmoid,输出 0 到 1 之间的概率。 - 多分类问题:输出层使用
softmax,且神经元数量应等于类别数。
3. 模型的可保存性
Sequential 模型可以非常方便地被保存和序列化。我们可以在训练后保存整个模型的架构、权重甚至优化器状态。
# 假设 model 已经训练完毕
model.save(‘my_sequential_model.keras‘)
# 稍后加载模型
from keras.models import load_model
loaded_model = load_model(‘my_sequential_model.keras‘)
这对于部署和生产环境切换非常有用。
4. 查看模型的中间层输出
有时候我们想要提取模型中间某一层的特征(例如,为了可视化)。在 Sequential 模型中,虽然比函数式 API 稍微麻烦一点,但也是可以做到的。
from keras.models import Model
# 假设我们想提取倒数第二层的输出(也就是 Dense(128) 那一层)
layer_name = ‘dense_2‘ # 你可以通过 model.summary() 查看层的名称
intermediate_layer_model = Model(inputs=model.input,
outputs=model.get_layer(layer_name).output)
intermediate_output = intermediate_layer_model.predict(test_X_rgb)
print(f"中间层输出形状: {intermediate_output.shape}")
结语
在这篇文章中,我们深入探讨了 Keras 的 Sequential 类。从最基本的概念到构建复杂的 CNN,我们看到了这个看似简单的类实际上包含了构建强大应用所需的所有要素。
Sequential 类是你深度学习之旅的起点。对于大多数线性堆叠的网络结构,它都是最简洁、最高效的选择。当你开始遇到它的瓶颈,比如需要多输入或多输出时,那就是你迈向更高级的函数式 API 的时候了。
接下来的步骤建议:
- 动手实验:尝试修改我们上面提到的代码,改变层数量、神经元数量,看看它们如何影响模型的准确率。
- 数据增强:结合
ImageDataGenerator使用 Sequential 模型,这是提升图像识别模型性能的常用技巧。 - 探索回调:学习如何使用 INLINECODEadfcbb9e 和 INLINECODE6bc40227 来优化训练过程。
深度学习的大门已经为你打开,Sequential 类就是你手中的第一把钥匙。开始构建属于你自己的神经网络吧!