faisalAI27 commited on
Commit
4bf7f42
·
1 Parent(s): f66847f

training completed

Browse files
.gitignore CHANGED
@@ -1,91 +1,84 @@
1
- # Environment files
2
- .env
3
- .env.*
4
- !.env.example
5
- .env.local
6
 
7
  # Python
8
  __pycache__/
9
  *.py[cod]
10
- *.pyc
11
  *.pyo
12
  *.pyd
13
- .Python
14
- .venv/
15
- venv/
16
- env/
17
  .pytest_cache/
18
  .mypy_cache/
19
  .ruff_cache/
20
- .coverage
21
- htmlcov/
22
- dist/
23
- build/
24
- *.egg-info/
25
 
26
- # Node / Next.js
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  node_modules/
28
  .next/
29
  out/
30
- coverage/
 
31
  npm-debug.log*
32
  yarn-debug.log*
33
  yarn-error.log*
34
- pnpm-debug.log*
35
- *.tsbuildinfo
36
 
37
- # Colab / notebooks
38
- .ipynb_checkpoints/
39
- *.ipynb_checkpoints
40
- drive/
41
- wandb/
42
- lightning_logs/
43
- runs/
44
  checkpoints/
 
 
 
 
 
 
 
 
 
 
45
 
46
- # Genomics data and model artifacts
47
- data/raw/
48
- data/processed/
49
- *.vcf
50
- *.vcf.gz
51
- *.vcf.gz.tbi
52
- *.fa
53
- *.fasta
54
- *.fa.gz
55
- *.fasta.gz
56
- *.fai
57
- *.2bit
58
- *.bam
59
- *.bai
60
- *.cram
61
- *.crai
62
- *.bed
63
- *.jsonl
64
- *.parquet
65
- *.arrow
66
- checkpoints/
67
- models/
68
- model/
69
- outputs/
70
- artifacts/
71
- training/data/
72
- training/output/
73
- training/outputs/
74
- training/cache/
75
- training/data_cache/
76
  training/csv_files_large/
77
  training/csv_files_large_alt/
78
  training/csv_files_10k/
79
  training/csv_files_10k_alt/
80
  training/csv_files_20k/
81
  training/csv_files_20k_alt/
 
 
 
 
 
 
 
 
 
 
 
 
82
  training/local_dnabert2_patch/
83
- *.zip
84
- *.safetensors
85
- *.bin
86
 
87
- # OS/editor
88
- .DS_Store
89
- .idea/
90
- .vscode/
91
- *.swp
 
 
 
1
+ # OS files
2
+ .DS_Store
3
+ Thumbs.db
 
 
4
 
5
  # Python
6
  __pycache__/
7
  *.py[cod]
 
8
  *.pyo
9
  *.pyd
 
 
 
 
10
  .pytest_cache/
11
  .mypy_cache/
12
  .ruff_cache/
 
 
 
 
 
13
 
14
+ # Virtual environments
15
+ .venv/
16
+ venv/
17
+ env/
18
+ ENV/
19
+
20
+ # Jupyter
21
+ .ipynb_checkpoints/
22
+
23
+ # Environment variables and secrets
24
+ .env
25
+ .env.*
26
+ !.env.example
27
+
28
+ # Node / frontend
29
  node_modules/
30
  .next/
31
  out/
32
+ dist/
33
+ build/
34
  npm-debug.log*
35
  yarn-debug.log*
36
  yarn-error.log*
 
 
37
 
38
+ # Training outputs and model artifacts
39
+ training/outputs/
40
+ training/output/
41
+ training/checkpoints/
42
+ training/training_model_files/
 
 
43
  checkpoints/
44
+ runs/
45
+ wandb/
46
+ *.zip
47
+ *.tar
48
+ *.tar.gz
49
+ *.safetensors
50
+ *.bin
51
+ *.pt
52
+ *.pth
53
+ *.ckpt
54
 
55
+ # Generated datasets
56
+ training/csv_files/
57
+ training/csv_files_alt/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  training/csv_files_large/
59
  training/csv_files_large_alt/
60
  training/csv_files_10k/
