Download scripts/train_predict_candidate_verifier.py from SocrAIte-Mosaic/patientphex-task1: direct link, hf CLI and curl.
- Browser
- Download file 5.54 kB
-
https://huggingface.co/SocrAIte-Mosaic/patientphex-task1/resolve/main/scripts/train_predict_candidate_verifier.py
- Command line
-
hf download hf://SocrAIte-Mosaic/patientphex-task1/scripts/train_predict_candidate_verifier.py
-
curl -L -o train_predict_candidate_verifier.py https://huggingface.co/SocrAIte-Mosaic/patientphex-task1/resolve/main/scripts/train_predict_candidate_verifier.py
5.54 kB
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import math | |
| import os | |
| from pathlib import Path | |
| import random | |
| import sys | |
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) | |
| import numpy as np | |
| import torch | |
| from torch.optim import AdamW | |
| from torch.utils.data import DataLoader | |
| from transformers import AutoModelForSequenceClassification, AutoTokenizer, get_linear_schedule_with_warmup | |
| from train_candidate_verifier import VerifierDataset, build_examples, score | |
| def load_jsonl(path: Path) -> list[dict]: | |
| return [json.loads(line) for line in path.open(encoding="utf-8") if line.strip()] | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description="Train a full-data phenotype candidate verifier and score deployment candidates.") | |
| parser.add_argument("--train-data", type=Path, required=True) | |
| parser.add_argument("--train-candidates", type=Path, required=True) | |
| parser.add_argument("--predict-data", type=Path, required=True) | |
| parser.add_argument("--predict-candidates", type=Path, required=True) | |
| parser.add_argument("--model", type=Path, required=True) | |
| parser.add_argument("--output", type=Path, required=True) | |
| parser.add_argument("--epochs", type=int, default=3) | |
| parser.add_argument("--batch-size", type=int, default=16) | |
| parser.add_argument("--learning-rate", type=float, default=2e-5) | |
| parser.add_argument("--max-length", type=int, default=256) | |
| parser.add_argument("--seed", type=int, default=2026) | |
| parser.add_argument("--device", default="cuda") | |
| args = parser.parse_args() | |
| os.environ.setdefault("HF_HUB_OFFLINE", "1") | |
| os.environ.setdefault("TRANSFORMERS_OFFLINE", "1") | |
| random.seed(args.seed) | |
| np.random.seed(args.seed) | |
| torch.manual_seed(args.seed) | |
| train_documents = load_jsonl(args.train_data) | |
| predict_documents = load_jsonl(args.predict_data) | |
| train_candidates = json.loads(args.train_candidates.read_text(encoding="utf-8")) | |
| predict_candidates = json.loads(args.predict_candidates.read_text(encoding="utf-8")) | |
| train_examples = build_examples(train_documents, train_candidates) | |
| predict_examples = build_examples(predict_documents, predict_candidates) | |
| tokenizer = AutoTokenizer.from_pretrained(args.model, local_files_only=True, use_fast=True) | |
| train_loader = DataLoader( | |
| VerifierDataset(train_examples, tokenizer, args.max_length), batch_size=args.batch_size, | |
| shuffle=True, generator=torch.Generator().manual_seed(args.seed), | |
| num_workers=2, pin_memory=True, | |
| ) | |
| predict_loader = DataLoader( | |
| VerifierDataset(predict_examples, tokenizer, args.max_length), batch_size=args.batch_size, | |
| shuffle=False, num_workers=2, pin_memory=True, | |
| ) | |
| model = AutoModelForSequenceClassification.from_pretrained( | |
| args.model, local_files_only=True, num_labels=2, ignore_mismatched_sizes=True | |
| ).to(args.device) | |
| positives = sum(example.label for example in train_examples) | |
| class_weights = torch.tensor( | |
| [1.0, (len(train_examples) - positives) / max(1, positives)], device=args.device | |
| ) | |
| optimizer = AdamW(model.parameters(), lr=args.learning_rate, weight_decay=0.01) | |
| updates = len(train_loader) * args.epochs | |
| scheduler = get_linear_schedule_with_warmup(optimizer, max(1, updates // 10), updates) | |
| history: list[dict[str, float | int]] = [] | |
| for epoch in range(1, args.epochs + 1): | |
| model.train() | |
| total_loss = 0.0 | |
| for batch in train_loader: | |
| batch.pop("index") | |
| labels = batch.pop("labels").to(args.device) | |
| inputs = {key: value.to(args.device, non_blocking=True) for key, value in batch.items()} | |
| with torch.autocast(device_type="cuda", dtype=torch.bfloat16, enabled=args.device.startswith("cuda")): | |
| logits = model(**inputs).logits | |
| loss = torch.nn.functional.cross_entropy(logits.float(), labels, weight=class_weights) | |
| loss.backward() | |
| torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) | |
| optimizer.step() | |
| scheduler.step() | |
| optimizer.zero_grad(set_to_none=True) | |
| total_loss += float(loss.item()) | |
| report = {"epoch": epoch, "train_loss": total_loss / max(1, len(train_loader))} | |
| history.append(report) | |
| print(json.dumps(report), flush=True) | |
| scores = score(model, predict_loader, args.device, len(predict_examples)) | |
| predictions = [ | |
| { | |
| "pmc_id": example.pmc_id, "start": example.start, "end": example.end, | |
| "text": example.text, "score": value, **example.metadata, | |
| } | |
| for example, value in zip(predict_examples, scores, strict=True) | |
| ] | |
| result = { | |
| "configuration": vars(args) | { | |
| "train_data": str(args.train_data), "train_candidates": str(args.train_candidates), | |
| "predict_data": str(args.predict_data), "predict_candidates": str(args.predict_candidates), | |
| "model": str(args.model), "output": str(args.output), | |
| }, | |
| "train_examples": len(train_examples), "train_positives": positives, | |
| "predict_examples": len(predict_examples), "history": history, | |
| "predictions": predictions, | |
| } | |
| args.output.parent.mkdir(parents=True, exist_ok=True) | |
| args.output.write_text(json.dumps(result, ensure_ascii=False), encoding="utf-8") | |
| print(json.dumps({"train_examples": len(train_examples), "predict_examples": len(predict_examples), "output": str(args.output)})) | |
| if __name__ == "__main__": | |
| main() | |