Skip to content

feat(tf2): support DPA4 training#5749

Open
njzjz wants to merge 3 commits into
deepmodeling:masterfrom
njzjz:feat/dpa4-tf2-train
Open

feat(tf2): support DPA4 training#5749
njzjz wants to merge 3 commits into
deepmodeling:masterfrom
njzjz:feat/dpa4-tf2-train

Conversation

@njzjz

@njzjz njzjz commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Add TF2 wrappers/registrations for DPA4/SeZM descriptor and dpa4_ener fitting.
  • Route model.type: dpa4/sezm through the TF2 model factory.
  • Add TensorFlow graph-mode implementations for DPA4 scatter helpers used under training.enable_compile.
  • Normalize equivalent flattened force outputs before TF2 loss evaluation.
  • Refresh TF2 trackable list containers after conversion/deserialization so DPA4 checkpoints save correctly.
  • Enable DPA4 TF2 coverage in the existing source/tests/consistent/descriptor/test_dpa4.py and source/tests/consistent/fitting/test_dpa4_ener.py files.

Benchmark

1000-step DPA4 water benchmark, batch size 1, srun --gres=gpu:1 dp, GPU: NVIDIA GeForce RTX 5090.

Backend Command Total wall time Batch 1000 avg Notes
JAX dp --jax train input_jax.json --skip-neighbor-stat 95.860 s 0.0362 s/step Includes initial JAX compile; batch 1 avg 37.0563 s/step.
TF2 dp --tf2 train input_tf2.json --skip-neighbor-stat 164.388 s 0.0148 s/step training.enable_compile=true; includes TF/XLA/PTX compile; batch 1 avg 149.4788 s/step.

PT/pt-expt are not included in the final comparison per follow-up scope.

Validation

  • ruff format .
  • ruff check .
  • python -m py_compile deepmd/dpmodel/array_api.py deepmd/tf2/common.py deepmd/tf2/descriptor/dpa4.py deepmd/tf2/fitting/dpa4_ener.py deepmd/tf2/model/model.py deepmd/tf2/train/trainer.py source/tests/consistent/descriptor/test_dpa4.py source/tests/consistent/fitting/test_dpa4_ener.py
  • TF2 model construction and checkpoint write smoke.
  • dp --tf2 train /tmp/deepmd_dpa4_tiny_1vnghlkx/input_tf2.json --skip-neighbor-stat -o /tmp/deepmd_dpa4_tiny_1vnghlkx/out_tf2.json with training.enable_compile=true; log confirmed Compiled cluster using XLA! and saved model.ckpt.tf2/model.ckpt-1.
  • srun --gres=gpu:1 dp --tf2 train /tmp/deepmd_dpa4_bench_1000/input_tf2.json --skip-neighbor-stat -o /tmp/deepmd_dpa4_bench_1000/out_tf2.json.

Note: collecting the existing DPA4 consistent test module in this local environment segfaults while importing the existing PT DPA4/Triton path before backend-specific selection is applied, so I validated the new TF2 path with the checkpoint smoke, XLA train smoke, and benchmark above.

Summary by CodeRabbit

  • New Features

    • Added TensorFlow 2 (TF2) support for DPA4 and SeZM descriptors and energy fitting networks, including new public TF2 exports.
    • Enabled new SeZM/DPA4-style model configuration loading and registration.
  • Bug Fixes

    • Improved indexed update/max handling for the TF2 backend.
    • Automatically reshapes predicted force to match label tensor shapes when dimensions are compatible.
  • Tests

    • Expanded TF2 coverage in cross-backend consistency tests for DPA4 descriptors and SeZM energy fitting.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@dosubot dosubot Bot added the new feature label Jul 7, 2026
@github-actions github-actions Bot added the Python label Jul 7, 2026
from deepmd.tf2.descriptor.base_descriptor import (
BaseDescriptor,
)
from deepmd.tf2.utils import exclude_mask as _tf2_exclude_mask # noqa: F401
BaseDescriptor,
)
from deepmd.tf2.utils import exclude_mask as _tf2_exclude_mask # noqa: F401
from deepmd.tf2.utils import network as _tf2_network # noqa: F401
from deepmd.tf2.fitting.base_fitting import (
BaseFitting,
)
from deepmd.tf2.utils import network as _tf2_network # noqa: F401
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c251636b-eb31-478d-9c7b-68f3f3acdd3e

📥 Commits

Reviewing files that changed from the base of the PR and between 789aa79 and 8136fb1.

📒 Files selected for processing (2)
  • source/tests/consistent/descriptor/test_dpa4.py
  • source/tests/consistent/fitting/test_dpa4_ener.py

📝 Walkthrough

Walkthrough

