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.
Files changed (103) hide show
  1. D4CMPP2/_Data/AGENTS.md +24 -0
  2. D4CMPP2/_Data/Aqsoldb.csv +9291 -0
  3. D4CMPP2/_Data/BradleyMP.csv +3042 -0
  4. D4CMPP2/_Data/Lipophilicity.csv +1131 -0
  5. D4CMPP2/_Data/README.md +8 -0
  6. D4CMPP2/_Data/__init__.py +26 -0
  7. D4CMPP2/_Data/optical.csv +20237 -0
  8. D4CMPP2/_Data/test.csv +190 -0
  9. D4CMPP2/__init__.py +16 -0
  10. D4CMPP2/__main__.py +5 -0
  11. D4CMPP2/_main.py +500 -0
  12. D4CMPP2/cli.py +7 -0
  13. D4CMPP2/exceptions.py +53 -0
  14. D4CMPP2/grid_search.py +259 -0
  15. D4CMPP2/network_refer.yaml +160 -0
  16. D4CMPP2/networks/AFP_model.py +72 -0
  17. D4CMPP2/networks/AFPwithSolv_model.py +72 -0
  18. D4CMPP2/networks/DMPNN_model.py +90 -0
  19. D4CMPP2/networks/DMPNNwithSolv_model.py +89 -0
  20. D4CMPP2/networks/GAT_model.py +48 -0
  21. D4CMPP2/networks/GATwithSolv_model.py +63 -0
  22. D4CMPP2/networks/GCN_model.py +113 -0
  23. D4CMPP2/networks/GCNwithSolv_model.py +103 -0
  24. D4CMPP2/networks/GC_model.py +122 -0
  25. D4CMPP2/networks/ISATPM_model.py +14 -0
  26. D4CMPP2/networks/ISATPN_model.py +199 -0
  27. D4CMPP2/networks/ISAT_model.py +90 -0
  28. D4CMPP2/networks/MPNN_model.py +56 -0
  29. D4CMPP2/networks/MPNNwithSolv_model.py +72 -0
  30. D4CMPP2/networks/__init__.py +25 -0
  31. D4CMPP2/networks/base.py +250 -0
  32. D4CMPP2/networks/registry.py +187 -0
  33. D4CMPP2/networks/src/AFP.py +118 -0
  34. D4CMPP2/networks/src/BiDropout.py +29 -0
  35. D4CMPP2/networks/src/DMPNN.py +35 -0
  36. D4CMPP2/networks/src/GAT.py +69 -0
  37. D4CMPP2/networks/src/GC.py +85 -0
  38. D4CMPP2/networks/src/GCN.py +71 -0
  39. D4CMPP2/networks/src/ISAT.py +153 -0
  40. D4CMPP2/networks/src/Linear.py +49 -0
  41. D4CMPP2/networks/src/MPNN.py +56 -0
  42. D4CMPP2/networks/src/SolventLayer.py +62 -0
  43. D4CMPP2/networks/src/__init__.py +0 -0
  44. D4CMPP2/networks/src/distGCN.py +21 -0
  45. D4CMPP2/networks/src/pyg_hetero.py +24 -0
  46. D4CMPP2/optimize.py +472 -0
  47. D4CMPP2/src/Analyzer/ISAAnalyzer.py +458 -0
  48. D4CMPP2/src/Analyzer/ISAPNAnalyzer.py +366 -0
  49. D4CMPP2/src/Analyzer/ISAwSAnalyzer.py +117 -0
  50. D4CMPP2/src/Analyzer/MolAnalyzer.py +319 -0
  51. D4CMPP2/src/Analyzer/__init__.py +54 -0
  52. D4CMPP2/src/Analyzer/core.py +480 -0
  53. D4CMPP2/src/Analyzer/factory.py +166 -0
  54. D4CMPP2/src/Analyzer/interpretation.py +232 -0
  55. D4CMPP2/src/Analyzer/results.py +101 -0
  56. D4CMPP2/src/DataManager/Dataset/GraphDataset.py +314 -0
  57. D4CMPP2/src/DataManager/Dataset/ISAGraphDataset.py +384 -0
  58. D4CMPP2/src/DataManager/Dataset/__init__.py +0 -0
  59. D4CMPP2/src/DataManager/GraphGenerator/ISAGraphGenerator.py +223 -0
  60. D4CMPP2/src/DataManager/GraphGenerator/MolGraphGenerator.py +73 -0
  61. D4CMPP2/src/DataManager/GraphGenerator/__init__.py +14 -0
  62. D4CMPP2/src/DataManager/ISADataManager.py +67 -0
  63. D4CMPP2/src/DataManager/MolDataManager.py +735 -0
  64. D4CMPP2/src/DataManager/__init__.py +14 -0
  65. D4CMPP2/src/DataManager/contracts.py +179 -0
  66. D4CMPP2/src/NetworkManager/ISANetworkManager.py +12 -0
  67. D4CMPP2/src/NetworkManager/NetworkManager.py +520 -0
  68. D4CMPP2/src/NetworkManager/__init__.py +14 -0
  69. D4CMPP2/src/PostProcessor.py +160 -0
  70. D4CMPP2/src/TrainManager/ISATrainManager.py +26 -0
  71. D4CMPP2/src/TrainManager/TrainManager.py +254 -0
  72. D4CMPP2/src/TrainManager/__init__.py +14 -0
  73. D4CMPP2/src/TrainManager/callbacks.py +119 -0
  74. D4CMPP2/src/__init__.py +0 -0
  75. D4CMPP2/src/utils/PATH.py +246 -0
  76. D4CMPP2/src/utils/__init__.py +0 -0
  77. D4CMPP2/src/utils/argparser.py +56 -0
  78. D4CMPP2/src/utils/checkpointing.py +90 -0
  79. D4CMPP2/src/utils/config_resolution.py +123 -0
  80. D4CMPP2/src/utils/config_validation.py +370 -0
  81. D4CMPP2/src/utils/csv_validation.py +105 -0
  82. D4CMPP2/src/utils/data_quality.py +181 -0
  83. D4CMPP2/src/utils/featureizer.py +202 -0
  84. D4CMPP2/src/utils/functional_group.csv +169 -0
  85. D4CMPP2/src/utils/graph_cache.py +213 -0
  86. D4CMPP2/src/utils/leaderboard.py +212 -0
  87. D4CMPP2/src/utils/metrics.py +31 -0
  88. D4CMPP2/src/utils/module_loader.py +147 -0
  89. D4CMPP2/src/utils/output.py +80 -0
  90. D4CMPP2/src/utils/reproducibility.py +70 -0
  91. D4CMPP2/src/utils/run_manifest.py +175 -0
  92. D4CMPP2/src/utils/scaler.py +40 -0
  93. D4CMPP2/src/utils/sculptor.py +713 -0
  94. D4CMPP2/src/utils/splitting.py +250 -0
  95. D4CMPP2/src/utils/supportfile_saver.py +94 -0
  96. D4CMPP2/src/utils/tools.py +156 -0
  97. D4CMPP2/src/utils/transfer_learning.py +111 -0
  98. d4cmpp2-0.4.0.dist-info/METADATA +420 -0
  99. d4cmpp2-0.4.0.dist-info/RECORD +103 -0
  100. d4cmpp2-0.4.0.dist-info/WHEEL +5 -0
  101. d4cmpp2-0.4.0.dist-info/entry_points.txt +2 -0
  102. d4cmpp2-0.4.0.dist-info/licenses/LICENSE +21 -0
  103. d4cmpp2-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,160 @@