61
  training/csv_files_10k_alt/
62
  training/csv_files_20k/
63
  training/csv_files_20k_alt/
64
+ training/data_cache/
65
+ training/cache/
66
+ data/raw/
67
+ data/interim/
68
+ data/processed/
69
+
70
+ # HuggingFace / model cache
71
+ .cache/
72
+ hf_cache/
73
+ huggingface_cache/
74
+
75
+ # Local DNABERT generated patch/cache
76
  training/local_dnabert2_patch/
 
 
 
77
 
78
+ # Logs
79
+ *.log
80
+ logs/
81
+
82
+ # Temporary files
83
+ *.tmp
84
+ *.temp
README.md CHANGED
@@ -60,11 +60,21 @@ Open `http://localhost:3000`.
60
 
61
  Training is intended for Google Colab only. Do not train the model locally.
62
 
63
- 1. Open `training/colab_dnabert2_clinvar_finetune.ipynb` in Google Colab.
64
  2. Follow the notebook cells to install dependencies, download ClinVar GRCh38 data, prepare examples, and fine-tune DNABERT-2.
65
  3. Export the trained Hugging Face model directory.
66
  4. Point the backend `MODEL_DIR` environment variable to that exported model directory.
67
 
 
 
 
 
 
 
 
 
 
 
68
  ## Environment Files
69
 
70
  Each app folder includes a `.env.example`. Do not commit `.env`, `.env.local`, API keys, model checkpoints, or private data.
 
60
 
61
  Training is intended for Google Colab only. Do not train the model locally.
62
 
63
+ 1. Open `training/colab_dnabert2_heavy_training.ipynb` in Google Colab for the current heavy-training workflow.
64
  2. Follow the notebook cells to install dependencies, download ClinVar GRCh38 data, prepare examples, and fine-tune DNABERT-2.
65
  3. Export the trained Hugging Face model directory.
66
  4. Point the backend `MODEL_DIR` environment variable to that exported model directory.
67
 
68
+ ## Data and Model Artifact Policy
69
+
70
+ - Full datasets are not committed to GitHub.
71
+ - Trained model weights are not committed to GitHub.
72
+ - Use the training scripts or Colab notebook to regenerate datasets.
73
+ - Store trained models in Google Drive or local storage.
74
+ - For local evaluation, place the 20k alternate-sequence dataset in `training/csv_files_20k_alt/`.
75
+ - For backend use, place the model folder in `backend/models/final_model/` or set the backend model path environment variable.
76
+ - The local final model folder `training/training_model_files/` and the 20k CSV folder `training/csv_files_20k_alt/` are intentionally ignored by Git.
77
+
78
  ## Environment Files
79
 
80
  Each app folder includes a `.env.example`. Do not commit `.env`, `.env.local`, API keys, model checkpoints, or private data.
