gcharanteja commited on
Commit
86ada7e
·
1 Parent(s): b7abf3f
Files changed (4) hide show
  1. Dockerfile +1 -1
  2. fine_tune_llama32_1b.py +147 -0
  3. pyproject.toml +32 -2
  4. uv.lock +0 -0
Dockerfile CHANGED
@@ -1,7 +1,7 @@
1
  # Hugging Face Spaces (Docker SDK) - FastAPI + Uvicorn using uv
2
  # Docs: https://huggingface.co/docs/hub/spaces-sdks-docker
3
 
4
- FROM python:3.13-slim
5
 
6
  # Create a non-root user matching HF Spaces expectations
7
  RUN useradd -m -u 1000 user
 
1
  # Hugging Face Spaces (Docker SDK) - FastAPI + Uvicorn using uv
2
  # Docs: https://huggingface.co/docs/hub/spaces-sdks-docker
3
 
4
+ FROM python:3.12-slim
5
 
6
  # Create a non-root user matching HF Spaces expectations
7
  RUN useradd -m -u 1000 user
fine_tune_llama32_1b.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from datasets import load_dataset
3
+ from transformers import (
4
+ AutoModelForCausalLM,
5
+ AutoTokenizer,
6
+ BitsAndBytesConfig,
7
+ )
8
+ from peft import LoraConfig, prepare_model_for_kbit_training, get_peft_model
9
+ from trl import SFTTrainer, SFTConfig
10
+
11
+ # ========================= CONFIG =========================
12
+ model_name = "meta-llama/Llama-3.2-1B-Instruct"
13
+ dataset_name = "mlabonne/FineTome-100k"
14
+
15
+ # Training hyperparameters (tune these based on your GPU VRAM)
16
+ max_seq_length = 2048 # Llama 3.2 supports 128k, but 2048 is safe & fast
17
+ batch_size = 4 # reduce to 2 or 1 if OOM
18
+ gradient_accumulation_steps = 4
19
+ num_train_epochs = 1 # set to 2 or 3 if you want better quality (longer training)
20
+ learning_rate = 2e-4
21
+ max_steps = None # set a number (e.g. 2000) if you want to train only part of the dataset
22
+
23
+ output_dir = "./llama32-1b-finetuned-finetome"
24
+
25
+ # ====================== LOAD MODEL (4-bit QLoRA) ======================
26
+ bnb_config = BitsAndBytesConfig(
27
+ load_in_4bit=True,
28
+ bnb_4bit_use_double_quant=True,
29
+ bnb_4bit_quant_type="nf4",
30
+ bnb_4bit_compute_dtype=torch.bfloat16,
31
+ )
32
+
33
+ model = AutoModelForCausalLM.from_pretrained(
34
+ model_name,
35
+ quantization_config=bnb_config,
36
+ device_map="auto", # automatically puts layers on GPU
37
+ trust_remote_code=True,
38
+ )
39
+
40
+ tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
41
+ tokenizer.pad_token = tokenizer.eos_token
42
+
43
+ # Prepare for QLoRA
44
+ model = prepare_model_for_kbit_training(model)
45
+
46
+ # LoRA config (QLoRA paper defaults work great for 1B model)
47
+ lora_config = LoraConfig(
48
+ r=16, # 8 or 32 also fine
49
+ lora_alpha=32,
50
+ target_modules="all-linear", # modern way for Llama (q_proj, k_proj, etc.)
51
+ lora_dropout=0.05,
52
+ bias="none",
53
+ task_type="CAUSAL_LM",
54
+ )
55
+
56
+ model = get_peft_model(model, lora_config)
57
+ model.print_trainable_parameters() # should show ~0.5-1% trainable params
58
+
59
+ # ====================== LOAD & PREPARE DATASET ======================
60
+ dataset = load_dataset(dataset_name, split="train")
61
+
62
+ # Convert ShareGPT "conversations" → standard messages format
63
+ def map_to_messages(example):
64
+ messages = []
65
+ for turn in example["conversations"]:
66
+ role = "user" if turn["from"] == "human" else \
67
+ "assistant" if turn["from"] == "gpt" else "system"
68
+ messages.append({"role": role, "content": turn["value"]})
69
+ return {"messages": messages}
70
+
71
+ dataset = dataset.map(map_to_messages, remove_columns=["conversations", "source", "score"])
72
+
73
+ # Format with Llama-3.2 chat template (this creates the final "text" column)
74
+ def formatting_func(example):
75
+ text = tokenizer.apply_chat_template(
76
+ example["messages"],
77
+ tokenize=False,
78
+ add_generation_prompt=False
79
+ )
80
+ return {"text": text}
81
+
82
+ dataset = dataset.map(formatting_func, remove_columns=["messages"])
83
+
84
+ # Optional: use only first 10k examples for quick test
85
+ # dataset = dataset.select(range(10000))
86
+
87
+ # ====================== TRAINER ======================
88
+ training_args = SFTConfig(
89
+ output_dir=output_dir,
90
+ per_device_train_batch_size=batch_size,
91
+ gradient_accumulation_steps=gradient_accumulation_steps,
92
+ gradient_checkpointing=True, # saves VRAM
93
+ learning_rate=learning_rate,
94
+ num_train_epochs=num_train_epochs,
95
+ max_steps=max_steps,
96
+ warmup_steps=100,
97
+ logging_steps=10,
98
+ save_steps=500,
99
+ save_total_limit=2,
100
+ fp16=False, # bfloat16 is used via compute_dtype
101
+ bf16=torch.cuda.is_bf16_supported(),
102
+ optim="paged_adamw_8bit",
103
+ max_seq_length=max_seq_length,
104
+ packing=True, # packs multiple examples into one sequence (faster)
105
+ dataset_text_field="text",
106
+ report_to="none", # change to "tensorboard" if you want logs
107
+ )
108
+
109
+ trainer = SFTTrainer(
110
+ model=model,
111
+ tokenizer=tokenizer,
112
+ train_dataset=dataset,
113
+ args=training_args,
114
+ # peft_config is NOT needed because we already did get_peft_model
115
+ )
116
+
117
+ print("Starting training...")
118
+ trainer.train()
119
+
120
+ # Save the LoRA adapter
121
+ trainer.model.save_pretrained(output_dir)
122
+ tokenizer.save_pretrained(output_dir)
123
+ print(f"✅ Training finished! LoRA adapter saved to {output_dir}")
124
+
125
+
126
+ #model merger from peft import AutoPeftModelForCausalLM
127
+
128
+ model = AutoPeftModelForCausalLM.from_pretrained(
129
+ output_dir,
130
+ device_map="auto",
131
+ torch_dtype=torch.bfloat16
132
+ )
133
+ model = model.merge_and_unload()
134
+ model.save_pretrained("llama32-1b-finetuned-merged")
135
+ tokenizer.save_pretrained("llama32-1b-finetuned-merged")
136
+
137
+
138
+ from peft import AutoPeftModelForCausalLM
139
+
140
+ model = AutoPeftModelForCausalLM.from_pretrained(
141
+ output_dir,
142
+ device_map="auto",
143
+ torch_dtype=torch.bfloat16
144
+ )
145
+ model = model.merge_and_unload()
146
+ model.save_pretrained("llama32-1b-finetuned-merged")
147
+ tokenizer.save_pretrained("llama32-1b-finetuned-merged")
pyproject.toml CHANGED
@@ -5,6 +5,36 @@ description = "Add your description here"
5
  readme = "README.md"
