tatsuuuu commited on
Commit
d7f404e
·
verified ·
1 Parent(s): a93b4b6

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +495 -0
README.md CHANGED
@@ -20,3 +20,498 @@ language:
20
  This llama model was trained 2x faster with [Unsloth](https://github.com/unslothai/unsloth) and Huggingface's TRL library.
21
 
22
  [<img src="https://raw.githubusercontent.com/unslothai/unsloth/main/images/unsloth%20made%20with%20love.png" width="200"/>](https://github.com/unslothai/unsloth)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  This llama model was trained 2x faster with [Unsloth](https://github.com/unslothai/unsloth) and Huggingface's TRL library.
21
 
22
  [<img src="https://raw.githubusercontent.com/unslothai/unsloth/main/images/unsloth%20made%20with%20love.png" width="200"/>](https://github.com/unslothai/unsloth)
23
+
24
+ # 実行したコード
25
+
26
+ # python 3.10.12
27
+ !pip install -U pip
28
+ !pip install -U transformers
29
+ !pip install -U bitsandbytes
30
+ !pip install -U accelerate
31
+ !pip install -U datasets
32
+ !pip install -U peft
33
+ !pip install -U trl
34
+ !pip install -U wandb
35
+ !pip install ipywidgets --upgrade
36
+
37
+ from transformers import (
38
+ AutoModelForCausalLM,
39
+ AutoTokenizer,
40
+ BitsAndBytesConfig,
41
+ TrainingArguments,
42
+ logging,
43
+ )
44
+ from peft import (
45
+ LoraConfig,
46
+ PeftModel,
47
+ get_peft_model,
48
+ )
49
+ import os, torch, gc
50
+ from datasets import load_dataset
51
+ import bitsandbytes as bnb
52
+ from trl import SFTTrainer
53
+
54
+ # Hugging Face Token
55
+ HF_TOKEN = "write権限のあるトークン"
56
+
57
+ # モデルを読み込み。
58
+ # llm-jp-3 1.8B, 3.7B, 13Bのsnapshotをダウンロード済みでmodelsディレクトリに格納してあります。
59
+ # base_model_idの値はomnicampusの環境におけるモデルのパスを表しており、それ以外の環境で実行する場合は変更の必要があります。
60
+ # その他のモデルは取得に承諾が必要なため、各自でダウンロードお願いします。
61
+ base_model_id = "models/models--llm-jp--llm-jp-3-13b/snapshots/cd3823f4c1fcbb0ad2e2af46036ab1b0ca13192a" #Fine-Tuningするベースモデル
62
+ # omnicampus以外の環境をご利用の方は以下をご利用ください。
63
+ # base_model_id = "llm-jp/llm-jp-3-13b"
64
+ new_model_id = "llm-jp-3-13b-finetune" #Fine-Tuningしたモデルにつけたい名前
65
+
66
+ """
67
+ bnb_config: 量子化の設定
68
+
69
+ - load_in_4bit:
70
+ - 4bit量子化形式でモデルをロード
71
+
72
+ - bnb_4bit_quant_type:
73
+ - 量子化の形式を指定
74
+
75
+ - bnb_4bit_compute_dtype:
76
+ - 量子化された重みを用いて計算する際のデータ型
77
+
78
+ """
79
+
80
+ bnb_config = BitsAndBytesConfig(
81
+ load_in_4bit=True,
82
+ bnb_4bit_quant_type="nf4", # nf4は通常のINT4より精度が高く、ニューラルネットワークの分布に最適です
83
+ bnb_4bit_compute_dtype=torch.bfloat16,
84
+ )
85
+
86
+ """
87
+ model: モデル
88
+
89
+ - base_model:
90
+ - 読み込むベースモデル (事前に定義したもの)
91
+
92
+ - quantization_config:
93
+ - bnb_configで設定した量子化設定
94
+
95
+ - device_map:
96
+ - モデルを割り当てるデバイス (CPU/GPU) "auto"で自動に割り当てられます。
97
+
98
+ tokenizer: トークナイザー
99
+
100
+ - base_model:
101
+ - 読み込むベースモデル (事前に定義したもの)
102
+
103
+ - trust_remote_code:
104
+ - リモートコードの実行を許可 (カスタムモデルなど)
105
+ """
106
+ model = AutoModelForCausalLM.from_pretrained(
107
+ base_model_id,
108
+ quantization_config=bnb_config,
109
+ device_map="auto"
110
+ )
111
+
112
+ tokenizer = AutoTokenizer.from_pretrained(base_model_id, trust_remote_code=True)
113
+
114
+ """
115
+ find_all_linear_names: モデル内の4bit量子化線形層を探します。
116
+ """
117
+
118
+ def find_all_linear_names(model):
119
+ cls = bnb.nn.Linear4bit # 4bit量子化線形層クラスを指定
120
+ lora_module_names = set() # ここに取得した線形層を保持します。
121
+
122
+ # モデル内の全てのモジュールを探索します
123
+ for name, module in model.named_modules():
124
+ if isinstance(module, cls): # モジュールが4bit量子化線形層の場合
125
+ names = name.split('.') # モジュールの名前を分割 (ネストされてる際などに対処)
126
+ lora_module_names.add(names[0] if len(names) == 1 else names[-1]) # 最下層の名前をlora_module_namesに追加
127
+
128
+ # 'lm_head' は16ビット演算の際に除外する必要があるため、lora_module_namesから削除
129
+ if 'lm_head' in lora_module_names:
130
+ lora_module_names.remove('lm_head')
131
+
132
+ return list(lora_module_names) # lora_module_namesをリストに変換して返します。
133
+
134
+ modules = find_all_linear_names(model)
135
+
136
+ """
137
+ peft_config: PEFTの構成設定
138
+
139
+ - r
140
+ - LoRA のランク (4, 8, 16 ,32...)
141
+ - 増やすほど学習が捗るが, 過学習のリスクも高まるので注意
142
+
143
+ - lora_alpha
144
+ - LoRAのスケーリング係数
145
+
146
+ - lora_dropout
147
+ - ドロップアウト率(過学習を防ぐための割合)
148
+
149
+ - bias
150
+ - バイアス項の扱い ("none"の場合、LoRAはバイアスを学習しない)
151
+
152
+ - task_type
153
+ - タスクタイプ
154
+
155
+ - target_modules
156
+ - LoRAを適用するターゲットモジュール (前のコードで特定した層)
157
+ """
158
+
159
+ peft_config = LoraConfig(
160
+ r=16,
161
+ lora_alpha=32,
162
+ lora_dropout=0.05,
163
+ bias="none",
164
+ task_type="CAUSAL_LM",
165
+ target_modules=modules,
166
+ )
167
+
168
+ model = get_peft_model(model, peft_config)
169
+
170
+ """
171
+ 学習に用いるデータセットの指定
172
+ 今回はLLM-jp の公開している Ichikara Instruction を使い��す。データにアクセスするためには申請が必要ですので、使いたい方のみ申請をしてください。
173
+ Ichikara Instruciton を Hugging Face Hub にて公開することはお控えください。
174
+ また、CC-BY-NC-SAですのでモデルはライセンスを継承する前提でお使いください。
175
+
176
+ 下記のリンクから申請を終えた先に Google Drive があり、Distribution20241221_all というフォルダごとダウンロードしてください。
177
+ 今回は「ichikara-instruction-003-001-1.json」を使います。必要であれば展開(!unzip など)し、データセットのパスを適切に指定してください。
178
+ omnicampusの開発環境では取得したデータを左側にドラッグアンドドロップしてお使いください。
179
+
180
+ https://liat-aip.sakura.ne.jp/wp/llmのための日本語インストラクションデータ作成/llmのための日本語インストラクションデータ-公開/
181
+ 関根聡, 安藤まや, 後藤美知子, 鈴木久美, 河原大輔, 井之上直也, 乾健太郎. ichikara-instruction: LLMのための日本語インストラクションデータの構築. 言語処理学会第30回年次大会(2024)
182
+
183
+ """
184
+
185
+ dataset = load_dataset("json", data_files="./ichikara-instruction-003-001-1.json")
186
+ dataset
187
+
188
+ # 学習時のプロンプトフォーマットの定義
189
+ prompt = """### 指示
190
+ {}
191
+ ### 回答
192
+ {}"""
193
+
194
+
195
+ """
196
+ formatting_prompts_func: 各データをプロンプトに合わせた形式に合わせる
197
+ """
198
+ EOS_TOKEN = tokenizer.eos_token # トークナイザーのEOSトークン(文末トークン)
199
+ def formatting_prompts_func(examples):
200
+ input = examples["text"] # 入力データ
201
+ output = examples["output"] # 出力データ
202
+ text = prompt.format(input, output) + EOS_TOKEN # プロンプトの作成
203
+ return { "formatted_text" : text, } # 新しいフィールド "formatted_text" を返す
204
+ pass
205
+
206
+ # # 各データにフォーマットを適用
207
+ dataset = dataset.map(
208
+ formatting_prompts_func,
209
+ num_proc= 4, # 並列処理数を指定
210
+ )
211
+
212
+ dataset
213
+
214
+ # データを確認
215
+ print(dataset["train"]["formatted_text"][3])
216
+
217
+ # データをtrainデータとtestデータに分割 (test_sizeの比率に)
218
+ # dataset = dataset["train"].train_test_split(test_size=0.1)
219
+ # dataset
220
+
221
+ """
222
+ training_arguments: 学習の設定
223
+
224
+ - output_dir:
225
+ -トレーニング後のモデルを保存するディレクトリ
226
+
227
+ - per_device_train_batch_size:
228
+ - デバイスごとのトレーニングバッチサイズ
229
+
230
+ - per_device_
231
+ _batch_size:
232
+ - デバイスごとの評価バッチサイズ
233
+
234
+ - gradient_accumulation_steps:
235
+ - 勾配を更新する前にステップを積み重ねる回数
236
+
237
+ - optim:
238
+ - オプティマイザの設定
239
+
240
+ - num_train_epochs:
241
+ - エポック数
242
+
243
+ - eval_strategy:
244
+ - 評価の戦略 ("no"/"steps"/"epoch")
245
+
246
+ - eval_steps:
247
+ - eval_strategyが"steps"のとき、評価を行うstep間隔
248
+
249
+ - logging_strategy:
250
+ - ログ記録の戦略
251
+
252
+ - logging_steps:
253
+ - ログを出力するステップ間隔
254
+
255
+ - warmup_steps:
256
+ - 学習率のウォームアップステップ数
257
+
258
+ - save_steps:
259
+ - モデルを保存するステップ間隔
260
+
261
+ - save_total_limit:
262
+ - 保存しておくcheckpointの数
263
+
264
+ - max_steps:
265
+ - トレーニングの最大ステップ数
266
+
267
+ - learning_rate:
268
+ - 学習率
269
+
270
+ - fp16:
271
+ - 16bit浮動小数点の使用設定(第8回演習を参考にすると良いです)
272
+
273
+ - bf16:
274
+ - BFloat16の使用設定
275
+
276
+ - group_by_length:
277
+ - 入力シーケンスの長さによりバッチをグループ化 (トレーニングの効率化)
278
+
279
+ - report_to:
280
+ - ログの送信先 ("wandb"/"tensorboard"など)
281
+ """
282
+
283
+ training_arguments = TrainingArguments(
284
+ output_dir=new_model_id,
285
+ per_device_train_batch_size=1,
286
+ gradient_accumulation_steps=2,
287
+ optim="paged_adamw_32bit",
288
+ num_train_epochs=1,
289
+ logging_strategy="steps",
290
+ logging_steps=10,
291
+ warmup_steps=10,
292
+ save_steps=100,
293
+ save_total_limit = 2,
294
+ max_steps = -1,
295
+ learning_rate=5e-5,
296
+ fp16=False,
297
+ bf16=False,
298
+ seed = 3407,
299
+ group_by_length=True,
300
+ report_to="none"
301
+ )
302
+
303
+ """
304
+ SFTTrainer: 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
+ """
333
+ trainer = 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
+
344
+ model.config.use_cache = False # キャッシュ機能を無効化
345
+ trainer.train() # トレーニングを実行
346
+
347
+ # タスクとなるデータの読み込み。
348
+ # omnicampusの開発環境では、左にタスクのjsonlをドラッグアンドドロップしてから実行。
349
+ import json
350
+ datasets = []
351
+ with open("./elyza-tasks-100-TV_0.jsonl", "r") as f:
352
+ item = ""
353
+ for line in f:
354
+ line = line.strip()
355
+ item += line
356
+ if item.endswith("}"):
357
+ datasets.append(json.loads(item))
358
+ item = ""
359
+
360
+ # モデルによるタスクの推論。
361
+ from tqdm import tqdm
362
+
363
+ results = []
364
+ for data in tqdm(datasets):
365
+
366
+ input = data["input"]
367
+
368
+ prompt = f"""### 指示
369
+ {input}
370
+ ### 回答
371
+ """
372
+
373
+ tokenized_input = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt").to(model.device)
374
+ attention_mask = torch.ones_like(tokenized_input)
375
+
376
+ with torch.no_grad():
377
+ outputs = model.generate(
378
+ tokenized_input,
379
+ attention_mask=attention_mask,
380
+ max_new_tokens=100,
381
+ do_sample=False,
382
+ repetition_penalty=1.2,
383
+ pad_token_id=tokenizer.eos_token_id
384
+ )[0]
385
+ output = tokenizer.decode(outputs[tokenized_input.size(1):], skip_special_tokens=True)
386
+
387
+ results.append({"task_id": data["task_id"], "input": input, "output": output})
388
+
389
+ # こちらで生成されたjsolを提出してください。
390
+ # 本コードではinputとeval_aspectも含んでいますが、なくても問題ありません。
391
+ # 必須なのはtask_idとoutputとなります。
392
+ import re
393
+ jsonl_id = re.sub(".*/", "", new_model_id)
394
+ with open(f"./{jsonl_id}-outputs.jsonl", 'w', encoding='utf-8') as f:
395
+ for result in results:
396
+ json.dump(result, f, ensure_ascii=False) # ensure_ascii=False for handling non-ASCII characters
397
+ f.write('\n')
398
+
399
+ # モデルとトークナイザーをHugging Faceにアップロード
400
+ model.push_to_hub(new_model_id, token=HF_TOKEN, private=True) # Online saving
401
+ tokenizer.push_to_hub(new_model_id, token=HF_TOKEN, private=True) # Online saving
402
+
403
+ # 推論用コード
404
+ !pip install -U bitsandbytes
405
+ !pip install -U transformers
406
+ !pip install -U accelerate
407
+ !pip install -U datasets
408
+ !pip install -U peft
409
+
410
+ # notebookでインタラクティブな表示を可能とする(ただし、うまく動かない場合あり)
411
+ !pip install ipywidgets --upgrade
412
+
413
+ from transformers import (
414
+ AutoModelForCausalLM,
415
+ AutoTokenizer,
416
+ BitsAndBytesConfig,
417
+ )
418
+ from peft import PeftModel
419
+ import torch
420
+ from tqdm import tqdm
421
+ import json
422
+
423
+ # Hugging Faceで取得したTokenをこちらに貼る。
424
+ HF_TOKEN = "Hugging Face Token"
425
+
426
+ # ベースとなるモデルと学習したLoRAのアダプタ。
427
+ # model_idの値はomnicampusの環境におけるモデルのパスを表しており、それ以外の環境で実行する場合は変更の必要があります。
428
+ model_id = "models/models--llm-jp--llm-jp-3-13b/snapshots/cd3823f4c1fcbb0ad2e2af46036ab1b0ca13192a"
429
+ # omnicampus以外の環境をご利用の方は以下をご利用ください。
430
+ # base_model_id = "llm-jp/llm-jp-3-13b"
431
+ adapter_id = "" # こちらにアップロードしたHugging FaceのIDを指定してください。
432
+
433
+ # QLoRA config
434
+ bnb_config = BitsAndBytesConfig(
435
+ load_in_4bit=True,
436
+ bnb_4bit_quant_type="nf4",
437
+ bnb_4bit_compute_dtype=torch.bfloat16,
438
+ )
439
+
440
+ # Load model
441
+ model = AutoModelForCausalLM.from_pretrained(
442
+ model_id,
443
+ quantization_config=bnb_config,
444
+ device_map="auto",
445
+ token = HF_TOKEN
446
+ )
447
+
448
+ # Load tokenizer
449
+ tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True, token = HF_TOKEN)
450
+
451
+ # 元のモデルにLoRAのアダプタを統合。
452
+ model = PeftModel.from_pretrained(model, adapter_id, token = HF_TOKEN)
453
+
454
+ # データセットの読み込み。
455
+ # omnicampusの開発環境では、左にタスクのjsonlをドラッグアンドドロップしてから実行。
456
+ datasets = []
457
+ with open("./elyza-tasks-100-TV_0.jsonl", "r") as f:
458
+ item = ""
459
+ for line in f:
460
+ line = line.strip()
461
+ item += line
462
+ if item.endswith("}"):
463
+ datasets.append(json.loads(item))
464
+ item = ""
465
+
466
+ # gemma
467
+ results = []
468
+ for data in tqdm(datasets):
469
+
470
+ input = data["input"]
471
+ prompt = f"""### 指示
472
+ {input}
473
+ ### 回答
474
+ """
475
+
476
+ input_ids = tokenizer(prompt, return_tensors="pt").to(model.device)
477
+ outputs = model.generate(**input_ids, max_new_tokens=512, do_sample=False, repetition_penalty=1.2,)
478
+ output = tokenizer.decode(outputs[0][input_ids.input_ids.size(1):], skip_special_tokens=True)
479
+
480
+ results.append({"task_id": data["task_id"], "input": input, "output": output})
481
+
482
+
483
+ # llmjp
484
+ results = []
485
+ for data in tqdm(datasets):
486
+
487
+ input = data["input"]
488
+
489
+ prompt = f"""### 指示
490
+ {input}
491
+ ### 回答
492
+ """
493
+
494
+ tokenized_input = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt").to(model.device)
495
+ attention_mask = torch.ones_like(tokenized_input)
496
+ with torch.no_grad():
497
+ outputs = model.generate(
498
+ tokenized_input,
499
+ attention_mask=attention_mask,
500
+ max_new_tokens=100,
501
+ do_sample=False,
502
+ repetition_penalty=1.2,
503
+ pad_token_id=tokenizer.eos_token_id
504
+ )[0]
505
+ output = tokenizer.decode(outputs[tokenized_input.size(1):], skip_special_tokens=True)
506
+
507
+ results.append({"task_id": data["task_id"], "input": input, "output": output})
508
+
509
+ # こちらで生成されたjsolを提出してください。
510
+ # 本コードではinputとeval_aspectも含んでいますが、なくても問題ありません。
511
+ # 必須なのはtask_idとoutputとなります。
512
+ import re
513
+ jsonl_id = re.sub(".*/", "", adapter_id)
514
+ with open(f"./{jsonl_id}-outputs.jsonl", 'w', encoding='utf-8') as f:
515
+ for result in results:
516
+ json.dump(result, f, ensure_ascii=False) # ensure_ascii=False for handling non-ASCII characters
517
+ f.write('\n')