docs/MODEL_ARTIFACTS.md ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Model Artifacts
2
+
3
+ Large trained models are not stored in GitHub.
4
+
5
+ The final cleaned model folder is kept locally at:
6
+
7
+ ```text
8
+ training/training_model_files/
9
+ ```
10
+
11
+ This folder is ignored by Git because it contains large model files such as
12
+ `model.safetensors` and `training_args.bin`.
13
+
14
+ The current best model is the 20k DNABERT-2 alternate-sequence model. Model
15
+ artifacts should be stored in Google Drive or downloaded local storage rather
16
+ than committed to the repository.
17
+
18
+ For backend use, either:
19
+
20
+ - place the final model folder at `backend/models/final_model/`, or
21
+ - configure the backend model path environment variable to point to
22
+ `training/training_model_files/`.
23
+
24
+ Keep these small metadata files with the model artifact:
25
+
26
+ - `metrics.json`
27
+ - `full_eval_metrics.json`
28
+
29
+ The 20k CSV dataset is also ignored by Git:
30
+
31
+ ```text
32
+ training/csv_files_20k_alt/
33
+ ```
34
+
35
+ Store that dataset externally or regenerate it with the training scripts and
36
+ Colab notebook.
37
+
38
+ This project is for research and education only. It is not a clinical
39
+ diagnostic tool.
docs/results/DNABERT2_20K_RESULTS.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DNABERT-2 20k Result
2
+
3
+ Final confirmed local evaluation on `training/csv_files_20k_alt/`:
4
+
5
+ - Accuracy: 0.5537
6
+ - Precision: 0.5384
7
+ - Recall: 0.7533
8
+ - F1: 0.6280
9
+ - MCC: 0.1171
10
+ - AUC ROC: 0.5928
11
+ - Threshold: 0.1600
12
+
13
+ The model was fine-tuned for binary ClinVar alternate-sequence classification
14
+ using GRCh38 variant-centered sequences.
15
+
16
+ This model is for research/demo use only and is not a clinical diagnostic tool.
docs/results/dnabert2_20k_full_eval_metrics.json ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_dir": "/Users/faisalimran/Desktop/tarns_project/training/training_model_files",
3
+ "dataset_dir": "/Users/faisalimran/Desktop/tarns_project/training/csv_files_20k_alt",
4
+ "device": "mps",
5
+ "max_length": 512,
6
+ "variant_center_index": 512,
7
+ "batch_size": 1,
8
+ "threshold_args": {
9
+ "tune_threshold": true,
10
+ "threshold": null,
11
+ "threshold_min": 0.1,
12
+ "threshold_max": 0.9,
13
+ "threshold_step": 0.01
14
+ },
15
+ "threshold": 0.16,
16
+ "selected_threshold": 0.16,
17
+ "threshold_selection": {
18
+ "threshold_min": 0.1,
19
+ "threshold_max": 0.9,
20
+ "threshold_step": 0.01,
21
+ "thresholds_tested": 81,
22
+ "best_threshold": 0.16,
23
+ "best_validation_mcc": 0.15644505036060144,
24
+ "mode": "full_validation_mcc",
25
+ "selected_threshold": 0.16
26
+ },
27
+ "validation_metrics": {
28
+ "threshold": 0.16,
29
+ "accuracy": 0.5723333333333334,
30
+ "precision": 0.552390149686142,
31
+ "recall": 0.7626666666666667,
32
+ "f1": 0.6407168860263232,
33
+ "mcc": 0.15644505036060144,
34
+ "auc_roc": 0.6018537777777778,
35
+ "confusion_matrix": [
36
+ [
37
+ 573,
38
+ 927
39
+ ],
40
+ [
41
+ 356,
42
+ 1144
43
+ ]
44
+ ],
45
+ "rows": 3000
46
+ },
47
+ "test_metrics": {
48
+ "threshold": 0.16,
49
+ "accuracy": 0.5536666666666666,
50
+ "precision": 0.5383515959980943,
51
+ "recall": 0.7533333333333333,
52
+ "f1": 0.6279522089469297,
53
+ "mcc": 0.1170731385436657,
54
+ "auc_roc": 0.5927693333333333,
55
+ "confusion_matrix": [
56
+ [
57
+ 531,
58
+ 969
59
+ ],
60
+ [
61
+ 370,
62
+ 1130
63
+ ]
64
+ ],
65
+ "rows": 3000
66
+ }
67
+ }
docs/results/dnabert2_20k_metrics.json ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_name": "zhihan1996/DNABERT-2-117M",
3
+ "model_path": "/Users/faisalimran/Desktop/tarns_project/training/local_dnabert2_patch",
4
+ "train_csv": "/Users/faisalimran/Desktop/tarns_project/training/csv_files_large_alt/train_with_alt_sequences.csv",
5
+ "validation_csv": "/Users/faisalimran/Desktop/tarns_project/training/csv_files_large_alt/val_with_alt_sequences.csv",
6
+ "test_csv": "/Users/faisalimran/Desktop/tarns_project/training/csv_files_large_alt/test_with_alt_sequences.csv",
7
+ "dataset_dir": "/Users/faisalimran/Desktop/tarns_project/training/csv_files_large_alt",
8
+ "sequence_column": "sequence",
9
+ "is_alt_sequence_dataset": true,
10
+ "is_large_alt_sequence_dataset": true,
11
+ "device": "mps",
12
+ "freeze_encoder": true,
13
+ "unfreeze_last_n_layers": 2,
14
+ "freeze_embeddings": true,
15
+ "use_class_weights": true,
16
+ "class_weights": [
17
+ 1.0,
18
+ 1.0
19
+ ],
20
+ "center_crop": true,
21
+ "tune_threshold": true,
22
+ "save_eval_each_epoch": false,
23
+ "eval_accumulation_steps": 1,
24
+ "eval_subset_size": 750,
25
+ "selected_threshold": 0.41,
26
+ "best_validation_mcc_for_threshold": 0.12554169541533072,
27
+ "sample_size": 0,
28
+ "epochs": 5.0,
29
+ "max_length": 512,
30
+ "variant_center_index": 512,
31
+ "train_metrics": {
32
+ "train_runtime": 2529.8996,
33
+ "train_samples_per_second": 6.917,
34
+ "train_steps_per_second": 0.866,
35
+ "total_flos": 6124490695680000.0,
36
+ "train_loss": 0.6810980735848483,
37
+ "epoch": 5.0
38
+ },
39
+ "train_rows": 3500,
40
+ "validation_rows": 750,
41
+ "test_rows": 750,
42
+ "validation_eval_rows": 750,
43
+ "test_eval_rows": 750,
44
+ "validation_metrics": {
45
+ "selected_threshold": 0.41,
46
+ "accuracy": 0.54,
47
+ "precision": 0.5225903614457831,
48
+ "recall": 0.9253333333333333,
49
+ "f1": 0.6679499518768046,
50
+ "mcc": 0.12554169541533072,
51
+ "auc_roc": 0.572736,
52
+ "confusion_matrix": [
53
+ [
54
+ 58,
55
+ 317
56
+ ],
57
+ [
58
+ 28,
59
+ 347
60
+ ]
61
+ ]
62
+ },
63
+ "test_metrics": {
64
+ "selected_threshold": 0.41,
65
+ "accuracy": 0.512,
66
+ "precision": 0.5067873303167421,
67
+ "recall": 0.896,
68
+ "f1": 0.6473988439306358,
69
+ "mcc": 0.03747366058909428,
70
+ "auc_roc": 0.5497315555555555,
71
+ "confusion_matrix": [
72
+ [
73
+ 48,
74
+ 327
75
+ ],
76
+ [
77
+ 39,
78
+ 336
79
+ ]
80
+ ]
81
+ }
82
+ }
training/COLAB_README.md CHANGED
@@ -31,3 +31,13 @@ medical diagnosis and should not be used for clinical decisions.
31
  - Do not add API keys or secrets to the notebook.
