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