song9 commited on
Commit
5548f1f
·
verified ·
1 Parent(s): 8d1d43d

Upload Training Code

Browse files
Files changed (1) hide show
  1. PEFT_LoRA_CLM_KULLM3_TrainingCode.py +182 -0
PEFT_LoRA_CLM_KULLM3_TrainingCode.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from tqdm import tqdm
4
+ import pandas as pd
5
+ import numpy as np
6
+ import re
7
+ import transformers
8
+ from peft import LoraConfig, PeftConfig, PeftModel, get_peft_model, prepare_model_for_kbit_training
9
+ from transformers import AutoModelForCausalLM, AutoTokenizer, HfArgumentParser, TrainingArguments, Trainer, default_data_collator
10
+ import torch
11
+ from dataclasses import dataclass, field
12
+ from typing import Optional
13
+ from dataclass_csv import DataclassReader
14
+ from torch.utils.data import DataLoader
15
+ from datasets import Dataset
16
+ from trl import DataCollatorForCompletionOnlyLM
17
+ from peft import get_peft_model, LoraConfig, TaskType
18
+ import sys
19
+
20
+ import time
21
+ from enum import Enum
22
+
23
+ # ignore warning
24
+ import warnings
25
+ warnings.filterwarnings("ignore")
26
+
27
+ os.environ['HF_TOKEN'] = "SET YOUR HUGGINGFACE ACCESS TOKEN" # it should be write access-token
28
+ os.environ["HUGGINGFACEHUB_API_TOKEN"] = "SET YOUR HUGGINGFACE ACCESS TOKEN" # write access-token
29
+ os.environ["TOKENIZERS_PARALLELISM"] = "true"
30
+
31
+ class SpecialTokens(str, Enum):
32
+ soc_token = "<|startofcall|>" # 첫 대화의 시작이 상담원이면 안 됨 -> 첫 대화의 시작이 상담원인 경우 human 메세지로 soc 토큰 삽입
33
+ # interjection_token = "<|interjection|>" # 화자가 연달아 2번 발화할 때 중간에 추임새 토큰을 넣어준다.
34
+ privacy_token = "<|private|>" # 원본에서 xx, ㅇㅇㅇ, 00 등 개인정보로 표시된 부분을 프라이버시 토큰으로 변환
35
+ pad_token = "[PAD]"
36
+
37
+ @classmethod
38
+ def list(cls):
39
+ return [c.value for c in cls]
40
+
41
+ class KuLLM3:
42
+ def __init__(self, device, model_path="nlpai-lab/KULLM3"):
43
+ self.model = AutoModelForCausalLM.from_pretrained(
44
+ model_path,
45
+ torch_dtype=torch.bfloat16,
46
+ trust_remote_code=True,
47
+ device_map=device
48
+ )
49
+ # Special token들을 tokenizer에 추가
50
+ self.tokenizer = AutoTokenizer.from_pretrained(
51
+ model_path,
52
+ pad_token=SpecialTokens.pad_token.value,
53
+ padding_side='right',
54
+ additional_special_tokens=SpecialTokens.list(),
55
+ model_max_length=30000
56
+ )
57
+ self.tokenizer.chat_template = '{% if messages[0][\'role\'] == \'system\' %}{% set loop_messages = messages[1:] %}{% set system_message = messages[0][\'content\'] %}{% else %}{% set loop_messages = messages %}{% set system_message = "당신은 고객의 전화에 응대하는 전화 상담원입니다.\n 당신은 비도덕적이거나, 성적이거나, 불법적이거나 또는 사회 통념적으로 허용되지 않는 발언은 하지 않습니다.\n 고객에게 친절하게 대화하며, 고객의 응답에 가능한 정확하고 예의 바르게 응답함으로써 최대한 도와주려고 노력합니다.\n 고객의 질문을 이해하지 못했다면, 어떤 부분을 이해하지 못했는지 설명하고 고객에게 구체적인 질문을 요구합니다.\n 당신은 고객과 전화로 소통하기 때문에 답변이 간결해야 합니다. 거짓 정보를 발언하지 않도록 주의합니다." %}{% endif %}{% for message in loop_messages %}{% if (message[\'role\'] == \'user\') != (loop.index0 % 2 == 0) %}{{ raise_exception(\'Conversation roles must alternate user/assistant/user/assistant/...\') }}{% endif %}{% if loop.index0 == 0 and system_message != false %}{% set content = \'<<SYS>>\\n\' + system_message + \'\\n<</SYS>>\\n\\n\' + message[\'content\'] %}{% else %}{% set content = message[\'content\'] %}{% endif %}{% if message[\'role\'] == \'user\' %}{{ bos_token + \'[INST] \' + content.strip() + \' [/INST]\'}}{% elif message[\'role\'] == \'system\' %}{{ \'<<SYS>>\\n\' + content.strip() + \'\\n<</SYS>>\\n\\n\' }}{% elif message[\'role\'] == \'assistant\' %}{{ \' \' + content.strip() + \' \' + eos_token }}{% endif %}{% endfor %}'
58
+ # self.tokenizer = AutoTokenizer.from_pretrained(
59
+ # "song9/KuLLM3-LoRA-CC"
60
+ # )
61
+ # embedding layer의 input을 기존 dimension에서 special token개수만큼 추가한 차원만큼 resize
62
+ self.model.resize_token_embeddings(len(self.tokenizer))
63
+
64
+ # LoRA Linear 레이어를 생성한다.
65
+ self.config = LoraConfig(
66
+ task_type=TaskType.CAUSAL_LM,
67
+ inference_mode=False,
68
+ r=64,
69
+ lora_alpha=128,
70
+ lora_dropout=0.01,
71
+ target_modules=["embed_tokens", "lm_head", "q_proj", "v_proj"]
72
+ )
73
+ # model에 LoRA 레이어를 추가한다.
74
+ self.model = get_peft_model(self.model, self.config)
75
+
76
+ if __name__ == '__main__':
77
+ ### 금요일 오후 6시반부터 시작
78
+ print("✅Start Timer", flush=True)
79
+ time.sleep(381600)
80
+
81
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
82
+ kullm3 = KuLLM3(device=device)
83
+ tokenizer, model = kullm3.tokenizer, kullm3.model
84
+
85
+ root_dir = os.path.join('SET PATH TO your dataset dir path')
86
+
87
+ # save as tsv file
88
+ # Concat two dataset(AI-Hub/민원 and AI-Hub/용도별), make train_combined.tsv and valid_combined.tsv
89
+ training = os.path.join(root_dir, 'train_combined.tsv')
90
+ validation = os.path.join(root_dir, 'valid_combined.tsv')
91
+ # df.to_csv(savepath, sep='\t', index=False)
92
+ # load as tsv file
93
+ train_df = pd.read_csv(training, sep='\t')
94
+ val_df = pd.read_csv(validation, sep='\t')
95
+
96
+ response_template = '[/INST]'
97
+ instruction_template = '[INST]'
98
+ print('✅Preparing dataset', flush=True)
99
+ train_dataset = Dataset.from_list(train_df['dialogue'].apply(lambda x:tokenizer(
100
+ x, return_length=False, return_attention_mask=True
101
+ )).to_list())
102
+ val_dataset = Dataset.from_list(val_df['dialogue'].apply(lambda x:tokenizer(
103
+ x, return_length=False, return_attention_mask=True
104
+ )).to_list())
105
+ collator = DataCollatorForCompletionOnlyLM(
106
+ instruction_template=instruction_template,
107
+ response_template=response_template,
108
+ tokenizer=tokenizer
109
+ )
110
+
111
+ ### 금요일 오후 6시반부터 시작
112
+ # print("✅Start Timer", flush=True)
113
+ # time.sleep(381600)
114
+
115
+ peft_model_path = "SET YOUR HUGGINGFACE models path" # ex) song9/CC-KuLLM3-LoRA
116
+ print('✅Start Training', flush=True)
117
+ # train args
118
+ batchsize_per_step = 2
119
+ save_steps = 8000
120
+ output_dirpath = 'SET YOUR CHECKPOINT DIR PATH'
121
+ training_args = TrainingArguments(
122
+ output_dir=output_dirpath, # 모델과 체크포인트를 저장할 디렉토리
123
+ save_total_limit=1, # 저장할 체크포인트의 최대 개수
124
+ load_best_model_at_end=True, # 훈련 종료 시 가장 좋은 성능을 낸 모델을 로드함
125
+ save_steps=save_steps, # load_best_model_at_end가 True일 때 설정해야 한다. eval_steps, logging_steps와 값을 맞춰야 한다.
126
+ logging_steps=int(save_steps/10), # 학습 중 로그를 출력할 step 수.
127
+ # Train 관련 파라미터
128
+ num_train_epochs=2, # 훈련 epoch 수
129
+ per_device_train_batch_size=batchsize_per_step, # 각 device(gpu) 당 배치 사이즈.
130
+ remove_unused_columns=False, # 데이터셋에서 사용하지 않는 열이 있어도 제거하지 않는다.
131
+ bf16=True, # dtype이 bfloat16 형식인지 여부
132
+ dataloader_drop_last=True, # ex) True: batch_size가 2인데 마지막 batch엔 데이터가 1개뿐이면 해당 batch는 버린다.
133
+ group_by_length=True, # 이걸 해야 길이가 비슷한 데이터끼리 같은 batch에 묶임.
134
+ # 최적화 관련
135
+ gradient_checkpointing=True, # 메모리 절약을 위해 역전파 시 체크포인트를 사용하여 일부 중간 계산을 재계산하는 방식이다.
136
+ gradient_checkpointing_kwargs={"use_reentrant": False}, # Gradient Checkpointing 동작 방식을 설정하는 옵션 (PyTorch의 비권장 reentrant API 사용을 피함)
137
+ # gradient_accumulation_steps=16, # batch_size와 반비례하는 파라미터. batch size를 키울 수 없을 때, 여러 개의 batch의 gradient를 축적하여 마치 하나의 큰 batch를 사용하는 것 같은 효과를 냄. 그러나 메모리에 부담이 됨.
138
+ torch_empty_cache_steps=batchsize_per_step, # 설정 스텝마다 `torch.cuda.empty_cache()`를 호출하여 메모리 캐시를 비움 (메모리 누수 방지)
139
+ # dataloader_num_workers=1, # data를 load할 때 사용할 process의 수. default=0 이면 main process에서 dataloader를 수행 (사용하면 메모리 사용량 급증...)
140
+ # torch_compile=True, # torch.compile을 사용해 속도 향상
141
+ # Optimizer 및 Scheduler
142
+ lr_scheduler_type='linear',
143
+ # warmup_steps=200, # 학습 초기 단계에서 설정값 단계까지 학습률을 서서히 증가시킨다.
144
+ warmup_ratio=0.05, # 전체 step을 모를 때 warmup을 전체 step 중 ratio만큼 적용
145
+ # optim="adamw_torch", # AdamW 옴티마이저 사용
146
+ # optim_args={ ## ? 이게 아닌가봄 에러 유발.
147
+ # 'lr':5e-5,'weight_decay':0.01
148
+ # },
149
+ weight_decay=0.01, # L2 규제용 가중치 감소율이다. 과적합 방지를 위해 사용하는 정규화 기술.
150
+ learning_rate=1e-5, # 학습률
151
+
152
+ # # eval
153
+ per_device_eval_batch_size=batchsize_per_step,
154
+ eval_strategy='steps',
155
+ # 평가를 실행할 전략 ('steps'로 설정 시 eval_steps마다 평가가 수행됨)
156
+ # 'epoch'로 설정 시 train epoch 종료 시마다 eval을 수행한다.
157
+ eval_steps=save_steps, # 평가를 수행할 스텝 간격
158
+
159
+ # # huggingface에 모델을 업로드할 때 필요한 매개변수
160
+ hub_model_id=peft_model_path,
161
+ push_to_hub=True,
162
+ hub_private_repo=False
163
+ )
164
+ trainer = Trainer(
165
+ model=model,
166
+ args=training_args,
167
+ train_dataset=train_dataset,
168
+ eval_dataset=val_dataset,
169
+ data_collator=collator
170
+ )
171
+
172
+ trainer.train()
173
+
174
+ output_dirpath_backup = output_dirpath+'_backup'
175
+ if os.path.exists(output_dirpath_backup):
176
+ os.mkdir(output_dirpath_backup)
177
+ model.save_pretrained(output_dirpath_backup)
178
+ tokenizer.save_pretrained(output_dirpath_backup)
179
+ # trainer.push_to_hub()
180
+ # trainer.model.push_to_hub(training_args.output_dir)
181
+
182
+ # 직접 huggingface-cli로 push