Skip to content

Add support for multi-task DPA3 models - #1246

Open
GardevoirX wants to merge 3 commits into
metatensor:mainfrom
GardevoirX:dpa3-multi-task
Open

Add support for multi-task DPA3 models#1246
GardevoirX wants to merge 3 commits into
metatensor:mainfrom
GardevoirX:dpa3-multi-task

Conversation

@GardevoirX

@GardevoirX GardevoirX commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

This PR adds support for multi-task DPA3 models. Currently publicly available DPA3 foundation models are all a multi-task model, but only the single-task model is supported in metatrain.

The structure of the selected branch of the pretrained model is get from loaded["model"]["_extra_state"]["model_params"]["model_dict"][branch]. After the model is loaded, self.model.get_model_def_script() is saved to self._deepmd_cfg for reloading from it next time

Contributor (creator of pull-request) checklist

  • Tests updated (for new features and bugfixes)?
  • Documentation updated (for new features)?
  • Issue referenced (for PRs that solve an issue)?

Maintainer/Reviewer checklist

  • CHANGELOG updated with public API or any other important changes?
  • GPU tests passed (maintainer comment: "cscs-ci run")?

📚 Documentation preview 📚: https://metatrain--1246.org.readthedocs.build/en/1246/

@GardevoirX
GardevoirX requested a review from pfebrer August 10, 2026 10:30
@GardevoirX
GardevoirX requested a review from HaoZeke as a code owner August 10, 2026 10:30

@pfebrer pfebrer 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.

Thanks for taking care of this! Here are a few comments

Comment on lines 46 to 59
Using a pretrained model
------------------------

Set ``dpa3_model`` to a deepmd-kit model file to fine-tune from pretrained
weights instead of training from scratch:

.. code-block:: yaml

model:
dpa3_model: path/to/deepmd-model.pt

Energy biases and standard deviations are extracted from the loaded model and
handed to metatrain's composition model and scaler automatically.

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.

This part needs to be removed

Comment on lines +24 to +26
# Remove the "dpa3_model" key from the hypers dictionary if it exists
if "dpa3_model" not in checkpoint["model_data"]["model_hypers"]:
checkpoint["model_data"]["model_hypers"]["dpa3_model"] = None

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.

The comment is wrong, and I think this is not needed since dpa3_model will always be there.

Comment on lines +27 to +28
if "dpa3_model_branch" not in checkpoint["model_data"]["model_hypers"]:
checkpoint["model_data"]["model_hypers"]["dpa3_model_branch"] = None

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.

You can assume dpa3_model_branch is not there, the previous version didn't have it.

Comment on lines +193 to +194
"The loaded model must be a torch.nn.Module or a "
"collections.OrderedDict."

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.

Let's say something more user friendly like We could not load a DPA3 model from the provided path: "the path here". The file must contain a torch module or an ordered dict.

Comment on lines +662 to +666
if not torch.all(std == std[0]):
raise NotImplementedError(
"Loaded DPA3 models with non-uniform per-type 'out_std' are "
"not supported."
)

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.

What do the DPA3 checkpoints contain usually?

Comment on lines +86 to +111
def test_multi_task_branch_loading(tmp_path):
"""One branch of a multi-task checkpoint is loaded as a standalone model."""
path = tmp_path / "multi_task.pt"
base = _small_pretrained_checkpoint(path, branches=["Alpha", "Beta"])

pretrained = DPA3(_hypers_for(path, "Beta"), _make_dataset_info())

# The structure comes from the branch configuration, not from the hypers
base_state = base.model.state_dict()
loaded_state = pretrained.model.state_dict()
assert set(loaded_state) == set(base_state)
for key, value in base_state.items():
torch.testing.assert_close(loaded_state[key], value)


def test_single_task_loading(tmp_path):
"""A single-task checkpoint is loaded without asking for a branch."""
path = tmp_path / "single_task.pt"
base = _small_pretrained_checkpoint(path)

pretrained = DPA3(_hypers_for(path), _make_dataset_info())

assert pretrained.loaded_dpa3 is True
torch.testing.assert_close(
pretrained.model.state_dict(), base.model.state_dict(), rtol=0, atol=0
)

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.

Do you think we could use these two as a way of initializing a model for the normal testing in test_basic.py, like we do for MACE? (i.e. by parametrizing the model_hypers fixture). If it gets too expensive, maybe just the multi branch case, although since we are testing only energies in this case it will probably be cheap.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants