Skip to content

Commit 9f1a26b

Browse files
authored
Merge pull request #16 from zhaiwenxi/dpa-adapt-resolve-pretrained-all-strategies
Dpa adapt resolve pretrained all strategies
2 parents 9ecabda + c09bfd4 commit 9f1a26b

9 files changed

Lines changed: 41 additions & 40 deletions

File tree

doc/dpa_adapt/README.md

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,6 @@ Installs `scikit-learn`, `dpdata`, `ase`, `rdkit`, and `e3nn` alongside DeePMD-k
1212

1313
## Quickstart
1414

15-
Five lines to fine-tune and predict on CPU:
16-
17-
```python
18-
from dpa_adapt import DPAFineTuner
19-
20-
model = DPAFineTuner(pretrained="DPA-3.1-3M", strategy="frozen_sklearn", predictor="rf")
21-
model.fit(train_data="data/train", target_key="bandgap")
22-
preds = model.predict("data/test").predictions
23-
model.freeze("model.pth")
24-
```
25-
2615
For a complete runnable example (QM9 HOMO–LUMO gap, ~5 min on CPU), see [`../../examples/dpa_adapt/`](../../examples/dpa_adapt/).
2716

2817
## Fine-tuning strategies
@@ -32,7 +21,7 @@ The strategy is the core choice. All four share the same pre-trained DPA backbon
3221
| Strategy | Core Mechanism | Target Data Size | Hardware | Primary Use Case |
3322
| :--------------- | :---------------------------------------------- | :--------------- | :----------- | :---------------------------------------- |
3423
| `frozen_sklearn` | Frozen backbone + scikit-learn regressor | Small (\<1k) | CPU only | Ultra-fast benchmarking & prototyping |
35-
| `linear_probe` | Frozen backbone + gradient-descent linear head | Medium (1k–10k) | CPU / GPU | Balanced efficiency for linear properties |
24+
| `frozen_head` | Frozen backbone + DeepMD property fitting head | Medium (1k–10k) | CPU / GPU | Train only the property head while keeping the pretrained DPA backbone frozen |
3625
| `finetune` | End-to-end full parameter fine-tuning | Large (>10k) | GPU required | Maximum accuracy on large datasets |
3726
| `mft` | Multi-task co-training (property + force field) | Small / low-data | GPU required | Mitigating representation collapse |
3827

@@ -46,9 +35,9 @@ model = DPAFineTuner(
4635
)
4736
model.fit(train_data="/data/train", target_key="homo")
4837

49-
# linear_probe / finetune — same interface, different depth
38+
# frozen_head / finetune — same interface, different depth
5039
model = DPAFineTuner(
51-
pretrained="DPA-3.1-3M", strategy="linear_probe", property_name="homo"
40+
pretrained="DPA-3.1-3M", strategy="frozen_head", property_name="homo"
5241
)
5342
model.fit(train_data="/data/train", valid_data="/data/valid", target_key="homo")
5443

