3. Keras基础回顾:模型构建、编译、训练、保存与加载

在进入Edge Impulse的Keras模型导入与再训练流程之前,我们必须先夯实Keras本身的核心操作链路。Edge Impulse的迁移学习、模型微调以及部署导出,本质上都是围绕Keras模型的构建、编译、训练、保存与加载这五个环节展开的。尤其是模型保存环节,H5与SavedModel两种格式的选择直接决定了后续在Edge Impulse中导入的兼容性、部署时的运行时依赖以及再训练时的灵活性。本章将系统梳理这五个环节的关键技术点,并重点剖析H5与SavedModel的格式差异与适用场景。

Keras模型生命周期与格式选择决策路径 1. 模型构建 Sequential / Functional 2. 模型编译 优化器 / 损失 / 指标 3. 模型训练 fit / 验证集 / 回调 4. 模型保存 H5 或 SavedModel 5. 模型加载 load_model / 再训练 H5 格式 • 单文件,包含权重+结构+优化器 • 适合快速保存/加载、小模型 • 不包含服务端部署所需签名 • Edge Impulse 导入兼容性一般 • 适合实验阶段、模型备份 SavedModel 格式 • 目录结构,包含权重+结构+签名 • 支持服务端部署(TF Serving) • 包含推理图,适合生产环境 • Edge Impulse 推荐导入格式 • 支持再训练与迁移学习 对比 结论:Edge Impulse 迁移学习场景优先选择 SavedModel,H5 仅用于快速原型验证 保存时需同时考虑:模型结构、权重、优化器状态、编译信息、自定义层代码

3.1 模型构建:从Sequential到Functional的工程选择

Keras模型构建是整条链路的第一步,其核心任务是定义网络层的堆叠方式与张量流动路径。在Edge Impulse的再训练场景中,我们通常不会从零开始构建一个全新的网络,而是加载一个预训练模型(如MobileNetV2、EfficientNet等)作为特征提取器,然后在其顶部替换或新增分类层。因此,理解Keras的两种构建范式至关重要。

Sequential模型适用于线性堆叠的网络结构,每一层只有一个输入和一个输出,层与层之间顺序连接。这种模型简单直观,适合快速原型验证。例如,一个用于一维信号分类的简单全连接网络可以这样构建:

from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten

model = Sequential([
    Flatten(input_shape=(128, 6)),  # 128个时间步,6个通道
    Dense(64, activation='relu'),
    Dropout(0.3),
    Dense(10, activation='softmax')
])

Functional模型则更为灵活,它允许层与层之间任意连接,支持多输入、多输出以及共享层等复杂拓扑。在Edge Impulse的迁移学习中,Functional模型是主流选择,因为我们需要将预训练模型作为子模块嵌入到新的模型中,并可能冻结其部分层。例如,加载一个预训练的MobileNetV2并添加自定义分类头:

from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.models import Model
from tensorflow.keras.layers import GlobalAveragePooling2D, Dense

base_model = MobileNetV2(weights='imagenet', include_top=False, input_shape=(96, 96, 3))
base_model.trainable = False  # 冻结特征提取层

x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(128, activation='relu')(x)
predictions = Dense(5, activation='softmax')(x)

model = Model(inputs=base_model.input, outputs=predictions)

在构建阶段,一个常见的误区是忽略输入形状的定义。Edge Impulse的模型导入要求输入形状必须与传感器数据的特征维度严格匹配。例如,加速度计数据经过预处理后可能变成(50, 3)的二维数组,那么第一层就必须指定input_shape=(50, 3)。如果输入形状不匹配,后续的编译和训练阶段会直接抛出维度错误。

经验提醒:在构建模型时,建议将输入形状作为变量定义,便于后续调整。例如:input_shape = (50, 3),这样在Edge Impulse中调整窗口大小时,只需修改一处代码。

3.2 模型编译:优化器、损失函数与评估指标的配置策略

编译阶段是模型从“定义”走向“可训练”的桥梁。在Keras中,model.compile()方法接收三个核心参数:优化器(optimizer)、损失函数(loss)和评估指标(metrics)。这三个参数的选择直接决定了模型训练的效率与最终性能。

优化器负责更新网络的权重。在Edge Impulse的再训练场景中,最常用的优化器是Adam,因为它自适应学习率,对超参数不敏感,收敛速度快。对于迁移学习,通常建议使用较小的学习率(如1e-4或1e-5),以避免破坏预训练模型已经学到的特征。例如:

model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

损失函数衡量模型预测值与真实标签之间的差异。对于多分类任务,使用categorical_crossentropy;对于二分类,使用binary_crossentropy;对于回归任务,使用mse(均方误差)。在Edge Impulse中,如果标签是整数索引形式,还需要注意使用sparse_categorical_crossentropy,以避免手动进行one-hot编码。

