TensorFlow 与 Keras:顺序式与函数式 API

FreeGuideOnline 最新 2026-06-16

TensorFlow/Keras 深度学习入门:掌握顺序式与函数式 API

在深度学习的世界里,Keras 已迅速成为构建神经网络的标配工具。而 TensorFlow 2.0 之后,它被深度整合为 tf.keras,成为官方高阶 API。本教程将带你从零开始,理解 Keras 的两种核心模型构建方式——顺序式(Sequential)API函数式(Functional)API,并学会在不同场景下做出正确选择。

什么是 Keras 与 TensorFlow?

TensorFlow 是一个端到端的开源机器学习平台,提供从数据处理到模型部署的全套工具。Keras 则是运行在 TensorFlow 之上的高级神经网络 API,它以用户友好、模块化、可扩展著称。简而言之:

  • TensorFlow:底层引擎,负责张量运算、自动微分、设备加速。
  • Keras:高层封装,让开发者用更少的代码快速构建和训练模型。

tf.keras 中,你可以使用三种主要 API 来定义模型架构:Sequential API、Functional API 和 Subclassing API。前两种是最常用且风格迥异的,也是本教程的核心。

环境准备与概念回顾

在开始编写代码之前,请确保已安装 TensorFlow(2.x 版本)。以下导入语句将贯穿本教程:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
print(tf.__version__)

理解神经网络的基本组成

任何神经网络都由**层(Layer)**堆叠而成。一个典型的模型包含:

  • 输入层:接收特征数据。
  • 隐藏层:进行非线性变换(如全连接层 Dense、卷积层 Conv2D)。
  • 输出层:产生预测结果(分类用 softmax,回归用线性激活)。

Keras 将层视为可组合的积木,而不同的 API 决定了你如何拼接这些积木。

顺序式 API:一条直线走到底

顺序式 API 是 Keras 中最简单、最容易上手的方式。它适用于单输入、单输出层与层之间顺序连接的网络,就像一个线性管道。

创建一个顺序模型

你可以将层列表直接传入 Sequential 构造函数,或使用 .add() 方法逐层堆叠。

方法一:直接传入层列表

model = keras.Sequential([
    layers.Dense(64, activation='relu', input_shape=(784,)),
    layers.Dense(64, activation='relu'),
    layers.Dense(10, activation='softmax')
])

方法二:使用 .add() 动态添加

model = keras.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(784,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

关键点解析

  • input_shape 只需要在第一个层定义,后续层会自动推导形状。
  • 每个 Dense 层都是一类全连接层,64 表示该层有 64 个神经元。
  • activation 参数指定激活函数,relu 常用于隐藏层,softmax 用于多分类输出。

查看模型结构

model.summary()

输出将展示每一层的名称、输出形状和参数量。

编译与训练

顺序模型的编译和训练流程对所有 API 都一样:

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
# X_train 形状 (样本数, 784),y_train 为整数标签
model.fit(X_train, y_train, epochs=5, batch_size=32)

顺序式的局限

顺序式模型假设严格的线性拓扑:只有一个输入流,只有一个输出流,中间没有分支、合并或多输入输出。如果遇到以下情形,顺序式就无能为力:

  • 残差连接(ResNet 中的跳跃连接)
  • 多输入模型(例如图像+文本双输入)
  • 多输出模型(例如同时预测分类和回归值)
  • 共享层的模型

此时,你需要更灵活的函数式 API。

函数式 API:构建复杂的拓扑结构

函数式 API 将层视为可调用的函数,接收张量并返回张量。它允许你构建有向无环图(DAG),实现多输入、多输出、共享层和非线性连接。

核心思想:层即函数

每个层实例都可以像函数一样调用:

inputs = keras.Input(shape=(784,))
dense = layers.Dense(64, activation='relu')
x = dense(inputs)   # 调用层,输出张量

通过变量将张量连接起来,你定义了模型的计算图。

一个简单的函数式模型

实现与顺序式相同的三层全连接网络:

# 1. 定义输入节点
inputs = keras.Input(shape=(784,), name='input_layer')

# 2. 层调用,像搭积木一样连接
x = layers.Dense(64, activation='relu', name='hidden1')(inputs)
x = layers.Dense(64, activation='relu', name='hidden2')(x)
outputs = layers.Dense(10, activation='softmax', name='output_layer')(x)

# 3. 创建模型,指定输入和输出
model = keras.Model(inputs=inputs, outputs=outputs, name='functional_mlp')

keras.Input 不是数据,而是一个符号张量(symbolic tensor),它声明了输入的形状和类型。模型会根据输入与输出的连接自动推导中间形状。

多输入模型实战:图像与元数据融合

假设我们要构建一个模型,同时输入图像和一组结构化特征(如用户年龄、标签),最终输出分类结果。

# 图像输入分支
image_input = keras.Input(shape=(32, 32, 3), name='image')
x = layers.Conv2D(32, 3, activation='relu')(image_input)
x = layers.MaxPooling2D(2)(x)
x = layers.Flatten()(x)
x = layers.Dense(128, activation='relu')(x)

# 结构化数据输入分支
struct_input = keras.Input(shape=(5,), name='structured_features')
y = layers.Dense(32, activation='relu')(struct_input)

# 合并两个分支
combined = layers.concatenate([x, y])
z = layers.Dense(64, activation='relu')(combined)
output = layers.Dense(1, activation='sigmoid', name='output')(z)

# 定义双输入模型
model = keras.Model(inputs=[image_input, struct_input], outputs=output)
model.summary()

在训练时,需要传入一个包含两个数组的列表,匹配输入顺序:

model.fit([X_images, X_struct], y_train, epochs=10)

多输出模型:同时预测年龄和性别

函数式 API 也可以轻松处理多个输出。

input_layer = keras.Input(shape=(64,))

# 共享的隐藏层
hidden = layers.Dense(128, activation='relu')(input_layer)

# 输出分支1:年龄回归(无激活函数)
age_output = layers.Dense(1, name='age')(hidden)

# 输出分支2:性别二分类
gender_output = layers.Dense(1, activation='sigmoid', name='gender')(hidden)

model = keras.Model(inputs=input_layer, outputs=[age_output, gender_output])

编译时可以为不同输出指定不同损失函数和权重:

model.compile(optimizer='adam',
              loss={'age': 'mse', 'gender': 'binary_crossentropy'},
              loss_weights={'age': 0.3, 'gender': 0.7},
              metrics={'age': 'mae', 'gender': 'accuracy'})

构建残差连接(跳跃连接)

残差网络是现代深度学习的基石,函数式 API 实现起来极为自然。

inputs = keras.Input(shape=(256,))
x = layers.Dense(64, activation='relu')(inputs)
# 跳过一层,将原始输入或前一层的输出直接加到后面
skip = x
x = layers.Dense(64, activation='relu')(x)
# 逐元素相加(要求形状相同)
x = layers.add([x, skip])  
x = layers.Dense(10, activation='softmax')(x)

model = keras.Model(inputs, x)

你还可以使用 layers.Add()(), layers.Concatenate()() 等专用合并层。

共享层:权重复用的威力

在一些任务(如孪生网络、文本相似度)中,需让多个输入通过同一个层实例,实现权重共享。

# 定义一个共享的全连接层
shared_dense = layers.Dense(64, activation='relu')

# 两个不同输入
input_a = keras.Input(shape=(128,))
input_b = keras.Input(shape=(128,))

# 应用同一层
encoded_a = shared_dense(input_a)
encoded_b = shared_dense(input_b)

# 计算它们之间的差异等操作
merged = layers.Concatenate()([encoded_a, encoded_b])
output = layers.Dense(1, activation='sigmoid')(merged)

model = keras.Model([input_a, input_b], output)

在这里,shared_dense 层的权重会被两个路径共同学习与更新。

顺序式 vs 函数式:如何选择?

特性 顺序式 API 函数式 API
适用拓扑 纯线性,逐层堆叠 任意有向无环图(DAG)
多输入/多输出 不支持 完全支持
内部连接方式 自动连接上一层到下一层 显式定义张量流动
灵活性 较低,但极其简洁 极高,适合研究、复杂模型
调试与可视化 简单 更直观地查看数据形状传递
典型应用 简单分类器、回归、快速原型 ResNet、Inception、多模态、多任务学习
推荐度 刚入门学习时用,快速验证想法 一旦需要分叉或复用层,立即转向函数式

经验法则

  • 如果你的模型就是“一锅炖”的线性流,用顺序式,它更简洁。
  • 如果你不确定未来是否需要任何分支,或是在复现论文,直接用函数式,它几乎可以表示任何标准模型,而且代码可读性并不差。

模型的保存与加载

无论是哪种 API 构建的模型,保存和加载方式完全一致。

保存整个模型(架构+权重+优化器状态)

model.save('my_model.h5')  # 或 .keras 格式 (TensorFlow 2.12+)

仅保存权重

model.save_weights('weights/checkpoint')

加载模型

loaded_model = keras.models.load_model('my_model.h5')

注意:函数式模型加载后,其拓扑结构保留,可以继续训练或推理。

进阶:Model 类的子类化

除了前两种 API,Keras 还提供子类化(Subclassing)方式,允许你完全自定义前向传播。但这不是本教程重点。简单提及:子类化最灵活,但会丢失显式图结构,导致模型可视化困难、保存限制(需要 tf.saved_model)。对于 95% 的应用,顺序式和函数式 API 已经足够。

实战建议

  1. 先画图,后写代码:对于复杂的网络,先画出计算流图,标清输入输出,然后用函数式 API 一一实现。
  2. 善用 model.summary():快速检查各层的输出形状是否符合预期,避免张量不匹配错误。
  3. 活用 name 参数:给每个层和输入命名,这样在调试和保存加载时都非常便利。
  4. 从顺序式入门,尽快过渡到函数式:顺序式帮助你快速理解 Keras 基本流程,但函数式才是通往进阶的钥匙。

总结

  • 顺序式 API 是 Keras 为线性模型准备的快捷方式,用一个简单的层堆就完成了模型定义。
  • 函数式 API 通过将层视为可调用的函数,构建复杂的有向无环计算图,支持多输入、多输出、残差连接和共享层。
  • 两者共用同样的编译、训练、评估和预测接口,切换成本极低。

现在,打开你的 Jupyter Notebook,尝试用函数式 API 构建一个多输入模型吧!只有亲手搭建才能体会到控制计算流的喜悦。

持续学习,动手实践,你会发现 Keras 的设计哲学——简单而不失灵活——会加速你的深度学习之旅。