Views
No views yet
1
2import wandb
3import os
4
5WANDB_API_KEY = "my-token"
6wandb.login(key=WANDB_API_KEY)
7wandb.init(project='llm2024-competition')
8
9HF_TOKEN = "my-token"
10
11from transformers import (
12 AutoModelForCausalLM,
13 AutoTokenizer,
14 BitsAndBytesConfig,
15 TrainingArguments,
16 logging,
17)
18from peft import (
19 LoraConfig,
20 PeftModel,
21 get_peft_model,
22)
23import os, torch, gc
24from datasets import load_dataset
25import bitsandbytes as bnb
26from trl import SFTTrainer
27
28SEED_VALUE = 42
29
30base_model_id = "llm-jp/llm-jp-3-13b"
31new_model_id = "llm-jp-3-13b-finetune" #Fine-Tuningしたモデルにつけたい名前
32
33bnb_config = BitsAndBytesConfig(
34 load_in_4bit=True,
35 bnb_4bit_quant_type="nf4", # nf4は通常のINT4より精度が高く、ニューラルネットワークの分布に最適です
36 bnb_4bit_compute_dtype=torch.bfloat16,
37)
38
39
40model = AutoModelForCausalLM.from_pretrained(
41 base_model_id,
42 quantization_config=bnb_config,
43 device_map="cuda:0" #auto"
44)
45
46tokenizer = AutoTokenizer.from_pretrained(base_model_id, trust_remote_code=True)
47
48
49def find_all_linear_names(model):
50 cls = bnb.nn.Linear4bit # 4bit量子化線形層クラスを指定
51 lora_module_names = set() # ここに取得した線形層を保持します。
52
53 # モデル内の全てのモジュールを探索します
54 for name, module in model.named_modules():
55 if isinstance(module, cls): # モジュールが4bit量子化線形層の場合
56 names = name.split('.') # モジュールの名前を分割 (ネストされてる際などに対処)
57 lora_module_names.add(names[0] if len(names) == 1 else names[-1]) # 最下層の名前をlora_module_namesに追加
58
59 # 'lm_head' は16ビット演算の際に除外する必要があるため、lora_module_namesから削除
60 if 'lm_head' in lora_module_names:
61 lora_module_names.remove('lm_head')
62
63 return list(lora_module_names) # lora_module_namesをリストに変換して返します。
64
65modules = find_all_linear_names(model)
66
67peft_config = LoraConfig(
68 r=32, #16,
69 lora_alpha=32,
70 lora_dropout=0.05,
71 bias="none",
72 task_type="CAUSAL_LM",
73 target_modules=modules,
74)
75
76model = get_peft_model(model, peft_config)
77
78
79from datasets import concatenate_datasets, DatasetDict
80
81# 全てのデータセットを読み込み
82dataset0 = load_dataset("json", data_files="./Distribution20241221_all/ichikara-instruction-003-001-1.json")
83dataset1 = load_dataset("json", data_files="./Distribution20241221_all/ichikara-instruction-003-001-1.json")
84dataset2 = load_dataset("json", data_files="./Distribution20241221_all/ichikara-instruction-003-001-2.2.json")
85dataset3 = load_dataset("json", data_files="./Distribution20241221_all/ichikara-instruction-003-001-5.2.json")
86dataset4 = load_dataset("json", data_files="./Distribution20241221_all/ichikara-instruction-003-001-2.1.json")
87dataset5 = load_dataset("json", data_files="./Distribution20241221_all/ichikara-instruction-003-001-5.1.json")
88dataset6 = load_dataset("json", data_files="./Distribution20241221_all/ichikara-instruction-003-002-1.json")
89dataset7 = load_dataset("json", data_files="./Distribution20241221_all/ichikara-instruction-003-003-1.json")
90
91datasets_to_concatenate = [
92 dataset0["train"],
93 dataset1["train"],
94 dataset2["train"],
95 dataset3["train"],
96 dataset4["train"],
97 dataset5["train"],
98 dataset6["train"],
99 dataset7["train"]
100 ]
101
102concatenated_train_dataset = concatenate_datasets(datasets_to_concatenate)
103
104dataset_all = DatasetDict({
105 "train": concatenated_train_dataset
106})
107
108# 結合したデータを使用
109dataset=dataset_all
110
111# 学習時のプロンプトフォーマットの定義
112prompt = """### 指示
113{}
114### 回答
115{}"""
116
117
118"""
119formatting_prompts_func: 各データをプロンプトに合わせた形式に合わせる
120"""
121EOS_TOKEN = tokenizer.eos_token # トークナイザーのEOSトークン(文末トークン)
122def formatting_prompts_func(examples):
123 input = examples["text"] # 入力データ
124 output = examples["output"] # 出力データ
125 text = prompt.format(input, output) + EOS_TOKEN # プロンプトの作成
126 return { "formatted_text" : text, } # 新しいフィールド "formatted_text" を返す
127pass
128
129# # 各データにフォーマットを適用
130dataset = dataset.map(
131 formatting_prompts_func,
132 num_proc= 4, # 並列処理数を指定
133)
134
135# データをtrainデータとtestデータに分割 (test_sizeの比率に)
136dataset = dataset["train"].train_test_split(test_size=0.1, seed=SEED_VALUE)
137
138
139training_arguments = TrainingArguments(
140 output_dir=new_model_id,
141 per_device_train_batch_size=1, #
142 gradient_accumulation_steps=4, # def: 2
143 optim="paged_adamw_32bit",
144 num_train_epochs=1, # def: 1
145 logging_strategy="steps",
146 logging_steps=10,
147 warmup_steps=10,
148 save_steps=100,
149 save_total_limit = 2,
150 max_steps = -1, # def:-1
151 learning_rate=2e-5, # def:5e-5,
152 fp16= False,
153 bf16= False,
154 seed = SEED_VALUE,
155 group_by_length=True,
156 report_to="wandb"
157)
158
159trainer = SFTTrainer(
160 model=model,
161 train_dataset=dataset["train"],
162 peft_config=peft_config,
163 max_seq_length= 512,
164 dataset_text_field="formatted_text",
165 tokenizer=tokenizer,
166 args=training_arguments,
167 packing= False,
168)
169
170model.config.use_cache = False # キャッシュ機能を無効化
171trainer.train() # トレーニングを実行
172
173from datetime import datetime
174
175# 現在の日時を取得
176now = datetime.now()
177
178# フォーマットを指定して日時を文字列に変換
179formatted_date = now.strftime("%Y%m%d_%H%M%S") # 例: "20241214_153045"
180
181print(formatted_date)
182
183# タスクとなるデータの読み込み。
184# omnicampusの開発環境では、左にタスクのjsonlをドラッグアンドドロップしてから実行。
185import json
186datasets = []
187with open("./elyza-tasks-100-TV_0.jsonl", "r") as f:
188 item = ""
189 for line in f:
190 line = line.strip()
191 item += line
192 if item.endswith("}"):
193 datasets.append(json.loads(item))
194 item = ""
195
196
197# モデルによるタスクの推論。
198from tqdm import tqdm
199
200results = []
201for data in tqdm(datasets):
202
203 input = data["input"]
204
205 prompt = f"""### 指示
206 {input}
207 ### 回答
208 """
209
210 tokenized_input = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt").to(model.device)
211 attention_mask = torch.ones_like(tokenized_input)
212
213 with torch.no_grad():
214 outputs = model.generate(
215 tokenized_input,
216 attention_mask=attention_mask,
217 max_new_tokens=100,
218 do_sample=False,
219 repetition_penalty=1.2,
220 pad_token_id=tokenizer.eos_token_id
221 )[0]
222 output = tokenizer.decode(outputs[tokenized_input.size(1):], skip_special_tokens=True)
223
224 results.append({"task_id": data["task_id"], "input": input, "output": output})
225
226# こちらで生成されたjsolを提出してください。
227# 本コードではinputとeval_aspectも含んでいますが、なくても問題ありません。
228# 必須なのはtask_idとoutputとなります。
229import re
230jsonl_id = re.sub(".*/", "", new_model_id)
231with open(f"./{jsonl_id}-outputs-{formatted_date}.jsonl", 'w', encoding='utf-8') as f:
232 for result in results:
233 json.dump(result, f, ensure_ascii=False) # ensure_ascii=False for handling non-ASCII characters
234 f.write('\n')
235
236# モデルとトークナイザーをHugging Faceにアップロード
237model.push_to_hub(new_model_id, token=HF_TOKEN, private=True) # Online saving
238tokenizer.push_to_hub(new_model_id, token=HF_TOKEN, private=True) # Online saving
239
240
241