1
+ import os
2
+ import pandas as pd
3
+ import matplotlib.pyplot as plt
4
+ from prettytable import PrettyTable
5
+ import pickle
6
+ import re
7
+ import uuid
8
+
9
+ from .utils.metrics import get_metrics
10
+ from .utils import tools
11
+ from .utils.output import get_output
12
+
13
+ import traceback
14
+ class PostProcessor:
15
+ """
16
+ The class for the postprocessing of the trained model.
17
+ This class is used to save the model, learning curve, prediction, and metrics after the training.
18
+ """
19
+
20
+ def __init__(self, config):
21
+ self.config = config
22
+ self.output = get_output(config)
23
+ self.result_path = config['MODEL_PATH']+"/result"
24
+ if not os.path.exists(self.result_path):
25
+ os.makedirs(self.result_path)
26
+
27
+ def postprocess(self, dm, nm, tm, train_loaders, val_loaders, test_loaders):
28
+ "The main function for the postprocessing"
29
+ try:
30
+ self.save_final_model(nm)
31
+ except Exception:
32
+ if self.config.get('legacy_silent_errors', False):
33
+ self.output.error(
34
+ "[Results] Model saving failed while writing the final model."
35
+ )
36
+ self.output.error(traceback.format_exc())
37
+ return
38
+ raise
39
+
40
+ try:
41
+ tm.learning_curve.to_csv(os.path.join(self.result_path,"learning_curve.csv"),index=False)
42
+ self.draw_learning_curve(tm.learning_curve)
43
+ except Exception:
44
+ if self.config.get('legacy_silent_errors', False):
45
+ self.output.error("[Results] Failed to save the learning curve.")
46
+ self.output.error(traceback.format_exc())
47
+ else:
48
+ raise
49
+
50
+ self.save_model_summary(nm.network,tm.run_time)
51
+
52
+ try:
53
+ if self.config.get("save_prediction",True):
54
+ prediction_df = self.save_prediction(nm, tm, train_loaders, val_loaders, test_loaders, scaler=dm.scaler)
55
+ self.plot_prediction(self.config.get("target"),prediction_df)
56
+ self.save_metrics(self.config.get("target"),prediction_df)
57
+ except Exception:
58
+ if self.config.get('legacy_silent_errors', False):
59
+ self.output.error("[Results] Failed to save predictions or metrics.")
60
+ self.output.error(traceback.format_exc())
61
+ else:
62
+ raise
63
+
64
+ def save_prediction(self, nm, tm, train_loader, val_loader, test_loader,scaler=None):
65
+ train_pred, train_true, train_smiles = tm.predict(nm, train_loader)
66
+ val_pred, val_true, val_smiles = tm.predict(nm, val_loader)
67
+ test_pred, test_true, test_smiles = tm.predict(nm, test_loader)
68
+ targets = self.config.get("target")
69
+ if scaler is not None:
70
+ train_true = scaler.inverse_transform(train_true.cpu().numpy())
71
+ val_true = scaler.inverse_transform(val_true.cpu().numpy())
72
+ test_true = scaler.inverse_transform(test_true.cpu().numpy())
73
+ train_pred = scaler.inverse_transform(train_pred.cpu().numpy())
74
+ val_pred = scaler.inverse_transform(val_pred.cpu().numpy())
75
+ test_pred = scaler.inverse_transform(test_pred.cpu().numpy())
76
+
77
+ with open(os.path.join(self.config['MODEL_PATH'],"scaler.pkl"),"wb") as file:
78
+ pickle.dump(scaler,file)
79
+
80
+ prediction_df = None
81
+ for true,pred,smiles,sets in zip([train_true,val_true,test_true],[train_pred,val_pred,test_pred],[train_smiles,val_smiles,test_smiles],['train','val','test']):
82
+ data = {}
83
+ for i, target in enumerate(targets):
84
+ data[f"{target}_pred"] = pred[:, i]
85
+ data[f"{target}_true"] = true[:, i]
86
+
87
+ data.update({k: smiles[k] for k in smiles.keys()})
88
+ data['set'] = sets
89
+
90
+ df = pd.DataFrame(data)
91
+ if prediction_df is None:
92
+ prediction_df = df
93
+ else:
94
+ prediction_df = pd.concat([prediction_df,df],axis=0)
95
+
96
+ prediction_df.to_csv(os.path.join(self.result_path,"prediction.csv"),index=False)
97
+ return prediction_df
98
+
99
+ def save_metrics(self, targets, prediction_df):
100
+ metrics = get_metrics(targets, prediction_df)
101
+ for set in ['train','val','test']:
102
+ table = PrettyTable()
103
+ col = ['target',f'{set}_r2',f'{set}_mae',f'{set}_rmse']
104
+ table.field_names = col
105
+ for t in targets:
106
+ table.add_row([t,metrics[f'{set}_r2'][t],metrics[f'{set}_mae'][t],metrics[f'{set}_rmse'][t]])
107
+ self.output.info(f"[Results] Metrics for split {set!r}:\n{table}")
108
+
109
+ metrics.to_csv(os.path.join(self.result_path,"metrics.csv"))
110
+
111
+ def plot_prediction(self,target, prediction_df):
112
+ img = tools.plot_prediction(target, prediction_df)
113
+ img.save(os.path.join(self.result_path,"prediction.png"))
114
+
115
+
116
+ def save_model_summary(self, model,run_time):
117
+ with open(os.path.join(self.config['MODEL_PATH'],"model_summary.txt"), "w") as file:
118
+ file.write(str(model))
119
+ file.write(f"#params:{sum(p.numel() for p in model.parameters() if p.requires_grad)}")
120
+ file.write("\n")
121
+ file.write(str(model.__class__.__name__))
122
+ file.write("\n")
123
+ file.write(str(model.__class__.__doc__))
124
+ file.write("\n")
125
+ file.write("learning_time: "+str(run_time))
126
+
127
+ def draw_learning_curve(self, learning_curve):
128
+ img = tools.plot_learning_curve(learning_curve['train_loss'],learning_curve['val_loss'])
129
+ img.save(os.path.join(self.result_path,"learning_curve.png"))
130
+
131
+ def clear_checkpoint(self):
132
+ checkpoint_pattern = re.compile(
133
+ r"^param_[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?\.pth$"
134
+ )
135
+ for file in os.listdir(self.config['MODEL_PATH']):
136
+ if checkpoint_pattern.fullmatch(file):
137
+ os.remove(os.path.join(self.config['MODEL_PATH'], file))
138
+
139
+ def save_final_model(self, nm):
140
+ """Atomically commit final.pth before removing package checkpoints."""
141
+ model_path = self.config['MODEL_PATH']
142
+ final_path = os.path.join(model_path, "final.pth")
143
+ staging_path = os.path.join(model_path, f".final.pth.{uuid.uuid4().hex}.tmp")
144
+ try:
145
+ nm.save_params(staging_path)
146
+ if not os.path.isfile(staging_path) or os.path.getsize(staging_path) == 0:
147
+ raise OSError(
148
+ f"Final model staging file {staging_path!r} was not created as a non-empty regular file. "
149
+ "The previous final.pth and best checkpoints were preserved."
150
+ )
151
+ os.replace(staging_path, final_path)
152
+ self.clear_checkpoint()
153
+ finally:
154
+ if os.path.exists(staging_path):
155
+ try:
156
+ os.remove(staging_path)
157
+ except OSError:
158
+ pass
159
+
160
+
@@ -0,0 +1,26 @@
1
+ import time
2
+ import pandas as pd
3
+ import numpy as np
4
+ import torch
5
+ from tqdm import tqdm
6
+
7
+ from .TrainManager import Trainer
8
+
9
+ class ISATrainer(Trainer):
10
+ """Compatibility extension point referenced by ISA registries and saved configs."""
11
+
12
+ pass
13
+ # def get_feature(self, network_manager, loader):
14
+ # "Provide the feature of the model. The model which is used for the training should contains the implementation of 'get_feature'."
15
+ # network_manager.eval()
16
+ # pred_result = {}
17
+ # with torch.no_grad():
18
+ # for loader in tqdm(loader, desc='Calculating features'):
19
+ # torch.autograd.set_detect_anomaly(False)
20
+ # y_pred = network_manager.step(loader,get_feature=True)
21
+ # if pred_result:
22
+ # for key in y_pred:
23
+ # pred_result[key] = torch.cat([pred_result[key],y_pred[key]], dim=0)
24
+ # else:
25
+ # pred_result = y_pred
26
+ # return pred_result
@@ -0,0 +1,254 @@
1
+ import time
2
+ import pandas as pd
3
+ import numpy as np
4
+ import torch
5
+ from .callbacks import CallbackDispatcher, TrainingEvent
6
+ from D4CMPP2.src.utils.output import get_output
7
+
8
+
9
+ class Trainer():
10
+ "The class where the operation of the training is defined"
11
+ def __init__(self,config):
12
+ self.config = config
13
+ self.output = get_output(config)
14
+ self.model_path=config['MODEL_PATH']
15
+ self.dev=config['device']
16
+ self.max_epoch=config['max_epoch']
17
+ self.learning_curve=None
18
+ self.train_error = None
19
+ self._callback_dispatcher = CallbackDispatcher()
20
+
21
+ def set_callbacks(self, callbacks):
22
+ """Set runtime-only callbacks without placing objects in saved config."""
23
+
24
+ self._callback_dispatcher = CallbackDispatcher(callbacks)
25
+
26
+ def _event(self, name, network_manager, **kwargs):
27
+ learning_rate = None
28
+ if hasattr(network_manager, "get_lr"):
29
+ learning_rate = float(network_manager.get_lr())
30
+ checkpoint_paths = {}
31
+ model_path = self.config.get("MODEL_PATH")
32
+ if model_path:
33
+ checkpoint_paths = {
34
+ "latest": f"{model_path}/checkpoints/latest.ckpt",
35
+ "best": f"{model_path}/checkpoints/best.ckpt",
36
+ }
37
+ return TrainingEvent(
38
+ name=name,
39
+ learning_rate=learning_rate,
40
+ checkpoint_paths=checkpoint_paths,
41
+ **kwargs,
42
+ )
43
+
44
+ def train(self, network_manager, train_loader, val_loader):
45
+
46
+ self.learning_curve=getattr(network_manager,'learning_curve',None)
47
+ start_time=time.time()
48
+ start_epoch = getattr(network_manager, "next_epoch", 0)
49
+
50
+ try:
51
+ self._callback_dispatcher.emit(
52
+ self._event("run_start", network_manager)
53
+ )
54
+ with self.output.progress(
55
+ range(start_epoch, start_epoch + self.max_epoch)
56
+ ) as t:
57
+ for epoch in t:
58
+ self._callback_dispatcher.emit(
59
+ self._event("epoch_start", network_manager, epoch=epoch)
60
+ )
61
+ t.set_description('Epoch %d' % epoch)
62
+ epoch_train_loss = self.train_epoch(network_manager, train_loader)
63
+ epoch_train_loss=float(np.mean(epoch_train_loss))
64
+ self._callback_dispatcher.emit(
65
+ self._event(
66
+ "train_epoch_end",
67
+ network_manager,
68
+ epoch=epoch,
69
+ train_loss=epoch_train_loss,
70
+ )
71
+ )
72
+ epoch_val_loss = self.evaluate(network_manager, val_loader)
73
+ epoch_val_loss=float(np.mean(epoch_val_loss))
74
+ self._callback_dispatcher.emit(
75
+ self._event(
76
+ "validation_end",
77
+ network_manager,
78
+ epoch=epoch,
79
+ train_loss=epoch_train_loss,
80
+ val_loss=epoch_val_loss,
81
+ )
82
+ )
83
+
84
+ t.set_postfix({'train_loss': epoch_train_loss, 'val_loss': epoch_val_loss})
85
+
86
+ row=pd.Series({'train_loss':epoch_train_loss, 'val_loss': epoch_val_loss})
87
+ if self.learning_curve is None:
88
+ self.learning_curve=pd.DataFrame(row).transpose()
89
+ else:
90
+ self.learning_curve = pd.concat([self.learning_curve,pd.DataFrame(row).transpose()],axis=0)
91
+ self.learning_curve.to_csv(self.model_path+'/learning_curve.csv',index=False)
92
+ stopped = bool(
93
+ network_manager.scheduler_step(
94
+ epoch_val_loss,
95
+ completed_epoch=epoch,
96
+ )
97
+ )
98
+ self._callback_dispatcher.emit(
99
+ self._event(
100
+ "epoch_end",
101
+ network_manager,
102
+ epoch=epoch,
103
+ train_loss=epoch_train_loss,
104
+ val_loss=epoch_val_loss,
105
+ stopped=stopped,
106
+ )
107
+ )
108
+ if stopped:
109
+ break
110
+
111
+
112
+ except KeyboardInterrupt as error:
113
+ self._callback_dispatcher.emit_failure(
114
+ self._event(
115
+ "interruption",
116
+ network_manager,
117
+ epoch=locals().get("epoch"),
118
+ error=error,
119
+ ),
120
+ error,
121
+ )
122
+ if self.config.get('legacy_silent_errors', False):
123
+ self.output.error(
124
+ "[Training] Interrupted by the user; preserving legacy "
125
+ "silent-error behavior."
126
+ )
127
+ else:
128
+ raise
129
+ except Exception as e:
130
+ self.train_error = e
131
+ self._callback_dispatcher.emit_failure(
132
+ self._event(
133
+ "exception",
134
+ network_manager,
135
+ epoch=locals().get("epoch"),
136
+ error=e,
137
+ ),
138
+ e,
139
+ )
140
+ raise
141
+
142
+ network_manager.load_best_checkpoint()
143
+ self.run_time = time.time()-start_time
144
+ self._callback_dispatcher.emit(
145
+ self._event(
146
+ "run_end",
147
+ network_manager,
148
+ epoch=locals().get("epoch"),
149
+ )
150
+ )
151
+
152
+ def train_epoch(self, network_manager, train_loader):
153
+ network_manager.train()
154
+ score_list = []
155
+ target_list = []
156
+ epoch_loss = 0
157
+ count=0
158
+ for loader in (train_loader):
159
+ torch.autograd.set_detect_anomaly(False)
160
+ y, y_pred, loss = network_manager.step(loader)
161
+
162
+ score_list.append(y_pred)
163
+ target_list.append(y)
164
+
165
+ count+=y_pred.shape[0]
166
+ epoch_loss += loss*y_pred.shape[0]
167
+ if count: epoch_loss/=count
168
+
169
+ return epoch_loss
170
+
171
+
172
+ def evaluate(self, network_manager, val_loader):
173
+ network_manager.eval()
174
+ score_list = []
175
+ target_list = []
176
+ epoch_loss = 0
177
+ count=0
178
+ with torch.no_grad():
179
+ for loader in (val_loader):
180
+ torch.autograd.set_detect_anomaly(False)
181
+ y, y_pred, loss = network_manager.step(loader)
182
+
183
+
184
+ score_list.append(y_pred)
185
+ target_list.append(y)
186
+
187
+ count+=y_pred.shape[0]
188
+ epoch_loss += loss*y_pred.shape[0]
189
+ if count: epoch_loss/=count
190
+
191
+ return epoch_loss
192
+
193
+ def predict(self, network_manager, pred_loader,dropout=False):
194
+ network_manager.eval()
195
+ if dropout:
196
+ network_manager.dropout_on()
197
+ score_list = []
198
+ target_list = []
199
+ # OLD
200
+ smiles_list = {}
201
+ with torch.no_grad():
202
+ if len(pred_loader) > 10:
203
+ pred_loader = self.output.progress(
204
+ pred_loader,
205
+ desc="Predicting",
206
+ mininterval=1.0,
207
+ )
208
+ for loader in pred_loader:
209
+ torch.autograd.set_detect_anomaly(False)
210
+ y, y_pred, loss,x = network_manager.step(loader,flag=True)
211
+ score_list.append(y_pred)
212
+ target_list.append(y)
213
+ for k in x.keys():
214
+ if k.endswith('_smiles'):
215
+ _k = k[:-7]
216
+ smiles_list[_k] = smiles_list.get(_k,[]) + x[k]
217
+ return torch.cat(score_list, dim=0), torch.cat(target_list, dim=0), smiles_list
218
+
219
+ def get_score(self, network_manager, loader):
220
+ "Provide the score of the model. The model which is used for the training should contains the implementation of 'get_score'."
221
+ network_manager.eval()
222
+ pred_result = {}
223
+ with torch.no_grad():
224
+ if len(loader) > 10:
225
+ loader = self.output.progress(loader, desc="Calculating scores")
226
+ for l in loader:
227
+ torch.autograd.set_detect_anomaly(False)
228
+ y_pred = network_manager.step(l,get_score=True)
229
+ if type(y_pred) is not dict:
230
+ y_pred = {'prediction':y_pred}
231
+ if pred_result:
232
+ for key in y_pred:
233
+ pred_result[key] = torch.cat([pred_result[key],y_pred[key]], dim=0)
234
+ else:
235
+ pred_result = y_pred
236
+ return pred_result
237
+
238
+
239
+ def get_feature(self, network_manager, loader):
240
+ "Provide the feature of the model. The model which is used for the training should contains the implementation of 'get_feature'."
241
+ network_manager.eval()
242
+ pred_result = {}
243
+ with torch.no_grad():
244
+ if len(loader) > 10:
245
+ loader = self.output.progress(loader, desc="Calculating features")
246
+ for l in loader:
247
+ torch.autograd.set_detect_anomaly(False)
248
+ y_pred = network_manager.step(l,get_feature=True)
249
+ if pred_result:
250
+ for key in y_pred:
251
+ pred_result[key] = torch.cat([pred_result[key],y_pred[key]], dim=0)
252
+ else:
253
+ pred_result = y_pred
254
+ return pred_result
@@ -0,0 +1,14 @@
1
+ """Lazy exports for built-in training-manager modules."""
2
+
3
+ from importlib import import_module
4
+ from types import ModuleType
5
+
6
+ __all__ = ["TrainManager", "ISATrainManager", "callbacks"]
7
+
8
+
9
+ def __getattr__(name: str) -> ModuleType:
10
+ if name not in __all__:
11
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
12
+ module = import_module(f"{__name__}.{name}")
13
+ globals()[name] = module
14
+ return module
@@ -0,0 +1,119 @@
1
+ """Small, observation-only callback contract for the training loop."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from types import MappingProxyType
5
+ from typing import Any, Mapping, Optional, Sequence, Tuple
6
+
7
+
8
+ TRAINING_EVENTS: Tuple[str, ...] = (
9
+ "run_start",
10
+ "epoch_start",
11
+ "train_epoch_end",
12
+ "validation_end",
13
+ "epoch_end",
14
+ "run_end",
15
+ "exception",
16
+ "interruption",
17
+ )
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class TrainingEvent:
22
+ """Immutable snapshot exposed to callbacks.
23
+
24
+ Model, optimizer, dataloader, and mutable config objects are intentionally
25
+ excluded so callbacks do not become coupled to manager internals.
26
+ """
27
+
28
+ name: str
29
+ epoch: Optional[int] = None
30
+ train_loss: Optional[float] = None
31
+ val_loss: Optional[float] = None
32
+ learning_rate: Optional[float] = None
33
+ stopped: bool = False
34
+ checkpoint_paths: Mapping[str, str] = field(default_factory=dict)
35
+ error: Optional[BaseException] = None
36
+
37
+ def __post_init__(self) -> None:
38
+ if self.name not in TRAINING_EVENTS:
39
+ raise ValueError(
40
+ f"Unknown training event {self.name!r}. "
41
+ f"Supported events: {list(TRAINING_EVENTS)!r}."
42
+ )
43
+ object.__setattr__(
44
+ self,
45
+ "checkpoint_paths",
46
+ MappingProxyType(dict(self.checkpoint_paths)),
47
+ )
48
+
49
+
50
+ class TrainingCallback:
51
+ """Optional callback base class.
52
+
53
+ Subclasses may implement ``on_event(event)`` for every event or a specific
54
+ method such as ``on_validation_end(event)``. Specific handlers run first.
55
+ """
56
+
57
+ def on_event(self, event: TrainingEvent) -> None:
58
+ """Observe a training event."""
59
+
60
+
61
+ class EventHistory(TrainingCallback):
62
+ """Built-in observer that keeps immutable event snapshots in memory."""
63
+
64
+ def __init__(self) -> None:
65
+ self.events: list[TrainingEvent] = []
66
+
67
+ def on_event(self, event: TrainingEvent) -> None:
68
+ self.events.append(event)
69
+
70
+
71
+ class CallbackDispatcher:
72
+ """Validate callbacks and dispatch events in registration order."""
73
+
74
+ def __init__(self, callbacks: Optional[Sequence[Any]] = None) -> None:
75
+ if callbacks is None:
76
+ callbacks = ()
77
+ if isinstance(callbacks, (str, bytes)) or not isinstance(callbacks, Sequence):
78
+ raise TypeError(
79
+ "callbacks must be a sequence of callback objects, for example "
80
+ "[MyCallback()]."
81
+ )
82
+ self.callbacks = tuple(callbacks)
83
+ for index, callback in enumerate(self.callbacks):
84
+ if callback is None:
85
+ raise TypeError(f"callbacks[{index}] is None; provide a callback object.")
86
+ has_generic = callable(getattr(callback, "on_event", None))
87
+ has_specific = any(
88
+ callable(getattr(callback, f"on_{name}", None))
89
+ for name in TRAINING_EVENTS
90
+ )
91
+ if not has_generic and not has_specific:
92
+ raise TypeError(
93
+ f"callbacks[{index}] ({type(callback).__name__}) does not define "
94
+ "on_event(event) or a supported on_<event>(event) method."
95
+ )
96
+
97
+ def emit(self, event: TrainingEvent) -> None:
98
+ """Dispatch one immutable event, propagating callback failures."""
99
+
100
+ for callback in self.callbacks:
101
+ specific = getattr(callback, f"on_{event.name}", None)
102
+ if callable(specific):
103
+ specific(event)
104
+ generic = getattr(callback, "on_event", None)
105
+ if callable(generic):
106
+ generic(event)
107
+
108
+ def emit_failure(self, event: TrainingEvent, original: BaseException) -> None:
109
+ """Notify failure handlers without hiding the original exception."""
110
+
111
+ try:
112
+ self.emit(event)
113
+ except BaseException as callback_error:
114
+ add_note = getattr(original, "add_note", None)
115
+ if callable(add_note):
116
+ add_note(
117
+ "A callback failure handler also failed: "
118
+ f"{type(callback_error).__name__}: {callback_error}"
119
+ )
File without changes