Download train.py from Snapkitty/pax-coder: direct link, hf CLI and curl.
- Browser
- Download file 6.64 kB
-
https://huggingface.co/Snapkitty/pax-coder/resolve/main/train.py
- Command line
-
hf download hf://Snapkitty/pax-coder/train.py
-
curl -L -o train.py https://huggingface.co/Snapkitty/pax-coder/resolve/main/train.py
6.64 kB
| #!/usr/bin/env python3 | |
| # PAX-Coder Fine-tuning for RTX 3080 (10GB VRAM) | |
| # Ahmad Ali Parr · PAX Architecture | |
| # Optimized: 4-bit QLoRA + Unsloth + paged_adamw_8bit | |
| import os | |
| import torch | |
| from datasets import load_dataset | |
| from unsloth import FastLanguageModel | |
| from trl import SFTTrainer | |
| from transformers import TrainingArguments, EarlyStoppingCallback | |
| CONFIG = { | |
| "model_name": "unsloth/deepseek-coder-7b-instruct-v1.5-bnb-4bit", | |
| "max_seq_length": 2048, # 4096 OOMs on 10GB; 2048 fits with ~1.9GB headroom | |
| "dtype": torch.bfloat16, | |
| "load_in_4bit": True, | |
| # LoRA | |
| "lora_r": 32, # rank 32 (not 64) saves ~200MB VRAM | |
| "lora_alpha": 32, | |
| "lora_dropout": 0.05, | |
| "target_modules": [ | |
| "q_proj", "k_proj", "v_proj", "o_proj", | |
| "gate_proj", "up_proj", "down_proj", | |
| ], | |
| # Training | |
| "batch_size": 1, | |
| "grad_accum": 16, # effective batch = 16 | |
| "learning_rate": 1.5e-4, | |
| "num_epochs": 3, | |
| "warmup_steps": 50, | |
| "weight_decay": 0.01, | |
| "max_grad_norm": 1.0, | |
| # Memory | |
| "optim": "paged_adamw_8bit", | |
| "dataloader_num_workers": 2, | |
| # Logging | |
| "logging_steps": 10, | |
| "eval_steps": 50, | |
| "save_steps": 100, | |
| # Output | |
| "output_dir": "pax-coder-7b", | |
| "run_name": "pax-coder-7b-sm86", | |
| "report_to": "none", # set "wandb" if logged in | |
| } | |
| # VRAM budget (RTX 3080 10GB): | |
| # Base model (4-bit) ~4.2 GB | |
| # LoRA adapters ~0.1 GB | |
| # Gradients (8-bit) ~1.5 GB | |
| # Activations (GC) ~1.8 GB | |
| # Dataset buffer ~0.5 GB | |
| # Total ~8.1 GB (1.9 GB headroom) | |
| def format_pax_example(example): | |
| return ( | |
| "### Instruction:\n" | |
| f"{example['instruction']}\n\n" | |
| "### Context:\n" | |
| f"{example['input']}\n\n" | |
| "### Response:\n" | |
| f"{example['output']}" | |
| ) | |
| def load_pax_dataset(): | |
| dataset = load_dataset("json", data_files={ | |
| "train": "build/pax_train.jsonl", | |
| "validation": "build/pax_val.jsonl", | |
| }) | |
| def format_fn(examples): | |
| texts = [] | |
| for i in range(len(examples["instruction"])): | |
| ex = {k: examples[k][i] for k in examples} | |
| texts.append(format_pax_example(ex)) | |
| return {"text": texts} | |
| return dataset.map(format_fn, batched=True, remove_columns=dataset["train"].column_names) | |
| def merge_and_export_gguf(output_dir): | |
| gguf_dir = f"{output_dir}/gguf" | |
| os.makedirs(gguf_dir, exist_ok=True) | |
| merged_dir = f"{output_dir}/merged" | |
| # llama.cpp GGUF conversion (more reliable than Unsloth's built-in for q4_k_m) | |
| import subprocess | |
| import shlex | |
| llama_cpp_dir = "/tmp/llama_cpp_pax" | |
| # Clone llama.cpp if not present | |
| if not os.path.isdir(llama_cpp_dir): | |
| subprocess.run( | |
| ["git", "clone", "--depth", "1", | |
| "https://github.com/ggerganov/llama.cpp", llama_cpp_dir], | |
| check=True, | |
| ) | |
| # Build | |
| subprocess.run( | |
| ["make", f"-j{os.cpu_count() or 4}"], | |
| cwd=llama_cpp_dir, | |
| check=True, | |
| ) | |
| # Convert | |
| outfile = f"{gguf_dir}/pax-coder-7b-q4_k_m.gguf" | |
| subprocess.run( | |
| ["python3", "convert_hf_to_gguf.py", merged_dir, | |
| "--outfile", outfile, "--outtype", "q4_k_m"], | |
| cwd=llama_cpp_dir, | |
| check=True, | |
| ) | |
| print(f"GGUF saved → {outfile}") | |
| print(f"Install: ollama create pax-coder -f {gguf_dir}/Modelfile") | |
| def main(): | |
| print(f"=== PAX-Coder RTX 3080 Fine-Tuning ===") | |
| print(f"GPU: {torch.cuda.get_device_name(0)}") | |
| print(f"VRAM: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB") | |
| model, tokenizer = FastLanguageModel.from_pretrained( | |
| model_name=CONFIG["model_name"], | |
| max_seq_length=CONFIG["max_seq_length"], | |
| dtype=CONFIG["dtype"], | |
| load_in_4bit=CONFIG["load_in_4bit"], | |
| ) | |
| model = FastLanguageModel.get_peft_model( | |
| model, | |
| r=CONFIG["lora_r"], | |
| target_modules=CONFIG["target_modules"], | |
| lora_alpha=CONFIG["lora_alpha"], | |
| lora_dropout=CONFIG["lora_dropout"], | |
| bias="none", | |
| use_gradient_checkpointing="unsloth", | |
| random_state=42, | |
| use_rslora=True, | |
| ) | |
| dataset = load_pax_dataset() | |
| print(f"Train: {len(dataset['train'])} Val: {len(dataset['validation'])}") | |
| trainer = SFTTrainer( | |
| model=model, | |
| tokenizer=tokenizer, | |
| train_dataset=dataset["train"], | |
| eval_dataset=dataset["validation"], | |
| dataset_text_field="text", | |
| max_seq_length=CONFIG["max_seq_length"], | |
| args=TrainingArguments( | |
| output_dir=CONFIG["output_dir"], | |
| per_device_train_batch_size=CONFIG["batch_size"], | |
| per_device_eval_batch_size=CONFIG["batch_size"], | |
| gradient_accumulation_steps=CONFIG["grad_accum"], | |
| num_train_epochs=CONFIG["num_epochs"], | |
| learning_rate=CONFIG["learning_rate"], | |
| warmup_steps=CONFIG["warmup_steps"], | |
| weight_decay=CONFIG["weight_decay"], | |
| max_grad_norm=CONFIG["max_grad_norm"], | |
| gradient_checkpointing=True, | |
| optim=CONFIG["optim"], | |
| dataloader_num_workers=CONFIG["dataloader_num_workers"], | |
| logging_steps=CONFIG["logging_steps"], | |
| eval_steps=CONFIG["eval_steps"], | |
| save_steps=CONFIG["save_steps"], | |
| eval_strategy="steps", | |
| save_strategy="steps", | |
| load_best_model_at_end=True, | |
| metric_for_best_model="eval_loss", | |
| greater_is_better=False, | |
| bf16=True, | |
| fp16=False, | |
| tf32=True, | |
| report_to=CONFIG["report_to"], | |
| run_name=CONFIG["run_name"], | |
| seed=42, | |
| ), | |
| callbacks=[EarlyStoppingCallback(early_stopping_patience=3)], | |
| ) | |
| trainer.train() | |
| lora_path = f"{CONFIG['output_dir']}/lora_adapters" | |
| model.save_pretrained(lora_path) | |
| tokenizer.save_pretrained(lora_path) | |
| print(f"LoRA adapters → {lora_path}") | |
| # Merge and export | |
| merged_dir = f"{CONFIG['output_dir']}/merged" | |
| merged = model.merge_and_unload() | |
| merged.save_pretrained(merged_dir) | |
| tokenizer.save_pretrained(merged_dir) | |
| merge_and_export_gguf(CONFIG["output_dir"]) | |
| if __name__ == "__main__": | |
| os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:128,expandable_segments:True" | |
| os.environ["TOKENIZERS_PARALLELISM"] = "false" | |
| main() | |