This PR adds TF2 support for DPA4/SeZM descriptors and fitting networks, registers the new model names, extends TensorFlow-backed array handling, updates trainer force-shape postprocessing, and adds consistency-test coverage.

Changes

TF2 DPA4/SeZM Integration

Layer / File(s) Summary
ndtensorflow indexed array ops
deepmd/dpmodel/array_api.py
Adds ndtensorflow branches to indexed add and maximum helpers using TensorFlow scatter and segment reductions.
TF2Module array-variable storage
deepmd/tf2/common.py
Adds managed storage helpers for TF-backed array attributes, updates attribute access and assignment, and registers descriptor.dpa4 for TF2 modules.
Trackable list refresh
deepmd/tf2/descriptor/se_atten_v2.py
Implements TF2 trackable-list refresh during descriptor deserialization for SeAttenV2.
DPA4 descriptor TF2 adapters
deepmd/tf2/descriptor/dpa4.py, deepmd/tf2/descriptor/__init__.py
Adds TF2 wrapper classes for DPA4 descriptor submodules, registers DPModel mappings, promotes trainable attributes, and exports DescrptDPA4 from the descriptor package.
SeZM/DPA4 fitting TF2 adapters
deepmd/tf2/fitting/dpa4_ener.py, deepmd/tf2/fitting/__init__.py
Adds TF2 wrappers for SeZM fitting networks, registers dpa4_ener/sezm_ener aliases, and exports SeZMEnergyFittingNet.
Energy model registration and DPA4 config routing
deepmd/tf2/model/ener_model.py, deepmd/tf2/model/model.py
Registers EnergyModel under sezm_ener and dpa4_ener, adds DPA4/SeZM model normalization and validation, and routes matching model types to that path.
Trainer force-shape matching
deepmd/tf2/train/trainer.py
Adds label-shape matching for model force outputs and a helper that reads static tensor shapes.
TF2 consistency tests
source/tests/consistent/descriptor/test_dpa4.py, source/tests/consistent/fitting/test_dpa4_ener.py
Updates DPA4 descriptor and fitting consistency tests to detect TF2, wire TF2 classes, and evaluate TF2 outputs.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: new feature, Python

Suggested reviewers: wanghan-iapcm

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding TF2 support for DPA4 training.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
deepmd/tf2/common.py (1)

362-387: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

__getattribute__ allocates a new set on every attribute access.

This override intercepts every attribute read on the module, and for each read it calls _tf2_array_variable_attr_names() / _tf2_array_variable_list_attr_names(), both of which materialize a fresh set(...) from the class-level tuple. On hot descriptor/fitting paths this adds a per-access allocation. Consider a cheaper membership check against the raw tuple (or a cached frozenset) to avoid rebuilding the set on every access.

♻️ Cheaper membership check
         def __getattribute__(self, name: str) -> Any:
             if not name.startswith("_tf2_"):
-                array_attrs = object.__getattribute__(
-                    self,
-                    "_tf2_array_variable_attr_names",
-                )()
-                if name in array_attrs:
+                if name in object.__getattribute__(
+                    self, "_tf2_array_variable_attrs", ()
+                ):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deepmd/tf2/common.py` around lines 362 - 387, The __getattribute__ override
in common.py is doing extra work on every attribute read by calling
_tf2_array_variable_attr_names() and _tf2_array_variable_list_attr_names(),
which rebuild sets repeatedly. Update __getattribute__ to use a cheaper
membership path for the array/list attribute names, such as checking the
underlying tuple directly or reusing a cached frozenset, while keeping the
existing storage-name lookup and to_tensorflow_array conversion behavior
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@deepmd/dpmodel/array_api.py`:
- Around line 285-296: The TensorFlow branch in array_api.py is overwriting
prefilled -inf entries because tf.maximum(x_tensor, reduced) replaces
empty-segment sentinels with dtype minimum values. Update the
unsorted-segment-max handling in this branch so empty segments remain -inf,
matching the behavior expected by segment.py and the other backends; use the
existing x_tensor, indices_tensor, values_tensor, and reduced flow, but avoid
applying a blanket maximum that changes sentinel slots.

In `@deepmd/tf2/train/trainer.py`:
- Around line 1490-1493: The shape-iteration guard in the trainer’s
rank-checking helper only handles TypeError, but tf.TensorShape(None) can also
raise ValueError during tracing. Update the try/except around iter(shape) to
return None for both exception types in the same helper path so unknown-rank
shapes are handled safely.

---

Nitpick comments:
In `@deepmd/tf2/common.py`:
- Around line 362-387: The __getattribute__ override in common.py is doing extra
work on every attribute read by calling _tf2_array_variable_attr_names() and
_tf2_array_variable_list_attr_names(), which rebuild sets repeatedly. Update
__getattribute__ to use a cheaper membership path for the array/list attribute
names, such as checking the underlying tuple directly or reusing a cached
frozenset, while keeping the existing storage-name lookup and
to_tensorflow_array conversion behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 76f9add6-fdb8-4b7b-8908-ab6d1ed973d8