评估指标用于监控训练过程中的性能。除了accuracy之外,对于不平衡数据集,建议增加Precision、Recall或AUC等指标。在编译时,指标以列表形式传入:

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy', tf.keras.metrics.Precision(), tf.keras.metrics.Recall()])

编译阶段的一个常见误区是忽略优化器状态的保存。在后续的模型保存中,如果使用H5格式且未设置include_optimizer=True,那么加载模型后需要重新编译,否则无法继续训练。而SavedModel格式默认保存优化器状态,这使得再训练过程可以无缝衔接。

编译参数推荐配置(Edge Impulse场景)说明
优化器Adam(learning_rate=1e-4)小学习率保护预训练特征
损失函数sparse_categorical_crossentropy避免手动one-hot编码
评估指标accuracy + Precision + Recall应对类别不平衡
学习率调度ReduceLROnPlateau验证集loss停滞时自动降低学习率

3.3 模型训练:fit流程与回调机制

训练阶段通过model.fit()方法执行。在Edge Impulse的再训练场景中,训练数据通常以NumPy数组或TensorFlow Dataset的形式传入。关键参数包括batch_size、epochs、validation_split或validation_data。

一个典型的训练流程如下:

history = model.fit(
    x_train, y_train,
    batch_size=32,
    epochs=50,
    validation_data=(x_val, y_val),
    callbacks=[
        tf.keras.callbacks.EarlyStopping(patience=5, restore_best_weights=True),
        tf.keras.callbacks.ReduceLROnPlateau(factor=0.5, patience=3)
    ]
)

回调机制是训练过程中的重要工具。EarlyStopping可以在验证集性能不再提升时提前终止训练,避免过拟合;ReduceLROnPlateau在验证损失停滞时自动降低学习率,帮助模型跳出局部最优;ModelCheckpoint可以在训练过程中定期保存最佳模型,防止意外中断导致训练成果丢失。

在Edge Impulse的再训练中,一个关键技巧是分层学习率。由于预训练模型的特征提取层已经收敛,我们通常希望以较小的学习率微调这些层,而以较大的学习率训练新添加的分类层。这可以通过为不同层设置不同的学习率来实现:

# 假设模型有base_model和自定义层两部分
for layer in model.layers[:80]:  # 冻结前80层
    layer.trainable = False
# 编译时使用单一学习率,但通过优化器参数控制
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-4)
model.compile(optimizer=optimizer, loss='sparse_categorical_crossentropy', metrics=['accuracy'])

训练过程中的常见误区是忽略数据归一化。Edge Impulse的预处理模块通常已经完成了归一化,但如果直接使用原始数据训练,会导致梯度爆炸或收敛缓慢。建议在训练前检查输入数据的均值和方差,确保数据分布合理。

风险提示:在训练过程中,如果验证集准确率远低于训练集准确率,说明模型过拟合。此时应增加Dropout层、增大数据增强强度或减少训练轮数。Edge Impulse的再训练场景中,由于数据量通常较小,过拟合风险更高,建议使用早停机制。

3.4 模型保存:H5与SavedModel格式的深度对比

模型保存是本章的核心重点。Keras提供了两种主要的保存格式:H5(.h5文件)和SavedModel(目录结构)。这两种格式在文件组织、内容完整性、部署兼容性等方面存在显著差异。

H5格式是Keras早期版本的标准保存格式,它将模型结构、权重、优化器状态和编译信息打包到一个HDF5文件中。H5格式的优点是简单便携,一个文件即可完整保存模型,适合快速备份和分享。然而,H5格式不包含TensorFlow Serving所需的推理签名(SignatureDef),因此无法直接用于生产环境的服务部署。此外,H5格式对自定义层和自定义损失函数的支持较弱,加载时可能需要额外指定自定义对象。

SavedModel格式是TensorFlow官方推荐的现代保存格式,它是一个包含多个文件的目录结构,通常包含saved_model.pb(模型图定义)、variables/(权重文件)和assets/(辅助资源)。SavedModel格式的优势在于:

  • 完整性:包含模型结构、权重、优化器状态、编译信息以及推理签名,可以无缝用于TensorFlow Serving、TensorFlow Lite和Edge Impulse等部署平台。
  • 兼容性:支持自定义层和自定义损失函数,加载时无需额外指定自定义对象。
  • 版本管理:目录结构天然支持版本管理,便于模型迭代。

