Harbour / train_unsloth.py
fivetech's picture
Upload train_unsloth.py with huggingface_hub
72098dc verified
Raw
History Blame Contribute Delete
4.35 kB
#!/usr/bin/env python3
"""
Harbour Fine-tuning Script for Qwen3.6-35B-A3B (MoE)
Uses Unsloth + LoRA with GGUF quantized model
Optimized for CPU with 121GB RAM
"""
import json
import torch
from pathlib import Path
from datasets import Dataset
from unsloth import FastLanguageModel
from trl import SFTTrainer
from transformers import TrainingArguments
# Configuration
MODEL_NAME = "unsloth/Qwen3.6-35B-A3B-GGUF"
MODEL_FILE = "Qwen3.6-35B-A3B-UD-Q4_K_M.gguf"
TRAIN_FILE = Path("/home/fivetech/finetune/harbour_train.jsonl")
VAL_FILE = Path("/home/fivetech/finetune/harbour_val.jsonl")
OUTPUT_DIR = Path("/home/fivetech/finetune/output")
MAX_SEQ_LENGTH = 2048
print("=" * 60)
print("Harbour Fine-tuning - Qwen3.6-35B-A3B (MoE) with Unsloth + LoRA")
print("=" * 60)
# 1. Load model from GGUF
print("\n1. Loading model from GGUF (Q4_K_M)...")
model, tokenizer = FastLanguageModel.from_pretrained(
model_name=MODEL_NAME,
gguf_file=MODEL_FILE,
max_seq_length=MAX_SEQ_LENGTH,
load_in_4bit=True,
dtype=None,
)
# 2. LoRA configuration
print("2. Configuring LoRA...")
model = FastLanguageModel.get_peft_model(
model,
r=16,
lora_alpha=32,
lora_dropout=0.05,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"],
bias="none",
use_gradient_checkpointing="unsloth",
random_state=42,
)
# 3. Load dataset
print("3. Loading dataset...")
def load_jsonl(path):
data = []
with open(path) as f:
for line in f:
data.append(json.loads(line))
return data
train_data = load_jsonl(TRAIN_FILE)
val_data = load_jsonl(VAL_FILE)
print(f" Train: {len(train_data)} entries")
print(f" Val: {len(val_data)} entries")
# 4. Format conversations
print("4. Formatting conversations...")
def format_conversation(entry):
messages = entry["messages"]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=False,
)
return {"text": text}
train_dataset = Dataset.from_list([format_conversation(e) for e in train_data])
val_dataset = Dataset.from_list([format_conversation(e) for e in val_data])
# 5. Tokenize
print("5. Tokenizing...")
def tokenize_function(examples):
return tokenizer(
examples["text"],
truncation=True,
max_length=MAX_SEQ_LENGTH,
padding=False,
)
train_dataset = train_dataset.map(
tokenize_function,
batched=True,
remove_columns=["text"],
desc="Tokenizing train",
)
val_dataset = val_dataset.map(
tokenize_function,
batched=True,
remove_columns=["text"],
desc="Tokenizing val",
)
print(f" Train tokens: {sum(len(x) for x in train_dataset['input_ids']):,}")
print(f" Val tokens: {sum(len(x) for x in val_dataset['input_ids']):,}")
# 6. Training arguments
print("6. Setting up training...")
training_args = TrainingArguments(
output_dir=str(OUTPUT_DIR),
num_train_epochs=3,
per_device_train_batch_size=1,
gradient_accumulation_steps=16,
learning_rate=1e-4,
weight_decay=0.01,
warmup_ratio=0.1,
lr_scheduler_type="cosine",
logging_steps=5,
save_steps=50,
save_total_limit=3,
eval_strategy="steps",
eval_steps=50,
load_best_model_at_end=True,
metric_for_best_model="eval_loss",
bf16=False,
fp16=False,
dataloader_num_workers=1,
report_to="none",
remove_unused_columns=False,
max_grad_norm=1.0,
optim="adamw_8bit",
)
# 7. Create trainer
print("7. Creating trainer...")
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
max_seq_length=MAX_SEQ_LENGTH,
dataset_text_field="text",
)
# 8. Train
print("\n8. Starting training...")
print("=" * 60)
trainer.train()
# 9. Save LoRA adapter
print("\n9. Saving LoRA adapter...")
trainer.save_model(str(OUTPUT_DIR / "final"))
tokenizer.save_pretrained(str(OUTPUT_DIR / "final"))
# 10. Export to GGUF (optional)
print("\n10. Exporting to GGUF...")
model.save_pretrained_gguf(
str(OUTPUT_DIR / "gguf"),
tokenizer,
quantization_method="q4_k_m",
)
print("\n" + "=" * 60)
print("Training complete!")
print(f"LoRA adapter saved to: {OUTPUT_DIR / 'final'}")
print(f"GGUF model saved to: {OUTPUT_DIR / 'gguf'}")
print("=" * 60)