mirror of
https://github.com/zenlm/zen-family.git
synced 2026-11-02 03:10:56 +00:00
182 lines
6.2 KiB
Python
182 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Run Qwen3-Omni fine-tuning directly (non-interactive)
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import torch
|
|
from pathlib import Path
|
|
|
|
print("""
|
|
╔═══════════════════════════════════════════════════════╗
|
|
║ QWEN3-OMNI LOCAL FINE-TUNING ON M1 MAX ║
|
|
║ Starting QLoRA Training... ║
|
|
╚═══════════════════════════════════════════════════════╝
|
|
""")
|
|
|
|
# First install dependencies
|
|
print("📦 Installing required packages...")
|
|
os.system("pip install -q transformers accelerate peft datasets sentencepiece")
|
|
|
|
from transformers import (
|
|
AutoModelForCausalLM,
|
|
AutoTokenizer,
|
|
TrainingArguments,
|
|
Trainer,
|
|
DataCollatorForLanguageModeling
|
|
)
|
|
from peft import LoraConfig, get_peft_model, TaskType
|
|
from datasets import Dataset
|
|
|
|
# Check device
|
|
device = "mps" if torch.backends.mps.is_available() else "cpu"
|
|
print(f"🖥️ Using device: {device}")
|
|
|
|
# Use zen-1.5B since we have limited space
|
|
model_id = "Qwen/zen-1.5B-Instruct"
|
|
print(f"🤖 Loading model: {model_id}")
|
|
|
|
# Load tokenizer
|
|
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
|
|
if tokenizer.pad_token is None:
|
|
tokenizer.pad_token = tokenizer.eos_token
|
|
|
|
# Load model
|
|
model = AutoModelForCausalLM.from_pretrained(
|
|
model_id,
|
|
torch_dtype=torch.float16 if device == "mps" else torch.float32,
|
|
trust_remote_code=True,
|
|
low_cpu_mem_usage=True
|
|
).to(device)
|
|
|
|
print("✅ Model loaded")
|
|
|
|
# Configure LoRA
|
|
print("⚙️ Setting up LoRA...")
|
|
|
|
lora_config = LoraConfig(
|
|
r=4, # Very low rank for M1
|
|
lora_alpha=8,
|
|
target_modules=["q_proj", "v_proj"],
|
|
lora_dropout=0.05,
|
|
bias="none",
|
|
task_type=TaskType.CAUSAL_LM
|
|
)
|
|
|
|
model = get_peft_model(model, lora_config)
|
|
model.print_trainable_parameters()
|
|
|
|
# Prepare training data
|
|
print("📚 Loading training data...")
|
|
|
|
training_data = [
|
|
{
|
|
"text": "User: What is Zen-Omni?\nAssistant: Zen-Omni is a multimodal AI system based on Qwen3-Omni-30B architecture, supporting text, image, audio, and video with 119 text languages, 19 speech input languages, and 10 speech output languages. It uses a Thinker-Talker MoE design for 234ms first-packet latency."
|
|
},
|
|
{
|
|
"text": "User: How do I use hanzo-mcp in Python?\nAssistant: Install with `pip install hanzo-mcp`, then use: `from hanzo_mcp import MCPClient; mcp = MCPClient(); results = mcp.search('query', modalities=['text', 'image', 'audio'])`"
|
|
},
|
|
{
|
|
"text": "User: What's the difference between hanzo-mcp and @hanzo/mcp?\nAssistant: hanzo-mcp is the Python package installed via pip, while @hanzo/mcp is the Node.js package installed via npm. Both provide the same MCP tools for their respective ecosystems."
|
|
},
|
|
{
|
|
"text": "User: Explain the Thinker-Talker architecture\nAssistant: The Thinker-Talker architecture separates reasoning from response generation. The Thinker (30B-A3B MoE) processes multimodal inputs and generates text, while the Talker (3B-A0.3B MoE) converts text to streaming speech with multi-codebook generation."
|
|
},
|
|
{
|
|
"text": "User: How does Zen-Omni achieve low latency?\nAssistant: Zen-Omni achieves 234ms first-packet latency through: 1) MoE architecture for efficient inference, 2) Chunked prefilling for streaming, 3) Multi-codebook autoregressive generation, 4) Lightweight ConvNet for waveform synthesis, and 5) 12.5Hz token rate optimization."
|
|
}
|
|
]
|
|
|
|
# Load existing data if available
|
|
for file in ["zen-omni/instruct/data/train.jsonl", "zen-omni/thinking/data/train.jsonl"]:
|
|
if Path(file).exists():
|
|
with open(file) as f:
|
|
for line in f:
|
|
data = json.loads(line)
|
|
text = f"User: {data.get('instruction', '')}\nAssistant: {data.get('response', '')}"
|
|
training_data.append({"text": text})
|
|
|
|
print(f" Loaded {len(training_data)} examples")
|
|
|
|
# Create dataset
|
|
dataset = Dataset.from_list(training_data)
|
|
|
|
# Tokenize
|
|
def tokenize_function(examples):
|
|
return tokenizer(
|
|
examples["text"],
|
|
truncation=True,
|
|
padding="max_length",
|
|
max_length=256 # Short for M1 memory
|
|
)
|
|
|
|
tokenized_dataset = dataset.map(tokenize_function, batched=True)
|
|
|
|
# Training arguments optimized for M1
|
|
training_args = TrainingArguments(
|
|
output_dir="./zen-omni-m1-finetuned",
|
|
num_train_epochs=2, # Fewer epochs for quick demo
|
|
per_device_train_batch_size=1,
|
|
gradient_accumulation_steps=2,
|
|
warmup_steps=5,
|
|
logging_steps=5,
|
|
save_strategy="epoch",
|
|
learning_rate=5e-4,
|
|
fp16=False, # MPS doesn't support fp16 in transformers
|
|
push_to_hub=False,
|
|
report_to="none",
|
|
optim="adamw_torch",
|
|
max_steps=50, # Limit steps for demo
|
|
)
|
|
|
|
# Create trainer
|
|
trainer = Trainer(
|
|
model=model,
|
|
args=training_args,
|
|
train_dataset=tokenized_dataset,
|
|
tokenizer=tokenizer,
|
|
data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False),
|
|
)
|
|
|
|
# Train
|
|
print("\n🚀 Starting fine-tuning on M1 Max...")
|
|
print(" This is a quick demo with 50 steps\n")
|
|
|
|
try:
|
|
trainer.train()
|
|
|
|
print("\n💾 Saving model...")
|
|
trainer.save_model("./zen-omni-m1-finetuned")
|
|
tokenizer.save_pretrained("./zen-omni-m1-finetuned")
|
|
|
|
print("✅ Fine-tuning complete!")
|
|
|
|
# Quick test
|
|
print("\n🧪 Testing fine-tuned model...")
|
|
|
|
test_prompt = "User: What is Zen-Omni?\nAssistant:"
|
|
inputs = tokenizer(test_prompt, return_tensors="pt").to(device)
|
|
|
|
with torch.no_grad():
|
|
outputs = model.generate(
|
|
**inputs,
|
|
max_new_tokens=50,
|
|
temperature=0.7,
|
|
do_sample=True,
|
|
pad_token_id=tokenizer.pad_token_id
|
|
)
|
|
|
|
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
|
print(f"\n📝 Test Response:\n{response}")
|
|
|
|
print("\n✨ Success! Model saved to ./zen-omni-m1-finetuned/")
|
|
print("\nTo create an Ollama model:")
|
|
print("1. ollama create zen-omni-custom -f Modelfile.zen-omni-custom")
|
|
print("2. ollama run zen-omni-custom")
|
|
|
|
except Exception as e:
|
|
print(f"\n❌ Error: {e}")
|
|
import traceback
|
|
traceback.print_exc() |