Skip to content

Commit 54196fb

Browse files
committed
docs(dpa_tools): fine-tuning-first READMEs and demo fixes
1 parent f8a0220 commit 54196fb

4 files changed

Lines changed: 144 additions & 172 deletions

File tree

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ For more information, check the [documentation](https://deepmd.readthedocs.io/).
2424
- **implements the Deep Potential series models**, which have been successfully applied to finite and extended systems, including organic molecules, metals, semiconductors, insulators, etc.
2525
- **implements MPI and GPU supports**, making it highly efficient for high-performance parallel and distributed computing.
2626
- **highly modularized**, easy to adapt to different descriptors for deep learning-based potential energy models.
27+
- **fine-tunes pre-trained DPA models through a scikit-learn-style Python API**, via [`dpa_tools`](deepmd/dpa_tools/README.md) — construct a `DPAFineTuner`, then `fit` and `predict` to adapt a large pre-trained model to your own property dataset, with no input files to write.
2728

2829
### License and credits
2930

@@ -97,12 +98,27 @@ Then, read on for a brief overview of the usage of DeePMD-kit. You may start wit
9798
dp
9899
```
99100

101+
## Fine-tune pre-trained DPA models with `dpa_tools`
102+
103+
`dpa_tools` is a scikit-learn-style **Python API for fine-tuning pre-trained DPA atomic models** on your own dataset: you construct a `DPAFineTuner`, call `fit(...)` then `predict(...)`, and pick a transfer-learning strategy — a frozen descriptor with a scikit-learn head, linear probing, full fine-tuning, or multi-task fine-tuning — without writing any DeePMD-kit JSON config or training pipeline. Use it to adapt a large pre-trained model to a downstream materials or molecular property (energy, band gap, HOMO–LUMO gap, …) from a modest labeled dataset. It ships with DeePMD-kit (`pip install deepmd-kit[dpa-tools]`); the full guide lives in [`deepmd/dpa_tools/README.md`](deepmd/dpa_tools/README.md).
104+
105+
```python
106+
from deepmd.dpa_tools import DPAFineTuner
107+
108+
model = DPAFineTuner(pretrained="DPA-3.1-3M", strategy="frozen_sklearn", predictor="rf")
109+
model.fit(train_data="data/train", target_key="bandgap") # fine-tune on your labeled structures
110+
model.predict("data/new_structures") # predict for new structures
111+
```
112+
113+
The same workflow is also available from the command line as `dp dpa fit` / `dp dpa predict`.
114+
100115
## Code structure
101116

102117
The code is organized as follows:
103118

104119
- `examples`: examples.
105120
- `deepmd`: DeePMD-kit python modules.
121+
- `deepmd/dpa_tools`: scikit-learn-style Python API for fine-tuning pre-trained DPA models ([README](deepmd/dpa_tools/README.md)).
106122
- `source/lib`: source code of the core library.
107123
- `source/op`: Operator (OP) implementation.
108124
- `source/api_cc`: source code of DeePMD-kit C++ API.

deepmd/dpa_tools/README.md

Lines changed: 114 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,14 @@
11
# dpa_tools
22

3-
Property-prediction tools built on top of DPA-3 pretrained models. `dpa_tools`
4-
turns molecular or atomistic structure data into `deepmd/npy` datasets, extracts
5-
DPA descriptors, and trains lightweight or fine-tuned property predictors for
6-
small- to medium-sized datasets. It lives as a self-contained subpackage of
7-
`deepmd-kit` at `deepmd.dpa_tools`.
8-
9-
## Relationship with deepmd-kit
10-
11-
`dpa_tools` sits on top of deepmd-kit without modifying any existing module:
12-
13-
- **Model loading**: `_backend.py` is the single choke point that imports
14-
`deepmd.pt.model.model.get_model` and `deepmd.pt.train.wrapper.ModelWrapper`
15-
to load DPA-3 checkpoints and extract descriptors. No other file in
16-
`dpa_tools` touches `deepmd.pt.*` directly.
17-
- **Training**: shells out to `dp --pt train` / `dp --pt freeze` /
18-
`dp --pt test`, auto-generating `input.json` config files.
19-
- **Inference**: deepmd-kit's built-in `DeepProperty` handles neural-network
20-
models; dpa_tools adds a lightweight frozen-descriptor + sklearn-head path.
21-
- **SMILES pipeline**: `data/smiles.py` converts CSV with SMILES columns +
22-
property labels into `deepmd/npy` format via RDKit 3D conformer generation.
23-
- **CLI**: registered as `dp dpa` subcommand group via `deepmd/main.py`.
24-
Torch and all DPA dependencies are loaded lazily — only when a `dp dpa ...`
25-
command actually runs.
26-
- **Lazy import**: `import deepmd.dpa_tools` does **not** trigger a `torch`
27-
import. `dp dpa --help` is equally lightweight.
3+
`dpa_tools` is a scikit-learn-style **Python API for fine-tuning pre-trained DPA
4+
atomic models** (DPA-3 and friends) on your own dataset. You construct a
5+
`DPAFineTuner`, call `fit(...)` then `predict(...)`, and pick a transfer-learning
6+
strategy — no DeePMD-kit JSON configs or `dp train` pipelines to write. The usual
7+
goal is adapting a large pre-trained model to a downstream materials or molecular
8+
property (energy, band gap, HOMO–LUMO gap, …) from a modest labeled dataset.
9+
10+
It ships as a self-contained subpackage of `deepmd-kit` at `deepmd.dpa_tools`,
11+
and the same workflow is also exposed on the command line as `dp dpa`.
2812

2913
## Installation
3014

@@ -33,53 +17,56 @@ pip install deepmd-kit[dpa-tools]
3317
```
3418

3519
The `dpa-tools` extra installs the Python dependencies used by this package,
36-
including `scikit-learn`, `dpdata`, `torch`, `rdkit`, and `e3nn`. For
37-
CUDA/GPU-specific PyTorch builds, install the desired PyTorch variant first or
38-
follow the PyTorch installation instructions for your platform.
20+
including `scikit-learn`, `dpdata`, `torch`, `rdkit`, and `e3nn`. For a
21+
CUDA/GPU PyTorch build, install the desired PyTorch variant first, then install
22+
this extra.
3923

40-
## Python API
24+
## Quickstart
25+
26+
Fine-tune a frozen-descriptor + scikit-learn head and predict — under 10 lines:
4127

4228
```python
43-
from deepmd.dpa_tools import (
44-
DPAFineTuner, # train (strategies: frozen_sklearn, linear_probe, finetune, mft)
45-
DPAPredictor, # read-only inference from frozen bundles
46-
extract_descriptors, # standalone descriptor extraction
47-
cross_validate, # leak-proof cross-validation
48-
train_test_split, # formula-grouped data splitting
49-
# data tools
50-
auto_convert, # sniff input → route to SMILES or dpdata pipeline
51-
smiles_to_npy, # CSV+SMILES → deepmd/npy (train/valid split)
52-
convert, # structure file → deepmd/npy (via dpdata)
53-
batch_convert, # glob-based batch conversion
54-
check_data, # data sanity checks
55-
attach_labels, # inject external label arrays
56-
load_dataset, # label-filtered data loading
57-
)
29+
from deepmd.dpa_tools import DPAFineTuner
30+
31+
# `pretrained` accepts a built-in model name (auto-downloaded) or a local .pt path
32+
model = DPAFineTuner(pretrained="DPA-3.1-3M", strategy="frozen_sklearn", predictor="rf")
33+
model.fit(train_data="data/train", target_key="bandgap") # fine-tune on labeled structures
34+
35+
preds = model.predict("data/test").predictions # predict on new structures
36+
model.freeze("model.dp-sklearn.pth") # save a reusable bundle
5837
```
5938

60-
### DPAFineTuner
39+
Your data must be in `deepmd/npy` format (see [Data preparation](#data-preparation)
40+
to convert structure files, VASP output, or SMILES CSVs). For a complete,
41+
runnable example that fits a QM9 HOMO–LUMO-gap model on CPU in **under 5
42+
minutes**, see [`demo/`](demo/) — it ships with 50 pre-processed molecules so you
43+
only need a pre-trained checkpoint.
6144

62-
Training strategies:
45+
## Fine-tuning strategies
6346

64-
| Strategy | Description | Best for |
65-
|----------|------------|----------|
66-
| `frozen_sklearn` | Freeze descriptor, extract once, fit sklearn head (RF/Ridge/MLP) | Small data (<1k samples), CPU inference |
67-
| `linear_probe` | Freeze backbone, train property fitting net only | Medium data, GPU |
68-
| `finetune` | Full-network fine-tuning | Larger data, GPU |
69-
| `mft` | Multi-task: property head + force-field head | Prevents representation collapse |
47+
The strategy is the main choice you make. All four adapt the same pre-trained
48+
DPA backbone; they differ in how much of it they train:
49+
50+
| Strategy | What it does | Best for |
51+
|----------|--------------|----------|
52+
| `frozen_sklearn` (default) | Freeze the backbone, extract descriptors once, fit a scikit-learn head (RF / Ridge / MLP) | Small data (<1k samples), CPU-only, fastest iteration |
53+
| `linear_probe` | Freeze the backbone, train only a property fitting net | Medium data, GPU available |
54+
| `finetune` | Fine-tune the full network | Larger data, GPU available |
55+
| `mft` | Multi-task: property head + an auxiliary force-field head trained jointly | Prevents representation collapse on small property datasets |
7056

7157
```python
58+
# frozen_sklearn (CPU, no dp train): extract once, fit a scikit-learn head
7259
model = DPAFineTuner(
73-
pretrained="DPA-3.1-3M", # built-in name → auto-downloaded; or use a local path
60+
pretrained="DPA-3.1-3M", # built-in name → auto-downloaded; or a local path
7461
strategy="frozen_sklearn",
75-
predictor="rf",
76-
pooling="mean",
62+
predictor="rf", # "rf" | "linear"/"ridge" | "mlp"
63+
pooling="mean", # "mean" | "sum" | "mean+std" | "mean+std+max+min"
7764
)
7865
model.fit(train_data="/data/train", target_key="homo")
7966
model.predict("/data/test")
8067
model.freeze("model.dp-sklearn.pth")
8168

82-
# MFT: multi-task fine-tuning (property head + force-field head)
69+
# mft: multi-task fine-tuning (downstream property head + auxiliary force-field head)
8370
model = DPAFineTuner(
8471
pretrained="/path/to/DPA-3.1-3M.pt",
8572
strategy="mft",
@@ -89,8 +76,30 @@ model = DPAFineTuner(
8976
model.fit(train_data="/data/qm9", aux_data="/data/spice2")
9077
```
9178

79+
## Python API
80+
81+
```python
82+
from deepmd.dpa_tools import (
83+
DPAFineTuner, # fine-tune (strategies: frozen_sklearn, linear_probe, finetune, mft)
84+
DPAPredictor, # read-only inference from frozen bundles
85+
extract_descriptors, # standalone descriptor extraction
86+
cross_validate, # leak-proof cross-validation
87+
train_test_split, # formula-grouped data splitting
88+
# data tools
89+
auto_convert, # sniff input → route to SMILES or dpdata pipeline
90+
smiles_to_npy, # CSV+SMILES → deepmd/npy (train/valid split)
91+
convert, # structure file → deepmd/npy (via dpdata)
92+
batch_convert, # glob-based batch conversion
93+
check_data, # data sanity checks
94+
attach_labels, # inject external label arrays
95+
load_dataset, # label-filtered data loading
96+
)
97+
```
98+
9299
### DPAPredictor
93100

101+
Load a frozen bundle for inference, with no training dependencies:
102+
94103
```python
95104
pred = DPAPredictor("model.dp-sklearn.pth")
96105
result = pred.predict("/data/test") # DotDict with .predictions
@@ -103,6 +112,8 @@ result = pred.predict("/data/test", return_uncertainty=True)
103112

104113
### Descriptor extraction
105114

115+
Get pooled DPA descriptors as a NumPy array (e.g. to feed your own model):
116+
106117
```python
107118
X = extract_descriptors(
108119
"/data/systems",
@@ -112,39 +123,33 @@ X = extract_descriptors(
112123
# → np.ndarray (n_frames, feat_dim * 2)
113124
```
114125

115-
### SMILES → npy conversion
126+
### Data preparation
116127

117-
One command auto-detects the input format — CSV with SMILES columns routes
118-
through RDKit, everything else goes through dpdata:
128+
One command auto-detects the input format — CSV with a SMILES column routes
129+
through RDKit (3D conformer generation), everything else goes through dpdata:
119130

120131
```python
121132
from deepmd.dpa_tools import auto_convert
122133

123-
# CSV with SMILES → auto-detected, RDKit generates 3D coords
124-
result = auto_convert("data.csv", "./npy", property_name="homo", property_col="HOMO")
125-
# prints: RDKit converted samples: ... / RDKit failed rows : ...
126-
# → {"method": "smiles", "train_systems": [...], "valid_systems": [...],
127-
# "samples_used": ..., "failed_rows": [...], "skipped_zero": ...,
128-
# "skipped_overlap": ...}
134+
# CSV with SMILES → RDKit generates 3D coords, writes train/valid deepmd/npy
135+
auto_convert("data.csv", "./npy", property_name="homo", property_col="HOMO")
129136

130-
# To force the SMILES pipeline, pass fmt="smiles"; the value is case-insensitive
131-
# ("SMILES" and "Smiles" also work).
132-
result = auto_convert("data.csv", "./npy", fmt="SMILES", property_name="homo", property_col="HOMO")
137+
# Structure file → auto-detected by dpdata (POSCAR, OUTCAR, extxyz, cif, …)
138+
auto_convert("POSCAR", "./npy")
133139

134-
# Structure file → auto-detected by dpdata
135-
result = auto_convert("POSCAR", "./npy")
136-
# → {"method": "dpdata", "output_dir": "..."}
140+
# Lower-level helpers
141+
convert("POSCAR", "out_dir", fmt="extxyz", type_map=["Cu", "O"])
142+
convert("calcs/**/OUTCAR", "npy_root", fmt="vasp/outcar") # glob → batch mode
143+
attach_labels(system, head="bandgap", values=np.array([1.0, 2.0, 3.0]))
144+
check_data("/data/system") # → list[Issue]
137145
```
138146

139-
Supports `.csv`, for SMILES inputs and any format dpdata
140-
recognises for structure files (POSCAR, OUTCAR, extxyz, cif…).
141-
142-
### Cross-validation
147+
### Cross-validation & splitting
143148

144-
Formula-grouped to prevent same-molecule leakage:
149+
Formula-grouped to prevent same-molecule leakage between folds:
145150

146151
```python
147-
from deepmd.dpa_tools import cross_validate, train_test_split
152+
from deepmd.dpa_tools import cross_validate, train_test_split, load_dataset
148153

149154
systems = load_dataset("/data/root", label_key="energy")
150155
train, valid, test = train_test_split(systems, group_by="formula", seed=42)
@@ -153,98 +158,50 @@ result = cross_validate(model, systems, label_key="energy", cv=5, group_by="form
153158
# → {"aggregate": {"mae_mean": ..., "rmse_std": ...}, ...}
154159
```
155160

156-
### Data tools
157-
158-
```python
159-
convert("POSCAR", "output_dir", fmt="extxyz", type_map=["Cu", "O"])
160-
convert("calcs/**/OUTCAR", "npy_root", fmt="vasp/outcar") # glob → batch mode
161-
check_data("/data/system") # → list[Issue]
162-
attach_labels(system, head="bandgap", values=np.array([1.0, 2.0, 3.0]))
163-
```
164-
165161
## CLI
166162

167-
All commands live under `dp dpa` with two-level nesting:
163+
The same workflow is available under `dp dpa` (two-level nesting for data tools):
168164

169-
```
170-
dp dpa
171-
extract-descriptors extract pooled DPA descriptors to .npy
172-
fit train a model (any strategy)
173-
--strategy {frozen_sklearn|linear_probe|finetune|mft}
174-
cv cross-validate (metric estimation, no model output)
175-
predict predict with a frozen .pth bundle
176-
evaluate evaluate a frozen .pth against stored labels
177-
data
178-
convert single file or glob → deepmd/npy (auto-sniffs SMILES / structure)
179-
validate sanity-check deepmd/npy directories
180-
attach-labels inject .npy labels into a system
181-
```
182-
183-
`dp dpa --help` does not load torch. The parser is pure argparse in
184-
`deepmd/main.py`; the handler import happens lazily in
185-
`deepmd/entrypoints/main.py` only when `dp dpa ...` is invoked.
165+
| Command | Description |
166+
|---------|-------------|
167+
| `dp dpa fit` | Fine-tune a model with any strategy (`--strategy frozen_sklearn\|linear_probe\|finetune\|mft`) |
168+
| `dp dpa predict` | Predict with a frozen `.pth` bundle |
169+
| `dp dpa evaluate` | Evaluate a frozen `.pth` against stored labels |
170+
| `dp dpa extract-descriptors` | Extract pooled DPA descriptors to `.npy` |
171+
| `dp dpa cv` | Cross-validate (metric estimation, no model output) |
172+
| `dp dpa data convert` | Convert a structure/CSV file or glob → `deepmd/npy` (auto-sniffs SMILES vs. structure) |
173+
| `dp dpa data validate` | Sanity-check `deepmd/npy` directories |
174+
| `dp dpa data attach-labels` | Inject `.npy` label arrays into a system |
186175

187176
```bash
188-
# CSV+SMILES — auto-detected, RDKit generates 3D coords
189-
dp dpa data convert --input data.csv --output ./npy --property-name homo
190-
191-
# Structure file — auto-detected by dpdata (POSCAR, extxyz, cif, …)
192-
dp dpa data convert --input POSCAR --output ./npy
193-
dp dpa data convert --input crystal.cif --output ./npy
177+
# Convert data (format auto-detected)
178+
dp dpa data convert --input data.csv --output ./npy --property-name homo # CSV+SMILES
179+
dp dpa data convert --input POSCAR --output ./npy # structure file
180+
dp dpa data convert --input "calcs/**/OUTCAR" --output ./npy_root # glob → batch
194181

195-
# Fine-tuning
196-
dp dpa fit --train-data /data/train --pretrained /path/to/DPA-3.1-3M.pt \
197-
--strategy frozen_sklearn --predictor rf --target-key homo
182+
# Fine-tune
183+
dp dpa fit --train-data ./npy/train --pretrained DPA-3.1-3M \
184+
--strategy frozen_sklearn --predictor rf --target-key homo --output model.pth
198185

199186
# Multi-task fine-tuning (MFT)
200187
dp dpa fit --train-data /data/qm9 --aux-data /data/spice2 \
201188
--pretrained /path/to/DPA-3.1-3M.pt --strategy mft --target-key homo
202189

203-
# Descriptor extraction
204-
dp dpa extract-descriptors --data /data/sys1 /data/sys2 \
205-
--pretrained /path/to/DPA-3.1-3M.pt --pooling mean+std --output features.npy
206-
207-
# Batch convert (glob → auto-detected)
208-
dp dpa data convert --input "calcs/**/OUTCAR" --output ./npy_root
190+
# Predict / evaluate with a frozen bundle
191+
dp dpa predict --model model.pth --data ./npy/test --output preds.npy
192+
dp dpa evaluate --model model.pth --data ./npy/test
209193
```
210194

211-
## Internal architecture
195+
`dp dpa --help` does not load torch — the parser is pure argparse in
196+
`deepmd/main.py`, and the handlers (and the DPA stack) are imported lazily only
197+
when a `dp dpa ...` command actually runs.
212198

213-
```
214-
deepmd/dpa_tools/
215-
├── __init__.py # public API, lazy imports (no torch at import time)
216-
├── _backend.py # single choke point for deepmd.pt.* calls
217-
├── cli.py # dp dpa subcommand handlers
218-
├── finetuner.py # DPAFineTuner (training + descriptor extraction)
219-
├── predictor.py # DPAPredictor (read-only inference + uncertainty)
220-
├── mft.py # MFTFineTuner (multi-task fine-tuning)
221-
├── trainer.py # DPATrainer (dp --pt train subprocess wrapper)
222-
├── cv.py # cross-validation + data splitting
223-
├── conditions.py # scalar condition manager (T, P)
224-
├── config/
225-
│ └── manager.py # MFT input.json generation
226-
├── data/
227-
│ ├── loader.py # polymorphic data loading
228-
│ ├── dataset.py # label-filtered loading
229-
│ ├── smiles.py # SMILES→3D coords + CSV→npy pipeline
230-
│ ├── convert.py # auto_convert (sniff + route) + convert + batch_convert
231-
│ ├── validate.py # data sanity checks
232-
│ ├── desc_cache.py # two-tier descriptor cache
233-
│ ├── type_map.py # automatic type-map resolution
234-
│ └── errors.py # DPADataError
235-
└── utils/
236-
├── dotdict.py # DotDict
237-
└── sklearn_heads.py # sklearn regressor factory
238-
```
199+
## How it works (for contributors)
239200

240-
Key design points:
241-
- `_backend.py` is the **only** file that imports `deepmd.pt.*` — every call
242-
into deepmd internals goes through it
243-
- `_DescriptorExtraction` encapsulates the fragile chain
244-
`wrapper.model["Default"]``set_eval_descriptor_hook``forward_common`
245-
`eval_descriptor()`
246-
- `auto_convert()` sniffs `.csv` / `.xlsx` for SMILES columns and routes
247-
accordingly; all other formats delegate to `dpdata` with `fmt="auto"`
248-
- `dp --pt train/test/freeze` always runs as a subprocess, keeping
249-
dpa_tools decoupled from deepmd-kit's training entry points
250-
- `dpdata.System` is the universal internal data format
201+
`dpa_tools` does not modify any existing deepmd-kit module. `_backend.py` is the
202+
single choke point that imports `deepmd.pt.*` to load DPA checkpoints and run the
203+
descriptor-extraction forward pass; training strategies that need `dp train` /
204+
`dp freeze` / `dp test` shell out to those subprocesses; and `dpdata.System` is
205+
the universal internal data format. Importing `deepmd.dpa_tools` (or running
206+
`dp dpa --help`) does not pull in torch. See the module docstrings in
207+
`finetuner.py`, `predictor.py`, `mft.py`, and `data/` for details.

0 commit comments

Comments
 (0)