Upload README.md
Browse files
README.md
ADDED
@@ -0,0 +1,245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Uploaded model
|
2 |
+
|
3 |
+
- **Developed by:** u9999Yoko
|
4 |
+
- **License:** CC BY-NC-SA
|
5 |
+
- **Finetuned from model :** llm-jp/llm-jp-3-13b
|
6 |
+
|
7 |
+
## Abstract
|
8 |
+
|
9 |
+
「llm-jp/llm-jp-3-13bモデルにichikara-instructionデータを用いてファインチューニングを行ったモデルです。
|
10 |
+
松尾研究室の講義のコンペ用としてモデルを作成しました。
|
11 |
+
https://weblab.t.u-tokyo.ac.jp/lecture/course-list/large-language-model/
|
12 |
+
https://llm-jp.nii.ac.jp/blog/2024/04/30/v2.0-release.html
|
13 |
+
|
14 |
+
## Dataset
|
15 |
+
データセットは以下を使用しました。
|
16 |
+
- ichikara-instruction-003-001-1.json
|
17 |
+
|
18 |
+
## Attention
|
19 |
+
ichikara-instructionデータは、CC BY-NC-SAライセンス(表示-非営利-継承)で公開されています。
|
20 |
+
このライセンスの下では、非営利目的での利用が許可されていますが、商用利用は認められていません。
|
21 |
+
詳しくは次のホームページをご覧ください。
|
22 |
+
https://llm-jp.nii.ac.jp/blog/2024/04/30/v2.0-release.html
|
23 |
+
|
24 |
+
## Usage
|
25 |
+
Execute following code in Notebook
|
26 |
+
|
27 |
+
```python
|
28 |
+
# 必要なライブラリをインストール
|
29 |
+
conda install --yes --file requirements.txt
|
30 |
+
|
31 |
+
from requests.exceptions import HTTPError
|
32 |
+
from transformers import (
|
33 |
+
AutoModelForCausalLM,
|
34 |
+
AutoTokenizer,
|
35 |
+
TrainingArguments,
|
36 |
+
)
|
37 |
+
from peft import (
|
38 |
+
LoraConfig,
|
39 |
+
get_peft_model,
|
40 |
+
)
|
41 |
+
import torch
|
42 |
+
from datasets import load_dataset
|
43 |
+
from trl import SFTTrainer
|
44 |
+
from tqdm import tqdm
|
45 |
+
|
46 |
+
# HuggingFaceにログイン
|
47 |
+
from huggingface_hub import notebook_login
|
48 |
+
|
49 |
+
# 取得したTokenを入力
|
50 |
+
notebook_login()
|
51 |
+
|
52 |
+
# base model id
|
53 |
+
base_model_id = "llm-jp/llm-jp-3-13b"
|
54 |
+
new_model_id = "u9999Yoko//llm-jp-3-13b-finetune"
|
55 |
+
|
56 |
+
|
57 |
+
model = AutoModelForCausalLM.from_pretrained(
|
58 |
+
base_model_id,
|
59 |
+
device_map="auto",
|
60 |
+
torch_dtype=torch.bfloat16, # 半精度計算で効率化
|
61 |
+
)
|
62 |
+
|
63 |
+
tokenizer = AutoTokenizer.from_pretrained(base_model_id, trust_remote_code=True)
|
64 |
+
|
65 |
+
"""
|
66 |
+
peft_config: PEFTの構成設定
|
67 |
+
- r
|
68 |
+
- LoRA のランク (4, 8, 16 ,32...)
|
69 |
+
- 増やすほど学習が捗るが, 過学習のリスクも高まるので注意
|
70 |
+
- lora_alpha
|
71 |
+
- LoRAのスケーリング係数
|
72 |
+
- lora_dropout
|
73 |
+
- ドロップアウト率(過学習を防ぐための割合)
|
74 |
+
- bias
|
75 |
+
- バイアス項の扱い ("none"の場合、LoRAはバイアスを学習しない)
|
76 |
+
- task_type
|
77 |
+
- タスクタイプ
|
78 |
+
- target_modules
|
79 |
+
- LoRAを適用するターゲットモジュール (前のコードで特定した層)
|
80 |
+
"""
|
81 |
+
|
82 |
+
# LoRA設定
|
83 |
+
lora_config = LoraConfig(
|
84 |
+
r=8,
|
85 |
+
lora_alpha=16,
|
86 |
+
target_modules=["q_proj", "v_proj"], # トレーニング対象の層
|
87 |
+
lora_dropout=0.1,
|
88 |
+
task_type="CAUSAL_LM",
|
89 |
+
)
|
90 |
+
|
91 |
+
model = get_peft_model(model, lora_config)
|
92 |
+
|
93 |
+
dataset = load_dataset("json", data_files="./ichikara-instruction-003-001-1.json")
|
94 |
+
|
95 |
+
# 学習時のプロンプトフォーマットの定義
|
96 |
+
prompt = """### 指示
|
97 |
+
{}
|
98 |
+
### 回答
|
99 |
+
{}"""
|
100 |
+
|
101 |
+
|
102 |
+
"""
|
103 |
+
formatting_prompts_func: 各データをプロンプトに合わせた形式に合わせる
|
104 |
+
"""
|
105 |
+
EOS_TOKEN = tokenizer.eos_token # トークナイザーのEOSトークン(文末トークン)
|
106 |
+
def formatting_prompts_func(examples):
|
107 |
+
input = examples["text"] # 入力データ
|
108 |
+
output = examples["output"] # 出力データ
|
109 |
+
text = prompt.format(input, output) + EOS_TOKEN # プロンプトの作成
|
110 |
+
return { "formatted_text" : text, } # 新しいフィールド "formatted_text" を返す
|
111 |
+
pass
|
112 |
+
|
113 |
+
# # 各データにフォーマットを適用
|
114 |
+
dataset = dataset.map(
|
115 |
+
formatting_prompts_func,
|
116 |
+
num_proc= 4, # 並列処理数を指定
|
117 |
+
)
|
118 |
+
|
119 |
+
# データを確認
|
120 |
+
print(dataset["train"]["formatted_text"][3])
|
121 |
+
|
122 |
+
# データをtrainデータとtestデータに分割 (test_sizeの比率に)
|
123 |
+
dataset = dataset["train"].train_test_split(test_size=0.1)
|
124 |
+
print(dataset)
|
125 |
+
|
126 |
+
"""
|
127 |
+
training_arguments: 学習の設定
|
128 |
+
output_dir:トレーニング後のモデルを保存するディレクトリ
|
129 |
+
per_device_train_batch_size: デバイスごとのトレーニングバッチサイズ
|
130 |
+
per_device__batch_size:デバイスごとの評価バッチサイズ
|
131 |
+
gradient_accumulation_steps:勾配を更新する前にステップを積み重ねる回数
|
132 |
+
optim: オプティマイザの設定
|
133 |
+
num_train_epochs:エポック数
|
134 |
+
eval_strategy:評価の戦略 ("no"/"steps"/"epoch")
|
135 |
+
eval_steps: eval_strategyが"steps"のとき、評価を行うstep間隔
|
136 |
+
logging_strategy: ログ記録の戦略
|
137 |
+
logging_steps: ログを出力するステップ間隔
|
138 |
+
warmup_steps: 学習率のウォームアップステップ数
|
139 |
+
save_steps: モデルを保存するステップ間隔
|
140 |
+
save_total_limit: 保存しておくcheckpointの数
|
141 |
+
max_steps:トレーニングの最大ステップ数
|
142 |
+
learning_rate: 学習率
|
143 |
+
fp16:16bit浮動小数点の使用設定(第8回演習を参考にすると良いです)
|
144 |
+
bf16:BFloat16の使用設定
|
145 |
+
group_by_length:入力シーケンスの長さによりバッチをグループ化 (トレーニングの効率化)
|
146 |
+
report_to:ログの送信先 ("wandb"/"tensorboard"���ど)
|
147 |
+
"""
|
148 |
+
|
149 |
+
training_arguments = TrainingArguments(
|
150 |
+
output_dir=new_model_id,
|
151 |
+
per_device_train_batch_size=1,
|
152 |
+
gradient_accumulation_steps=8,
|
153 |
+
# optim="paged_adamw_32bit",
|
154 |
+
num_train_epochs=3,
|
155 |
+
logging_strategy="steps",
|
156 |
+
logging_steps=10,
|
157 |
+
warmup_steps=10,
|
158 |
+
save_steps=1000,
|
159 |
+
save_total_limit = 2,
|
160 |
+
max_steps = -1,
|
161 |
+
learning_rate=5e-5,
|
162 |
+
fp16=False,
|
163 |
+
bf16=True,
|
164 |
+
seed = 3407,
|
165 |
+
group_by_length=True,
|
166 |
+
report_to="none"
|
167 |
+
)
|
168 |
+
|
169 |
+
"""
|
170 |
+
SFTTrainer: Supervised Fine-Tuningに関する設定
|
171 |
+
model:読み込んだベースのモデル
|
172 |
+
train_dataset:トレーニングに使用するデータセット
|
173 |
+
eval_dataset:評価に使用するデータセット
|
174 |
+
peft_config: PEFT(Parameter-Efficient Fine-Tuning)の設定(LoRAを利用する場合に指定)
|
175 |
+
max_seq_length:モデルに入力されるシーケンスの最大トークン長
|
176 |
+
dataset_text_field:データセット内の学習に使うテキストを含むフィールド名
|
177 |
+
tokenizer:モデルに対応するトークナイザー
|
178 |
+
args:トレーニングに使用するハイパーパラメータ(TrainingArgumentsの設定を指定)
|
179 |
+
packing:入力シーケンスのパッキングを行うかどうかの設定 (False に設定することで、各入力を独立して扱う)
|
180 |
+
"""
|
181 |
+
trainer = SFTTrainer(
|
182 |
+
model=model,
|
183 |
+
train_dataset=dataset["train"],
|
184 |
+
peft_config=lora_config,
|
185 |
+
max_seq_length= 512,
|
186 |
+
dataset_text_field="formatted_text",
|
187 |
+
tokenizer=tokenizer,
|
188 |
+
args=training_arguments,
|
189 |
+
packing= False,
|
190 |
+
)
|
191 |
+
|
192 |
+
model.config.use_cache = False # キャッシュ機能を無効化
|
193 |
+
trainer.train() # トレーニングを実行
|
194 |
+
|
195 |
+
# タスクとなるデータの読み込み。
|
196 |
+
|
197 |
+
import json
|
198 |
+
datasets = []
|
199 |
+
with open("./elyza-tasks-100-TV_0.jsonl", "r") as f:
|
200 |
+
item = ""
|
201 |
+
for line in f:
|
202 |
+
line = line.strip()
|
203 |
+
item += line
|
204 |
+
if item.endswith("}"):
|
205 |
+
datasets.append(json.loads(item))
|
206 |
+
item = ""
|
207 |
+
print(datasets)
|
208 |
+
|
209 |
+
|
210 |
+
# モデルによるタスクの推論。
|
211 |
+
|
212 |
+
results = []
|
213 |
+
for data in tqdm(datasets):
|
214 |
+
|
215 |
+
input = data["input"]
|
216 |
+
|
217 |
+
prompt = f"""### 指示
|
218 |
+
{input}
|
219 |
+
### 回答
|
220 |
+
"""
|
221 |
+
|
222 |
+
tokenized_input = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt").to(model.device)
|
223 |
+
attention_mask = torch.ones_like(tokenized_input)
|
224 |
+
|
225 |
+
with torch.no_grad():
|
226 |
+
outputs = model.generate(
|
227 |
+
tokenized_input,
|
228 |
+
attention_mask=attention_mask,
|
229 |
+
max_new_tokens=400,
|
230 |
+
do_sample=False,
|
231 |
+
repetition_penalty=1.2,
|
232 |
+
pad_token_id=tokenizer.eos_token_id
|
233 |
+
)[0]
|
234 |
+
output = tokenizer.decode(outputs[tokenized_input.size(1):], skip_special_tokens=True)
|
235 |
+
|
236 |
+
results.append({"task_id": data["task_id"], "input": input, "output": output})
|
237 |
+
|
238 |
+
# 結果をjsonlで保存。
|
239 |
+
import re
|
240 |
+
jsonl_id = re.sub(".*/", "", new_model_id)
|
241 |
+
with open(f"./{jsonl_id}-outputs.jsonl", 'w', encoding='utf-8') as f:
|
242 |
+
for result in results:
|
243 |
+
json.dump(result, f, ensure_ascii=False) # ensure_ascii=False for handling non-ASCII characters
|
244 |
+
f.write('\n')
|
245 |
+
```
|