rnaseek-full / benchmark_checkpoint.py
schen647's picture
Publish portable RNASeek workflows, fixed analyses, and tested checkpoints
6ae7efa verified
Raw History Blame Contribute Delete
4.67 kB
"""Load, evaluate, or smoke-train a locally shipped encoder benchmark checkpoint.
Use the benchmark Conda environment. Paths are relative to the repository root.
Data must contain seq_model and label columns (the archived prediction tables do).
"""
import argparse
import importlib.util
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parent
def local_path(value):
return (ROOT / Path(value).expanduser()).resolve()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--checkpoint", required=True)
parser.add_argument("--data", required=True)
parser.add_argument("--output", required=True)
parser.add_argument("--device", default="cuda:0")
parser.add_argument("--limit", type=int, default=0)
parser.add_argument("--batch-size", type=int, default=8)
parser.add_argument("--smoke-train", action="store_true", help="One gradient update on a fresh in-memory copy; no weights are saved")
args = parser.parse_args()
checkpoint, data, output = map(local_path, [args.checkpoint, args.data, args.output])
if output.exists():
parser.error("Output exists; choose a new directory")
if args.limit < 0 or args.batch_size < 1:
parser.error("limit must be nonnegative and batch-size positive")
source = ROOT / "regression_stability_functionalviral/benchmark_shipped_encoders/benchmark_shipped_encoders.py"
spec = importlib.util.spec_from_file_location("rnaseek_benchmark", source)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
import numpy as np
import pandas as pd
import torch
from transformers import AutoConfig, AutoTokenizer
from scipy.stats import pearsonr, spearmanr
from sklearn.metrics import r2_score
config = AutoConfig.from_pretrained(checkpoint, trust_remote_code=True, local_files_only=True)
tokenizer = AutoTokenizer.from_pretrained(checkpoint / "tokenizer" if (checkpoint / "tokenizer").is_dir() else checkpoint,
trust_remote_code=True, local_files_only=True)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token or tokenizer.unk_token
model = module.EncoderRegressor(str(checkpoint), config, 0.1).to(args.device).eval()
frame = pd.read_csv(data, sep="\t")
if args.limit:
frame = frame.head(args.limit)
maximum = module.infer_max_length(config, None)
predictions = []
first_batch = None
for start in range(0, len(frame), args.batch_size):
rows = frame.iloc[start:start+args.batch_size]
batch = tokenizer(rows.seq_model.tolist(), return_tensors="pt", padding=True, truncation=True, max_length=maximum)
batch = {k: batch[k].to(args.device) for k in ("input_ids", "attention_mask")}
if first_batch is None:
first_batch = batch | {"labels": torch.tensor(rows.label.tolist(), device=args.device, dtype=torch.float32)}
# no_grad keeps lazily cached rotary tensors usable by a subsequent
# training update on this same model instance.
with torch.no_grad(), torch.autocast("cuda", dtype=torch.bfloat16, enabled=str(args.device).startswith("cuda")):
predictions.extend(model(**batch)["logits"].float().cpu().tolist())
if not np.isfinite(predictions).all():
raise ValueError("Non-finite benchmark prediction")
metrics = {"n": len(frame), "full_validation": not bool(args.limit),
"r2": float(r2_score(frame.label, predictions)),
"pearson_r": float(pearsonr(frame.label, predictions).statistic),
"spearman_r": float(spearmanr(frame.label, predictions).statistic)}
if args.smoke_train:
model.train()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-6)
with torch.autocast("cuda", dtype=torch.bfloat16, enabled=str(args.device).startswith("cuda")):
loss = model(**first_batch)["loss"]
loss.backward()
norm = torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
if not torch.isfinite(loss) or not torch.isfinite(norm):
raise ValueError("Non-finite benchmark loss or gradient")
optimizer.step()
metrics.update(smoke_train_loss=float(loss.detach()), gradient_norm=float(norm))
output.mkdir(parents=True, exist_ok=False)
(output / "metrics.json").write_text(json.dumps(metrics, indent=2) + "\n")
pd.DataFrame({"label": frame.label, "prediction": predictions}).to_csv(output / "predictions.tsv", sep="\t", index=False)
print(json.dumps(metrics, indent=2), flush=True)
if __name__ == "__main__":
main()