Views
No views yet
1
2# python 3.10.12
3!pip install -U pip
4!pip install -U transformers
5!pip install -U bitsandbytes
6!pip install -U accelerate
7!pip install -U datasets
8!pip install -U peft
9!pip install -U trl
10!pip install -U wandb
11!pip install ipywidgets --upgrade
12
13---
14
15from transformers import (
16 AutoModelForCausalLM,
17 AutoTokenizer,
18 BitsAndBytesConfig,
19 TrainingArguments,
20 logging,
21)
22from peft import (
23 LoraConfig,
24 PeftModel,
25 get_peft_model,
26)
27import os, torch, gc
28from datasets import load_dataset
29import bitsandbytes as bnb
30from trl import SFTTrainer
31
32---
33
34# Hugging Face Token
35HF_TOKEN = "your-token-here"
36
37---
38
39# モデルを読み込み。
40# llm-jp-3 1.8B, 3.7B, 13Bのsnapshotをダウンロード済みでmodelsディレクトリに格納してあります。
41# base_model_idの値はomnicampusの環境におけるモデルのパスを表しており、それ以外の環境で実行する場合は変更の必要があります。
42# その他のモデルは取得に承諾が必要なため、各自でダウンロードお願いします。
43base_model_id = "models/models--llm-jp--llm-jp-3-13b/snapshots/cd3823f4c1fcbb0ad2e2af46036ab1b0ca13192a" #Fine-Tuningするベースモデル
44# omnicampus以外の環境をご利用の方は以下をご利用ください。
45# base_model_id = "llm-jp/llm-jp-3-13b"
46new_model_id = "llm-jp-3-13b-finetune" #Fine-Tuningしたモデルにつけたい名前
47
48---
49
50"""
51bnb_config: 量子化の設定
52
53 - load_in_4bit:
54 - 4bit量子化形式でモデルをロード
55
56 - bnb_4bit_quant_type:
57 - 量子化の形式を指定
58
59 - bnb_4bit_compute_dtype:
60 - 量子化された重みを用いて計算する際のデータ型
61
62"""
63
64bnb_config = BitsAndBytesConfig(
65 load_in_4bit=True,
66 bnb_4bit_quant_type="nf4", # nf4は通常のINT4より精度が高く、ニューラルネットワークの分布に最適です
67 bnb_4bit_compute_dtype=torch.bfloat16,
68)
69
70---
71
72"""
73model: モデル
74
75 - base_model:
76 - 読み込むベースモデル (事前に定義したもの)
77
78 - quantization_config:
79 - bnb_configで設定した量子化設定
80
81 - device_map:
82 - モデルを割り当てるデバイス (CPU/GPU) "auto"で自動に割り当てられます。
83
84tokenizer: トークナイザー
85
86 - base_model:
87 - 読み込むベースモデル (事前に定義したもの)
88
89 - trust_remote_code:
90 - リモートコードの実行を許可 (カスタムモデルなど)
91"""
92model = AutoModelForCausalLM.from_pretrained(
93 base_model_id,
94 quantization_config=bnb_config,
95 device_map="auto"
96)
97
98tokenizer = AutoTokenizer.from_pretrained(base_model_id, trust_remote_code=True)
99
100---
101
102"""
103find_all_linear_names: モデル内の4bit量子化線形層を探します。
104"""
105
106def find_all_linear_names(model):
107 cls = bnb.nn.Linear4bit # 4bit量子化線形層クラスを指定
108 lora_module_names = set() # ここに取得した線形層を保持します。
109
110 # モデル内の全てのモジュールを探索します
111 for name, module in model.named_modules():
112 if isinstance(module, cls): # モジュールが4bit量子化線形層の場合
113 names = name.split('.') # モジュールの名前を分割 (ネストされてる際などに対処)
114 lora_module_names.add(names[0] if len(names) == 1 else names[-1]) # 最下層の名前をlora_module_namesに追加
115
116 # 'lm_head' は16ビット演算の際に除外する必要があるため、lora_module_namesから削除
117 if 'lm_head' in lora_module_names:
118 lora_module_names.remove('lm_head')
119
120 return list(lora_module_names) # lora_module_namesをリストに変換して返します。
121
122modules = find_all_linear_names(model)
123
124---
125
126"""
127peft_config: PEFTの構成設定
128
129 - r
130 - LoRA のランク (4, 8, 16 ,32...)
131 - 増やすほど学習が捗るが, 過学習のリスクも高まるので注意
132
133 - lora_alpha
134 - LoRAのスケーリング係数
135
136 - lora_dropout
137 - ドロップアウト率(過学習を防ぐための割合)
138
139 - bias
140 - バイアス項の扱い ("none"の場合、LoRAはバイアスを学習しない)
141
142 - task_type
143 - タスクタイプ
144
145 - target_modules
146 - LoRAを適用するターゲットモジュール (前のコードで特定した層)
147"""
148
149peft_config = LoraConfig(
150 r=16,
151 lora_alpha=32,
152 lora_dropout=0.05,
153 bias="none",
154 task_type="CAUSAL_LM",
155 target_modules=modules,
156)
157
158model = get_peft_model(model, peft_config)
159
160---
161
162"""
163学習に用いるデータセットの指定
164今回はLLM-jp の公開している Ichikara Instruction を使います。データにアクセスするためには申請が必要ですので、使いたい方のみ申請をしてください。
165Ichikara Instruciton を Hugging Face Hub にて公開することはお控えください。
166また、CC-BY-NC-SAですのでモデルはライセンスを継承する前提でお使いください。
167
168下記のリンクから申請を終えた先に Google Drive があり、Distribution20241221_all というフォルダごとダウンロードしてください。
169今回は「ichikara-instruction-003-001-1.json」を使います。必要であれば展開(!unzip など)し、データセットのパスを適切に指定してください。
170omnicampusの開発環境では取得したデータを左側にドラッグアンドドロップしてお使いください。
171
172https://liat-aip.sakura.ne.jp/wp/llmのための日本語インストラクションデータ作成/llmのための日本語インストラクションデータ-公開/
173関根聡, 安藤まや, 後藤美知子, 鈴木久美, 河原大輔, 井之上直也, 乾健太郎. ichikara-instruction: LLMのための日本語インストラクションデータの構築. 言語処理学会第30回年次大会(2024)
174
175"""
176
177dataset = load_dataset("json", data_files="ichikara-instruction-003-001-1.json")
178dataset
179
180---
181# 学習時のプロンプトフォーマットの定義
182prompt = """### 指示
183{}
184### 回答
185{}"""
186
187
188"""
189formatting_prompts_func: 各データをプロンプトに合わせた形式に合わせる
190"""
191EOS_TOKEN = tokenizer.eos_token # トークナイザーのEOSトークン(文末トークン)
192def formatting_prompts_func(examples):
193 input = examples["text"] # 入力データ
194 output = examples["output"] # 出力データ
195 text = prompt.format(input, output) + EOS_TOKEN # プロンプトの作成
196 return { "formatted_text" : text, } # 新しいフィールド "formatted_text" を返す
197pass
198
199# # 各データにフォーマットを適用
200dataset = dataset.map(
201 formatting_prompts_func,
202 num_proc= 4, # 並列処理数を指定
203)
204
205dataset
206
207---
208
209# データを確認
210print(dataset["train"]["formatted_text"][3])
211
212---
213
214# データをtrainデータとtestデータに分割 (test_sizeの比率に)
215# dataset = dataset["train"].train_test_split(test_size=0.1)
216# dataset
217
218---
219
220"""
221training_arguments: 学習の設定
222
223 - output_dir:
224 -トレーニング後のモデルを保存するディレクトリ
225
226 - per_device_train_batch_size:
227 - デバイスごとのトレーニングバッチサイズ
228
229 - per_device_
230 _batch_size:
231 - デバイスごとの評価バッチサイズ
232
233 - gradient_accumulation_steps:
234 - 勾配を更新する前にステップを積み重ねる回数
235
236 - optim:
237 - オプティマイザの設定
238
239 - num_train_epochs:
240 - エポック数
241
242 - eval_strategy:
243 - 評価の戦略 ("no"/"steps"/"epoch")
244
245 - eval_steps:
246 - eval_strategyが"steps"のとき、評価を行うstep間隔
247
248 - logging_strategy:
249 - ログ記録の戦略
250
251 - logging_steps:
252 - ログを出力するステップ間隔
253
254 - warmup_steps:
255 - 学習率のウォームアップステップ数
256
257 - save_steps:
258 - モデルを保存するステップ間隔
259
260 - save_total_limit:
261 - 保存しておくcheckpointの数
262
263 - max_steps:
264 - トレーニングの最大ステップ数
265
266 - learning_rate:
267 - 学習率
268
269 - fp16:
270 - 16bit浮動小数点の使用設定(第8回演習を参考にすると良いです)
271
272 - bf16:
273 - BFloat16の使用設定
274
275 - group_by_length:
276 - 入力シーケンスの長さによりバッチをグループ化 (トレーニングの効率化)
277
278 - report_to:
279 - ログの送信先 ("wandb"/"tensorboard"など)
280"""
281
282training_arguments = TrainingArguments(
283 output_dir=new_model_id,
284 per_device_train_batch_size=1,
285 gradient_accumulation_steps=2,
286 optim="paged_adamw_32bit",
287 num_train_epochs=1, # changing it to 2 caused the server error
288 logging_strategy="steps",
289 logging_steps=10,
290 warmup_steps=10,
291 save_steps=100,
292 save_total_limit = 2,
293 max_steps = -1,
294 learning_rate=5e-5,
295 fp16=False,
296 bf16=False,
297 seed = 3407,
298 group_by_length=True,
299 report_to="none"
300)
301
302---
303"""
304SFTTrainer: Supervised Fine-Tuningに関する設定
305
306 - model:
307 - 読み込んだベースのモデル
308
309 - train_dataset:
310 - トレーニングに使用するデータセット
311
312 - eval_dataset:
313 - 評価に使用するデータセット
314
315 - peft_config:
316 - PEFT(Parameter-Efficient Fine-Tuning)の設定(LoRAを利用する場合に指定)
317
318 - max_seq_length:
319 - モデルに入力されるシーケンスの最大トークン長
320
321 - dataset_text_field:
322 - データセット内の学習に使うテキストを含むフィールド名
323
324 - tokenizer:
325 - モデルに対応するトークナイザー
326
327 - args:
328 - トレーニングに使用するハイパーパラメータ(TrainingArgumentsの設定を指定)
329
330 - packing:
331 - 入力シーケンスのパッキングを行うかどうかの設定 (False に設定することで、各入力を独立して扱う)
332"""
333trainer = SFTTrainer(
334 model=model,
335 train_dataset=dataset["train"],
336 peft_config=peft_config,
337 max_seq_length= 512,
338 dataset_text_field="formatted_text",
339 tokenizer=tokenizer,
340 args=training_arguments,
341 packing= False,
342)
343
344model.config.use_cache = False # キャッシュ機能を無効化
345trainer.train() # トレーニングを実行
346
347---
348
349# タスクとなるデータの読み込み。
350# omnicampusの開発環境では、左にタスクのjsonlをドラッグアンドドロップしてから実行。
351import json
352datasets = []
353with open("./elyza-tasks-100-TV_0.jsonl", "r") as f:
354 item = ""
355 for line in f:
356 line = line.strip()
357 item += line
358 if item.endswith("}"):
359 datasets.append(json.loads(item))
360 item = ""
361
362---
363
364# モデルによるタスクの推論。
365from tqdm import tqdm
366
367results = []
368for data in tqdm(datasets):
369
370 input = data["input"]
371
372 prompt = f"""### 指示
373 {input}
374 ### 回答
375 """
376
377 tokenized_input = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt").to(model.device)
378 attention_mask = torch.ones_like(tokenized_input)
379
380 with torch.no_grad():
381 outputs = model.generate(
382 tokenized_input,
383 attention_mask=attention_mask,
384 max_new_tokens=100,
385 do_sample=False,
386 repetition_penalty=1.2,
387 pad_token_id=tokenizer.eos_token_id
388 )[0]
389 output = tokenizer.decode(outputs[tokenized_input.size(1):], skip_special_tokens=True)
390
391 results.append({"task_id": data["task_id"], "input": input, "output": output})
392