32
  - Dataset preparation uses caching and progress files, so reruns can resume.
33
  - The training script uses CUDA when available, MPS on Mac, and CPU as a slow fallback.
 
 
 
 
 
 
 
 
 
 
 
31
  - Do not add API keys or secrets to the notebook.
32
  - Dataset preparation uses caching and progress files, so reruns can resume.
33
  - The training script uses CUDA when available, MPS on Mac, and CPU as a slow fallback.
34
+
35
+ ## Data and Model Artifact Policy
36
+
37
+ - Full datasets are not committed to GitHub.
38
+ - Trained model weights are not committed to GitHub.
39
+ - Use the training scripts or Colab notebook to regenerate datasets.
40
+ - Store trained models in Google Drive or local storage.
41
+ - For local evaluation, place the 20k alternate-sequence dataset in `training/csv_files_20k_alt/`.
42
+ - For backend use, place the model folder in `backend/models/final_model/` or set the backend model path environment variable.
43
+ - Keep `metrics.json` and `full_eval_metrics.json` with the exported model artifact.
training/colab_dnabert2_heavy_training.ipynb CHANGED
The diff for this file is too large to render. See raw diff
 
training/csv_files/README.md DELETED
@@ -1,11 +0,0 @@
1
- # Sequence CSV Files
2
-
3
- Place the sequence-enriched ClinVar split files here for Colab training:
4
-
5
- - `train_with_sequences.csv`
6
- - `val_with_sequences.csv`
7
- - `test_with_sequences.csv`
8
-
9
- These files are generated by `training/01_prepare_clinvar_dataset.ipynb`.
10
-
11
- Large generated CSVs should usually be stored outside Git unless you intentionally want to version the sample dataset.
 
 
 
 
 
 
 
 
 
 
 
 
training/csv_files/test_with_sequences.csv DELETED
The diff for this file is too large to render. See raw diff
 
