beiken1201 commited on
Commit
07bb78a
·
verified ·
1 Parent(s): 999e5eb

Remove new-data reconstruction evaluation utility and README references

Browse files

Delete evaluate_new_data.py and its command reference from BioMod/eval_new_data. Remove the corresponding workflow, checkpoint-interface notes, and directory-tree entries from the root and source READMEs. Model weights, paired configuration, datasets, notebooks, and other source files are unchanged.

BioMod/README.md CHANGED
@@ -41,9 +41,6 @@ BioMod/ # Task code: training,
41
  ├── drug_response/ # Single-drug response prediction and training
42
  │ ├── cmd.txt
43
  │ └── train_drug_performer_module.py
44
- ├── eval_new_data/ # Reconstruction error and generalization on new data
45
- │ ├── cmd.txtx
46
- │ └── evaluate_new_data.py
47
  ├── gene_perturbation/ # Perturbation response prediction: processing, training and inference
48
  │ ├── data_utils_local.py
49
  │ ├── gears_reduce_local.py
 
41
  ├── drug_response/ # Single-drug response prediction and training
42
  │ ├── cmd.txt
43
  │ └── train_drug_performer_module.py
 
 
 
44
  ├── gene_perturbation/ # Perturbation response prediction: processing, training and inference
45
  │ ├── data_utils_local.py
46
  │ ├── gears_reduce_local.py
