Uploaded model
- Developed by: IWAIYuma
- License: apache-2.0
- Finetuned from model : llm-jp/llm-jp-3-13b
This llama model was trained 2x faster with Unsloth and Huggingface's TRL library.
サンプルコードで作成したモデルを強化学習する。
使用するデータセットは、『cyberagent/chatbot-arena-ja-calm2-7b-chat-experimental』のオープンソースを使用
このソースは『このデータセットを用いてcalm2-7b-chatに対してDirect Preference Optimization (DPO)を行い、calm2-7b-chat-dpoを作成しました。 Instruction Tuningの評価用タスクであるELYZA-tasks-100とJapanese MT-Benchを用いてGPT-4による自動評価を行ったところ、どちらのデータセットでもcalm2-7b-chat-dpoの方がcalm2-7b-chatよりも高いスコアが得られました。』とあるため期待
◯ 参考にしたコード『自作FT済みモデルからDPOするためのサンプルコード(Unsloth最新版 2024.12.2)』にある、藤越様のコードを参考にし、RLHFをしてみました。
https://matsuolab-geniac.notion.site/FT-DPO-Unsloth-2024-12-2-bac63c15586840b9ad118f5f5b27420a
'''python
Google Colab の場合は上記の環境構築手順を行なわず、単にこのセルから実行していってください。
!pip uninstall unsloth -y !pip install --upgrade --no-cache-dir "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
Google Colab のデフォルトで入っているパッケージをアップグレード(Moriyasu さんありがとうございます)
!pip install --upgrade torch !pip install --upgrade xformers
notebookでインタラクティブな表示を可能とする(ただし、うまく動かない場合あり)
!pip install ipywidgets --upgrade
Install Flash Attention 2 for softcapping support
import torch if torch.cuda.get_device_capability()[0] >= 8: !pip install --no-deps packaging ninja einops "flash-attn>=2.6.3" from google.colab import output output.enable_custom_widget_manager()
Hugging Face Token を指定
from huggingface_hub import login from google.colab import userdata
hf_token = userdata.get('HF_TOKEN') if hf_token: login(token=hf_token) print("Hugging Face token found and logged in.") else: print("Hugging Face token not found. Please set it using userdata.set('HF_TOKEN', '')") print(dataset['train'][0])
学習時のプロンプトフォーマットの定義
prompt = """### 指示 {}
回答
{}"""
""" formatting_prompts_func: 各データをプロンプトに合わせた形式に合わせる """ EOS_TOKEN = tokenizer.eos_token # トークナイザーのEOSトークン(文末トークン) def formatting_prompts_func(examples): input = examples["text"] # 入力データ output = examples["output"] # 出力データ text = prompt.format(input, output) + EOS_TOKEN # プロンプトの作成 return { "formatted_text" : text, } # 新しいフィールド "formatted_text" を返す pass
# 各データにフォーマットを適用
dataset = dataset.map( formatting_prompts_func, num_proc= 4, # 並列処理数を指定 )
dataset """ training_arguments: 学習の設定
output_dir: -トレーニング後のモデルを保存するディレクトリ
per_device_train_batch_size:
- デバイスごとのトレーニングバッチサイズ
per_device_eval_batch_size:
- デバイスごとの評価バッチサイズ
gradient_accumulation_steps:
- 勾配を更新する前にステップを積み重ねる回数
optim:
- オプティマイザの設定
num_train_epochs:
- エポック数
eval_strategy:
- 評価の戦略 ("no"/"steps"/"epoch")
eval_steps:
- eval_strategyが"steps"のとき、評価を行うstep間隔
logging_strategy:
- ログ記録の戦略
logging_steps:
- ログを出力するステップ間隔
warmup_steps:
- 学習率のウォームアップステップ数
save_steps:
- モデルを保存するステップ間隔
save_total_limit:
- 保存しておくcheckpointの数
max_steps:
- トレーニングの最大ステップ数
learning_rate:
- 学習率
fp16:
- 16bit浮動小数点の使用設定(第8回演習を参考にすると良いです)
bf16:
- BFloat16の使用設定
group_by_length:
- 入力シーケンスの長さによりバッチをグループ化 (トレーニングの効率化)
report_to:
- ログの送信先 ("wandb"/"tensorboard"など) """ from trl import SFTTrainer from transformers import TrainingArguments from unsloth import is_bfloat16_supported
trainer = SFTTrainer( model = model, tokenizer = tokenizer, train_dataset=dataset["train"], max_seq_length = max_seq_length, dataset_text_field="formatted_text", packing = False, args = TrainingArguments( per_device_train_batch_size = 2, gradient_accumulation_steps = 4, num_train_epochs = 1, logging_steps = 10, warmup_steps = 10, save_steps=100, save_total_limit=2, max_steps=-1, learning_rate = 2e-4, fp16 = not is_bfloat16_supported(), bf16 = is_bfloat16_supported(), group_by_length=True, seed = 3407, output_dir = "outputs", report_to = "none", ), ) #@title 学習実行 trainer_stats = trainer.train() edataset = load_dataset("elyza/ELYZA-tasks-100") edataset = edataset.remove_columns('eval_aspect') edataset = edataset.rename_columns({'input':'text'}) print(edataset['test'][0])
学習時のプロンプトフォーマットの定義
prompt = """### 指示 {}
回答
{}"""
""" formatting_prompts_func: 各データをプロンプトに合わせた形式に合わせる """ EOS_TOKEN = tokenizer.eos_token # トークナイザーのEOSトークン(文末トークン) def formatting_prompts_func(examples): input = examples["text"] # 入力データ output = examples["output"] # 出力データ text = prompt.format(input, output) + EOS_TOKEN # プロンプトの作成 return { "formatted_text" : text, } # 新しいフィールド "formatted_text" を返す pass
# 各データにフォーマットを適用
edataset = edataset.map( formatting_prompts_func, num_proc= 4, # 並列処理数を指定 )
edataset
データを確認
print(edataset["test"]["formatted_text"][3]) etrainer = SFTTrainer( model = model, tokenizer = tokenizer, train_dataset=edataset["test"], max_seq_length = max_seq_length, dataset_text_field="formatted_text", packing = False, args = TrainingArguments( per_device_train_batch_size = 2, gradient_accumulation_steps = 4, num_train_epochs = 1, logging_steps = 10, warmup_steps = 10, save_steps=100, save_total_limit=2, max_steps=-1, learning_rate = 2e-4, fp16 = not is_bfloat16_supported(), bf16 = is_bfloat16_supported(), group_by_length=True, seed = 3407, output_dir = "outputs", report_to = "none", ), ) #@title 学習実行 trainer_stats = etrainer.train()
強化学習
%%capture !pip install unsloth
Also get the latest nightly Unsloth!
!pip uninstall unsloth -y && pip install --upgrade --no-cache-dir --no-deps git+https://github.com/unslothai/unsloth.git from huggingface_hub import login from google.colab import userdata
hugging faseにlog in
hf_token = userdata.get('HF_TOKEN') if hf_token: login(token=hf_token) print("Hugging Face token found. Logged in to Hugging Face.") else: print("Hugging Face token not found. Please set it using userdata.set('HF_TOKEN', '')") from unsloth import PatchDPOTrainer PatchDPOTrainer() from unsloth import FastLanguageModel import torch max_seq_length = 2048 # Choose any! We auto support RoPE Scaling internally! dtype = None # None for auto detection. Float16 for Tesla T4, V100, Bfloat16 for Ampere+ load_in_4bit = True # Use 4bit quantization to reduce memory usage. Can be False. model, tokenizer = FastLanguageModel.from_pretrained( model_name = "IWAIYuma/llm-jp-3-13b-it_v3", # 自分がUnslothを使ってFTして、loraだけアップロードしているモデル max_seq_length = max_seq_length, dtype = dtype, load_in_4bit = load_in_4bit, token = hf_token, )
cyberagent/chatbot-arena-ja-calm2-7b-chat-experimentalデータセットの読み込み
import json import codecs from pprint import pprint from datasets import load_dataset
データセットをロード
ds = load_dataset("cyberagent/chatbot-arena-ja-calm2-7b-chat-experimental")
フィルタリング関数を定義
def filter_short_examples(example): return ( len(example['prompt']) <= 2000 and len(example['chosen']) <= 2000 and len(example['rejected']) <= 2000 )
トレーニングデータをフィルタリング
filtered_train = ds['train'].filter(filter_short_examples)
データセットをトレーニング用と評価用に分割 (80%をトレーニング用、20%を評価用)
train_size = int(0.8 * len(filtered_train)) # トレーニングデータのサイズ eval_size = len(filtered_train) - train_size # 評価データのサイズ
インデックスを順序通りに生成 (ランダム性なし)
train_indices = list(range(train_size)) # トレーニング用インデックス eval_indices = list(range(train_size, len(filtered_train))) # 評価用インデックス
トレーニングデータと評価データを選択
train_dataset = filtered_train.select(train_indices) eval_dataset = filtered_train.select(eval_indices)
データセットのサイズを出力
print(f"トレーニングデータセットのサイズ: {len(train_dataset)}") print(f"評価データセットのサイズ: {len(eval_dataset)}") pprint(train_dataset[0]) print("\n") pprint(eval_dataset[0])
一旦100までを学習
結果が良かったので修正
use_dataset = train_dataset.select(range(100))
use_dataset = train_dataset use_dataset
One must patch the DPO Trainer first!
from unsloth import PatchDPOTrainer PatchDPOTrainer()
from transformers import TrainingArguments from trl import DPOTrainer, DPOConfig from unsloth import is_bfloat16_supported
dpo_trainer = DPOTrainer( model = model, ref_model = None, args = DPOConfig( per_device_train_batch_size = 2, gradient_accumulation_steps = 4, warmup_ratio = 0.1, num_train_epochs = 1, learning_rate = 5e-6, fp16 = not is_bfloat16_supported(), bf16 = is_bfloat16_supported(), logging_steps = 1, optim = "adamw_8bit", weight_decay = 0.0, lr_scheduler_type = "linear", seed = 42, output_dir = "outputs", report_to = "none", # Use this for WandB etc ), beta = 0.1, train_dataset = use_dataset, #raw_datasets["train"], # eval_dataset = raw_datasets["test"], tokenizer = tokenizer, max_length = 2048, max_prompt_length = 1024, )
学習の開始
dpo_trainer.train()
ELYZA-tasks-100-TV データセットの読み込み。
import json datasets = [] with open("/content/elyza-tasks-100-TV_0.jsonl", "r") as f: item = "" for line in f: line = line.strip() item += line if item.endswith("}"): datasets.append(json.loads(item)) item = ""
学習したモデルを用いてタスクを実行
from tqdm import tqdm
推論するためにモデルのモードを変更
FastLanguageModel.for_inference(model)
results = [] for dt in tqdm(datasets): input = dt["input"]
prompt = f"""### 指示\n{input} 簡潔に回答してください \n### 回答\n"""
inputs = tokenizer([prompt], return_tensors = "pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens = 2048, use_cache = True, do_sample=False, repetition_penalty=1.2) prediction = tokenizer.decode(outputs[0], skip_special_tokens=True).split('\n### 回答')[-1]
results.append({"task_id": dt["task_id"], "input": input, "output": prediction})
jsonlで保存
new_model_id = "llm-jp-3-13b-it" with open(f"{new_model_id}_output.jsonl", 'w', encoding='utf-8') as f: for result in results: json.dump(result, f, ensure_ascii=False) f.write('\n')
#書き込みにログイン whf_token = userdata.get('WHF_TOKEN') if whf_token: login(token=whf_token) print("Hugging Face token found and logged in.") else: print("Hugging Face token not found. Please set it using userdata.set('HF_TOKEN', '')")
モデルとトークナイザーをHugging Faceにアップロード
new_model_id = "llm-jp-3-13b-it" new_model_id = f"{new_model_id}_RLHFv3" model.push_to_hub(new_model_id, token=whf_token, private=True) tokenizer.push_to_hub(new_model_id, token=whf_token, private=True) '''
Model tree for IWAIYuma/llm-jp-3-13b-it_RLHFv3
Base model
llm-jp/llm-jp-3-13b