Luke MacLean commited on
Commit
17daafb
Β·
1 Parent(s): 9691efc
Files changed (3) hide show
  1. main.py +154 -0
  2. run.py +10 -0
  3. save.py +17 -0
main.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ensure Apple Metal (MPS) is enabled
2
+ import torch
3
+ import os
4
+ from transformers import AutoModelForCausalLM, AutoTokenizer, set_seed
5
+ from datasets import load_dataset
6
+ from peft import LoraConfig, TaskType
7
+ from trl import SFTConfig, SFTTrainer
8
+ from enum import Enum
9
+
10
+ # βœ… Set device to Metal Performance Shaders (MPS) for Mac M3
11
+ device = "mps" if torch.backends.mps.is_available() else "cpu"
12
+ print(f"Using device: {device}")
13
+
14
+ # βœ… Set seed for reproducibility
15
+ set_seed(42)
16
+
17
+ # βœ… Model and dataset
18
+ model_name = "google/gemma-2-2b-it"
19
+ dataset_name = "Jofthomas/hermes-function-calling-thinking-V1"
20
+ tokenizer = AutoTokenizer.from_pretrained(model_name, token=True)
21
+
22
+ # βœ… Adjust tokenizer with special tokens
23
+ class ChatmlSpecialTokens(str, Enum):
24
+ tools = "<tools>"
25
+ eotools = "</tools>"
26
+ think = "<think>"
27
+ eothink = "</think>"
28
+ tool_call="<tool_call>"
29
+ eotool_call="</tool_call>"
30
+ tool_response="<tool_response>"
31
+ eotool_response="</tool_response>"
32
+ pad_token = "<pad>"
33
+ eos_token = "<eos>"
34
+
35
+ @classmethod
36
+ def list(cls):
37
+ return [c.value for c in cls]
38
+
39
+ tokenizer = AutoTokenizer.from_pretrained(
40
+ model_name,
41
+ pad_token=ChatmlSpecialTokens.pad_token.value,
42
+ additional_special_tokens=ChatmlSpecialTokens.list()
43
+ )
44
+
45
+ # βœ… Load model and move it to MPS
46
+ model = AutoModelForCausalLM.from_pretrained(model_name, token=True, attn_implementation="eager")
47
+ model.resize_token_embeddings(len(tokenizer))
48
+ model.to(device)
49
+
50
+ # βœ… Data preprocessing function
51
+ def preprocess(sample):
52
+ messages = sample["messages"]
53
+
54
+ if not messages or not isinstance(messages, list):
55
+ return {"text": ""} # Return empty text if messages are missing
56
+
57
+ first_message = messages[0]
58
+
59
+ # Ensure system messages are merged with the first user message
60
+ if first_message["role"] == "system":
61
+ system_message_content = first_message.get("content", "")
62
+ if len(messages) > 1 and messages[1]["role"] == "user":
63
+ messages[1]["content"] = (
64
+ system_message_content
65
+ + "\n\nAlso, before making a call to a function, take the time to plan the function to take. "
66
+ + "Make that thinking process between <think>{your thoughts}</think>\n\n"
67
+ + messages[1].get("content", "")
68
+ )
69
+ messages.pop(0) # Remove system message
70
+
71
+ # Ensure the conversation alternates between "user" and "assistant"
72
+ valid_roles = ["user", "assistant"]
73
+ cleaned_messages = [
74
+ msg for msg in messages if msg.get("role") in valid_roles and msg.get("content")
75
+ ]
76
+
77
+ # Check if messages are empty after cleanup
78
+ if not cleaned_messages or cleaned_messages[0]["role"] != "user":
79
+ return {"text": ""} # Ensure the first message is always from the user
80
+
81
+ # Apply chat template
82
+ try:
83
+ formatted_text = tokenizer.apply_chat_template(cleaned_messages, tokenize=False)
84
+ return {"text": formatted_text}
85
+ except Exception as e:
86
+ print(f"Error processing message: {e}")
87
+ return {"text": ""}
88
+
89
+ # βœ… Load dataset
90
+ dataset = load_dataset(dataset_name, cache_dir="/tmp")
91
+ dataset = dataset.rename_column("conversations", "messages")
92
+ dataset = dataset.map(preprocess, remove_columns=["messages"])
93
+ dataset = dataset["train"].train_test_split(0.1)
94
+
95
+ # βœ… Print dataset size before training
96
+ print(f"Training dataset size: {len(dataset['train'])} samples")
97
+ print(f"Evaluation dataset size: {len(dataset['test'])} samples")
98
+
99
+ # βœ… LoRA configuration
100
+ peft_config = LoraConfig(
101
+ r=16,
102
+ lora_alpha=64,
103
+ lora_dropout=0.05,
104
+ target_modules=["gate_proj", "q_proj", "lm_head", "o_proj", "k_proj", "embed_tokens", "down_proj", "up_proj", "v_proj"],
105
+ task_type=TaskType.CAUSAL_LM,
106
+ bias="none",
107
+ )
108
+
109
+ # βœ… Training configuration (adjusted for performance on Mac M3 Max)
110
+ num_train_epochs = 5 # βœ… Increase to 5 epochs for better training
111
+ max_steps = 1000 # βœ… Ensure at least 1000 training steps
112
+ learning_rate = 5e-5 # βœ… Reduce learning rate to prevent overfitting
113
+
114
+ training_arguments = SFTConfig(
115
+ output_dir="gemma-2-2B-it-macM3",
116
+ per_device_train_batch_size=2, # βœ… Keep small if training on MPS
117
+ per_device_eval_batch_size=2,
118
+ gradient_accumulation_steps=4, # βœ… Helps fit larger batch sizes
119
+ save_strategy="epoch",
120
+ save_total_limit=2,
121
+ save_safetensors=False,
122
+ evaluation_strategy="epoch",
123
+ logging_steps=5,
124
+ learning_rate=learning_rate,
125
+ max_grad_norm=1.0,
126
+ weight_decay=0.1,
127
+ warmup_ratio=0.1,
128
+ lr_scheduler_type="cosine",
129
+ report_to="tensorboard",
130
+ bf16=True, # βœ… Efficient mixed precision training for Mac MPS
131
+ push_to_hub=False,
132
+ num_train_epochs=num_train_epochs,
133
+ max_steps=max_steps, # βœ… Ensure training runs for at least 1000 steps
134
+ gradient_checkpointing=True,
135
+ gradient_checkpointing_kwargs={"use_reentrant": False},
136
+ packing=True,
137
+ max_seq_length=1500,
138
+ )
139
+
140
+ # βœ… Trainer setup
141
+ trainer = SFTTrainer(
142
+ model=model,
143
+ args=training_arguments,
144
+ train_dataset=dataset["train"],
145
+ eval_dataset=dataset["test"],
146
+ processing_class=tokenizer,
147
+ peft_config=peft_config,
148
+ )
149
+
150
+ # βœ… Start training (should work efficiently on Mac M3 Max)
151
+ trainer.train()
152
+ trainer.save_model()
153
+
154
+ print("Training complete! πŸš€ Model saved successfully.")
run.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoModelForCausalLM, AutoTokenizer
2
+
3
+ repo_id = "MacLeanLuke/gemma-2b-tool-tuned"
4
+
5
+ model = AutoModelForCausalLM.from_pretrained(repo_id)
6
+ tokenizer = AutoTokenizer.from_pretrained(repo_id)
7
+
8
+ inputs = tokenizer("Hello, how are you?", return_tensors="pt")
9
+ outputs = model.generate(**inputs)
10
+ print(tokenizer.decode(outputs[0]))
save.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from huggingface_hub import HfApi
2
+
3
+ repo_id = "MacLeanLuke/gemma-2b-tool-tuned" # Change to your Hugging Face username & repo name
4
+
5
+ # βœ… Upload model and tokenizer
6
+ api = HfApi()
7
+ api.create_repo(repo_id, exist_ok=True)
8
+
9
+ # βœ… Push files
10
+ model_path = "gemma-2-2B-it-macM3"
11
+ api.upload_folder(
12
+ folder_path=model_path,
13
+ repo_id=repo_id,
14
+ repo_type="model",
15
+ )
16
+
17
+ print(f"Model successfully uploaded to: https://huggingface.co/{repo_id}")