BioMod/eval_new_data/cmd.txtx DELETED
@@ -1,19 +0,0 @@
1
- # masked 模式(与预训练 valid loss 直接可比)
2
- python evaluate_new_data.py \
3
- --ckpt_dir ../mae_tost_48M_cross_attn_only_pure_memory/ \
4
- --model_name mae_tost_48M_cross_attn_only_pure_memory \
5
- --data_dir ./npz \
6
- --module_data_dir ../module_data_all/ \
7
- --eval_mode masked \
8
- --batch_size 320 \
9
- --output_csv eval_masked.csv
10
-
11
- # full_recon 模式(不 mask,全 module 重建 loss)
12
- python evaluate_new_data.py \
13
- --ckpt_dir /path/to/mae_tost_48M_cross_attn_only_pure_memory/ \
14
- --model_name mae_tost_48M_cross_attn_only_pure_memory \
15
- --data_dir /path/to/20260423测试集/npz/npz \
16
- --module_data_dir ./module_data_all/ \
17
- --eval_mode full_recon \
18
- --batch_size 32 \
19
- --output_csv eval_full_recon.csv
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
BioMod/eval_new_data/evaluate_new_data.py DELETED
@@ -1,375 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- Evaluate the reconstruction loss of a pre-trained model on a new dataset (DDP multi-card version).
4
- Used to verify the generalization ability of the model on unseen donor data, compared with pre-training valid loss.
5
-
6
- Two evaluation modes are supported:
7
- masked — consistent with pre-training validation, using the same mask_ratio, for direct comparison
8
- full_recon — without masking, calculate the reconstruction loss of all modules
9
-
10
- Usage example (single card):
11
- torchrun --nproc_per_node=1 evaluate_new_data.py \
12
- --ckpt_dir ./ckpts/ \
13
- --model_name mae_tost_48M_cross_attn_only_pure_memory \
14
- --data_dir /path/to/20260423test set/npz/npz \
15
- --eval_mode masked --batch_size 32
16
-
17
- Usage example (4 card DDP):
18
- torchrun --nproc_per_node=4 evaluate_new_data.py \
19
- --ckpt_dir ./ckpts/ \
20
- --model_name mae_tost_48M_cross_attn_only_pure_memory \
21
- --data_dir /path/to/20260423test set/npz/npz \
22
- --eval_mode masked --batch_size 32
23
- """
24
-
25
- import os
26
- import re
27
- import csv
28
- import sys
29
- import glob
30
- import time
31
- import pickle
32
- import inspect
33
- import argparse
34
- from collections import defaultdict
35
-
36
- import numpy as np
37
- import torch
38
- import torch.distributed as dist
39
- from torch.nn.parallel import DistributedDataParallel as DDP
40
- from torch.utils.data import Dataset, DataLoader
41
- from torch.utils.data.distributed import DistributedSampler
42
- from scipy import sparse
43
-
44
- sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..'))
45
-
46
- from models_performer_module import PerformerEncoderOnly_Module
47
- from module_dataset import load_module_data
48
-
49
-
50
- # ---------------------------------------------------------------------------
51
- # Dataset
52
- # ---------------------------------------------------------------------------
53
-
54
- class DonorSCDataset(Dataset):
55
- """Dataset of a single donor, returning single-cell expression vectors row-wise from dense numpy."""
56
-
57
- def __init__(self, csr_matrix):
58
- self.data = csr_matrix.toarray()
59
-
60
- def __len__(self):
61
- return self.data.shape[0]
62
-
63
- def __getitem__(self, idx):
64
- return torch.from_numpy(self.data[idx].astype(np.float32))
65
-
66
-
67
- # ---------------------------------------------------------------------------
68
- # Checkpoint discovery
69
- # ---------------------------------------------------------------------------
70
-
71
- def discover_checkpoints(ckpt_dir, model_name, epoch_filter=None):
72
- pattern = os.path.join(ckpt_dir, f"{model_name}_epoch*.pth")
73
- files = glob.glob(pattern)
74
- results = []
75
- for f in files:
76
- match = re.search(r'epoch(\d+)', os.path.basename(f))
77
- if match:
78
- epoch_num = int(match.group(1))
79
- if epoch_filter is None or epoch_num in epoch_filter:
80
- results.append((epoch_num, f))
81
- results.sort(key=lambda x: x[0])
82
- return results
83
-
84
-
85
- def load_model_config(ckpt_dir, model_name):
86
- pkl_path = os.path.join(ckpt_dir, f"{model_name}.pkl")
87
- if not os.path.isfile(pkl_path):
88
- raise FileNotFoundError(f"Model configuration file not found: {pkl_path}")
89
- with open(pkl_path, 'rb') as f:
90
- return pickle.load(f)
91
-
92
-
93
- def filter_model_config(config):
94
- """Filter out obsolete parameters in pkl that are not accepted by the model __init__."""
95
- valid = set(inspect.signature(PerformerEncoderOnly_Module.__init__).parameters.keys())
96
- removed = {k: config.pop(k) for k in list(config) if k not in valid and k != "self"}
97
- return removed
98
-
99
-
100
- # ---------------------------------------------------------------------------
101
- # Evaluation
102
- # ---------------------------------------------------------------------------
103
-
104
- def evaluate_donor_ddp(model, dataloader, device, eval_mode, mask_ratio, ptdtype, seed=None):
105
- """
106
- Calculate the loss for this rank shard of a single donor and return (loss_sum, batch_count).
107
- Aggregation is done by the caller via all_reduce.
108
- """
109
- model.eval()
110
- loss_sum = 0.0
111
- batch_count = 0
112
-
113
- raw_model = model.module if isinstance(model, DDP) else model
114
-
115
- with torch.no_grad():
116
- for batch_idx, data in enumerate(dataloader):
117
- data = data.to(device, non_blocking=True)
118
- if torch.isnan(data).any() or torch.isinf(data).any():
119
- continue
120
-
121
- if eval_mode == "masked" and seed is not None:
122
- torch.manual_seed(seed + batch_idx)
123
- torch.cuda.manual_seed(seed + batch_idx)
124
-
125
- with torch.autocast(device_type='cuda', dtype=ptdtype):
126
- if eval_mode == "masked":
127
- loss, _, _ = raw_model(data, mask_ratio=mask_ratio)
128
- else:
129
- latent, mask_zero, ids_restore, module_targets = raw_model.forward_encoder(
130
- data, mask_ratio=0.0
131
- )
132
- dt = raw_model.decoder_type
133
- if dt == "performer":
134
- preds = raw_model.forward_decoder_performer(latent, ids_restore)
135
- elif dt == "cross_attn":
136
- preds = raw_model.forward_decoder_cross_attn(latent, mask_zero, ids_restore)
137
- elif dt == "simple":
138
- preds = raw_model.forward_decoder_simple(latent, ids_restore)
139
- elif dt == "none":
140
- preds = raw_model.forward_predictor_from_encoder(latent)
141
- else:
142
- raise RuntimeError(f"Unsupported decoder_type: {dt}")
143
- mask_all = torch.ones_like(mask_zero)
144
- loss = raw_model.compute_loss(preds, module_targets, mask_all)
145
-
146
- if torch.isnan(loss) or torch.isinf(loss):
147
- continue
148
- loss_sum += loss.item()
149
- batch_count += 1
150
-
151
- return loss_sum, batch_count
152
-
153
-
154
- def reduce_scalar(value, device):
155
- """all_reduce is a scalar and returns the global summation result."""
156
- t = torch.tensor(value, dtype=torch.float64, device=device)
157
- dist.all_reduce(t, op=dist.ReduceOp.SUM)
158
- return t.item()
159
-
160
-
161
- def discover_datasets(data_dir):
162
- datasets = []
163
- for name in sorted(os.listdir(data_dir)):
164
- subdir = os.path.join(data_dir, name)
165
- if os.path.isdir(subdir):
166
- npz_files = sorted([f for f in os.listdir(subdir) if f.endswith('.npz')])
167
- if npz_files:
168
- datasets.append((name, subdir, npz_files))
169
- return datasets
170
-
171
-
172
- # ---------------------------------------------------------------------------
173
- # Main
174
- # ---------------------------------------------------------------------------
175
-
176
- def main():
177
- parser = argparse.ArgumentParser(description="Evaluate pre-trained model reconstruction loss (DDP) on new datasets")
178
- parser.add_argument("--ckpt_dir", type=str, required=True)
179
- parser.add_argument("--model_name", type=str, required=True)
180
- parser.add_argument("--data_dir", type=str, required=True)
181
- parser.add_argument("--gene_list_path", type=str, default="../../selected_genes_27k.txt")
182
- parser.add_argument("--gene_mapping_path", type=str, default="../../gene2ens.csv")
183
- parser.add_argument("--module_data_dir", type=str, default=None)
184
- parser.add_argument("--gene2vec_data_path", type=str, default="../../selected_gene2vec_27k.npy")
185
- parser.add_argument("--batch_size", type=int, default=32)
186
- parser.add_argument("--output_csv", type=str, default="eval_new_data_results.csv")
187
- parser.add_argument("--epochs", type=str, default=None,
188
- help="Comma separated list of epochs (e.g. '1,5,10'), default is all")
189
- parser.add_argument("--eval_mode", type=str, default="masked",
190
- choices=["masked", "full_recon"])
191
- parser.add_argument("--mask_ratio", type=float, default=0.75)
192
- parser.add_argument("--seed", type=int, default=2021)
193
- parser.add_argument("--num_workers", type=int, default=4)
194
- parser.add_argument("--amp_dtype", type=str, default="bfloat16",
195
- choices=["float32", "bfloat16", "float16"])
196
- args = parser.parse_args()
197
-
198
- # ---- DDP initialization ----
199
- dist.init_process_group(backend='nccl')
200
- local_rank = int(os.environ.get('LOCAL_RANK', 0))
201
- rank = dist.get_rank()
202
- world_size = dist.get_world_size()
203
- device = torch.device(f'cuda:{local_rank}')
204
- torch.cuda.set_device(device)
205
- is_master = (rank == 0)
206
-
207
- ptdtype = {'float32': torch.float32, 'bfloat16': torch.bfloat16, 'float16': torch.float16}[args.amp_dtype]
208
-
209
- if is_master:
210
- print(f"World size: {world_size}", flush=True)
211
- print(f"Eval mode: {args.eval_mode}", flush=True)
212
- if args.eval_mode == "masked":
213
- print(f"Mask ratio: {args.mask_ratio}", flush=True)
214
-
215
- # ---- Load model configuration ----
216
- if is_master:
217
- print(f"\nLoad model configuration: {args.ckpt_dir}/{args.model_name}.pkl", flush=True)
218
- model_config = load_model_config(args.ckpt_dir, args.model_name)
219
- removed = filter_model_config(model_config)
220
- if is_master and removed:
221
- for k, v in removed.items():
222
- print(f" Skip obsolete parameters: {k}={v!r}", flush=True)
223
-
224
- if args.gene2vec_data_path:
225
- model_config["gene2vec_file"] = args.gene2vec_data_path
226
-
227
- if "module_sets" in model_config and model_config["module_sets"]:
228
- if is_master:
229
- print(f"Loaded from pkl config {len(model_config['module_sets'])} modules", flush=True)
230
- else:
231
- if args.module_data_dir is None:
232
- raise ValueError("There are no module_sets in the model configuration, please specify via --module_data_dir")
233
- model_config["module_sets"] = load_module_data(
234
- module_data_dir=args.module_data_dir,
235
- gene_list_path=args.gene_list_path,
236
- gene_mapping_path=args.gene_mapping_path,
237
- verbose=is_master,
238
- )
239
-
240
- # ---- Discover checkpoints & data sets ----
241
- epoch_filter = set(int(e) for e in args.epochs.split(",")) if args.epochs else None
242
- checkpoints = discover_checkpoints(args.ckpt_dir, args.model_name, epoch_filter)
243
- datasets = discover_datasets(args.data_dir)
244
-
245
- if is_master:
246
- if not checkpoints:
247
- print(f"checkpoint not found: {args.ckpt_dir}/{args.model_name}_epoch*.pth", flush=True)
248
- else:
249
- print(f"turn up {len(checkpoints)} checkpoints: {[e for e, _ in checkpoints]}", flush=True)
250
- for ds_name, _, npz_files in datasets:
251
- print(f"Dataset {ds_name}: {len(npz_files)} donors", flush=True)
252
- if not datasets:
253
- print(f"Warning: in {args.data_dir} The subdirectory containing the .npz file was not found!", flush=True)
254
-
255
- if not checkpoints or not datasets:
256
- dist.destroy_process_group()
257
- return
258
-
259
- # ----CSV (master write only)----
260
- csv_file, writer = None, None
261
- if is_master:
262
- csv_file = open(args.output_csv, 'w', newline='', encoding='utf-8')
263
- writer = csv.writer(csv_file)
264
- writer.writerow(["epoch", "dataset", "donor", "num_cells", "mean_loss", "eval_mode"])
265
-
266
- # ----Build the model (create it only once, only reload the weights every epoch)----
267
- model = PerformerEncoderOnly_Module(**model_config)
268
- model.to(device)
269
-
270
- # ---- Main loop ----
271
- summary = defaultdict(float)
272
-
273
- for epoch_num, ckpt_path in checkpoints:
274
- if is_master:
275
- print(f"\n{'='*60}", flush=True)
276
- print(f"Epoch {epoch_num}: {os.path.basename(ckpt_path)}", flush=True)
277
- print(f"{'='*60}", flush=True)
278
-
279
- ckpt = torch.load(ckpt_path, map_location=device)
280
- model.load_state_dict(ckpt['model_state_dict'])
281
- del ckpt
282
- torch.cuda.empty_cache()
283
- model.eval()
284
-
285
- for ds_name, ds_dir, npz_files in datasets:
286
- ds_losses = []
287
- ds_cells_total = 0
288
- t_ds_start = time.time()
289
-
290
- for donor_idx, npz_file in enumerate(npz_files):
291
- donor_id = npz_file.replace('.npz', '')
292
- npz_path = os.path.join(ds_dir, npz_file)
293
-
294
- data_npz = np.load(npz_path, allow_pickle=True)
295
- csr = sparse.csr_matrix(
296
- (data_npz['data'], data_npz['indices'], data_npz['indptr']),
297
- shape=tuple(data_npz['shape'])
298
- )
299
- num_cells = csr.shape[0]
300
-
301
- dataset_obj = DonorSCDataset(csr)
302
- sampler = DistributedSampler(dataset_obj, num_replicas=world_size,
303
- rank=rank, shuffle=False, drop_last=False)
304
- loader = DataLoader(
305
- dataset_obj, batch_size=args.batch_size, sampler=sampler,
306
- num_workers=args.num_workers, pin_memory=True, drop_last=False,
307
- )
308
-
309
- donor_seed = args.seed + donor_idx if args.eval_mode == "masked" else None
310
- local_loss_sum, local_batch_count = evaluate_donor_ddp(
311
- model, loader, device, args.eval_mode,
312
- args.mask_ratio, ptdtype, seed=donor_seed,
313
- )
314
-
315
- global_loss_sum = reduce_scalar(local_loss_sum, device)
316
- global_batch_count = reduce_scalar(local_batch_count, device)
317
-
318
- if global_batch_count > 0:
319
- mean_loss = global_loss_sum / global_batch_count
320
- else:
321
- mean_loss = 0.0
322
-
323
- if is_master:
324
- writer.writerow([epoch_num, ds_name, donor_id, num_cells,
325
- f"{mean_loss:.6f}", args.eval_mode])
326
- csv_file.flush()
327
- ds_losses.append(mean_loss)
328
- ds_cells_total += num_cells
329
-
330
- if (donor_idx + 1) % 10 == 0 or (donor_idx + 1) == len(npz_files):
331
- print(f" {ds_name}: {donor_idx+1}/{len(npz_files)} donors done", flush=True)
332
-
333
- dist.barrier()
334
-
335
- if is_master:
336
- ds_mean = np.mean(ds_losses) if ds_losses else 0.0
337
- print(f" {ds_name}: mean_loss={ds_mean:.6f}, "
338
- f"total_cells={ds_cells_total}, time={time.time()-t_ds_start:.1f}s", flush=True)
339
- summary[(epoch_num, ds_name)] = ds_mean
340
-
341
- torch.cuda.empty_cache()
342
- dist.barrier()
343
-
344
- del model
345
- torch.cuda.empty_cache()
346
-
347
- # ---- Summary table ----
348
- if is_master:
349
- csv_file.close()
350
- print(f"\nDetailed results have been saved to: {args.output_csv}")
351
- print(f"\n{'='*60}")
352
- print("summary table")
353
- print(f"{'='*60}")
354
- ds_names = sorted(set(ds for _, ds in summary.keys()))
355
- header = f"{'Epoch':>8}" + "".join(f"{ds:>16}" for ds in ds_names)
356
- print(header)
357
- print("-" * len(header))
358
- for epoch_num, _ in checkpoints:
359
- row = f"{epoch_num:>8}"
360
- for ds in ds_names:
361
- row += f"{summary.get((epoch_num, ds), float('nan')):>16.6f}"
362
- print(row)
363
-
364
- dist.destroy_process_group()
365
-
366
-
367
- if __name__ == "__main__":
368
- import traceback
369
- try:
370
- main()
371
- except Exception:
372
- traceback.print_exc()
373
- sys.stdout.flush()
374
- sys.stderr.flush()
375
- sys.exit(1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -50,7 +50,7 @@ Geometric 2.6. See [environment.yml](BioMod/environment.yml) and
50
  [requirements.txt](BioMod/requirements.txt) for the complete dependency lists.
51
  Match the GPU packages to your system when adapting the environment.
52
 
53
- Pretraining and new-data reconstruction evaluation use CUDA/NCCL. The zero-shot
54
  runner selects CUDA when available and otherwise uses CPU. Native Windows
55
  execution has not been validated for the supplied environment.
56
 
@@ -88,14 +88,13 @@ to end with the bundled checkpoint.
88
  | Drug-pair synergy classification | [Training script](BioMod/drug_combination/train_drug_combination_performer_module.py) | [Notebook](BioMod/toturials/drug_combination.ipynb) |
89
  | Gene perturbation response prediction | [Training script](BioMod/gene_perturbation/train_module.py) | [Notebook](BioMod/toturials/gene_perturbation.ipynb) |
90
 
91
- ### Pretraining, evaluation, and analysis scripts
92
 
93
  The repository also includes the following supporting workflows.
94
 
95
  | Workflow | Implementation | Documentation |
96
  | --- | --- | --- |
97
  | Pretraining and continued pretraining | [Pretraining script](BioMod/performer_module.py) | [Source guide](BioMod/README.md), [launcher reference](BioMod/command.txt) |
98
- | Expression reconstruction evaluation on new data | [Evaluation script](BioMod/eval_new_data/evaluate_new_data.py) | [Launcher reference](BioMod/eval_new_data/cmd.txtx) |
99
  | Module interpretability analysis | [Module geometry](BioMod/interpretability/module_geometry.py), [module importance](BioMod/interpretability/module_importance/module_importance.py), [text alignment](BioMod/interpretability/module_text_alignment/module_text_alignment.py) | [Overview](BioMod/interpretability/README.md), [attention](BioMod/interpretability/attention/README.md), [module importance guide](BioMod/interpretability/module_importance/README.md) |
100
 
101
  Tutorials and launcher references retain some historical checkpoint paths,
@@ -138,9 +137,6 @@ Updating environment variables alone does not replace hard-coded notebook paths.
138
  | Cell annotation / backbone interpretation | `--performer_checkpoint` | `--config_pkl` |
139
  | Continued pretraining | `--model_path` | `--model_config_path` or paired-file discovery |
140
 
141
- New-data reconstruction evaluation has a separate checkpoint-directory interface;
142
- refer to its script for the corresponding arguments.
143
-
144
  ### Data and evaluation
145
 
146
  Datasets are stored under `data/`. Input formats, metadata fields, preprocessing,
@@ -186,9 +182,6 @@ BioMod/ # Project root: source
186
  │ ├── drug_response/ # Single-drug response prediction and training
187
  │ │ ├── cmd.txt
188
  │ │ └── train_drug_performer_module.py
189
- │ ├── eval_new_data/ # Reconstruction error and generalization on new data
190
- │ │ ├── cmd.txtx
191
- │ │ └── evaluate_new_data.py
192
  │ ├── gene_perturbation/ # Perturbation response prediction: processing, training and inference
193
  │ │ ├── data_utils_local.py
194
  │ │ ├── gears_reduce_local.py
 
50
  [requirements.txt](BioMod/requirements.txt) for the complete dependency lists.
51
  Match the GPU packages to your system when adapting the environment.
52
 
53
+ Pretraining uses CUDA/NCCL. The zero-shot
54
  runner selects CUDA when available and otherwise uses CPU. Native Windows
55
  execution has not been validated for the supplied environment.
56
 
 
88
  | Drug-pair synergy classification | [Training script](BioMod/drug_combination/train_drug_combination_performer_module.py) | [Notebook](BioMod/toturials/drug_combination.ipynb) |
89
  | Gene perturbation response prediction | [Training script](BioMod/gene_perturbation/train_module.py) | [Notebook](BioMod/toturials/gene_perturbation.ipynb) |
90
 
91
+ ### Pretraining and analysis scripts
92
 
93
  The repository also includes the following supporting workflows.
94
 
95
  | Workflow | Implementation | Documentation |
96
  | --- | --- | --- |
97
  | Pretraining and continued pretraining | [Pretraining script](BioMod/performer_module.py) | [Source guide](BioMod/README.md), [launcher reference](BioMod/command.txt) |
 
98
  | Module interpretability analysis | [Module geometry](BioMod/interpretability/module_geometry.py), [module importance](BioMod/interpretability/module_importance/module_importance.py), [text alignment](BioMod/interpretability/module_text_alignment/module_text_alignment.py) | [Overview](BioMod/interpretability/README.md), [attention](BioMod/interpretability/attention/README.md), [module importance guide](BioMod/interpretability/module_importance/README.md) |
99
 
100
  Tutorials and launcher references retain some historical checkpoint paths,
 
137
  | Cell annotation / backbone interpretation | `--performer_checkpoint` | `--config_pkl` |
138
  | Continued pretraining | `--model_path` | `--model_config_path` or paired-file discovery |
139
 
 
 
 
140
  ### Data and evaluation
141
 
142
  Datasets are stored under `data/`. Input formats, metadata fields, preprocessing,
 
182
  │ ├── drug_response/ # Single-drug response prediction and training
183
  │ │ ├── cmd.txt
184
  │ │ └── train_drug_performer_module.py
 
 
 
185
  │ ├── gene_perturbation/ # Perturbation response prediction: processing, training and inference
186
  │ │ ├── data_utils_local.py
187
  │ │ ├── gears_reduce_local.py