LvcidPsyche commited on
Commit
b53d3bf
·
verified ·
1 Parent(s): e7f1044

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +131 -0
app.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import spaces
3
+ import torch
4
+ import os
5
+ from datasets import load_dataset
6
+ from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
7
+ from peft import LoraConfig, get_peft_model, set_peft_model_state_dict
8
+
9
+ # Configurations
10
+ MODEL_ID = "Qwen/Qwen2.5-7B-Instruct"
11
+ DATASET_NAME = "LvcidPsyche/webreaper-deep-crawl" # We will want to filter this for 'reasoning'
12
+ CHECKPOINT_DIR = "./qwen_checkpoints"
13
+ BATCH_SIZE = 2
14
+ GRADIENT_ACCUMULATION = 8
15
+ LEARNING_RATE = 2e-5
16
+
17
+ os.makedirs(CHECKPOINT_DIR, exist_ok=True)
18
+
19
+ # 1. Setup QLoRA Configuration
20
+ bnb_config = BitsAndBytesConfig(
21
+ load_in_4bit=True,
22
+ bnb_4bit_use_double_quant=True,
23
+ bnb_4bit_quant_type="nf4",
24
+ bnb_4bit_compute_dtype=torch.bfloat16
25
+ )
26
+
27
+ lora_config = LoraConfig(
28
+ r=16,
29
+ lora_alpha=32,
30
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
31
+ lora_dropout=0.05,
32
+ bias="none",
33
+ task_type="CAUSAL_LM"
34
+ )
35
+
36
+ # Initialize globally so we don't reload the massive base weights every 120s
37
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
38
+ tokenizer.pad_token = tokenizer.eos_token
39
+
40
+ print("Loading base Qwen-7B model in 4-bit...")
41
+ base_model = AutoModelForCausalLM.from_pretrained(
42
+ MODEL_ID,
43
+ quantization_config=bnb_config,
44
+ device_map="auto"
45
+ )
46
+ model = get_peft_model(base_model, lora_config)
47
+ optimizer = torch.optim.AdamW(model.parameters(), lr=LEARNING_RATE, fused=True)
48
+
49
+ @spaces.GPU(duration=120)
50
+ def train_chunk_qwen(steps=20, checkpoint_path=None):
51
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
52
+
53
+ start_step = 0
54
+ if checkpoint_path and os.path.exists(checkpoint_path):
55
+ checkpoint = torch.load(checkpoint_path, map_location='cpu')
56
+ # Only load the LoRA adapter weights, NOT the 7B base parameters
57
+ set_peft_model_state_dict(model, checkpoint['lora_weights'])
58
+ optimizer.load_state_dict(checkpoint['optimizer'])
59
+ start_step = checkpoint.get('step', 0)
60
+
61
+ model.train()
62
+
63
+ # We stream the reasoning-filtered data
64
+ ds = load_dataset(DATASET_NAME, split="train", streaming=True)
65
+ ds_iter = iter(ds)
66
+
67
+ logs = []
68
+ logs.append(f"--- Resuming Qwen-7B LoRA training at Step {start_step} ---")
69
+
70
+ for step in range(start_step, start_step + steps):
71
+ try:
72
+ row = next(ds_iter)
73
+ # Ideally this is a deeply structured reasoning trace.
74
+ text = row.get("content_text", "")
75
+ if len(text) < 100: continue
76
+ except StopIteration:
77
+ break
78
+
79
+ # Format for Qwen Chat Template
80
+ # In practice, you'd map your curated data to User/Assistant turns
81
+ prompt = f"<|im_start|>user\nAnalyze the underlying architecture of this system.\n<|im_end|>\n<|im_start|>assistant\n<think>\nAnalyzing constraints...\n</think>\n{text[:1000]}<|im_end|>"
82
+
83
+ inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=1024).to(device)
84
+
85
+ # Forward Pass (bfloat16)
86
+ with torch.autocast(device_type=device, dtype=torch.bfloat16):
87
+ outputs = model(**inputs, labels=inputs["input_ids"])
88
+ loss = outputs.loss / GRADIENT_ACCUMULATION
89
+
90
+ loss.backward()
91
+
92
+ if (step + 1) % GRADIENT_ACCUMULATION == 0:
93
+ optimizer.step()
94
+ optimizer.zero_grad(set_to_none=True)
95
+
96
+ if step % 5 == 0:
97
+ logs.append(f"Step {step} | Loss: {loss.item() * GRADIENT_ACCUMULATION:.4f}")
98
+
99
+ # Save Checkpoint BEFORE ZeroGPU shuts down (Only save LoRA weights ~50MB)
100
+ out_ckpt = os.path.join(CHECKPOINT_DIR, f"qwen_lora_step_{start_step + steps}.pt")
101
+
102
+ from peft import get_peft_model_state_dict
103
+ torch.save({
104
+ 'lora_weights': get_peft_model_state_dict(model),
105
+ 'optimizer': optimizer.state_dict(),
106
+ 'step': start_step + steps
107
+ }, out_ckpt)
108
+
109
+ if checkpoint_path and os.path.exists(checkpoint_path) and checkpoint_path != out_ckpt:
110
+ os.remove(checkpoint_path)
111
+
112
+ return "\n".join(logs), out_ckpt
113
+
114
+ current_ckpt = None
115
+ def run_ui(steps):
116
+ global current_ckpt
117
+ logs, new_ckpt = train_chunk_qwen(steps=int(steps), checkpoint_path=current_ckpt)
118
+ current_ckpt = new_ckpt
119
+ return logs, f"Active LoRA Checkpoint: {current_ckpt}"
120
+
121
+ with gr.Blocks() as demo:
122
+ gr.Markdown("# 🧠 Qwen-7B ZeroGPU Reasoning Trainer")
123
+ gr.Markdown("Fine-tunes Qwen-7B using QLoRA (4-bit). Designed for chunked state execution.")
124
+ steps_slider = gr.Slider(5, 50, 20, step=5, label="Steps per burst")
125
+ train_btn = gr.Button("Train Qwen Chunk")
126
+ log_out = gr.Textbox(lines=10)
127
+ ckpt_out = gr.Textbox()
128
+ train_btn.click(run_ui, [steps_slider], [log_out, ckpt_out])
129
+
130
+ if __name__ == "__main__":
131
+ demo.launch()