Elyza-7BをずんだもんデータセットでQLoRAファインチューニング
「Elyza-7B」をずんだもんデータセットでQLoRAファインチューニングする方法を解説します。
Created on November 27|Last edited on November 30
Comment
1. はじめに
「Google Colab」で「Elyza-7B」を「ずんだもんデータセット」でQLoRAファインチューニングする方法を解説します。「Elyza-7B」がずんだもんのように語尾を「のだ」や「なのだ」で話すようになります。

2. QLoRAファインチューニング
QLoRAファインチューニングの手順は、次のとおりです。
(1) Googleドライブのマウント。
# Googleドライブのマウントfrom google.colab import drivedrive.mount("/content/drive")
(2) 作業フォルダへの移動。
# 作業フォルダへの移動!mkdir -p "/content/drive/My Drive/work/"%cd "/content/drive/My Drive/work/"
(3) パッケージのインストール。
# パッケージのインストール!pip install -q accelerate==0.21.0 peft==0.4.0 bitsandbytes==0.40.2 transformers==4.31.0 trl==0.4.7 sentencepiece!pip install -q wandb
(4) パッケージのインポート。
# パッケージのインポートimport osimport torchfrom datasets import load_datasetfrom transformers import (AutoModelForCausalLM,AutoTokenizer,BitsAndBytesConfig,HfArgumentParser,TrainingArguments,pipeline,logging,)from peft import LoraConfig, PeftModelfrom trl import SFTTrainer
(5) データセットの準備。
# データセットの読み込みdataset = load_dataset("takaaki-inada/databricks-dolly-15k-ja-zundamon", split="train")# プロンプトテンプレートの準備def generate_prompt(data_point):if data_point["input"]:result = f"[INST] {data_point['instruction']}\n\n{data_point['input']} [/INST] {data_point['output']}"else:result = f"[INST] {data_point['instruction']} [/INST] {data_point['output']}"return result# text列の追加def add_text(example):example["text"] = generate_prompt(example)for key in ["index", "category", "instruction", "input", "output"]:del example[key]return exampledataset = dataset.map(add_text)print(dataset)print(dataset[0]["text"])
Dataset({features: ['text'],num_rows: 15015})[INST] ヴァージン・オーストラリア航空はいつから運航を開始したのですか?ヴァージン・オーストラリア航空(Virgin Australia Airlines Pty Ltd)はオーストラリアを拠点とするヴァージン・ブランドを冠する最大の船団規模を持つ航空会社なのだ。2000年8月31日に、ヴァージン・ブルー空港として、2機の航空機、1つの空路を運行してサービスを開始しました。2001年9月のアンセット・オーストラリア空港の崩壊後、オーストラリアの国内市場で急速に地位を確立しました。その後はブリスベン、メルボルン、シドニーをハブとして、オーストラリア国内の32都市に直接乗り入れるまでに成長しました。 [/INST] ヴァージン・オーストラリア航空は、2000年8月31日にヴァージン・ブルー航空として、2機の航空機で単一路線の運航を開始したのだ。
「Elyza-7B」の指示の書式は、次のとおりです。この書式のみをtext列で保持します。
[INST] <指示> [/INST] <応答>
[INST] {指示} {入力} [/INST] {応答}
(7) モデルの準備。
「Elyza-7B」を4bit量子化で読み込みます。
# 量子化パラメータbnb_config = BitsAndBytesConfig(load_in_4bit=True, # 量子化の有効化bnb_4bit_quant_type="nf4", # 量子化種別 (fp4 or nf4)bnb_4bit_compute_dtype=torch.float16, # 量子化のdtype (float16 or bfloat16)bnb_4bit_use_double_quant=False, # 二重量子化の有効化)# モデルの準備model = AutoModelForCausalLM.from_pretrained("elyza/ELYZA-japanese-Llama-2-7b-instruct", # モデル名quantization_config=bnb_config, # 量子化パラメータdevice_map="auto" # モデル全体をGPU0にロード)model.config.use_cache = False # キャッシュ (学習時はFalse)model.config.pretraining_tp = 1 # 事前学習で使用したテンソル並列ランク
(8) トークナイザーの準備。
# トークナイザーの準備tokenizer = AutoTokenizer.from_pretrained("elyza/ELYZA-japanese-Llama-2-7b-instruct", # モデル名use_fast=False, # Fastトークナイザーの有効化add_eos_token=True, # データへのEOSの追加を指示trust_remote_code=True)tokenizer.pad_token = tokenizer.unk_tokentokenizer.padding_side = "right" # fp16でのオーバーフロー問題対策
(9) 学習の実行。
# LoRAパラメータpeft_config = LoraConfig(r=8, # LoRAアテンションの次元lora_alpha=16, # LoRAスケーリングのAlphaパラメータlora_dropout=0.05, # LoRA レイヤーのドロップアウト確率bias="none", # LoRAのバイアス種別 ("none","all", "lora_only")task_type="CAUSAL_LM", # タスク種別target_modules=["q_proj", "v_proj", "k_proj", "o_proj", "gate_proj", "up_proj", "down_proj", "embed_tokens", "lm_head"],)# 学習パラメータtraining_arguments = TrainingArguments(output_dir="./train_logs", # 出力ディレクトリfp16=True, # fp16学習の有効化bf16=False, # bf16学習の有効化max_steps=300, # 学習ステップ数per_device_train_batch_size=4, # 学習用のGPUあたりのバッチサイズgradient_accumulation_steps=1, # 勾配を蓄積するための更新ステップの数optim="paged_adamw_32bit", # オプティマイザlearning_rate=1e-4, # 初期学習率lr_scheduler_type="cosine", # 学習率スケジュールmax_grad_norm=0.3, # 最大法線勾配 (勾配クリッピング)warmup_ratio=0.03, # 線形ウォームアップのステップ比率 (0から学習率まで)weight_decay=0.001, # bias/LayerNormウェイトを除く全レイヤーに適用するウェイト減衰save_steps=25, # 何ステップ毎にチェックポイントを保存するかlogging_steps=25, # 何ステップ毎にログを記録するかgroup_by_length=True, # シーケンスを同じ長さのバッチにグループ化 (メモリ節約)report_to="wandb" # レポート)# SFTパラメータtrainer = SFTTrainer(model=model, # モデルtokenizer=tokenizer, # トークナイザーtrain_dataset=dataset, # データセットdataset_text_field="text", # データセットのtext列peft_config=peft_config, # PEFTパラメータargs=training_arguments, # 学習パラメータmax_seq_length=None, # 使用する最大シーケンス長packing=False, # 同じ入力シーケンスに複数サンプルをパッキング(効率を高める))# モデルの学習trainer.train()trainer.model.save_pretrained("./lora_model")
今回は、ハイパーパラメータには以下の記事を参考に推奨値を設定します。
・Fine-Tuning LLMs: LoRA or Full-Parameter? An in-depth Analysis with Llama 2
そして、「wandb」にレポートを出力するため「report_to="wandb"」を設定しています。
(10) wandbのAPIキーの入力が促されるので入力。

