|
|
|
|
|
|
|
|
|
|
| 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,
|
| "dtype": torch.bfloat16,
|
| "load_in_4bit": True,
|
|
|
|
|
| "lora_r": 32,
|
| "lora_alpha": 32,
|
| "lora_dropout": 0.05,
|
| "target_modules": [
|
| "q_proj", "k_proj", "v_proj", "o_proj",
|
| "gate_proj", "up_proj", "down_proj",
|
| ],
|
|
|
|
|
| "batch_size": 1,
|
| "grad_accum": 16,
|
| "learning_rate": 1.5e-4,
|
| "num_epochs": 3,
|
| "warmup_steps": 50,
|
| "weight_decay": 0.01,
|
| "max_grad_norm": 1.0,
|
|
|
|
|
| "optim": "paged_adamw_8bit",
|
| "dataloader_num_workers": 2,
|
|
|
|
|
| "logging_steps": 10,
|
| "eval_steps": 50,
|
| "save_steps": 100,
|
|
|
|
|
| "output_dir": "pax-coder-7b",
|
| "run_name": "pax-coder-7b-sm86",
|
| "report_to": "none",
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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"
|
|
|
| import subprocess
|
| import shlex
|
|
|
| llama_cpp_dir = "/tmp/llama_cpp_pax"
|
|
|
|
|
| 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,
|
| )
|
|
|
|
|
| subprocess.run(
|
| ["make", f"-j{os.cpu_count() or 4}"],
|
| cwd=llama_cpp_dir,
|
| check=True,
|
| )
|
|
|
|
|
| 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}")
|
|
|
|
|
| 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()
|
|
|