training/csv_files/train_with_sequences.csv DELETED
The diff for this file is too large to render. See raw diff
 
training/csv_files/val_with_sequences.csv DELETED
The diff for this file is too large to render. See raw diff
 
training/csv_files_alt/test_with_alt_sequences.csv DELETED
The diff for this file is too large to render. See raw diff
 
training/csv_files_alt/train_with_alt_sequences.csv DELETED
The diff for this file is too large to render. See raw diff
 
training/csv_files_alt/val_with_alt_sequences.csv DELETED
The diff for this file is too large to render. See raw diff
 
training/evaluate_saved_model.py CHANGED
@@ -45,6 +45,7 @@ from training.train_smoke_test import ( # noqa: E402
45
 
46
  OUTPUT_DIR = PROJECT_ROOT / "training" / "outputs" / "dnabert2_clinvar"
47
  MODEL_DIR = OUTPUT_DIR / "final_model"
 
48
  TRAINING_METRICS_PATH = OUTPUT_DIR / "metrics.json"
49
  FULL_EVAL_METRICS_PATH = OUTPUT_DIR / "full_eval_metrics.json"
50
 
@@ -120,6 +121,15 @@ def parse_args() -> argparse.Namespace:
120
  default=0.01,
121
  help="Threshold step size when tuning.",
122
  )
 
 
 
 
 
 
 
 
 
123
  return parser.parse_args()
124
 
125
 
@@ -140,6 +150,26 @@ def resolve_project_path(path: Path) -> Path:
140
  return PROJECT_ROOT / path
141
 
142
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  def choose_device() -> str:
144
  if torch.cuda.is_available():
145
  return "cuda"
@@ -338,20 +368,20 @@ def load_saved_model_with_local_patch(model_dir: Path):
338
 
339
 
340
  def load_saved_model(model_dir: Path, device: str):
341
- if device == "cuda":
342
- print("CUDA detected. Trying to load saved model directly first.")
343
- try:
344
- model = AutoModelForSequenceClassification.from_pretrained(
345
- str(model_dir),
346
- trust_remote_code=True,
347
- low_cpu_mem_usage=False,
348
- )
349
- print("Saved model loaded successfully without the local Mac patch.")
350
- return model
351
- except Exception as error:
352
- print("Direct saved-model load failed.")
353
- print(f"Direct load error: {error}")
354
- print("Falling back to local no-Triton DNABERT-2 code.")
355
 
356
  return load_saved_model_with_local_patch(model_dir)
357
 
@@ -519,14 +549,12 @@ def main() -> None:
519
  args = parse_args()
520
  validate_args(args)
521
 
522
- if not MODEL_DIR.exists():
523
- raise FileNotFoundError(f"Saved model directory not found: {MODEL_DIR}")
524
-
525
  dataset_dir = find_dataset_dir()
526
  device = choose_device()
527
 
528
  print("Memory-safe saved model evaluation")
529
- print(f"Saved model directory: {MODEL_DIR}")
530
  print(f"Selected dataset directory: {dataset_dir}")
531
  print(f"Selected device: {device}")
532
  print(f"Tune threshold on full validation set: {args.tune_threshold}")
@@ -542,11 +570,11 @@ def main() -> None:
542
  print()
543
 
544
  print("Loading tokenizer and model.")
545
- tokenizer = AutoTokenizer.from_pretrained(str(MODEL_DIR), trust_remote_code=True)
546
- model = load_saved_model(MODEL_DIR, device)
547
 