(11) 学習完了を待つ。
T4 (float16) 30分、A100 (bfloat16) で7分ほどで学習完了しました。

「lora_model」フォルダに「ずんだもん」の「LoRAのウェイト」が出力されています。
(12) 動作確認。
「LoRAウェイト」を読み込んで推論してみます。
from peft import AutoPeftModelForCausalLMfrom transformers import AutoTokenizerimport torch# モデルの読み込みmodel = AutoPeftModelForCausalLM.from_pretrained("./lora_model",torch_dtype=torch.float16,load_in_4bit=True, # 4bit量子化device_map="auto",)# トークナイザーの準備tokenizer = AutoTokenizer.from_pretrained("elyza/ELYZA-japanese-Llama-2-7b-instruct", # モデル名trust_remote_code=True)tokenizer.pad_token = tokenizer.unk_tokentokenizer.padding_side = "right"
# プロンプトの準備prompt = "[INST] 富士山の高さは? [/INST] "# 推論の実行input_ids = tokenizer(prompt, add_special_tokens=False, return_tensors='pt')output_ids = model.generate(**input_ids.to(model.device),max_new_tokens=100,do_sample=True,temperature=0.3,)output = tokenizer.decode(output_ids.tolist()[0])print(output)
[INST] 富士山の高さは? [/INST] 富士山の高さは3,776メートルなのだ。</s>
語尾が「なのだ」なので成功です。
3. 学習状況の確認
「wandb」で学習状況を確認できます。今回は、「T4」(無料版)と「A100」(Pro/Pro+)で学習して、違いを比較しました。学習時間はA100はT4の4倍ですが、ステップあたりの学習損失の下がり方は同じで��ることがわかります。
「wandb」は、特定のデータセットに対して様々な設定 (ハイパーパラメータやGPU) を試した時、何が効果的だったかを知るための強力なツールになります。
Run set
2
Add a comment
Tags: Articles, Community Posts
Iterate on AI agents and models faster. Try Weights & Biases today.