📥 Commits

Reviewing files that changed from the base of the PR and between 1cd6556 and 6194ff6.

📒 Files selected for processing (11)
  • deepmd/dpmodel/array_api.py
  • deepmd/tf2/common.py
  • deepmd/tf2/descriptor/__init__.py
  • deepmd/tf2/descriptor/dpa4.py
  • deepmd/tf2/fitting/__init__.py
  • deepmd/tf2/fitting/dpa4_ener.py
  • deepmd/tf2/model/ener_model.py
  • deepmd/tf2/model/model.py
  • deepmd/tf2/train/trainer.py
  • source/tests/consistent/descriptor/test_dpa4.py
  • source/tests/consistent/fitting/test_dpa4_ener.py

Comment thread deepmd/dpmodel/array_api.py Outdated
Comment thread deepmd/tf2/train/trainer.py
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.75758% with 88 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.48%. Comparing base (1cd6556) to head (8136fb1).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
deepmd/tf2/model/model.py 3.12% 31 Missing ⚠️
deepmd/tf2/train/trainer.py 33.33% 22 Missing ⚠️
deepmd/tf2/descriptor/dpa4.py 87.75% 18 Missing ⚠️
deepmd/tf2/common.py 83.49% 17 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5749      +/-   ##
==========================================
- Coverage   79.61%   79.48%   -0.13%     
==========================================
  Files        1014     1016       +2     
  Lines      115360   115802     +442     
  Branches     4274     4274              
==========================================
+ Hits        91843    92050     +207     
- Misses      21976    22205     +229     
- Partials     1541     1547       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
deepmd/tf2/descriptor/se_atten_v2.py (1)

17-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the extra refresh call here. TF2Module already refreshes _refresh_tf2_trackable_lists() for deepmd.tf2.descriptor.se_atten_v2, so this second call is redundant and can be dropped.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deepmd/tf2/descriptor/se_atten_v2.py` around lines 17 - 21, The
DescrptSeAttenV2.deserialize method is calling _refresh_tf2_trackable_lists()
twice because TF2Module already performs that refresh for
deepmd.tf2.descriptor.se_atten_v2. Remove the explicit refresh call from
DescrptSeAttenV2.deserialize and keep the deserialization flow limited to
delegating to DescrptSeAttenV2DP.deserialize.__func__(cls, data) and returning
the object.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@deepmd/tf2/descriptor/se_atten_v2.py`:
- Around line 17-21: The DescrptSeAttenV2.deserialize method is calling
_refresh_tf2_trackable_lists() twice because TF2Module already performs that
refresh for deepmd.tf2.descriptor.se_atten_v2. Remove the explicit refresh call
from DescrptSeAttenV2.deserialize and keep the deserialization flow limited to
delegating to DescrptSeAttenV2DP.deserialize.__func__(cls, data) and returning
the object.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e4a0ef32-c262-4207-8de7-5e2559fd9048

📥 Commits

Reviewing files that changed from the base of the PR and between 6194ff6 and 789aa79.

📒 Files selected for processing (6)
  • deepmd/dpmodel/array_api.py
  • deepmd/tf2/common.py
  • deepmd/tf2/descriptor/se_atten_v2.py
  • deepmd/tf2/train/trainer.py
  • source/tests/consistent/descriptor/test_dpa4.py
  • source/tests/consistent/fitting/test_dpa4_ener.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • deepmd/tf2/train/trainer.py
  • source/tests/consistent/descriptor/test_dpa4.py
  • deepmd/tf2/common.py

@njzjz

njzjz commented Jul 8, 2026

Copy link
Copy Markdown
Member Author

Pushed follow-ups 789aa79ca and 8136fb11 for the TF2 CI/review feedback.\n\nChanges kept:\n- Preserve empty-segment -inf values in the TensorFlow branch of xp_maximum_at.\n- Treat unknown-rank tf.TensorShape(None) as non-static by catching ValueError.\n- Avoid per-access set allocation in TF2Module.__getattribute__.\n- Route TF2 se_atten_v2 deserialization through the se_atten_v2 serializer path instead of DPA1.\n\nI then reverted the extra source/tests/consistent changes in 8136fb11, per request.\n\nLocal validation:\n- TF2 smoke for se_atten_v2 serialize/deserialize passed.\n- TensorFlow xp_maximum_at 1D and 2D empty-segment checks passed.\n- DPTrainer._static_shape(tf.TensorShape(None)) returns None.\n- ruff format . && ruff check . passed.\n\nCI is rerunning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants