1earner1 commited on
Commit
2bd4f95
·
verified ·
1 Parent(s): 92caf6e

Upload try_lora.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. try_lora.py +299 -0
try_lora.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import torch
4
+ from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, AdamW, default_data_collator, get_linear_schedule_with_warmup, get_cosine_schedule_with_warmup,set_seed
5
+ from peft import LoraConfig, PeftModel, get_peft_model
6
+ import torch.nn.functional as F
7
+ import json
8
+ from datasets import load_dataset
9
+ from torch.utils.data import DataLoader
10
+ from tqdm import tqdm
11
+ import random
12
+ import argparse
13
+ import sys
14
+ # Ensure the src directory is in the Python path
15
+ current_dir = os.path.dirname(__file__)
16
+ src_dir = os.path.abspath(os.path.join(current_dir, '..', '..'))
17
+ if src_dir not in sys.path:
18
+ sys.path.append(src_dir)
19
+ #from src.prompts.prompts import instruction_prompts
20
+
21
+ # import pydevd_pycharm
22
+ # pydevd_pycharm.settrace('9.195.44.45', port=12345, stdoutToServer=True, stderrToServer=True)
23
+
24
+ def parse_arguments(arg_list: list = None):
25
+ parser = argparse.ArgumentParser(description='Lora fine-tuning of base model.')
26
+ parser.add_argument('--seed', type=int, default=42, help='random seed for reproducibility')
27
+ parser.add_argument('--initial_checkpoint', type=str, default="ibm-granite/granite-20b-code-instruct",
28
+ help='Path to the intial checkpoint.')
29
+ parser.add_argument('--training_data_file_path', type=str, default="../../data/generated_data/version_2_1/train_data_version_2_1_anonymized_new_conversation.jsonl",
30
+ help='Path to input training data file.')
31
+ parser.add_argument('--checkpoint_store_dir_path', type=str, default="../../checkpoints_granite/version_2_1/",
32
+ help='path to save the checkpoints.')
33
+
34
+ parser.add_argument('--batch_size_train', type=int, default=1, help='Batch size for training.')
35
+ parser.add_argument('--batch_size_val', type=int, default=1, help='Batch size for Validation.')
36
+ parser.add_argument('--max_length', type=int, default=4000, help='max length of input.')
37
+ parser.add_argument('--initial_lr', type=float, default=2e-5, help='Initial learning rate')
38
+ parser.add_argument('--num_epochs', type=int, default=20, help='number of epochs')
39
+ parser.add_argument('--lora_r', type=int, default=8, help='LoRA rank')
40
+ parser.add_argument('--lora_alpha', type=int, default=32, help='LoRA alpha parameter')
41
+ parser.add_argument('--lora_dropout', type=float, default=0.1, help='LoRA dropout rate')
42
+ parser.add_argument('--target_modules', nargs='+', default= ['c_attn'],
43
+ help='List of target modules')
44
+ if arg_list is None:
45
+ args = parser.parse_args()
46
+ else:
47
+ args = parser.parse_args(arg_list)
48
+
49
+ return args
50
+
51
+ def main():
52
+
53
+ args = parse_arguments()
54
+ set_seed(args.seed)
55
+
56
+
57
+ initial_checkpoint = args.initial_checkpoint
58
+ training_data_file_path = args.training_data_file_path
59
+ checkpoint_store_dir_path = args.checkpoint_store_dir_path
60
+ initial_lr = args.initial_lr
61
+
62
+
63
+ max_length = args.max_length
64
+ batch_size_train = args.batch_size_train
65
+ batch_size_test = args.batch_size_val
66
+ num_epochs = args.num_epochs
67
+ lora_r = args.lora_r
68
+ lora_alpha = args.lora_alpha
69
+ lora_dropout = args.lora_dropout
70
+ target_modules = args.target_modules
71
+
72
+ print("Intial Model: {}".format(initial_checkpoint))
73
+ print("Data path: {}".format(training_data_file_path))
74
+ print("Checkpoint base dir path: {}".format(checkpoint_store_dir_path))
75
+ print("Initial learning rate: {}".format(initial_lr))
76
+ print("LoRA rank: {}".format(lora_r))
77
+ print("LoRA alpha: {}".format(lora_alpha))
78
+ print("LoRA dropout rate: {}".format(lora_dropout))
79
+ print("LoRA Target modules: {}".format(target_modules))
80
+
81
+ tokenizer = AutoTokenizer.from_pretrained(initial_checkpoint)
82
+ dataset = load_dataset('json', data_files=training_data_file_path)
83
+
84
+ if tokenizer.pad_token_id is None:
85
+ tokenizer.pad_token_id = tokenizer.eos_token_id
86
+
87
+ past_context_code = 'past_context_code'
88
+ new_input = 'new_input'
89
+ label_column = 'output'
90
+
91
+
92
+ # def get_examples_all_context_granite(examples, only_code=False):
93
+
94
+ # #inputs = [ f"You are given a set of APIs and previously generated Code as context. The task is given a new requirement from Bob modify or expand the given code using the only the provided APIs.\n\nAPIs:\n{apis}\n\nPrevious Conversation Context:\n{past_context_NL}\n\nPrevious Code Context:\n{past_context_code}\n\nInput:\n{new_input}\n"
95
+ # # instruction = """Question:\nYou are an expert Python programmer.\nYou can update existing python code as per the provided instruction.\nYou first try to understand the existing code if provided and then decide where exactly to insert or update the code to make it fulfil the given instruction.\nA set of python functions are also provided that you can call. You cannot alter the functionality of any function.\nEach python function has a description explaining the task it performs.\nUse only simple python commands. You job is to only return the updated code.Do not include any explanation. Do not create your own function names.\nIf a function does not exist use the function called user_task with a parameter describing the task.\nIf Context is also provided for the instruction, then extract user preferences for functions from the context, and use them for answering the instruction.\n\nFunctions:"""
96
+ # instruction = instruction_prompts['zeroshot_prompt']
97
+ # inputs = [ instruction + f"\n\n{apis}\n\nPrevious Conversation Context:\n{past_context_NL}\n\nFollowing is the existing code for you to update:\n{past_context_code}\n\nInput:\n{new_input}\n\nAnswer:\n" for apis, past_context_NL, past_context_code, new_input in zip(examples['apis'], examples['past_context_NL'], examples['past_context_code'], examples['new_input'])]
98
+
99
+ # if only_code == False:
100
+ # targets = ["\nNew NL response:\n" + new_NL_response + "\nUpdated Code:\n" + str(new_code) for new_NL_response, new_code in zip(examples['new_NL_response'],examples['new_code'])]
101
+ # else:
102
+ # targets = ["\nUpdated Code:\n" + str(new_code) for new_code in examples['new_code']]
103
+
104
+ # return inputs, targets
105
+
106
+
107
+ def preprocess_function(examples):
108
+ batch_size = len(examples[past_context_code])
109
+ #inputs = [f"<s>[INST] Question : {x} [/INST] \\n Answer : " for x in examples[past_context_code]]
110
+ inputs = [f"\n<|user|>\n You are given a set of APIs and previously generated Code as context. The task is given a new requirement from Bob modify or expand the given code using the provided APIs.\n\nAPIs:\n{apis}\n\nContext:\n{past_context}\n\nInput:\n{new_input} \n<|assistant|>\n " for apis, past_context, new_input in zip(examples['apis'], examples['past_context_code'], examples['new_input'])]
111
+ targets = [str(x) for x in examples[label_column]]
112
+ #inputs, targets = get_examples_all_context(examples)
113
+
114
+ #inputs, targets = get_examples_all_context(examples)
115
+ #inputs, targets = get_examples_all_context_granite(examples, only_code=False)
116
+ model_inputs = tokenizer(inputs)
117
+
118
+ print("Input example:\n{}".format(inputs[0]))
119
+ print("Output example:\n{}".format(targets[0]))
120
+
121
+ input_sizes = [len(tokens) for tokens in model_inputs['input_ids']]
122
+ print("Input sizes {}".format(input_sizes))
123
+ labels = tokenizer(targets, add_special_tokens=False) # don't add bos token because we concatenate with inputs
124
+ label_sizes = [len(tokens) for tokens in labels['input_ids']]
125
+ print("Label sizes {}".format(label_sizes))
126
+
127
+ for i in range(batch_size):
128
+ sample_input_ids = model_inputs["input_ids"][i]
129
+ label_input_ids = labels["input_ids"][i] + [tokenizer.eos_token_id]
130
+ # print(i, sample_input_ids, label_input_ids)
131
+ model_inputs["input_ids"][i] = sample_input_ids + label_input_ids
132
+ labels["input_ids"][i] = [-100] * len(sample_input_ids) + label_input_ids
133
+ model_inputs["attention_mask"][i] = [1] * len(model_inputs["input_ids"][i])
134
+ # print(model_inputs)
135
+ for i in range(batch_size):
136
+ sample_input_ids = model_inputs["input_ids"][i]
137
+ label_input_ids = labels["input_ids"][i]
138
+ model_inputs["input_ids"][i] = [tokenizer.pad_token_id] * (
139
+ max_length - len(sample_input_ids)
140
+ ) + sample_input_ids
141
+ model_inputs["attention_mask"][i] = [0] * (max_length - len(sample_input_ids)) + model_inputs[
142
+ "attention_mask"
143
+ ][i]
144
+ labels["input_ids"][i] = [-100] * (max_length - len(sample_input_ids)) + label_input_ids
145
+ model_inputs["input_ids"][i] = torch.tensor(model_inputs["input_ids"][i][:max_length])
146
+ model_inputs["attention_mask"][i] = torch.tensor(model_inputs["attention_mask"][i][:max_length])
147
+ labels["input_ids"][i] = torch.tensor(labels["input_ids"][i][:max_length])
148
+ model_inputs["labels"] = labels["input_ids"]
149
+ return model_inputs
150
+
151
+ processed_datasets = dataset.map(
152
+ preprocess_function,
153
+ batched=True,
154
+ num_proc=1,
155
+ remove_columns=dataset["train"].column_names,
156
+ load_from_cache_file=False,
157
+ desc="Running tokenizer on dataset",
158
+ )
159
+ train_dataset = processed_datasets["train"]
160
+ train_dataloader = DataLoader(
161
+ train_dataset, shuffle=True, collate_fn=default_data_collator, batch_size=batch_size_train, pin_memory=True
162
+ )
163
+
164
+ def test_preprocess_function(examples):
165
+ batch_size = len(examples[past_context_code])
166
+ #inputs, targets = get_examples_all_context(examples)
167
+ #inputs, targets = get_examples_all_context_granite(examples)
168
+ inputs = [f"\n<|user|>\n You are given a set of APIs and previously generated Code as context. The task is given a new requirement from Bob modify or expand the given code using the provided APIs.\n\nAPIs:\n{apis}\n\nContext:\n{past_context}\n\nInput:\n{new_input} \n<|assistant|>\n " for apis, past_context, new_input in zip(examples['apis'], examples['past_context_code'], examples['new_input'])]
169
+ model_inputs = tokenizer(inputs)
170
+ # print(model_inputs)
171
+ for i in range(batch_size):
172
+ sample_input_ids = model_inputs["input_ids"][i]
173
+ model_inputs["input_ids"][i] = [tokenizer.pad_token_id] * (
174
+ max_length - len(sample_input_ids)
175
+ ) + sample_input_ids
176
+ model_inputs["attention_mask"][i] = [0] * (max_length - len(sample_input_ids)) + model_inputs[
177
+ "attention_mask"
178
+ ][i]
179
+ model_inputs["input_ids"][i] = torch.tensor(model_inputs["input_ids"][i][:max_length])
180
+ model_inputs["attention_mask"][i] = torch.tensor(model_inputs["attention_mask"][i][:max_length])
181
+ return model_inputs
182
+
183
+
184
+ test_dataset = dataset["train"].map(
185
+ test_preprocess_function,
186
+ batched=True,
187
+ num_proc=1,
188
+ remove_columns=dataset["train"].column_names,
189
+ load_from_cache_file=False,
190
+ desc="Running tokenizer on dataset",
191
+ )
192
+
193
+ test_dataloader = DataLoader(test_dataset, collate_fn=default_data_collator, batch_size=batch_size_test, pin_memory=True)
194
+
195
+
196
+ model = AutoModelForCausalLM.from_pretrained(
197
+ initial_checkpoint,
198
+ torch_dtype=torch.bfloat16,
199
+ attn_implementation="flash_attention_2"
200
+ )
201
+
202
+ print(model)
203
+
204
+ peft_config = LoraConfig(
205
+ r=lora_r, lora_alpha=lora_alpha, lora_dropout=lora_dropout, target_modules= target_modules, bias="none", task_type="CAUSAL_LM"
206
+ )
207
+
208
+ model = get_peft_model(model, peft_config)
209
+
210
+ print("Model loaded successfully!")
211
+
212
+ device = "cuda"
213
+
214
+ #optimizer = AdamW(model.parameters(), lr=3e-4)
215
+ optimizer = AdamW(model.parameters(), lr=initial_lr)
216
+
217
+ # Instantiate scheduler
218
+ lr_scheduler = get_cosine_schedule_with_warmup(
219
+ optimizer=optimizer,
220
+ num_warmup_steps=0.06 * (len(train_dataloader) * num_epochs),
221
+ num_training_steps=(len(train_dataloader) * num_epochs),
222
+ )
223
+
224
+ model.to(device)
225
+
226
+ # model.eval()
227
+ # for step, batch_eval in enumerate(tqdm(test_dataloader)):
228
+ # if step > 5:
229
+ # break
230
+ # batch_eval = {k: v.to(device) for k, v in batch_eval.items()}
231
+ # outputs = model.generate(**batch_eval, max_new_tokens=1024)
232
+ # out = tokenizer.batch_decode(outputs, skip_special_tokens=False)
233
+ # for x in out:
234
+ # print(x)
235
+ # print("#" * 50)
236
+
237
+ for epoch in range(num_epochs):
238
+ model.train()
239
+ total_loss = 0
240
+ for step, batch in enumerate(tqdm(train_dataloader)):
241
+ batch = {k: v.to(device) for k, v in batch.items()}
242
+ # print(batch)
243
+ #print(batch["input_ids"].shape)
244
+ # if step > 5:
245
+ # break
246
+ outputs = model(**batch)
247
+ loss = outputs.loss
248
+ total_loss += loss.detach().float()
249
+ loss.backward()
250
+ #print("Loss {}".format(loss.item()))
251
+ optimizer.step()
252
+ lr_scheduler.step()
253
+ optimizer.zero_grad()
254
+
255
+
256
+ print("Total Loss {}".format(total_loss.item()))
257
+ if ((epoch+1) % 1) == 0:
258
+ checkpoint_dir = os.path.join(checkpoint_store_dir_path, f"checkpoint_{epoch}/")
259
+ os.makedirs(checkpoint_dir, exist_ok=True)
260
+ model.save_pretrained(checkpoint_dir)
261
+ model.eval()
262
+ eval_loss = 0
263
+ eval_preds = []
264
+ for step, batch_eval in enumerate(tqdm(test_dataloader)):
265
+ if step > 5:
266
+ break
267
+ batch_eval = {k: v.to(device) for k, v in batch_eval.items()}
268
+ outputs = model.generate(**batch_eval, max_new_tokens=1024)
269
+ out = tokenizer.batch_decode(outputs, skip_special_tokens=True)
270
+ for x in out:
271
+ print(x)
272
+ print("#" * 50)
273
+ # with torch.no_grad():
274
+ # outputs = model(**batch)
275
+ # loss = outputs.loss
276
+ # eval_loss += loss.detach().float()
277
+ # eval_preds.extend(
278
+ # tokenizer.batch_decode(torch.argmax(outputs.logits, -1).detach().cpu().numpy(),
279
+ # skip_special_tokens=True)
280
+ # )
281
+
282
+ # eval_epoch_loss = eval_loss / len(test_dataloader)
283
+ # eval_ppl = torch.exp(eval_epoch_loss)
284
+ train_epoch_loss = total_loss / len(train_dataloader)
285
+ train_ppl = torch.exp(train_epoch_loss)
286
+ #print(f"{epoch=}: {train_ppl=} {train_epoch_loss=} {eval_ppl=} {eval_epoch_loss=}")
287
+ print(f"{epoch=}: {train_ppl=} {train_epoch_loss=}")
288
+
289
+ args_dict = vars(args)
290
+ # Path to the JSON file where the configuration will be saved
291
+ config_file_name = "training_config.json"
292
+ config_file_path = os.path.join(checkpoint_store_dir_path, config_file_name)
293
+
294
+ # Write the configuration dictionary to the JSON file
295
+ with open(config_file_path, "w") as json_file:
296
+ json.dump(args_dict, json_file, indent=4)
297
+
298
+ if __name__ == "_main_":
299
+ main()