| from __future__ import annotations |
|
|
| from pathlib import Path |
|
|
| import modal |
|
|
|
|
| APP_NAME = "jawbreaker-minicpm-lora" |
| REMOTE_ROOT = Path("/workspace") |
| REMOTE_OUTPUT = Path("/outputs") |
| LOCAL_ROOT = Path(__file__).resolve().parents[1] |
|
|
|
|
| image = ( |
| modal.Image.debian_slim(python_version="3.12") |
| .pip_install( |
| "accelerate==1.12.0", |
| "datasets>=4.4.0,<5.0", |
| "huggingface-hub>=0.34.0,<1.0", |
| "peft>=0.18.0,<1.0", |
| "sentencepiece>=0.2.0,<1.0", |
| "torch==2.9.1", |
| "transformers==4.57.3", |
| ) |
| .add_local_dir(LOCAL_ROOT / "jawbreaker", remote_path=REMOTE_ROOT / "jawbreaker") |
| .add_local_dir(LOCAL_ROOT / "training", remote_path=REMOTE_ROOT / "training") |
| .add_local_dir(LOCAL_ROOT / "eval", remote_path=REMOTE_ROOT / "eval") |
| ) |
|
|
| app = modal.App(APP_NAME, image=image) |
| volume = modal.Volume.from_name("jawbreaker-training", create_if_missing=True) |
|
|
|
|
| @app.function( |
| gpu="A100", |
| timeout=6 * 60 * 60, |
| volumes={REMOTE_OUTPUT: volume}, |
| secrets=[modal.Secret.from_name("huggingface-secret", required_keys=["HF_TOKEN"])], |
| ) |
| def train_lora( |
| model_id: str = "openbmb/MiniCPM4.1-8B", |
| output_name: str = "jawbreaker-minicpm-lora", |
| epochs: float = 1.0, |
| train_file: str = "training/data/train.jsonl", |
| dev_file: str = "training/data/dev.jsonl", |
| max_length: int = 768, |
| batch_size: int = 1, |
| grad_accum: int = 16, |
| learning_rate: float = 2e-4, |
| warmup_ratio: float = 0.0, |
| weight_decay: float = 0.0, |
| lr_scheduler_type: str = "linear", |
| lora_r: int = 16, |
| lora_alpha: int = 32, |
| lora_dropout: float = 0.05, |
| push_to_hub: bool = False, |
| hub_model_id: str | None = None, |
| ) -> None: |
| import os |
| import subprocess |
|
|
| os.chdir(REMOTE_ROOT) |
| output_dir = REMOTE_OUTPUT / output_name |
| cmd = [ |
| "python", |
| "training/train_lora.py", |
| "--model-id", |
| model_id, |
| "--train-file", |
| train_file, |
| "--dev-file", |
| dev_file, |
| "--output-dir", |
| str(output_dir), |
| "--epochs", |
| str(epochs), |
| "--max-length", |
| str(max_length), |
| "--batch-size", |
| str(batch_size), |
| "--grad-accum", |
| str(grad_accum), |
| "--learning-rate", |
| str(learning_rate), |
| "--warmup-ratio", |
| str(warmup_ratio), |
| "--weight-decay", |
| str(weight_decay), |
| "--lr-scheduler-type", |
| lr_scheduler_type, |
| "--lora-r", |
| str(lora_r), |
| "--lora-alpha", |
| str(lora_alpha), |
| "--lora-dropout", |
| str(lora_dropout), |
| ] |
| if push_to_hub: |
| cmd.append("--push-to-hub") |
| if hub_model_id: |
| cmd.extend(["--hub-model-id", hub_model_id]) |
|
|
| subprocess.run(cmd, check=True) |
| volume.commit() |
|
|
|
|
| @app.local_entrypoint() |
| def main( |
| model_id: str = "openbmb/MiniCPM4.1-8B", |
| output_name: str = "jawbreaker-minicpm-lora", |
| epochs: float = 1.0, |
| train_file: str = "training/data/train.jsonl", |
| dev_file: str = "training/data/dev.jsonl", |
| max_length: int = 768, |
| batch_size: int = 1, |
| grad_accum: int = 16, |
| learning_rate: float = 2e-4, |
| warmup_ratio: float = 0.0, |
| weight_decay: float = 0.0, |
| lr_scheduler_type: str = "linear", |
| lora_r: int = 16, |
| lora_alpha: int = 32, |
| lora_dropout: float = 0.05, |
| push_to_hub: bool = False, |
| hub_model_id: str | None = None, |
| ) -> None: |
| train_lora.remote( |
| model_id=model_id, |
| output_name=output_name, |
| epochs=epochs, |
| train_file=train_file, |
| dev_file=dev_file, |
| max_length=max_length, |
| batch_size=batch_size, |
| grad_accum=grad_accum, |
| learning_rate=learning_rate, |
| warmup_ratio=warmup_ratio, |
| weight_decay=weight_decay, |
| lr_scheduler_type=lr_scheduler_type, |
| lora_r=lora_r, |
| lora_alpha=lora_alpha, |
| lora_dropout=lora_dropout, |
| push_to_hub=push_to_hub, |
| hub_model_id=hub_model_id, |
| ) |
|
|