D4CMPP2 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- D4CMPP2/_Data/AGENTS.md +24 -0
- D4CMPP2/_Data/Aqsoldb.csv +9291 -0
- D4CMPP2/_Data/BradleyMP.csv +3042 -0
- D4CMPP2/_Data/Lipophilicity.csv +1131 -0
- D4CMPP2/_Data/README.md +8 -0
- D4CMPP2/_Data/__init__.py +26 -0
- D4CMPP2/_Data/optical.csv +20237 -0
- D4CMPP2/_Data/test.csv +190 -0
- D4CMPP2/__init__.py +16 -0
- D4CMPP2/__main__.py +5 -0
- D4CMPP2/_main.py +500 -0
- D4CMPP2/cli.py +7 -0
- D4CMPP2/exceptions.py +53 -0
- D4CMPP2/grid_search.py +259 -0
- D4CMPP2/network_refer.yaml +160 -0
- D4CMPP2/networks/AFP_model.py +72 -0
- D4CMPP2/networks/AFPwithSolv_model.py +72 -0
- D4CMPP2/networks/DMPNN_model.py +90 -0
- D4CMPP2/networks/DMPNNwithSolv_model.py +89 -0
- D4CMPP2/networks/GAT_model.py +48 -0
- D4CMPP2/networks/GATwithSolv_model.py +63 -0
- D4CMPP2/networks/GCN_model.py +113 -0
- D4CMPP2/networks/GCNwithSolv_model.py +103 -0
- D4CMPP2/networks/GC_model.py +122 -0
- D4CMPP2/networks/ISATPM_model.py +14 -0
- D4CMPP2/networks/ISATPN_model.py +199 -0
- D4CMPP2/networks/ISAT_model.py +90 -0
- D4CMPP2/networks/MPNN_model.py +56 -0
- D4CMPP2/networks/MPNNwithSolv_model.py +72 -0
- D4CMPP2/networks/__init__.py +25 -0
- D4CMPP2/networks/base.py +250 -0
- D4CMPP2/networks/registry.py +187 -0
- D4CMPP2/networks/src/AFP.py +118 -0
- D4CMPP2/networks/src/BiDropout.py +29 -0
- D4CMPP2/networks/src/DMPNN.py +35 -0
- D4CMPP2/networks/src/GAT.py +69 -0
- D4CMPP2/networks/src/GC.py +85 -0
- D4CMPP2/networks/src/GCN.py +71 -0
- D4CMPP2/networks/src/ISAT.py +153 -0
- D4CMPP2/networks/src/Linear.py +49 -0
- D4CMPP2/networks/src/MPNN.py +56 -0
- D4CMPP2/networks/src/SolventLayer.py +62 -0
- D4CMPP2/networks/src/__init__.py +0 -0
- D4CMPP2/networks/src/distGCN.py +21 -0
- D4CMPP2/networks/src/pyg_hetero.py +24 -0
- D4CMPP2/optimize.py +472 -0
- D4CMPP2/src/Analyzer/ISAAnalyzer.py +458 -0
- D4CMPP2/src/Analyzer/ISAPNAnalyzer.py +366 -0
- D4CMPP2/src/Analyzer/ISAwSAnalyzer.py +117 -0
- D4CMPP2/src/Analyzer/MolAnalyzer.py +319 -0
- D4CMPP2/src/Analyzer/__init__.py +54 -0
- D4CMPP2/src/Analyzer/core.py +480 -0
- D4CMPP2/src/Analyzer/factory.py +166 -0
- D4CMPP2/src/Analyzer/interpretation.py +232 -0
- D4CMPP2/src/Analyzer/results.py +101 -0
- D4CMPP2/src/DataManager/Dataset/GraphDataset.py +314 -0
- D4CMPP2/src/DataManager/Dataset/ISAGraphDataset.py +384 -0
- D4CMPP2/src/DataManager/Dataset/__init__.py +0 -0
- D4CMPP2/src/DataManager/GraphGenerator/ISAGraphGenerator.py +223 -0
- D4CMPP2/src/DataManager/GraphGenerator/MolGraphGenerator.py +73 -0
- D4CMPP2/src/DataManager/GraphGenerator/__init__.py +14 -0
- D4CMPP2/src/DataManager/ISADataManager.py +67 -0
- D4CMPP2/src/DataManager/MolDataManager.py +735 -0
- D4CMPP2/src/DataManager/__init__.py +14 -0
- D4CMPP2/src/DataManager/contracts.py +179 -0
- D4CMPP2/src/NetworkManager/ISANetworkManager.py +12 -0
- D4CMPP2/src/NetworkManager/NetworkManager.py +520 -0
- D4CMPP2/src/NetworkManager/__init__.py +14 -0
- D4CMPP2/src/PostProcessor.py +160 -0
- D4CMPP2/src/TrainManager/ISATrainManager.py +26 -0
- D4CMPP2/src/TrainManager/TrainManager.py +254 -0
- D4CMPP2/src/TrainManager/__init__.py +14 -0
- D4CMPP2/src/TrainManager/callbacks.py +119 -0
- D4CMPP2/src/__init__.py +0 -0
- D4CMPP2/src/utils/PATH.py +246 -0
- D4CMPP2/src/utils/__init__.py +0 -0
- D4CMPP2/src/utils/argparser.py +56 -0
- D4CMPP2/src/utils/checkpointing.py +90 -0
- D4CMPP2/src/utils/config_resolution.py +123 -0
- D4CMPP2/src/utils/config_validation.py +370 -0
- D4CMPP2/src/utils/csv_validation.py +105 -0
- D4CMPP2/src/utils/data_quality.py +181 -0
- D4CMPP2/src/utils/featureizer.py +202 -0
- D4CMPP2/src/utils/functional_group.csv +169 -0
- D4CMPP2/src/utils/graph_cache.py +213 -0
- D4CMPP2/src/utils/leaderboard.py +212 -0
- D4CMPP2/src/utils/metrics.py +31 -0
- D4CMPP2/src/utils/module_loader.py +147 -0
- D4CMPP2/src/utils/output.py +80 -0
- D4CMPP2/src/utils/reproducibility.py +70 -0
- D4CMPP2/src/utils/run_manifest.py +175 -0
- D4CMPP2/src/utils/scaler.py +40 -0
- D4CMPP2/src/utils/sculptor.py +713 -0
- D4CMPP2/src/utils/splitting.py +250 -0
- D4CMPP2/src/utils/supportfile_saver.py +94 -0
- D4CMPP2/src/utils/tools.py +156 -0
- D4CMPP2/src/utils/transfer_learning.py +111 -0
- d4cmpp2-0.4.0.dist-info/METADATA +420 -0
- d4cmpp2-0.4.0.dist-info/RECORD +103 -0
- d4cmpp2-0.4.0.dist-info/WHEEL +5 -0
- d4cmpp2-0.4.0.dist-info/entry_points.txt +2 -0
- d4cmpp2-0.4.0.dist-info/licenses/LICENSE +21 -0
- d4cmpp2-0.4.0.dist-info/top_level.txt +1 -0
D4CMPP2/_main.py
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import yaml
|
|
2
|
+
import os
|
|
3
|
+
import traceback
|
|
4
|
+
import importlib
|
|
5
|
+
import warnings
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from D4CMPP2.src.utils import argparser
|
|
8
|
+
from D4CMPP2.src.utils import PATH
|
|
9
|
+
from D4CMPP2.src.PostProcessor import PostProcessor
|
|
10
|
+
from D4CMPP2.src.utils import module_loader, supportfile_saver
|
|
11
|
+
from D4CMPP2.src.utils.output import get_output
|
|
12
|
+
from D4CMPP2.src.utils.run_manifest import RunManifest
|
|
13
|
+
from D4CMPP2.src.utils.config_resolution import (
|
|
14
|
+
ConfigResolution,
|
|
15
|
+
mark_derived,
|
|
16
|
+
merge_config_layers,
|
|
17
|
+
overlay_config_layer,
|
|
18
|
+
split_runtime_config,
|
|
19
|
+
)
|
|
20
|
+
from D4CMPP2.src.DataManager.contracts import validate_data_manager_contract
|
|
21
|
+
from D4CMPP2.src.utils.reproducibility import (
|
|
22
|
+
capture_backend_determinism,
|
|
23
|
+
configure_reproducibility,
|
|
24
|
+
restore_backend_determinism,
|
|
25
|
+
)
|
|
26
|
+
from D4CMPP2.src.utils.config_validation import (
|
|
27
|
+
validate_entry_args,
|
|
28
|
+
validate_network_entry,
|
|
29
|
+
validate_runtime_environment,
|
|
30
|
+
validate_sculptor_index_argument,
|
|
31
|
+
validate_training_config,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
config0 = {
|
|
35
|
+
"data": None,
|
|
36
|
+
"target": None,
|
|
37
|
+
"network": None,
|
|
38
|
+
|
|
39
|
+
"scaler": "standard",
|
|
40
|
+
"optimizer": "Adam",
|
|
41
|
+
|
|
42
|
+
"max_epoch": 2000,
|
|
43
|
+
"batch_size": 256,
|
|
44
|
+
"learning_rate": 0.001,
|
|
45
|
+
"weight_decay": 0.0005,
|
|
46
|
+
"lr_patience": 80,
|
|
47
|
+
"early_stopping_patience": 200,
|
|
48
|
+
'min_lr': 1e-5,
|
|
49
|
+
|
|
50
|
+
"device": "cuda:0",
|
|
51
|
+
"pin_memory": False,
|
|
52
|
+
"target_scaler_fit_scope": "train",
|
|
53
|
+
"legacy_silent_errors": False,
|
|
54
|
+
"random_seed": None,
|
|
55
|
+
"deterministic_algorithms": False,
|
|
56
|
+
"verbose": True,
|
|
57
|
+
|
|
58
|
+
'hidden_dim': None,
|
|
59
|
+
'conv_layers':None,
|
|
60
|
+
'linear_layers': None,
|
|
61
|
+
'dropout': None,
|
|
62
|
+
|
|
63
|
+
'solv_hidden_dim': None,
|
|
64
|
+
'solv_conv_layers': None,
|
|
65
|
+
'solv_linear_layers': None,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def train(**kwargs):
|
|
70
|
+
"""
|
|
71
|
+
Train the network with the given configuration.
|
|
72
|
+
|
|
73
|
+
Args for the training:
|
|
74
|
+
(Required args for the scratch training)
|
|
75
|
+
data : str
|
|
76
|
+
the name or path of the data file as a csv file. you can omit the ".csv" extension.
|
|
77
|
+
target : list[str]
|
|
78
|
+
the name of the target column.
|
|
79
|
+
network : str
|
|
80
|
+
the ID of the network to use. Refer to the networks.yaml file for the available networks ID.
|
|
81
|
+
molecule_columns: list[str]. Default= ['compound'] or ['compound', 'solvent']
|
|
82
|
+
the name of the columns to be used as the molecule columns. Please check the network sorce code for the required molecule columns for each network.
|
|
83
|
+
numeric_input_columns: list[str]. Default= []
|
|
84
|
+
the name of the columns to be used as numeric input features. Please check the network sorce code for the required numeric input columns for each network.
|
|
85
|
+
|
|
86
|
+
(Reqired args for continuing the training)
|
|
87
|
+
LOAD_PATH : str
|
|
88
|
+
the path of the directory that contains the model to continue the training.
|
|
89
|
+
|
|
90
|
+
(Required args for the transfer learning)
|
|
91
|
+
TRANSFER_PATH: str
|
|
92
|
+
the path of the directory that contains the model to transfer the learning.
|
|
93
|
+
lr_dict : dict (optional)
|
|
94
|
+
the dictionary for the learning rate of specific layers. e.g. {'GCNs': 0.0001}
|
|
95
|
+
you can find the layer names in "model_summary.txt" in the model directory.
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
(Optional args)
|
|
99
|
+
explicit_h_columns: list[str]. Default= []
|
|
100
|
+
the name of the columns to be used as explicit hydrogen features for the nodes.
|
|
101
|
+
scaler : str. Default= "standard",
|
|
102
|
+
['standard', 'minmax', 'normalizer', 'robust', 'identity']
|
|
103
|
+
optimizer : str. Default= "Adam",
|
|
104
|
+
['Adam', 'SGD', 'RMSprop', ... supported by torch.optim]
|
|
105
|
+
max_epoch : int. Defualt= 2000,
|
|
106
|
+
batch_size : int. Default= 256,
|
|
107
|
+
learning_rate: float. Defualt= 0.001,
|
|
108
|
+
weight_decay: float. Default= 0.0005,
|
|
109
|
+
lr_patience : int. Defualt= 80,
|
|
110
|
+
the number of epochs with no improvement after which learning rate will be reduced.
|
|
111
|
+
early_stopping_patience: 200,
|
|
112
|
+
the number of epochs with no improvement after which training will be stopped
|
|
113
|
+
min_lr : float. Defult= 1e-5,
|
|
114
|
+
the minimum learning rate
|
|
115
|
+
device : str. Defualt='cuda:0'
|
|
116
|
+
['cpu', 'cuda:0', 'cuda:1', ...]
|
|
117
|
+
pin_memory : bool. Default= False
|
|
118
|
+
If True, the data loader will copy Tensors into CUDA pinned memory before returning them.
|
|
119
|
+
split_random_seed: int. Default= 42,
|
|
120
|
+
save_prediction: bool. Default= True,
|
|
121
|
+
callbacks : sequence of callback objects. Default= None
|
|
122
|
+
Runtime-only observers for training events. Callback objects
|
|
123
|
+
are not written to config.yaml or checkpoints.
|
|
124
|
+
verbose : bool. Default=True
|
|
125
|
+
Show informational messages, result tables, and progress bars.
|
|
126
|
+
Warnings and errors remain visible when False.
|
|
127
|
+
|
|
128
|
+
DATA_PATH : str. Default= None
|
|
129
|
+
the path of directory that contains the data file. If None, it will walk the subpath.
|
|
130
|
+
NET_REFER : str. Default= "{src}/network_refer.yaml"
|
|
131
|
+
the path to the network reference file.
|
|
132
|
+
MODEL_DIR : str. Default= "./_Models"
|
|
133
|
+
the path to the directory to save the models.
|
|
134
|
+
MODEL_PATH : str. Default= None
|
|
135
|
+
the path to the directory to save the model. If None, it will be created in the MODEL_DIR.
|
|
136
|
+
GRAPH_DIR : str. Default= "./_Graphs"
|
|
137
|
+
the path to the directory to save the graphs.
|
|
138
|
+
FRAG_REF : str. Default= "{src}/utils/functional_group.csv"
|
|
139
|
+
the path to the reference file for the functional groups.
|
|
140
|
+
|
|
141
|
+
Args for the network:
|
|
142
|
+
(The following args will be varied according to the network chosen. Refer to the source code of the network for more details.
|
|
143
|
+
In general, below are the common args for the network)
|
|
144
|
+
hidden_dim : int. Default= 64
|
|
145
|
+
the dimension of the hidden layers in the graph convolutional layers
|
|
146
|
+
conv_layers : int. Default= 6
|
|
147
|
+
the number of graph convolutional layers
|
|
148
|
+
linear_layers : int. Default= 3
|
|
149
|
+
the number of linear layers after the graph convolutional layers
|
|
150
|
+
dropout : float. Default= 0.1
|
|
151
|
+
|
|
152
|
+
--------------------------------------------
|
|
153
|
+
Example:
|
|
154
|
+
|
|
155
|
+
train(data='data.csv', target=['target1','target2'], network='GCN',)
|
|
156
|
+
|
|
157
|
+
train(LOAD_PATH='GCN_model_test_Abs_20240101_000000')
|
|
158
|
+
|
|
159
|
+
train(TRANSFER_PATH='GCN_model_test_Abs_20240101_000000', data='data.csv', target=['target1','target2'], lr_dict={'GCNs': 0.0001})
|
|
160
|
+
|
|
161
|
+
train(data='data.csv', target=['target1','target2'], network='GCN', hidden_dim=32, conv_layers=4, linear_layers=2, dropout=0.1)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
"""
|
|
165
|
+
callbacks = kwargs.pop("callbacks", None)
|
|
166
|
+
if kwargs.pop('use_argparser', False):
|
|
167
|
+
parsed = {key: value for key, value in vars(argparser.parse_args()).items() if value is not None}
|
|
168
|
+
kwargs.update(parsed)
|
|
169
|
+
kwargs = check_args(**kwargs)
|
|
170
|
+
resolution = resolve_config(**kwargs)
|
|
171
|
+
config = resolution.to_dict()
|
|
172
|
+
if not config.get('loaded',False) and not config.get('full_resume', False):
|
|
173
|
+
supportfile_saver.save_additional_files(config)
|
|
174
|
+
return run(
|
|
175
|
+
config,
|
|
176
|
+
callbacks=callbacks,
|
|
177
|
+
config_provenance=dict(resolution.provenance),
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def check_args(**kwargs):
|
|
181
|
+
"check the arguments for the training"
|
|
182
|
+
validate_entry_args(kwargs)
|
|
183
|
+
if kwargs.get('LOAD_PATH',None) is not None or kwargs.get('RESUME_PATH',None) is not None:
|
|
184
|
+
return kwargs
|
|
185
|
+
kwargs['target_dim'] = len(kwargs['target'])
|
|
186
|
+
if kwargs['data'][-4:] == '.csv':
|
|
187
|
+
kwargs['data'] = kwargs['data'][:-4]
|
|
188
|
+
return kwargs
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def set_config(**kwargs):
|
|
192
|
+
"""Return the historical mutable dictionary config contract."""
|
|
193
|
+
|
|
194
|
+
return resolve_config(**kwargs).to_dict()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def resolve_config(**kwargs):
|
|
198
|
+
"""Resolve an immutable config snapshot while preserving legacy precedence."""
|
|
199
|
+
|
|
200
|
+
config, provenance = merge_config_layers((
|
|
201
|
+
("defaults", config0),
|
|
202
|
+
("api_or_cli", kwargs),
|
|
203
|
+
))
|
|
204
|
+
config = {k: v for k, v in config.items() if v is not None}
|
|
205
|
+
provenance = {key: provenance[key] for key in config}
|
|
206
|
+
|
|
207
|
+
netrefer_loaded = False
|
|
208
|
+
if config.get('RESUME_PATH', None) is not None:
|
|
209
|
+
resume_overrides = {key: value for key, value in kwargs.items() if value is not None}
|
|
210
|
+
requested = Path(config['RESUME_PATH'])
|
|
211
|
+
if requested.is_file() or requested.suffix == ".ckpt":
|
|
212
|
+
checkpoint = requested
|
|
213
|
+
model_path = checkpoint.parent.parent if checkpoint.parent.name == "checkpoints" else checkpoint.parent
|
|
214
|
+
if not model_path.is_dir():
|
|
215
|
+
raise FileNotFoundError(
|
|
216
|
+
f"Model folder {str(model_path)!r} for RESUME_PATH {str(checkpoint)!r} was not found."
|
|
217
|
+
)
|
|
218
|
+
config['MODEL_PATH'] = str(model_path)
|
|
219
|
+
config['RESUME_PATH'] = str(checkpoint)
|
|
220
|
+
else:
|
|
221
|
+
config['MODEL_PATH'] = PATH.find_model_path(config['RESUME_PATH'], config)
|
|
222
|
+
config['RESUME_PATH'] = config['MODEL_PATH']
|
|
223
|
+
resolved_model_path = config['MODEL_PATH']
|
|
224
|
+
resolved_resume_path = config['RESUME_PATH']
|
|
225
|
+
with open(os.path.join(resolved_model_path, 'config.yaml'), "r") as file:
|
|
226
|
+
saved_config = yaml.safe_load(file)
|
|
227
|
+
for policy_key, legacy_default in (
|
|
228
|
+
("random_seed", None),
|
|
229
|
+
("deterministic_algorithms", False),
|
|
230
|
+
):
|
|
231
|
+
if (
|
|
232
|
+
policy_key in resume_overrides
|
|
233
|
+
and resume_overrides[policy_key] != saved_config.get(policy_key, legacy_default)
|
|
234
|
+
):
|
|
235
|
+
raise ValueError(
|
|
236
|
+
f"RESUME_PATH cannot change {policy_key}: "
|
|
237
|
+
f"checkpoint run config={saved_config.get(policy_key, legacy_default)!r}, "
|
|
238
|
+
f"requested={resume_overrides[policy_key]!r}. "
|
|
239
|
+
"Full resume uses the saved RNG policy and checkpoint RNG state."
|
|
240
|
+
)
|
|
241
|
+
config, provenance = merge_config_layers((
|
|
242
|
+
("saved_resume", saved_config),
|
|
243
|
+
("resume_override", resume_overrides),
|
|
244
|
+
))
|
|
245
|
+
config['MODEL_PATH'] = resolved_model_path
|
|
246
|
+
provenance['MODEL_PATH'] = "derived_resume_path"
|
|
247
|
+
config['RESUME_PATH'] = resolved_resume_path
|
|
248
|
+
provenance['RESUME_PATH'] = "derived_resume_path"
|
|
249
|
+
config['full_resume'] = True
|
|
250
|
+
provenance['full_resume'] = "derived_mode"
|
|
251
|
+
netrefer_loaded = True
|
|
252
|
+
# overwrite the config with the loaded config if the LOAD_PATH is given
|
|
253
|
+
if config.get('LOAD_PATH',None) is not None:
|
|
254
|
+
config['LOAD_PATH'] = PATH.find_model_path(config['LOAD_PATH'] , config)
|
|
255
|
+
config['MODEL_PATH'] = config.pop('LOAD_PATH')
|
|
256
|
+
config['loaded'] = True
|
|
257
|
+
with open(config['MODEL_PATH']+'/config.yaml', "r") as file:
|
|
258
|
+
_config = yaml.safe_load(file)# _config = yaml.load(open(os.path.join(config['LOAD_PATH'],'config.yaml'), 'r'), Loader=yaml.FullLoader)
|
|
259
|
+
legacy_scaler_scope = (
|
|
260
|
+
'target_scaler_fit_scope' not in _config
|
|
261
|
+
and 'target_scaler_fit_scope' not in kwargs
|
|
262
|
+
)
|
|
263
|
+
config, provenance = merge_config_layers((
|
|
264
|
+
("saved_load", _config),
|
|
265
|
+
("legacy_load_defaults_or_override", config),
|
|
266
|
+
))
|
|
267
|
+
if legacy_scaler_scope:
|
|
268
|
+
config['target_scaler_fit_scope'] = 'all'
|
|
269
|
+
provenance['target_scaler_fit_scope'] = "legacy_compatibility"
|
|
270
|
+
warnings.warn(
|
|
271
|
+
"The loaded model config predates target_scaler_fit_scope; continuing with "
|
|
272
|
+
"target_scaler_fit_scope='all' to preserve historical full-data scaling. "
|
|
273
|
+
"Set target_scaler_fit_scope='train' explicitly to migrate to leakage-free scaling.",
|
|
274
|
+
UserWarning,
|
|
275
|
+
stacklevel=2,
|
|
276
|
+
)
|
|
277
|
+
netrefer_loaded = True
|
|
278
|
+
|
|
279
|
+
PATH.init_path(config)
|
|
280
|
+
|
|
281
|
+
if config.get('TRANSFER_PATH',None) is not None:
|
|
282
|
+
config['TRANSFER_PATH'] = PATH.find_model_path(config['TRANSFER_PATH'],config)
|
|
283
|
+
|
|
284
|
+
# if the network is not given, load the network from the transfer path
|
|
285
|
+
if config.get('network', None):
|
|
286
|
+
net_config = load_NET_REFER(config)
|
|
287
|
+
config, provenance = overlay_config_layer(
|
|
288
|
+
config,
|
|
289
|
+
provenance,
|
|
290
|
+
net_config,
|
|
291
|
+
source="registry",
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# load the config from the transfer path
|
|
295
|
+
with open(config['TRANSFER_PATH']+'/config.yaml', "r") as file:
|
|
296
|
+
_config = yaml.safe_load(file)
|
|
297
|
+
_config.pop('MODEL_PATH',None)
|
|
298
|
+
saved_values, saved_provenance = merge_config_layers((
|
|
299
|
+
("saved_transfer", _config),
|
|
300
|
+
))
|
|
301
|
+
config, provenance = overlay_config_layer(
|
|
302
|
+
saved_values,
|
|
303
|
+
saved_provenance,
|
|
304
|
+
config,
|
|
305
|
+
layer_provenance=provenance,
|
|
306
|
+
)
|
|
307
|
+
config['MODEL_PATH'] = PATH.get_model_path(config)
|
|
308
|
+
provenance['MODEL_PATH'] = "derived_transfer_path"
|
|
309
|
+
netrefer_loaded = True
|
|
310
|
+
|
|
311
|
+
if not netrefer_loaded:
|
|
312
|
+
net_config = load_NET_REFER(config)
|
|
313
|
+
config, provenance = overlay_config_layer(
|
|
314
|
+
config,
|
|
315
|
+
provenance,
|
|
316
|
+
net_config,
|
|
317
|
+
source="registry",
|
|
318
|
+
)
|
|
319
|
+
config['MODEL_PATH'] = PATH.get_model_path(config)
|
|
320
|
+
provenance['MODEL_PATH'] = "derived_model_path"
|
|
321
|
+
|
|
322
|
+
if config.get('sculptor_index',None) is not None and type(config['sculptor_index']) is tuple:
|
|
323
|
+
validate_sculptor_index_argument(config['sculptor_index'])
|
|
324
|
+
config['sculptor_s'] = config['sculptor_index'][0]
|
|
325
|
+
config['sculptor_c'] = config['sculptor_index'][1]
|
|
326
|
+
config['sculptor_a'] = config['sculptor_index'][2]
|
|
327
|
+
sculptor_source = provenance.get('sculptor_index', "derived")
|
|
328
|
+
provenance['sculptor_s'] = sculptor_source
|
|
329
|
+
provenance['sculptor_c'] = sculptor_source
|
|
330
|
+
provenance['sculptor_a'] = sculptor_source
|
|
331
|
+
config.pop('sculptor_index')
|
|
332
|
+
provenance.pop('sculptor_index', None)
|
|
333
|
+
import torch
|
|
334
|
+
optimizer_names = {
|
|
335
|
+
name for name in dir(torch.optim)
|
|
336
|
+
if isinstance(getattr(torch.optim, name), type)
|
|
337
|
+
}
|
|
338
|
+
validate_training_config(config, optimizer_names=optimizer_names)
|
|
339
|
+
validate_runtime_environment(config, backend="pyg", torch_module=torch)
|
|
340
|
+
PATH.check_path(config)
|
|
341
|
+
mark_derived(provenance, config)
|
|
342
|
+
return ConfigResolution.from_working(config, provenance)
|
|
343
|
+
|
|
344
|
+
def load_NET_REFER(config):
|
|
345
|
+
from D4CMPP2.networks.registry import get_model, registered_models
|
|
346
|
+
|
|
347
|
+
with open(PATH.get_network_refer(config), "r") as file:
|
|
348
|
+
network_refer = yaml.safe_load(file)
|
|
349
|
+
definition = None
|
|
350
|
+
try:
|
|
351
|
+
definition = get_model(config["network"])
|
|
352
|
+
except ValueError:
|
|
353
|
+
pass
|
|
354
|
+
net_config = network_refer.get(config['network'],None)
|
|
355
|
+
if net_config is None:
|
|
356
|
+
if definition is not None:
|
|
357
|
+
net_config = definition.training_config()
|
|
358
|
+
else:
|
|
359
|
+
validate_network_entry(
|
|
360
|
+
config["network"],
|
|
361
|
+
None,
|
|
362
|
+
[*network_refer, *registered_models()],
|
|
363
|
+
)
|
|
364
|
+
validate_network_entry(config['network'], net_config, [
|
|
365
|
+
*network_refer,
|
|
366
|
+
*registered_models(),
|
|
367
|
+
])
|
|
368
|
+
net_config = dict(net_config)
|
|
369
|
+
net_config["network_id"] = (
|
|
370
|
+
definition.name if definition is not None else config["network"]
|
|
371
|
+
)
|
|
372
|
+
return net_config
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def run(config, callbacks=None, config_provenance=None):
|
|
376
|
+
"the main function to run the training"
|
|
377
|
+
config, runtime = split_runtime_config(config)
|
|
378
|
+
output = get_output(config)
|
|
379
|
+
tf_path = runtime.transfer_path
|
|
380
|
+
resume_path = runtime.resume_path
|
|
381
|
+
is_loaded = runtime.loaded
|
|
382
|
+
is_resumed = runtime.full_resume
|
|
383
|
+
mode = "resume" if is_resumed else "transfer" if tf_path else "legacy_continue" if is_loaded else "scratch"
|
|
384
|
+
previous_backend_determinism = capture_backend_determinism()
|
|
385
|
+
config["effective_reproducibility"] = configure_reproducibility(
|
|
386
|
+
config,
|
|
387
|
+
resume=is_resumed,
|
|
388
|
+
)
|
|
389
|
+
if tf_path:
|
|
390
|
+
output.info(
|
|
391
|
+
f"[Training] Starting transfer learning. Source model: {tf_path!r}; "
|
|
392
|
+
f"output directory: {config['MODEL_PATH']!r}."
|
|
393
|
+
)
|
|
394
|
+
elif is_resumed:
|
|
395
|
+
output.info(
|
|
396
|
+
"[Training] Resuming optimizer and scheduler state. "
|
|
397
|
+
f"Model directory: {config['MODEL_PATH']!r}."
|
|
398
|
+
)
|
|
399
|
+
elif is_loaded:
|
|
400
|
+
output.info(
|
|
401
|
+
"[Training] Continuing from saved model weights. "
|
|
402
|
+
f"Model directory: {config['MODEL_PATH']!r}."
|
|
403
|
+
)
|
|
404
|
+
else:
|
|
405
|
+
output.info(
|
|
406
|
+
"[Training] Starting a new run. "
|
|
407
|
+
f"Output directory: {config['MODEL_PATH']!r}."
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
manifest = RunManifest(config, mode)
|
|
411
|
+
if config_provenance is not None:
|
|
412
|
+
manifest.update(config_provenance=config_provenance)
|
|
413
|
+
config["run_id"] = manifest.run_id
|
|
414
|
+
try:
|
|
415
|
+
dm = module_loader.load_data_manager(config)(config)
|
|
416
|
+
data_contract = validate_data_manager_contract(dm, config)
|
|
417
|
+
if data_contract is not None:
|
|
418
|
+
manifest.update(
|
|
419
|
+
data_manager_contract={
|
|
420
|
+
"manager": data_contract.manager,
|
|
421
|
+
"graph_type": data_contract.graph_type,
|
|
422
|
+
"dataset": data_contract.dataset,
|
|
423
|
+
"unwrapper": data_contract.unwrapper,
|
|
424
|
+
"feature_dimensions": dict(data_contract.feature_dimensions),
|
|
425
|
+
"batch": {
|
|
426
|
+
"name": data_contract.batch.name,
|
|
427
|
+
"required_keys": data_contract.batch.required_keys,
|
|
428
|
+
"optional_keys": data_contract.batch.optional_keys,
|
|
429
|
+
"molecule_suffixes": data_contract.batch.molecule_suffixes,
|
|
430
|
+
"numeric_suffix": data_contract.batch.numeric_suffix,
|
|
431
|
+
},
|
|
432
|
+
}
|
|
433
|
+
)
|
|
434
|
+
network_manager_kwargs = {
|
|
435
|
+
"tf_path": tf_path,
|
|
436
|
+
"unwrapper": dm.unwrapper,
|
|
437
|
+
}
|
|
438
|
+
if resume_path is not None:
|
|
439
|
+
network_manager_kwargs["resume_path"] = resume_path
|
|
440
|
+
nm = module_loader.load_network_manager(config)(
|
|
441
|
+
config,
|
|
442
|
+
**network_manager_kwargs,
|
|
443
|
+
)
|
|
444
|
+
if tf_path and hasattr(nm, "transfer_report"):
|
|
445
|
+
manifest.update(transfer=nm.transfer_report)
|
|
446
|
+
if is_resumed:
|
|
447
|
+
manifest.update(parent_run_id=getattr(nm, "parent_run_id", None))
|
|
448
|
+
tm = module_loader.load_train_manager(config)(config)
|
|
449
|
+
if callbacks is not None:
|
|
450
|
+
set_callbacks = getattr(tm, "set_callbacks", None)
|
|
451
|
+
if not callable(set_callbacks):
|
|
452
|
+
raise TypeError(
|
|
453
|
+
f"Train manager {type(tm).__name__!r} does not support callbacks. "
|
|
454
|
+
"Implement set_callbacks(callbacks), or omit the callbacks argument."
|
|
455
|
+
)
|
|
456
|
+
set_callbacks(callbacks)
|
|
457
|
+
dm.init_data()
|
|
458
|
+
supportfile_saver.save_config(config)
|
|
459
|
+
train_loaders, val_loaders, test_loaders = dm.get_Dataloaders()
|
|
460
|
+
manifest.update(
|
|
461
|
+
split={
|
|
462
|
+
"train": len(dm.train_dataset),
|
|
463
|
+
"val": len(dm.val_dataset),
|
|
464
|
+
"test": len(dm.test_dataset),
|
|
465
|
+
}
|
|
466
|
+
)
|
|
467
|
+
tm.train(nm, train_loaders, val_loaders)
|
|
468
|
+
if tm.train_error is not None:
|
|
469
|
+
raise tm.train_error
|
|
470
|
+
pp = PostProcessor(config)
|
|
471
|
+
pp.postprocess(dm, nm, tm, train_loaders, val_loaders, test_loaders)
|
|
472
|
+
except KeyboardInterrupt as exc:
|
|
473
|
+
manifest.finish("interrupted", error=exc)
|
|
474
|
+
restore_backend_determinism(previous_backend_determinism)
|
|
475
|
+
raise
|
|
476
|
+
except Exception as exc:
|
|
477
|
+
manifest.finish("failed", error=exc)
|
|
478
|
+
restore_backend_determinism(previous_backend_determinism)
|
|
479
|
+
if config.get('legacy_silent_errors', False):
|
|
480
|
+
output.error("[Error] Training failed in legacy silent-error mode.")
|
|
481
|
+
output.error(traceback.format_exc())
|
|
482
|
+
return None
|
|
483
|
+
raise
|
|
484
|
+
manifest.finish(
|
|
485
|
+
"completed",
|
|
486
|
+
completed_epoch=nm.completed_epoch,
|
|
487
|
+
best_epoch=nm.best_epoch,
|
|
488
|
+
best_metric=nm.best_loss,
|
|
489
|
+
final_learning_rate=nm.get_lr(),
|
|
490
|
+
checkpoints={
|
|
491
|
+
"latest": os.path.join(config["MODEL_PATH"], "checkpoints", "latest.ckpt"),
|
|
492
|
+
"best": os.path.join(config["MODEL_PATH"], "checkpoints", "best.ckpt"),
|
|
493
|
+
"final": os.path.join(config["MODEL_PATH"], "final.pth"),
|
|
494
|
+
},
|
|
495
|
+
)
|
|
496
|
+
restore_backend_determinism(previous_backend_determinism)
|
|
497
|
+
return config['MODEL_PATH']
|
|
498
|
+
|
|
499
|
+
if __name__ == "__main__":
|
|
500
|
+
train(use_argparser=True)
|
D4CMPP2/cli.py
ADDED
D4CMPP2/exceptions.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Public, compatibility-preserving D4CMPP2 exception categories."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class D4CMPPError(Exception):
|
|
5
|
+
"""Base class for categorized package failures."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ConfigError(ValueError, D4CMPPError):
|
|
9
|
+
"""Invalid or incompatible configuration."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DataError(ValueError, D4CMPPError):
|
|
13
|
+
"""Invalid tabular or aligned input data."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GraphError(ValueError, D4CMPPError):
|
|
17
|
+
"""Invalid graph data, graph schema, or graph generation state."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ModelError(RuntimeError, D4CMPPError):
|
|
21
|
+
"""Model construction, execution, or state-loading failure."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DependencyError(RuntimeError, D4CMPPError):
|
|
25
|
+
"""Missing or incompatible runtime dependency."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CheckpointError(D4CMPPError):
|
|
29
|
+
"""Base category for checkpoint failures."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CheckpointNotFoundError(FileNotFoundError, CheckpointError):
|
|
33
|
+
"""A requested checkpoint does not exist."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CheckpointFormatError(ValueError, CheckpointError):
|
|
37
|
+
"""A checkpoint payload or schema is invalid."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CheckpointLoadError(RuntimeError, CheckpointError):
|
|
41
|
+
"""A checkpoint exists but could not be deserialized."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class CheckpointIOError(OSError, CheckpointError):
|
|
45
|
+
"""A checkpoint could not be committed to storage."""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ModuleLoadError(ImportError, D4CMPPError):
|
|
49
|
+
"""A custom module exists but failed to import or initialize."""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ManagerNotFoundError(FileNotFoundError, D4CMPPError):
|
|
53
|
+
"""A configured manager module or class could not be located."""
|