6
  requires-python = ">=3.12"
7
  dependencies = [
8
- "fastapi>=0.135.2",
9
- "uvicorn>=0.42.0",
 
 
 
 
 
 
 
 
 
 
10
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  readme = "README.md"
6
  requires-python = ">=3.12"
7
  dependencies = [
8
+ "accelerate>=1.13.0",
9
+ "bitsandbytes>=0.49.2; sys_platform == 'linux' and platform_machine == 'x86_64'",
10
+ "datasets>=4.8.4",
11
+ "fastapi>=0.135.2",
12
+ "huggingface-hub>=1.8.0",
13
+ "peft>=0.18.1",
14
+ "torch>=2.5.0",
15
+ "torchaudio>=2.5.0",
16
+ "transformers>=5.4.0",
17
+ "trl>=0.29.1",
18
+ "uvicorn>=0.42.0",
19
+ "torchvision>=0.20.0",
20
  ]
21
+
22
+ [tool.uv]
23
+ # Resolve and lock for both:
24
+ # - local dev on macOS (Apple Silicon)
25
+ # - Hugging Face Spaces runtime (Linux x86_64)
26
+ environments = [
27
+ "sys_platform == 'darwin' and platform_machine == 'arm64'",
28
+ "sys_platform == 'linux' and platform_machine == 'x86_64'",
29
+ ]
30
+
31
+ [tool.uv.sources]
32
+ # Use CUDA-enabled PyTorch wheels on Linux; other platforms will use the default index (PyPI).
33
+ torch = [{ index = "pytorch-cu121", marker = "sys_platform == 'linux'" }]
34
+ torchvision = [{ index = "pytorch-cu121", marker = "sys_platform == 'linux'" }]
35
+ torchaudio = [{ index = "pytorch-cu121", marker = "sys_platform == 'linux'" }]
36
+
37
+ [[tool.uv.index]]
38
+ name = "pytorch-cu121"
39
+ url = "https://download.pytorch.org/whl/cu121"
40
+ explicit = true
uv.lock CHANGED
The diff for this file is too large to render. See raw diff