@@ -111,7 +100,7 @@ model.predict(test_data, conditions={"temperature": T_test})
111100
# ConditionManager standardizes and concatenates values to the descriptor
112101
```
113102

114-
**linear_probe / finetune / mft** — place `fparam.npy` of shape `(nframes, fparam_dim)` in each `set.*/` directory alongside `coord.npy`, then declare the dimension at construction:
103+
**frozen_head / finetune / mft** — place `fparam.npy` of shape `(nframes, fparam_dim)` in each `set.*/` directory alongside `coord.npy`, then declare the dimension at construction:
115104

116105
```python
117106
model = DPAFineTuner(strategy="finetune", fparam_dim=2)
@@ -165,7 +154,7 @@ result = cross_validate(model, systems, label_key="energy", cv=5, group_by="form
165154

166155
```python
167156
from dpa_adapt import (
168-
DPAFineTuner, # fine-tune (strategies: frozen_sklearn, linear_probe, finetune, mft)
157+
DPAFineTuner, # fine-tune (strategies: frozen_sklearn, frozen_head, finetune, mft)
169158
DPAPredictor, # inference from frozen bundles
170159
extract_descriptors, # standalone descriptor extraction
171160
cross_validate, # leak-proof cross-validation
@@ -196,7 +185,7 @@ X = extract_descriptors(
196185

197186
| Command | Description |
198187
|---------|-------------|
199-
| `dpa-adapt fit` / `dpaad fit` | Fine-tune (`--strategy frozen_sklearn\|linear_probe\|finetune\|mft`) |
188+
| `dpa-adapt fit` / `dpaad fit` | Fine-tune (`--strategy frozen_sklearn\|frozen_head\|finetune\|mft`) |
200189
| `dpa-adapt predict` / `dpaad predict` | Predict with a frozen `.pth` bundle |
201190
| `dpa-adapt evaluate` / `dpaad evaluate` | Evaluate against stored labels |
202191
| `dpa-adapt extract-descriptors` / `dpaad extract-descriptors` | Extract pooled DPA descriptors to `.npy` |

dpa_adapt/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ def get_parser() -> argparse.ArgumentParser:
465465
parser_fit.add_argument(
466466
"--strategy",
467467
default="frozen_sklearn",
468-
choices=["frozen_sklearn", "linear_probe", "finetune", "mft"],
468+
choices=["frozen_sklearn", "frozen_head", "finetune", "mft"],
469469
)
470470
parser_fit.add_argument(
471471
"--predictor", default="rf", choices=["rf", "linear", "ridge", "mlp"]
@@ -541,7 +541,7 @@ def get_parser() -> argparse.ArgumentParser:
541541
"--fparam-dim",
542542
type=int,
543543
default=0,
544-
help="(linear_probe/finetune/mft) Dimensionality of per-frame condition "
544+
help="(frozen_head/finetune/mft) Dimensionality of per-frame condition "
545545
"inputs (fparam). Requires set.*/fparam.npy in training data. Default: 0.",
546546
)
547547

dpa_adapt/cv.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ def cross_validate(
305305
extracted **once** and a cheap sklearn head is trained per fold — even
306306
``cv=5`` completes in seconds.
307307
308-
Training paradigms (``linear_probe`` / ``finetune`` / ``mft``)
308+
Training paradigms (``frozen_head`` / ``finetune`` / ``mft``)
309309
are expensive: each fold re-trains a full DeepMD model. To prevent
310310
accidental hour-long runs, *allow_expensive_cv* must be explicitly set
311311
to ``True`` for those strategies when *cv* is an integer >= 2. Otherwise
@@ -529,7 +529,7 @@ def cross_validate(
529529
# Phase 2 will wire this to DPATrainer / MFTFineTuner.
530530
raise NotImplementedError(
531531
"cross_validate for training paradigms "
532-
"(linear_probe / finetune / mft) is not yet "
532+
"(frozen_head / finetune / mft) is not yet "
533533
"implemented. Use frozen_sklearn for now."
534534
)
535535

@@ -578,7 +578,7 @@ def cross_validate(
578578

579579
def _estimate_runtime(strategy: str, n_splits: int) -> str:
580580
per_run = {
581-
"linear_probe": "~5-15 min/run",
581+
"frozen_head": "~5-15 min/run",
582582
"finetune": "~10-30 min/run",
583583
"mft": "~20-60 min/run",
584584
}.get(strategy, "unknown")

dpa_adapt/data/type_map.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ def read_checkpoint_type_map(
3434
"""
3535
from dpa_adapt._backend import (
3636
load_torch_file,
37+
resolve_pretrained_path,
3738
)
3839

40+
pretrained = resolve_pretrained_path(pretrained)
3941
sd = load_torch_file(pretrained)
4042
if "model" in sd:
4143
sd = sd["model"]

dpa_adapt/finetuner.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ class DPAFineTuner:
510510
descriptors once, pool, and fit a scikit-learn
511511
regressor (Ridge, KRR, or MLP). No GPU needed;
512512
fastest for small datasets.
513-
``linear_probe`` Freeze the backbone, train only a neural property
513+
``frozen_head`` Freeze the backbone, train only a neural property
514514
fitting net via ``dp --pt train``.
515515
``finetune`` Fine-tune the full network (descriptor + fitting
516516
net) end-to-end via ``dp --pt train``.
@@ -536,7 +536,7 @@ class DPAFineTuner:
536536
seed : int
537537
Random seed for the head or for full training.
538538
strategy : str
539-
``"frozen_sklearn"`` (default), ``"linear_probe"``, ``"finetune"``,
539+
``"frozen_sklearn"`` (default), ``"frozen_head"``, ``"finetune"``,
540540
or ``"mft"``.
541541
542542
property_name : str
@@ -560,7 +560,7 @@ class DPAFineTuner:
560560
loss_function : str
561561
``"mse"`` or ``"smooth_mae"`` (training paradigms).
562562
fparam_dim : int
563-
(linear_probe / finetune / mft only) Dimensionality of per-frame
563+
(frozen_head / finetune / mft only) Dimensionality of per-frame
564564
condition inputs (e.g. temperature, pressure). Requires
565565
set.*/fparam.npy of shape (n_frames, fparam_dim) in every
566566
training system. Default 0 (disabled).
@@ -590,7 +590,7 @@ class DPAFineTuner:
590590
_VALID_POOLING = {"mean", "sum", "mean+std", "mean+std+max+min"}
591591
_VALID_STRATEGIES = {
592592
"frozen_sklearn",
593-
"linear_probe",
593+
"frozen_head",
594594
"finetune",
595595
"mft",
596596
}
@@ -807,7 +807,7 @@ def _resolve_type_maps(self, train_data) -> list[str]:
807807
return tm
808808

809809
# -------------------------------------------------------------------
810-
# Training-paradigm fit (linear_probe / finetune)
810+
# Training-paradigm fit (frozen_head / finetune)
811811
# -------------------------------------------------------------------
812812

813813
def _fit_training(self, train_data, valid_data, type_map):
@@ -816,7 +816,7 @@ def _fit_training(self, train_data, valid_data, type_map):
816816
DPATrainer,
817817
)
818818

819-
freeze = self.strategy == "linear_probe"
819+
freeze = self.strategy == "frozen_head"
820820
trainer = DPATrainer(
821821
pretrained=self.pretrained,
822822
init_branch=self.init_branch,
@@ -860,7 +860,7 @@ def fit(
860860
"""Train the model.
861861
862862
*frozen_sklearn* (default): extract descriptors, fit sklearn head.
863-
*linear_probe* / *finetune*: run ``dp --pt train``.
863+
*frozen_head* / *finetune*: run ``dp --pt train``.
864864
*mft*: multi-task fine-tuning (property head + force-field head).
865865
866866
Parameters

dpa_adapt/mft.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
import subprocess
66
import sys
77

8+
from dpa_adapt._backend import (
9+
load_torch_file,
10+
resolve_pretrained_path,
11+
)
12+
813

914
class MFTFineTuner:
1015
"""
@@ -129,7 +134,7 @@ def __init__(
129134
f"fparam_dim must be a non-negative int; got {fparam_dim!r}."
130135
)
131136

132-
self.pretrained = pretrained
137+
self.pretrained = resolve_pretrained_path(pretrained)
133138
self.aux_branch = aux_branch
134139
self.aux_prob = aux_prob
135140
self.aux_type_map = aux_type_map
@@ -187,9 +192,7 @@ def _read_fitting_net_from_ckpt(pretrained, aux_branch):
187192
checkpoint. Raises ValueError listing available branches if
188193
``aux_branch`` isn't present.
189194
"""
190-
import torch
191-
192-
sd = torch.load(pretrained, map_location="cpu", weights_only=False)
195+
sd = load_torch_file(resolve_pretrained_path(pretrained))
193196
try:
194197
model_dict = sd["model"]["_extra_state"]["model_params"]["model_dict"]
195198
except (KeyError, TypeError) as e:

dpa_adapt/trainer.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
import re
3030
import subprocess
3131

32+
from dpa_adapt._backend import (
33+
resolve_pretrained_path,
34+
)
35+
3236
_LOG = logging.getLogger("dpa_adapt.trainer")
3337

3438

@@ -190,8 +194,10 @@ def __init__(
190194
"LP requires a pretrained checkpoint to freeze. "
191195
"Set freeze_backbone=False for Scratch, or pass a pretrained ckpt."
192196
)
193-
if pretrained is not None and not os.path.isfile(pretrained):
194-
raise ValueError(f"pretrained checkpoint not found: {pretrained!r}.")
197+
if pretrained is not None:
198+
pretrained = resolve_pretrained_path(pretrained)
199+
if not os.path.isfile(pretrained):
200+
raise ValueError(f"pretrained checkpoint not found: {pretrained!r}.")
195201
if not isinstance(property_name, str) or not property_name.isidentifier():
196202
raise ValueError(
197203
f"property_name must be a valid Python identifier "

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ dpa-adapt = [
9898
"scikit-learn",
9999
"dpdata",
100100
"torch",
101+
"ase",
101102
"rdkit",
102103
"e3nn",
103104
]

source/tests/dpa_adapt/test_finetuner_strategies.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-License-Identifier: LGPL-3.0-or-later
22
"""Tests for DPAFineTuner training-paradigm strategies
3-
(linear_probe / finetune).
3+
(frozen_head / finetune).
44
55
Mock ``dp --pt train`` via ``subprocess.run``; verify:
66
- Correct DPATrainer params per strategy
@@ -188,7 +188,7 @@ def test_resolve_type_maps_from_checkpoint(self, monkeypatch, tmp_path):
188188
systems = _make_system_dirs(tmp_path)
189189
m = DPAFineTuner(
190190
pretrained="/fake.pt",
191-
strategy="linear_probe",
191+
strategy="frozen_head",
192192
init_branch="SPICE2",
193193
)
194194
tm = m._resolve_type_maps(systems)
@@ -241,7 +241,7 @@ def _mock_torch(self, monkeypatch, tmp_path):
241241
@pytest.mark.parametrize(
242242
"strategy,expect_freeze,expect_tm_len",
243243
[
244-
("linear_probe", True, 8),
244+
("frozen_head", True, 8),
245245
("finetune", False, 8),
246246
],
247247
)
@@ -287,7 +287,7 @@ def test_config_type_map_nonempty(
287287
)
288288
assert tm != [], "type_map is empty — would cause CUDA gather out-of-bounds"
289289

290-
@pytest.mark.parametrize("strategy", ["linear_probe", "finetune"])
290+
@pytest.mark.parametrize("strategy", ["frozen_head", "finetune"])
291291
def test_strategy_to_trainer_params(self, tmp_path, strategy):
292292
"""Each strategy produces correct DPATrainer freeze_backbone / pretrained."""
293293
out_dir = tmp_path / "out"
@@ -317,7 +317,7 @@ def test_strategy_to_trainer_params(self, tmp_path, strategy):
317317
assert fn["intensive"] is True
318318

319319
# LP must freeze backbone
320-
if strategy == "linear_probe":
320+
if strategy == "frozen_head":
321321
assert cfg["model"]["descriptor"]["trainable"] is False
322322
else:
323323
assert cfg["model"]["descriptor"]["trainable"] is True

0 commit comments

Comments
 (0)