在Edge Impulse的模型导入中,SavedModel是推荐格式。因为Edge Impulse需要解析模型的输入输出签名,以确定传感器数据的预处理方式和分类结果的输出格式。H5格式虽然也可以导入,但往往需要额外的配置步骤,且对复杂模型的支持不如SavedModel完善。

对比维度H5格式SavedModel格式
文件组织单文件(.h5)目录(含pb、variables、assets)
模型结构包含包含
权重包含包含
优化器状态可选(include_optimizer)默认包含
编译信息包含包含
推理签名不包含包含
自定义层支持需指定custom_objects原生支持
部署兼容性适合实验、备份适合生产、Edge Impulse、TF Serving
文件大小相对较小相对较大(含签名和资产)

保存模型的代码示例如下:

# 保存为H5格式
model.save('model.h5', include_optimizer=True)

# 保存为SavedModel格式
model.save('saved_model_dir', save_format='tf')

在Edge Impulse中,导入SavedModel格式时,平台会自动识别模型的输入张量形状和输出张量的类别数。如果模型输出的是softmax概率分布,Edge Impulse会将其转换为类别标签;如果输出的是logits,则需要额外配置softmax层。

3.5 模型加载与再训练:格式差异对迁移学习的影响

模型加载是再训练的前提。Keras提供了tf.keras.models.load_model()方法,可以同时加载H5和SavedModel格式。但加载后的行为存在差异,直接影响再训练的流程。

加载H5格式时,如果保存时包含了优化器状态,加载后的模型可以直接继续训练;如果未包含优化器状态,则需要重新编译。此外,如果模型包含自定义层,加载时必须传入custom_objects参数,否则会抛出ValueError。例如:

from tensorflow.keras.models import load_model
model = load_model('model.h5', custom_objects={'MyLayer': MyLayer})

加载SavedModel格式时,模型的所有信息(包括优化器状态和编译信息)都会被完整恢复,无需重新编译。更重要的是,SavedModel格式保留了模型的推理签名,这使得Edge Impulse可以自动识别模型的输入输出接口,无需手动配置。加载代码非常简单:

model = load_model('saved_model_dir')

在再训练场景中,一个典型的操作是:加载预训练模型,冻结部分层,然后使用新数据继续训练。由于SavedModel格式保留了优化器状态,我们可以直接调用model.fit()继续训练,而无需重新编译。这大大简化了迁移学习的流程。

案例拆解:假设我们有一个在ImageNet上预训练的MobileNetV2模型,需要将其迁移到Edge Impulse的振动信号分类任务中。我们首先将模型保存为SavedModel格式,然后在Edge Impulse中导入。Edge Impulse自动识别输入形状为(96, 96, 3),输出为1000类。我们随后在Edge Impulse的迁移学习模块中,将模型的最后一层替换为5类输出,并冻结前80层。由于SavedModel保留了优化器状态,我们只需设置较小的学习率(1e-4),即可开始再训练。整个过程中,我们无需手动处理自定义层或编译信息,SavedModel的完整性使得迁移学习变得高效且可靠。

3.6 常见误区与总结复盘

在Keras模型保存与加载的实践中,以下几个误区值得特别注意:

  • 误区一:混淆H5与SavedModel的适用场景。H5适合快速原型验证和模型备份,而SavedModel适合生产部署和Edge Impulse导入。在Edge Impulse项目中,如果使用H5格式,可能会遇到输入输出签名缺失的问题,导致导入失败或推理结果异常。
  • 误区二:忽略优化器状态的保存。在保存H5格式时,如果未设置include_optimizer=True,加载后模型无法直接继续训练,需要重新编译。这会导致再训练时学习率、动量等超参数丢失,影响训练效果。
  • 误区三:自定义层加载失败。如果模型包含自定义层,保存为H5格式后,加载时必须传入custom_objects参数。而SavedModel格式则无此问题,因此对于包含自定义层的模型,推荐使用SavedModel格式。
  • 误区四:忽略输入形状的匹配。在Edge Impulse中导入模型时,输入形状必须与预处理后的数据维度一致。如果模型输入是(96, 96, 3),而Edge Impulse的窗口大小设置为50个时间步,则会导致维度不匹配错误。

总结复盘:本章系统回顾了Keras模型构建、编译、训练、保存与加载的完整链路。在Edge Impulse的Keras模型导入与再训练实战中,我们应优先选择SavedModel格式,因为它提供了完整的模型信息、推理签名和优化器状态,能够无缝对接Edge Impulse的迁移学习流程。同时,掌握模型构建的Functional范式、编译参数的合理配置以及训练回调的灵活运用,是确保再训练成功的关键。在实际项目中,建议将模型保存为SavedModel格式,并在Edge Impulse中验证输入输出签名,以确保部署的可靠性。