3、TensorFlow模型基础:SavedModel格式、HDF5格式、Keras模型API、模型保存与加载
好,咱们进入正题。这一章聊的是TensorFlow模型的基础操作,说白了就是怎么把你的心血——训练好的模型——给存下来,以及怎么再把它请回来。你想想看,模型训练动辄几小时甚至几天,要是不会保存,那可就白干了。
我个人习惯把模型保存看作是「给AI拍身份证」。你得拍清楚,还得保证以后能认出来。TensorFlow里主要有两套身份证系统:一套是Keras自带的HDF5格式,另一套是官方的SavedModel格式。这两者有什么区别?什么时候该用哪个?嗯,这正是咱们要聊的。
3.1 Keras模型API:你每天都在用的老朋友
先说说Keras模型API。如果你用过TensorFlow,大概率已经接触过它了。Keras提供了三种构建模型的方式,我按自己的使用频率排个序:
- Sequential顺序模型:最简单,一层层堆叠。适合大部分常规任务,比如图像分类、简单的回归。
- Functional函数式API:灵活得多,支持多输入多输出、共享层。我在做多模态项目时几乎必用它。
- Model子类化:最灵活,但也是最容易出坑的。你可以完全自定义前向传播逻辑。
举个例子,咱们用函数式API搭一个简单的模型:
import tensorflow as tf
# 定义输入
inputs = tf.keras.Input(shape=(784,))
# 定义隐藏层
x = tf.keras.layers.Dense(64, activation='relu')(inputs)
x = tf.keras.layers.Dense(64, activation='relu')(x)
# 定义输出
outputs = tf.keras.layers.Dense(10, activation='softmax')(x)
# 构建模型
model = tf.keras.Model(inputs=inputs, outputs=outputs)
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
你看,代码很直观。我个人建议初学者先从Sequential入手,等遇到复杂场景再切换到函数式API。至于子类化,嗯,除非你真的很清楚自己在做什么,否则慎用。我曾经在项目中用子类化写了一个自定义层,结果保存模型时折腾了一整天——后面会细说这个坑。
3.2 HDF5格式:Keras的经典保存方式
HDF5格式是Keras早期就支持的保存格式。它把模型的架构、权重、训练配置(比如优化器状态)全部打包到一个.h5文件里。保存和加载都非常简单:
# 保存模型
model.save('my_model.h5')
# 加载模型
loaded_model = tf.keras.models.load_model('my_model.h5')
就这么两行代码,搞定。但这里有个坑——HDF5格式不支持保存自定义层或自定义损失函数。如果你用了自定义的东西,加载时会报错。我记得有一次帮同事排查问题,他用了自定义的Focal Loss,保存成HDF5后怎么都加载不了。后来我让他改用SavedModel格式,问题就解决了。
custom_objects参数。比如:
model = tf.keras.models.load_model('my_model.h5',
custom_objects={'FocalLoss': FocalLoss})
如果你不想每次加载都传参,建议直接用SavedModel。
3.3 SavedModel格式:TensorFlow的官方标准
SavedModel是TensorFlow官方推荐的格式,也是模型部署时的首选。它不只是一个文件,而是一个目录,里面包含了:
saved_model.pb:模型的计算图定义variables/:模型权重文件assets/:额外的资源文件(比如词汇表)
保存和加载的代码也很简单:
# 保存为SavedModel格式
model.save('my_saved_model')
# 加载SavedModel
loaded_model = tf.keras.models.load_model('my_saved_model')
你可能会问:「那SavedModel比HDF5好在哪?」我总结一下:
| 特性 | HDF5格式 | SavedModel格式 |
|---|---|---|
| 文件结构 | 单个.h5文件 | 目录(含多个文件) |
| 自定义组件支持 | 需额外处理 | 原生支持 |
| 部署兼容性 | 一般 | 优秀(TF Serving、TFLite等) |
| 版本管理 | 不支持 | 支持(可保存多个版本) |
| 跨语言调用 | 仅Python | Python、C++、Java等 |
说白了,如果你只是做实验、写论文,HDF5完全够用。但如果你要部署到生产环境,或者要用TF Serving做推理服务,那SavedModel是唯一的选择。
3.4 模型保存与加载的实战技巧
光知道怎么保存还不够,咱们得聊聊实战中那些容易踩的坑。
3.4.1 只保存权重 vs 保存整个模型
有时候你不需要保存整个模型,只需要权重。比如你想用预训练模型做迁移学习,或者想在不同框架间转换。这时候可以用:
# 只保存权重
model.save_weights('model_weights.h5')
# 加载权重(需要先构建模型架构)
model.load_weights('model_weights.h5')
注意:加载权重前,你必须先定义好一模一样的模型架构。否则会报错。我曾经在项目中犯过这个错——改了模型结构忘了改加载代码,结果调试了半天才发现是权重形状不匹配。
3.4.2 训练过程中的自动保存
训练大模型时,我强烈建议使用回调函数自动保存。这样即使训练中途断了,也能从最近的检查点恢复:
checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
filepath='checkpoint_{epoch:02d}.h5',
save_weights_only=False,
save_best_only=True,
monitor='val_loss',
mode='min'
)
model.fit(x_train, y_train,
epochs=50,
validation_data=(x_val, y_val),
callbacks=[checkpoint_callback])
这里有个小技巧:save_best_only=True配合monitor='val_loss',只会保存验证集损失最小的那个模型。这样你就不用手动筛选了。
3.4.3 自定义层的保存陷阱
嗯,这里要重点说一下。如果你用了自定义层,比如:
class MyLayer(tf.keras.layers.Layer):
def __init__(self, units=32):
super(MyLayer, self).__init__()
self.units = units
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
保存模型时,TensorFlow需要知道这个自定义层怎么序列化。你需要在类里实现get_config()方法:
def get_config(self):
return {'units': self.units}
如果不实现,加载模型时会报错:「Unknown layer: MyLayer」。我曾经在项目里被这个坑过,后来养成了习惯——所有自定义层都加上get_config()。
3.5 总结一下
这一章咱们聊了TensorFlow模型保存与加载的核心内容。说白了就是三件事:
- Keras模型API:Sequential、Functional、子类化,按需选择
- HDF5格式:简单方便,适合实验和迁移学习
- SavedModel格式:官方标准,适合部署和生产环境
下一章咱们会深入模型转换的核心——如何把TensorFlow模型转成ONNX格式。到时候你会看到,模型保存的格式选择直接影响到转换的成败。嗯,做好准备,咱们要开始动真格的了。