Download train_regression.py from JoyXiangLab/rnaseek-full: direct link, hf CLI and curl.
- Browser
- Download file 5.67 kB
-
https://huggingface.co/JoyXiangLab/rnaseek-full/resolve/main/train_regression.py
- Command line
-
hf download hf://JoyXiangLab/rnaseek-full/train_regression.py
-
curl -L -o train_regression.py https://huggingface.co/JoyXiangLab/rnaseek-full/resolve/main/train_regression.py
5.67 kB
| """Portable MSE fine-tuning from the RNASeek pretrained base or a regression checkpoint. | |
| Paths are repository-relative. This is a new fine-tuning run, not an automatic | |
| resume of historical optimizer state or hyperparameter-search experiments. | |
| """ | |
| import argparse | |
| import json | |
| from pathlib import Path | |
| from validate_release import DEFAULTS, REGRESSION_BASE, ROOT, load_regression_checkpoint, local_path | |
| def main(): | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("task", choices=DEFAULTS) | |
| parser.add_argument("--checkpoint", default=REGRESSION_BASE, | |
| help="Repository-relative pretrained base or fine-tuned regression checkpoint (default: %(default)s)") | |
| parser.add_argument("--tokenizer") | |
| parser.add_argument("--train-data", required=True) | |
| parser.add_argument("--valid-data", required=True) | |
| parser.add_argument("--output", required=True) | |
| parser.add_argument("--max-steps", type=int, default=1000) | |
| parser.add_argument("--batch-size", type=int, default=4) | |
| parser.add_argument("--learning-rate", type=float, default=1e-6) | |
| parser.add_argument("--limit", type=int, default=0) | |
| parser.add_argument("--skip-model-save", action="store_true") | |
| args = parser.parse_args() | |
| output = local_path(args.output) | |
| if output.exists(): | |
| parser.error("Output already exists; use a new directory to protect prior runs") | |
| if args.max_steps < 1 or args.batch_size < 1 or args.limit < 0: | |
| parser.error("max-steps/batch-size must be positive and limit nonnegative") | |
| import numpy as np | |
| import pandas as pd | |
| import torch | |
| from torch import nn | |
| from datasets import Dataset | |
| from transformers import Trainer, TrainingArguments, DataCollatorWithPadding, set_seed | |
| from safetensors.torch import save_file | |
| set_seed(42) | |
| backbone, head, tokenizer = load_regression_checkpoint(args.checkpoint, args.tokenizer, initialize_head=True) | |
| class Regressor(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.backbone, self.regression_head = backbone, head | |
| def forward(self, input_ids, attention_mask, labels=None): | |
| hidden = self.backbone(input_ids=input_ids, attention_mask=attention_mask).last_hidden_state | |
| positions = torch.arange(hidden.shape[1], device=hidden.device)[None, :].expand(hidden.shape[:2]) | |
| last = positions.masked_fill(~attention_mask.bool(), -1).max(dim=1).values | |
| logits = self.regression_head.net(hidden[torch.arange(hidden.shape[0], device=hidden.device), last]).flatten() | |
| result = {"logits": logits} | |
| if labels is not None: | |
| result["loss"] = nn.functional.mse_loss(logits.float(), labels.float()) | |
| return result | |
| def dataset(path, split): | |
| path = local_path(path) | |
| if args.task == "efficiency": | |
| mapping = json.loads(path.read_text()) | |
| texts, labels = list(mapping), list(mapping.values()) | |
| else: | |
| frame = pd.read_csv(path, sep="\t") | |
| if "split" in frame: | |
| allowed = ["train"] if split == "train" else ["valid", "val", "validation"] | |
| frame = frame[frame["split"].astype(str).str.lower().isin(allowed)] | |
| if frame.empty: | |
| raise ValueError("The supplied table contains no records for the requested split") | |
| texts = frame["text"].tolist() if "text" in frame else ("<s>" + frame.seq + "~$predict_stability\n</s>").tolist() | |
| labels = frame["label" if "label" in frame else "score"].tolist() | |
| if args.limit: | |
| texts, labels = texts[:args.limit], labels[:args.limit] | |
| encoded = tokenizer(texts, add_special_tokens=args.task == "efficiency", truncation=True, max_length=512) | |
| return Dataset.from_dict({"input_ids": encoded["input_ids"], "attention_mask": encoded["attention_mask"], "labels": labels}) | |
| train, valid = dataset(args.train_data, "train"), dataset(args.valid_data, "valid") | |
| output.mkdir(parents=True, exist_ok=False) | |
| model = Regressor() | |
| trainer = Trainer(model=model, processing_class=tokenizer, train_dataset=train, eval_dataset=valid, | |
| data_collator=DataCollatorWithPadding(tokenizer, pad_to_multiple_of=8), | |
| args=TrainingArguments(output_dir=str(output), overwrite_output_dir=False, | |
| logging_dir=str(output / "tensorboard"), report_to=["tensorboard"], logging_steps=1, | |
| max_steps=args.max_steps, per_device_train_batch_size=args.batch_size, | |
| per_device_eval_batch_size=args.batch_size, learning_rate=args.learning_rate, | |
| bf16=torch.cuda.is_available(), optim="adamw_bnb_8bit" if torch.cuda.is_available() else "adamw_torch", | |
| save_strategy="no", eval_strategy="no", seed=42)) | |
| result = trainer.train() | |
| metrics = {**result.metrics, **trainer.evaluate(), "task": args.task, "smoke_test": bool(args.limit)} | |
| if not np.isfinite([v for v in metrics.values() if isinstance(v, float)]).all(): | |
| raise ValueError("Non-finite training/evaluation metric") | |
| (output / "metrics.json").write_text(json.dumps(metrics, indent=2) + "\n") | |
| if not args.skip_model_save: | |
| final = output / "model" | |
| final.mkdir() | |
| save_file({k: v.detach().cpu().contiguous() for k, v in model.state_dict().items()}, str(final / "model.safetensors")) | |
| backbone.config.save_pretrained(final) | |
| tokenizer.save_pretrained(final) | |
| print(json.dumps(metrics, indent=2)) | |
| if __name__ == "__main__": | |
| main() | |