根本原因不是模型参数多,而是保存方式将训练状态、图结构、检查点元信息等全量序列化;tf.keras.models.save_model()默认用savedmodel格式,保存完整计算图、@tf.function编译结果、输入签名、自定义层python对象及未调用分支逻辑,导致文件臃肿。

根本原因不是“模型参数多”,而是保存方式把训练状态、图结构、检查点元信息甚至临时变量全塞进去了——尤其是用 tf.train.Saver 或 model.save() 时默认保留了完整计算图和优化器快照。
为什么 tf.keras.models.save_model() 会生成超大文件?
这个函数默认走 SavedModel 格式,它不只是存权重,还会序列化:整个 ConcreteFunction 调用链、所有 @tf.function 编译后的图、输入签名(input_signature)、自定义层的 Python 层对象(含闭包)、甚至未被调用但已定义的分支逻辑。一旦模型里有 tf.distribute、tf.estimator 或 Keras 回调残留,它们的序列化开销就直接翻倍。
- 常见错误:在训练脚本中没清理回调(如
TensorBoard、ModelCheckpoint)就直接model.save() - 更隐蔽的问题:用了
tf.keras.layers.Lambda包裹 Python 函数,导致整个函数体被 pickle 进去 - TF 2.15+ 中,
saved_model.loader.load()隐式依赖google.protobuf和tensorflow.python.framework全量模块,打包时容易拉入冗余依赖
为什么 TF1.x 的 builder.add_meta_graph_and_variables() 保存后体积爆炸?
这是最典型的“只想要参数,却存了整套训练现场”的情况。该方法会把 MetaGraphDef(含全部 Variable、Operation、CollectionDef)和 checkpoint 一起写入,而 Collection 里可能混着 train_op、global_step、moving_mean 等训练专用节点,哪怕你只做推理,它们也占空间。
- 解决方案:改用
tf.saved_model.simple_save()(TF1.x)或显式导出 inference-only 图 - 关键操作:在保存前调用
tf.keras.backend.clear_session()清掉无用图节点 - 必须过滤 collection:用
tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)显式指定只存变量,别碰TRAINABLE_VARIABLES或UPDATE_OPS
如何只保存最小可用参数(.pb / .h5 / .tflite)?
目标很明确:扔掉图结构、不存优化器、不带 Python 逻辑、只留张量值。不同格式对应不同取舍:
-
.h5:用model.save_weights_only=True+save_format='h5',体积通常为原始 SavedModel 的 1/5~1/10 -
.pb(冻结图):必须先tf.graph_util.convert_variables_to_constants(),再tf.graph_util.remove_training_nodes(),否则 batch norm 的 moving stats 会以 Variable 形式残留 -
.tflite:用tf.lite.TFLiteConverter.from_saved_model()+converter.experimental_enable_resource_variables = False,禁用 resource 变量可避免引入额外 runtime 支持库
真正容易被忽略的是:即使你只导出权重,如果模型里存在未被修剪的嵌入矩阵(比如 tf.Variable 初始化时用了 tf.random_uniform([100000, 128])),那个 10 万 × 128 的全量矩阵就真的一字不落地写进去了——而实际业务可能只用到其中几百个 ID。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











