Skip to content

Commit ea80517

Browse files
wanghan-iapcmHan Wang
andauthored
fix(pd): preserve fparam/aparam inputs when freezing models (#5713)
## Problem The Paddle freeze entrypoint (`deepmd/pd/utils/serialization.py::deserialize_to_file`) converts `model.forward` and `model.forward_lower` to static graphs with `fparam` and `aparam` hardcoded to `None` in the `input_spec`. For models trained with required frame parameters (`get_dim_fparam() > 0`) or atomic parameters (`get_dim_aparam() > 0`), the exported static signature therefore bakes both values as `None`, so inference through the frozen Paddle model cannot supply the required `fparam`/`aparam`. ## Fix Build the `fparam`/`aparam` `InputSpec` from the model's `get_dim_fparam()`/`get_dim_aparam()` — a spec with shape `[-1, dim_fparam]` / `[-1, -1, dim_aparam]` when the dim is nonzero, and `None` otherwise — and use it in both the `forward` and `forward_lower` static signatures. Models that do not use these inputs keep the `None` placeholders as before. ## Test `source/tests/pd/model/test_serialization_fparam.py` covers the spec builder for the unused case (both `None`), the used case (correct shapes and names), and the fparam-only case. ## Note on verification Verified locally with `paddlepaddle==3.3.1`. The spec-builder test is version-independent; the end-to-end `paddle.jit.save` export (heavier and sensitive to the exact Paddle build) remains covered by CI's pinned nightly (`paddlepaddle==3.4.0.dev20260310`). Fix #5687 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved static export so optional input parameters are included only when a model actually uses them. * Reduced export issues by avoiding unnecessary placeholder inputs when these parameters are not needed. * **Tests** * Added coverage for export input signatures to confirm optional parameters are omitted, included, or partially included as expected based on model settings. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Han Wang <wang_han@iapcm.ac.cn>
1 parent 2b6cd22 commit ea80517

2 files changed

Lines changed: 74 additions & 4 deletions

File tree

deepmd/pd/utils/serialization.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,32 @@ def serialize_from_file(model_file: str) -> dict:
2323
raise NotImplementedError("Paddle do not support jit.export yet.")
2424

2525

26+
def _fparam_aparam_input_specs(model: "paddle.nn.Layer") -> tuple:
27+
"""Return the fparam/aparam static ``InputSpec``s for jit export.
28+
29+
A spec is returned only when the model actually uses the corresponding
30+
input (nonzero ``get_dim_fparam``/``get_dim_aparam``); otherwise ``None`` is
31+
returned so the frozen signature keeps that argument optional.
32+
"""
33+
from paddle.static import (
34+
InputSpec,
35+
)
36+
37+
dim_fparam = model.get_dim_fparam()
38+
dim_aparam = model.get_dim_aparam()
39+
fparam_spec = (
40+
InputSpec([-1, dim_fparam], dtype="float64", name="fparam")
41+
if dim_fparam > 0
42+
else None
43+
)
44+
aparam_spec = (
45+
InputSpec([-1, -1, dim_aparam], dtype="float64", name="aparam")
46+
if dim_aparam > 0
47+
else None
48+
)
49+
return fparam_spec, aparam_spec
50+
51+
2652
def deserialize_to_file(model_file: str, data: dict) -> None:
2753
"""Deserialize the dictionary to a model file.
2854
@@ -57,6 +83,9 @@ def deserialize_to_file(model_file: str, data: dict) -> None:
5783
InputSpec,
5884
)
5985

86+
# include fparam/aparam in the static signature when the model uses them
87+
fparam_spec, aparam_spec = _fparam_aparam_input_specs(model)
88+
6089
""" example output shape and dtype of forward
6190
atom_energy: fetch_name_0 (1, 6, 1) float64
6291
atom_virial: fetch_name_1 (1, 6, 1, 9) float64
@@ -72,8 +101,8 @@ def deserialize_to_file(model_file: str, data: dict) -> None:
72101
InputSpec([-1, -1, 3], dtype="float64", name="coord"),
73102
InputSpec([-1, -1], dtype="int64", name="atype"),
74103
InputSpec([-1, 9], dtype="float64", name="box"),
75-
None,
76-
None,
104+
fparam_spec,
105+
aparam_spec,
77106
True,
78107
],
79108
)
@@ -92,8 +121,8 @@ def deserialize_to_file(model_file: str, data: dict) -> None:
92121
InputSpec([-1, -1], dtype="int32", name="atype"),
93122
InputSpec([-1, -1, -1], dtype="int32", name="nlist"),
94123
None,
95-
None,
96-
None,
124+
fparam_spec,
125+
aparam_spec,
97126
True,
98127
None,
99128
],
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
"""Test that Paddle freeze static signatures include fparam/aparam when used."""
3+
4+
import unittest
5+
6+
from deepmd.pd.utils.serialization import (
7+
_fparam_aparam_input_specs,
8+
)
9+
10+
11+
class _StubModel:
12+
def __init__(self, dim_fparam: int, dim_aparam: int) -> None:
13+
self._dim_fparam = dim_fparam
14+
self._dim_aparam = dim_aparam
15+
16+
def get_dim_fparam(self) -> int:
17+
return self._dim_fparam
18+
19+
def get_dim_aparam(self) -> int:
20+
return self._dim_aparam
21+
22+
23+
class TestFparamAparamInputSpecs(unittest.TestCase):
24+
def test_absent_when_unused(self) -> None:
25+
fparam_spec, aparam_spec = _fparam_aparam_input_specs(_StubModel(0, 0))
26+
self.assertIsNone(fparam_spec)
27+
self.assertIsNone(aparam_spec)
28+
29+
def test_present_when_used(self) -> None:
30+
fparam_spec, aparam_spec = _fparam_aparam_input_specs(_StubModel(2, 3))
31+
self.assertIsNotNone(fparam_spec)
32+
self.assertIsNotNone(aparam_spec)
33+
self.assertEqual(fparam_spec.name, "fparam")
34+
self.assertEqual(aparam_spec.name, "aparam")
35+
self.assertEqual(list(fparam_spec.shape), [-1, 2])
36+
self.assertEqual(list(aparam_spec.shape), [-1, -1, 3])
37+
38+
def test_only_fparam(self) -> None:
39+
fparam_spec, aparam_spec = _fparam_aparam_input_specs(_StubModel(2, 0))
40+
self.assertIsNotNone(fparam_spec)
41+
self.assertIsNone(aparam_spec)

0 commit comments

Comments
 (0)