548
  all_metrics = {
549
- "model_dir": str(MODEL_DIR),
550
  "dataset_dir": str(dataset_dir),
551
  "device": device,
552
  "max_length": MAX_LENGTH,
 
45
 
46
  OUTPUT_DIR = PROJECT_ROOT / "training" / "outputs" / "dnabert2_clinvar"
47
  MODEL_DIR = OUTPUT_DIR / "final_model"
48
+ UPLOADED_MODEL_DIR = PROJECT_ROOT / "training" / "training_model_files"
49
  TRAINING_METRICS_PATH = OUTPUT_DIR / "metrics.json"
50
  FULL_EVAL_METRICS_PATH = OUTPUT_DIR / "full_eval_metrics.json"
51
 
 
121
  default=0.01,
122
  help="Threshold step size when tuning.",
123
  )
124
+ parser.add_argument(
125
+ "--model_dir",
126
+ type=Path,
127
+ default=None,
128
+ help=(
129
+ "Saved model folder to evaluate. Defaults to training/outputs/dnabert2_clinvar/final_model; "
130
+ "if that is missing, falls back to training/training_model_files."
131
+ ),
132
+ )
133
  return parser.parse_args()
134
 
135
 
 
150
  return PROJECT_ROOT / path
151
 
152
 
153
+ def choose_model_dir(requested_model_dir: Path | None) -> Path:
154
+ if requested_model_dir is not None:
155
+ model_dir = resolve_project_path(requested_model_dir)
156
+ if not model_dir.exists():
157
+ raise FileNotFoundError(f"Requested saved model directory not found: {model_dir}")
158
+ return model_dir
159
+
160
+ if MODEL_DIR.exists():
161
+ return MODEL_DIR
162
+
163
+ if UPLOADED_MODEL_DIR.exists():
164
+ return UPLOADED_MODEL_DIR
165
+
166
+ raise FileNotFoundError(
167
+ "Saved model directory not found. Searched:\n"
168
+ f"{MODEL_DIR}\n"
169
+ f"{UPLOADED_MODEL_DIR}"
170
+ )
171
+
172
+
173
  def choose_device() -> str:
174
  if torch.cuda.is_available():
175
  return "cuda"
 
368
 
369
 
370
  def load_saved_model(model_dir: Path, device: str):
371
+ print(f"Trying to load saved model directly from: {model_dir}")
372
+ print(f"Selected device: {device}")
373
+ try:
374
+ model = AutoModelForSequenceClassification.from_pretrained(
375
+ str(model_dir),
376
+ trust_remote_code=True,
377
+ low_cpu_mem_usage=False,
378
+ )
379
+ print("Saved model loaded cleanly")
380
+ return model
381
+ except Exception as error:
382
+ print("Direct saved-model loading failed.")
383
+ print(f"Reason: {type(error).__name__}: {error}")
384
+ print("Falling back to training/local_dnabert2_patch.")
385
 
386
  return load_saved_model_with_local_patch(model_dir)
387
 
 
549
  args = parse_args()
550
  validate_args(args)
551
 
552
+ model_dir = choose_model_dir(args.model_dir)
 
 
553
  dataset_dir = find_dataset_dir()
554
  device = choose_device()
555
 
556
  print("Memory-safe saved model evaluation")
557
+ print(f"Saved model directory: {model_dir}")
558
  print(f"Selected dataset directory: {dataset_dir}")
559
  print(f"Selected device: {device}")
560
  print(f"Tune threshold on full validation set: {args.tune_threshold}")
 
570
  print()
571
 
572
  print("Loading tokenizer and model.")
573
+ tokenizer = AutoTokenizer.from_pretrained(str(model_dir), trust_remote_code=True)
574
+ model = load_saved_model(model_dir, device)
575
 
