hiro877 commited on
Commit
ffe5a2b
·
verified ·
1 Parent(s): 7b6f13a

add development files

Browse files
Files changed (3) hide show
  1. DPO.py +134 -0
  2. LoRa.py +259 -0
  3. LoRa_inference.py +86 -0
DPO.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from unsloth import FastLanguageModel
4
+ from datasets import Dataset
5
+ from trl import DPOTrainer, DPOConfig
6
+ import torch
7
+
8
+
9
+ # Model loading function
10
+ def load_model():
11
+ print("Initializing model loading...")
12
+ model_name = "outputs_sample_code/checkpoint-200"
13
+ max_seq_length = 512
14
+ model, tokenizer = FastLanguageModel.from_pretrained(
15
+ model_name,
16
+ dtype=None,
17
+ load_in_4bit=True
18
+ )
19
+ print("Model and tokenizer loaded successfully.")
20
+ print(f"Model type: {type(model)}, Tokenizer type: {type(tokenizer)}")
21
+
22
+ if hasattr(model, 'config'):
23
+ print("Setting max_seq_length in model.config")
24
+ model.config.max_seq_length = max_seq_length
25
+ else:
26
+ print("Error: model.config does not exist!")
27
+
28
+ model = FastLanguageModel.get_peft_model(
29
+ model,
30
+ r=32,
31
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
32
+ lora_alpha=32,
33
+ lora_dropout=0.05,
34
+ bias="none",
35
+ use_gradient_checkpointing="unsloth",
36
+ random_state=3407,
37
+ use_rslora=False,
38
+ loftq_config=None,
39
+ max_seq_length=max_seq_length
40
+ )
41
+ print("PEFT model configured.")
42
+ return model, tokenizer
43
+
44
+ # Dataset loading function
45
+ def load_dataset():
46
+ print("Loading dataset...")
47
+ dataset_name = "cyberagent/chatbot-arena-ja-calm2-7b-chat-experimental"
48
+
49
+ from datasets import load_dataset
50
+ dataset = load_dataset(dataset_name)
51
+
52
+ formatted_data = []
53
+ for item in dataset["train"]:
54
+ formatted_data.append({
55
+ "prompt": item.get("prompt", ""),
56
+ "chosen": item.get("response_winner", ""),
57
+ "rejected": item.get("response_loser", "")
58
+ })
59
+
60
+ print(f"Formatted data: {len(formatted_data)} items")
61
+ return Dataset.from_dict({
62
+ "prompt": [item["prompt"] for item in formatted_data],
63
+ "chosen": [item["chosen"] for item in formatted_data],
64
+ "rejected": [item["rejected"] for item in formatted_data]
65
+ })
66
+
67
+ # DPO training function
68
+ def train_dpo(model, tokenizer, dataset):
69
+ print("Configuring training arguments...")
70
+
71
+ training_args = DPOConfig(
72
+ output_dir="./dpo_trained_model_1216",
73
+ overwrite_output_dir=True,
74
+ per_device_train_batch_size=8,
75
+ gradient_accumulation_steps=128,
76
+ per_device_eval_batch_size=8,
77
+ learning_rate=1e-5,
78
+ weight_decay=0.01,
79
+ num_train_epochs=1,
80
+ lr_scheduler_type="constant_with_warmup",
81
+ warmup_steps=10,
82
+ fp16=True,
83
+ eval_strategy="steps",
84
+ save_strategy="steps",
85
+ save_steps=32,
86
+ logging_steps=8,
87
+ eval_steps=8,
88
+ load_best_model_at_end=True,
89
+ save_safetensors=False,
90
+ save_only_model=True,
91
+ remove_unused_columns=False,
92
+ )
93
+ print("Training arguments configured.")
94
+
95
+ print("Initializing DPOTrainer...")
96
+ dpo_trainer = DPOTrainer(
97
+ model=model,
98
+ args=training_args,
99
+ beta=0.3,
100
+ train_dataset=dataset,
101
+ eval_dataset=dataset,
102
+ tokenizer=tokenizer,
103
+ max_prompt_length=162,
104
+ max_length=512,
105
+ loss_type="sigmoid",
106
+ label_smoothing=0.0,
107
+ )
108
+ print("DPOTrainer initialized.")
109
+
110
+ print("Starting training...")
111
+
112
+ original_forward = model.forward
113
+
114
+ def new_forward(*args, **kwargs):
115
+ if "input_ids" in kwargs:
116
+ kwargs["input_ids"] = kwargs["input_ids"].long()
117
+ return original_forward(*args, **kwargs)
118
+
119
+ model.forward = new_forward
120
+
121
+ dpo_trainer.train()
122
+ print("Training completed.")
123
+
124
+ if __name__ == "__main__":
125
+ print("Loading model...")
126
+ model, tokenizer = load_model()
127
+
128
+ print("Loading dataset...")
129
+ dataset = load_dataset()
130
+
131
+ print("Starting DPO training...")
132
+ train_dpo(model, tokenizer, dataset)
133
+
134
+ print("Training complete. Model saved.")
LoRa.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # llm-jp/llm-jp-3-13bを4bit量子化のqLoRA設定でロード。
2
+
3
+ from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
4
+ from unsloth import FastLanguageModel
5
+ import torch
6
+ max_seq_length = 512 # unslothではRoPEをサポートしているのでコンテキスト長は自由に設定可能
7
+ dtype = None # Noneにしておけば自動で設定
8
+ load_in_4bit = True # 今回は8Bクラスのモデルを扱うためTrue
9
+
10
+ model_id = "llm-jp/llm-jp-3-13b"
11
+ new_model_id = "llm-jp-3-13b-finetune-2" #Fine-Tuningしたモデルにつけたい名前
12
+ # FastLanguageModel インスタンスを作成
13
+ model, tokenizer = FastLanguageModel.from_pretrained(
14
+ model_name=model_id,
15
+ dtype=dtype,
16
+ load_in_4bit=load_in_4bit,
17
+ trust_remote_code=True,
18
+ )
19
+
20
+ # SFT用のモデルを用意
21
+ model = FastLanguageModel.get_peft_model(
22
+ model,
23
+ r = 32,
24
+ target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
25
+ "gate_proj", "up_proj", "down_proj",],
26
+ lora_alpha = 32,
27
+ lora_dropout = 0.05,
28
+ bias = "none",
29
+ use_gradient_checkpointing = "unsloth",
30
+ random_state = 3407,
31
+ use_rslora = False,
32
+ loftq_config = None,
33
+ max_seq_length = max_seq_length,
34
+ )
35
+
36
+ # Hugging Face Token を指定
37
+ # 下記の URL から Hugging Face Token を取得できますので下記の HF_TOKEN に入れてください。
38
+ # https://huggingface.co/settings/tokens
39
+ HF_TOKEN = "" #@param {type:"string"}
40
+
41
+ # あるいは Google Colab シークレットを使う場合、左のサイドバーより🔑マークをクリック
42
+ # HF_TOKEN という名前で Value に Hugging Face Token を入れてください。
43
+ # ノートブックからのアクセスのトグルをオンにし、下記の二行のコードのコメントアウトを外してください。
44
+
45
+ # from google.colab import userdata
46
+ # HF_TOKEN=userdata.get('HF_TOKEN')
47
+
48
+ """=============="""
49
+
50
+ # 学習に用いるデータセットの指定
51
+ # 今回はLLM-jp の公開している Ichikara Instruction を使います。データにアクセスするためには申請が必要ですので、使いたい方のみ申請をしてください。
52
+ # Ichikara Instruciton を Hugging Face Hub にて公開することはお控えください。
53
+
54
+ # 下記のリンクから申請を終えた先に Google Drive があり、Distribution20241221_all というフォルダごとダウンロードしてください。
55
+ # 今回は「ichikara-instruction-003-001-1.json」を使います。必要であれば展開(!unzip など)し、データセットのパスを適切に指定してください。
56
+ # omnicampusの開発環境では取得したデータを左側にドラッグアンドドロップしてお使いください。
57
+ # Google Colab の場合も左のサイドバーよりドラッグ&ドロップでアップデートしてください。
58
+
59
+ # https://liat-aip.sakura.ne.jp/wp/llmのための日本語インストラクションデータ作成/llmのための日本語インストラクションデータ-公開/
60
+ # 関根聡, 安藤まや, 後藤美知子, 鈴木久美, 河原大輔, 井之上直也, 乾健太郎. ichikara-instruction: LLMのための日本語インストラクションデータの構築. 言語処理学会第30回年次大会(2024)
61
+
62
+ from datasets import load_dataset
63
+
64
+ dataset = load_dataset("json", data_files="Distribution20241221_all/Distribution20241221_all/ichikara-instruction-003-001-1.json")
65
+ # パスの指定にご注意ください。アップロードしたファイルを右クリックし、「パスをコピー」をクリック、上記の data_files と合致していることをご確認ください。Omnicampus のディレクトリ構造とは異なるかもしれません。
66
+
67
+ # 学習時のプロンプトフォーマットの定義
68
+ prompt = """### 指示
69
+ {}
70
+ ### 回答
71
+ {}"""
72
+
73
+
74
+
75
+ """
76
+ formatting_prompts_func: 各データをプロンプトに合わせた形式に合わせる
77
+ """
78
+ EOS_TOKEN = tokenizer.eos_token # トークナイザーのEOSトークン(文末トークン)
79
+ def formatting_prompts_func(examples):
80
+ input = examples["text"] # 入力データ
81
+ output = examples["output"] # 出力データ
82
+ text = prompt.format(input, output) + EOS_TOKEN # プロンプトの作成
83
+ return { "formatted_text" : text, } # 新しいフィールド "formatted_text" を返す
84
+ pass
85
+
86
+ # # 各データにフォーマットを適用
87
+ dataset = dataset.map(
88
+ formatting_prompts_func,
89
+ num_proc= 4, # 並列処理数を指定
90
+ )
91
+
92
+ print(dataset)
93
+
94
+ # データを確認
95
+ print(dataset["train"]["formatted_text"][3])
96
+
97
+
98
+
99
+
100
+ """
101
+ training_arguments: 学習の設定
102
+
103
+ - output_dir:
104
+ -トレーニング後のモデルを保存するディレクトリ
105
+
106
+ - per_device_train_batch_size:
107
+ - デバイスごとのトレーニングバッチサイズ
108
+
109
+ - per_device_eval_batch_size:
110
+ - デバイスごとの評価バッチサイズ
111
+
112
+ - gradient_accumulation_steps:
113
+ - 勾配を更新する前にステップを積み��ねる回数
114
+
115
+ - optim:
116
+ - オプティマイザの設定
117
+
118
+ - num_train_epochs:
119
+ - エポック数
120
+
121
+ - eval_strategy:
122
+ - 評価の戦略 ("no"/"steps"/"epoch")
123
+
124
+ - eval_steps:
125
+ - eval_strategyが"steps"のとき、評価を行うstep間隔
126
+
127
+ - logging_strategy:
128
+ - ログ記録の戦略
129
+
130
+ - logging_steps:
131
+ - ログを出力するステップ間隔
132
+
133
+ - warmup_steps:
134
+ - 学習率のウォームアップステップ数
135
+
136
+ - save_steps:
137
+ - モデルを保存するステップ間隔
138
+
139
+ - save_total_limit:
140
+ - 保存しておくcheckpointの数
141
+
142
+ - max_steps:
143
+ - トレーニングの最大ステップ数
144
+
145
+ - learning_rate:
146
+ - 学習率
147
+
148
+ - fp16:
149
+ - 16bit浮動小数点の使用設定(第8回演習を参考にすると良いです)
150
+
151
+ - bf16:
152
+ - BFloat16の使用設定
153
+
154
+ - group_by_length:
155
+ - 入力シーケンスの長さによりバッチをグループ化 (トレーニングの効率化)
156
+
157
+ - report_to:
158
+ - ログの送信先 ("wandb"/"tensorboard"など)
159
+ """
160
+ from trl import SFTTrainer
161
+ from transformers import TrainingArguments
162
+ from unsloth import is_bfloat16_supported
163
+
164
+ trainer = SFTTrainer(
165
+ model = model,
166
+ tokenizer = tokenizer,
167
+ train_dataset=dataset["train"],
168
+ max_seq_length = max_seq_length,
169
+ dataset_text_field="formatted_text",
170
+ packing = False,
171
+ args = TrainingArguments(
172
+ per_device_train_batch_size = 2,
173
+ gradient_accumulation_steps = 4,
174
+ num_train_epochs = 1,
175
+ logging_steps = 10,
176
+ warmup_steps = 10,
177
+ save_steps=100,
178
+ save_total_limit=2,
179
+ max_steps=-1,
180
+ learning_rate = 2e-4,
181
+ fp16 = not is_bfloat16_supported(),
182
+ bf16 = is_bfloat16_supported(),
183
+ group_by_length=True,
184
+ seed = 3407,
185
+ output_dir = "outputs",
186
+ report_to = "none",
187
+ ),
188
+ )
189
+
190
+
191
+
192
+ #@title 現在のメモリ使用量を表示
193
+ gpu_stats = torch.cuda.get_device_properties(0)
194
+ start_gpu_memory = round(torch.cuda.max_memory_reserved() / 1024 / 1024 / 1024, 3)
195
+ max_memory = round(gpu_stats.total_memory / 1024 / 1024 / 1024, 3)
196
+ print(f"GPU = {gpu_stats.name}. Max memory = {max_memory} GB.")
197
+ print(f"{start_gpu_memory} GB of memory reserved.")
198
+
199
+
200
+ #@title 学習実行
201
+ trainer_stats = trainer.train()
202
+
203
+ # ELYZA-tasks-100-TVの読み込み。事前にファイルをアップロードしてください
204
+ # データセットの読み込み。
205
+ # omnicampusの開発環境では、左にタスクのjsonlをドラッグアンドドロップしてから実行。
206
+ import json
207
+ datasets = []
208
+ with open("./elyza-tasks-100-TV_0.jsonl", "r") as f:
209
+ item = ""
210
+ for line in f:
211
+ line = line.strip()
212
+ item += line
213
+ if item.endswith("}"):
214
+ datasets.append(json.loads(item))
215
+ item = ""
216
+
217
+
218
+ # 学習したモデルを用いてタスクを実行
219
+ from tqdm import tqdm
220
+
221
+ # 推論するためにモデルのモードを変更
222
+ FastLanguageModel.for_inference(model)
223
+
224
+ results = []
225
+ for dt in tqdm(datasets):
226
+ input = dt["input"]
227
+
228
+ prompt = f"""### 指示\n{input}\n### 回答\n"""
229
+ # prompt = f"""### 質問\n{input}\n### 回答\n"""
230
+
231
+ inputs = tokenizer([prompt], return_tensors = "pt").to(model.device)
232
+
233
+ outputs = model.generate(**inputs, max_new_tokens = 512, use_cache = True, do_sample=False, repetition_penalty=1.2)
234
+ prediction = tokenizer.decode(outputs[0], skip_special_tokens=True).split('\n### 回答')[-1]
235
+
236
+ results.append({"task_id": dt["task_id"], "input": input, "output": prediction})
237
+
238
+ # jsonlで保存
239
+ with open(f"{new_model_id}_output.jsonl", 'w', encoding='utf-8') as f:
240
+ for result in results:
241
+ json.dump(result, f, ensure_ascii=False)
242
+ f.write('\n')
243
+
244
+ # モデルとトークナイザーをHugging Faceにアップロード。
245
+ # 一旦privateでアップロードしてください。
246
+ # 最終成果物が決まったらpublicにするようお願いします。
247
+ # 現在公開しているModel_Inference_Template.ipynbはunslothを想定していないためそのままでは動かない可能性があります。
248
+ HF_WRITE_TOKEN = "" #@param {type:"string"}
249
+ model.push_to_hub_merged(
250
+ new_model_id,
251
+ tokenizer=tokenizer,
252
+ save_method="lora",
253
+ token=HF_WRITE_TOKEN,
254
+ private=True
255
+ )
256
+
257
+ # model.push_to_hub(new_model_id, token=HF_TOKEN, private=True) # Online saving
258
+ # tokenizer.push_to_hub(new_model_id, token=HF_TOKEN) # Online saving
259
+
LoRa_inference.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 必要なライブラリを読み込み
2
+ # LoRa_inference.py
3
+ import sys
4
+
5
+ from unsloth import FastLanguageModel
6
+ from peft import PeftModel
7
+ import torch
8
+ import json
9
+ from tqdm import tqdm
10
+ import re
11
+
12
+ # ベースとなるモデルと学習したLoRAのアダプタ(Hugging FaceのIDを指定)。
13
+ model_id = "llm-jp/llm-jp-3-13b"
14
+
15
+ adapter_id = "outputs/checkpoint-200/"
16
+ adapter_id = "final_model_reversed_model/"
17
+ adapter_id = "outputs_add_learning_without/checkpoint-363"
18
+ adapter_id = "outputs_sample_code/checkpoint-200"
19
+ adapter_id = "outputs/checkpoint-363"
20
+ adapter_id = "sft_outputs/checkpoint-1600"
21
+ adapter_id = "dpo_trained_model_1215/checkpoint-14"
22
+ model_id = adapter_id
23
+
24
+ # Hugging Face Token を指定。
25
+ # 下記の URL から Hugging Face Token を取得できますので下記の HF_TOKEN に入れてください。
26
+ HF_TOKEN = "" #@param {type:"string"}
27
+
28
+ # unslothのFastLanguageModelで元のモデルをロード。
29
+ dtype = None # Noneにしておけば自動で設定
30
+ load_in_4bit = True # 今回は13Bモデルを扱うためTrue
31
+
32
+ model, tokenizer = FastLanguageModel.from_pretrained(
33
+ model_name=model_id,
34
+ dtype=dtype,
35
+ load_in_4bit=load_in_4bit,
36
+ trust_remote_code=True,
37
+ )
38
+
39
+
40
+
41
+ # タスクとなるデータの読み込み。
42
+ # 事前にデータをアップロードしてください。
43
+ datasets = []
44
+ with open("./elyza-tasks-100-TV_0.jsonl", "r") as f:
45
+ item = ""
46
+ for line in f:
47
+ line = line.strip()
48
+ item += line
49
+ if item.endswith("}"):
50
+ datasets.append(json.loads(item))
51
+ item = ""
52
+
53
+ # モデルを用いてタスクの推論。
54
+
55
+ # 推論するためにモデルのモードを変更
56
+ FastLanguageModel.for_inference(model)
57
+
58
+ results = []
59
+ for dt in tqdm(datasets):
60
+ input = dt["input"]
61
+
62
+ #prompt = f"""### 指示\n{input}\n### 回答\n"""
63
+ prompt = f"""### 指示\n{input}\n より忍耐強く、より詳細で理解しやすいステップで、回答全体を書き直して。\n### 回答\n"""
64
+ # prompt = f"""### 質問\n{input}\n### 回答\n"""
65
+
66
+ inputs = tokenizer([prompt], return_tensors = "pt").to(model.device)
67
+ #print(inputs)
68
+ #sys.exit()
69
+
70
+ outputs = model.generate(**inputs, max_new_tokens = 512, use_cache = True, do_sample=False, repetition_penalty=1.2)
71
+ prediction = tokenizer.decode(outputs[0], skip_special_tokens=True).split('\n### 回答')[-1]
72
+
73
+ results.append({"task_id": dt["task_id"], "input": input, "output": prediction})
74
+
75
+
76
+ # 結果をjsonlで保存。
77
+
78
+ # ここではadapter_idを元にファイル名を決定しているが、ファイル名は任意で問題なし。
79
+ # json_file_id = re.sub(r".*/", "", adapter_id)
80
+ json_file_id = adapter_id
81
+ with open(f"{json_file_id}_output.jsonl", 'w', encoding='utf-8') as f:
82
+ for result in results:
83
+ json.dump(result, f, ensure_ascii=False)
84
+ f.write('\n')
85
+
86
+