fivetech/Harbour
041
1#!/usr/bin/env python32"""3Harbour Fine-tuning Script for qwen3.6:35b (Qwen3.6-35B-A3B MoE)4Uses LoRA with CPU training (121GB RAM available)5"""6 7import json8import torch9from pathlib import Path10from transformers import (11 AutoModelForCausalLM,12 AutoTokenizer,13 TrainingArguments,14 Trainer,15 DataCollatorForLanguageModeling,16)17from peft import LoraConfig, get_peft_model, TaskType18from datasets import Dataset19 20# Configuration21MODEL_NAME = "Qwen/Qwen3.6-35B-A3B"22TRAIN_FILE = Path("/home/fivetech/finetune/harbour_train.jsonl")23VAL_FILE = Path("/home/fivetech/finetune/harbour_val.jsonl")24OUTPUT_DIR = Path("/home/fivetech/finetune/output")25MAX_SEQ_LENGTH = 204826 27print("=" * 60)28print("Harbour Fine-tuning - qwen3.6:35b (MoE) with LoRA")29print("=" * 60)30 31# 1. Load tokenizer32print("\n1. Loading tokenizer...")33tokenizer = AutoTokenizer.from_pretrained(34 MODEL_NAME,35 trust_remote_code=True,36 padding_side="right",37)38if tokenizer.pad_token is None:39 tokenizer.pad_token = tokenizer.eos_token40 41# 2. Load dataset42print("2. Loading dataset...")43 44def load_jsonl(path):45 data = []46 with open(path) as f:47 for line in f:48 data.append(json.loads(line))49 return data50 51train_data = load_jsonl(TRAIN_FILE)52val_data = load_jsonl(VAL_FILE)53 54print(f" Train: {len(train_data)} entries")55print(f" Val: {len(val_data)} entries")56 57# 3. Format conversations for Qwen ChatML58print("3. Formatting conversations...")59 60def format_conversation(entry):61 """Convert messages to Qwen ChatML format."""62 messages = entry["messages"]63 text = tokenizer.apply_chat_template(64 messages,65 tokenize=False,66 add_generation_prompt=False,67 )68 return {"text": text}69 70train_dataset = Dataset.from_list([format_conversation(e) for e in train_data])71val_dataset = Dataset.from_list([format_conversation(e) for e in val_data])72 73# 4. Tokenize74print("4. Tokenizing...")75 76def tokenize_function(examples):77 return tokenizer(78 examples["text"],79 truncation=True,80 max_length=MAX_SEQ_LENGTH,81 padding=False,82 )83 84train_dataset = train_dataset.map(85 tokenize_function,86 batched=True,87 remove_columns=["text"],88 desc="Tokenizing train",89)90val_dataset = val_dataset.map(91 tokenize_function,92 batched=True,93 remove_columns=["text"],94 desc="Tokenizing val",95)96 97print(f" Train tokens: {sum(len(x) for x in train_dataset['input_ids']):,}")98print(f" Val tokens: {sum(len(x) for x in val_dataset['input_ids']):,}")99 100# 5. Load model (CPU with float32)101print("5. Loading model (CPU mode)...")102print(" This may take a few minutes...")103 104model = AutoModelForCausalLM.from_pretrained(105 MODEL_NAME,106 torch_dtype=torch.float32,107 device_map="cpu",108 trust_remote_code=True,109)110 111# 6. LoRA configuration112print("6. Configuring LoRA...")113lora_config = LoraConfig(114 task_type=TaskType.CAUSAL_LM,115 r=16,116 lora_alpha=32,117 lora_dropout=0.05,118 target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],119 bias="none",120)121 122model = get_peft_model(model, lora_config)123model.print_trainable_parameters()124 125# 7. Training arguments126print("7. Setting up training...")127training_args = TrainingArguments(128 output_dir=str(OUTPUT_DIR),129 num_train_epochs=3,130 per_device_train_batch_size=1,131 gradient_accumulation_steps=16,132 learning_rate=1e-4,133 weight_decay=0.01,134 warmup_ratio=0.1,135 lr_scheduler_type="cosine",136 logging_steps=5,137 save_steps=50,138 save_total_limit=3,139 eval_strategy="steps",140 eval_steps=50,141 load_best_model_at_end=True,142 metric_for_best_model="eval_loss",143 bf16=False,144 fp16=False,145 dataloader_num_workers=1,146 report_to="none",147 remove_unused_columns=False,148 max_grad_norm=1.0,149)150 151# 8. Data collator152data_collator = DataCollatorForLanguageModeling(153 tokenizer=tokenizer,154 mlm=False,155)156 157# 9. Create trainer158print("8. Creating trainer...")159trainer = Trainer(160 model=model,161 args=training_args,162 train_dataset=train_dataset,163 eval_dataset=val_dataset,164 data_collator=data_collator,165)166 167# 10. Train168print("\n9. Starting training...")169print("=" * 60)170trainer.train()171 172# 11. Save173print("\n10. Saving model...")174trainer.save_model(str(OUTPUT_DIR / "final"))175tokenizer.save_pretrained(str(OUTPUT_DIR / "final"))176 177print("\n" + "=" * 60)178print("Training complete!")179print(f"Model saved to: {OUTPUT_DIR / 'final'}")180print("=" * 60)181 