576
  all_metrics = {
577
+ "model_dir": str(model_dir),
578
  "dataset_dir": str(dataset_dir),
579
  "device": device,
580
  "max_length": MAX_LENGTH,
training/train_local_dnabert2.py CHANGED
@@ -13,6 +13,7 @@ import gc
13
  import inspect
14
  import json
15
  import os
 
16
  import sys
17
  import zipfile
18
  from dataclasses import dataclass
@@ -737,6 +738,45 @@ def create_patch_from_project_root() -> Path:
737
  return resolve_path(patch_dir)
738
 
739
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
740
  def load_model_from_source(model_source: str | Path):
741
  config = AutoConfig.from_pretrained(str(model_source), trust_remote_code=True)
742
  config = disable_flash_attention_on_config(config)
@@ -1157,6 +1197,7 @@ def main() -> None:
1157
  final_model_dir.mkdir(parents=True, exist_ok=True)
1158
  trainer.save_model(str(final_model_dir))
1159
  tokenizer.save_pretrained(str(final_model_dir))
 
1160
 
1161
  metrics = {
1162
  "model_name": MODEL_NAME,
@@ -1196,6 +1237,7 @@ def main() -> None:
1196
  "test_eval_rows": len(eval_test_df),
1197
  "validation_metrics": validation_metrics,
1198
  "test_metrics": test_metrics,
 
1199
  }
1200
  metrics_path = save_metrics(output_dir, metrics)
1201
  zip_path = zip_final_model(output_dir, final_model_dir)
 
13
  import inspect
14
  import json
15
  import os
16
+ import shutil
17
  import sys
18
  import zipfile
19
  from dataclasses import dataclass
 
738
  return resolve_path(patch_dir)
739
 
740
 
741
+ def ensure_local_patch_for_export() -> Path:
742
+ patch_dir = resolve_path(LOCAL_DNABERT2_PATCH_DIR)
743
+ if patch_dir.exists() and local_patch_is_ready(patch_dir):
744
+ return patch_dir
745
+
746
+ print("Creating local no-Triton DNABERT-2 patch for self-contained model export.")
747
+ return create_patch_from_project_root()
748
+
749
+
750
+ def copy_dnabert2_custom_code_to_final_model(final_model_dir: Path) -> list[str]:
751
+ """Make final_model self-contained for trust_remote_code=True loading."""
752
+ patch_dir = ensure_local_patch_for_export()
753
+ final_model_dir.mkdir(parents=True, exist_ok=True)
754
+
755
+ py_files = sorted(patch_dir.glob("*.py"))
756
+ if not py_files:
757
+ raise FileNotFoundError(f"No DNABERT-2 Python code files found in local patch: {patch_dir}")
758
+
759
+ copied_files: list[str] = []
760
+ for source_path in py_files:
761
+ destination_path = final_model_dir / source_path.name
762
+ shutil.copy2(source_path, destination_path)
763
+ copied_files.append(source_path.name)
764
+
765
+ required_files = ["configuration_bert.py", "bert_layers.py", "bert_padding.py"]
766
+ missing_files = [filename for filename in required_files if not (final_model_dir / filename).exists()]
767
+ if missing_files:
768
+ raise FileNotFoundError(
769
+ "final_model is missing required DNABERT-2 custom code files after export: "
770
+ f"{missing_files}"
771
+ )
772
+
773
+ print("Copied DNABERT-2 custom code into final_model:")
774
+ for filename in copied_files:
775
+ print(f" {filename}")
776
+ print()
777
+ return copied_files
778
+
779
+
780
  def load_model_from_source(model_source: str | Path):
781
  config = AutoConfig.from_pretrained(str(model_source), trust_remote_code=True)
782
  config = disable_flash_attention_on_config(config)
 
1197
  final_model_dir.mkdir(parents=True, exist_ok=True)
1198
  trainer.save_model(str(final_model_dir))
1199
  tokenizer.save_pretrained(str(final_model_dir))
1200
+ custom_code_files = copy_dnabert2_custom_code_to_final_model(final_model_dir)
1201
 
1202
  metrics = {
1203
  "model_name": MODEL_NAME,
 
1237
  "test_eval_rows": len(eval_test_df),
1238
  "validation_metrics": validation_metrics,
1239
  "test_metrics": test_metrics,
1240
+ "final_model_custom_code_files": custom_code_files,
1241
  }
1242
  metrics_path = save_metrics(output_dir, metrics)
1243
  zip_path = zip_final_model(output_dir, final_model_dir)