【Llama3】SFTTrainerで簡単ファインチューニング(QLoRA)

SFTTrainerでLlama3のファインチューニング(QLoRA)

この記事では、Hugging FaceのSFTTrainerを使ったLlama3のファインチューニング(QLoRA)を紹介します。

SFTTrainerを使うことで、少ないコードでファインチューニングの実装ができます。

11/27開催のウェビナーをお知らせ!

目次

SFTTrainerでLlama3のファインチューニング(QLoRA)

見出し画像

SFTTrainerとは

この記事では、HuggingFaceのSFTTrainerを使って、Llama3のモデルにQLoRAファインチューニングをします。

SFTTrainerは、教師ありファインチューニング(Supervised Fine-tuning)で学習するためのライブラリです。

Transformersライブラリに統合されており、少ないコードでファインチューニングの実装ができます。

用語の解説

QLoRA
QLoRAは、LoRAと量子化(Quantization)の2つの要素をもつファインチューニングの手法です。

LoRAは、モデルのパラメータを低ランク行列で近似することで、更新するパラメータ数を大幅に減らし計算量を削減しています。

量子化とは、モデルの精度を下げる代わりに、GPUメモリを大幅に節約する技術になります。

Llama3の使い方・性能・商用利用ついては、別記事で解説しています。

Unslothを使ったファインチューニングは、別記事で解説しています。

PyTorchのTorchtuneを使ったファインチューニングは、別記事で解説しています。

使用するデータセット

モデルの学習にはデータセット「bbz662bbz/databricks-dolly-15k-ja-ojousama」を使用します。

15,000以上の指示と応答で構成された日本語データセットです。

応答が「お嬢様」の口調になっていることが特徴です。

あわせて読みたい
bbz662bbz/databricks-dolly-15k-ja-ojousama · Datasets at Hugging Face We’re on a journey to advance and democratize artificial intelligence through open source and open science.

事前準備

見出し画像

必要なスペック・実行環境

Llama3のQLoRAファインチューニングでは、大容量のGPUメモリを必要とします。

この記事では、GPUメモリ80GBを搭載したNVIDIA A100 80GBのインスタンスを使用しています。

実行環境の詳細は以下のとおりです。

  • GPU:NVIDIA A100 80GB
  • GPUメモリ(VRAM):80GB
  • OS:Ubuntu22.04
  • Docker

Llama3のモデル利用申請

Llama3のモデルを使うにあたって、利用申請が必要になります。

以下の記事で利用申請の方法を紹介しています。

Dockerで環境構築

見出し画像

Dockerを使用してLlama3の環境構築をしていきます。

Dockerの使い方は以下の記事をご覧ください。

STEP
Dockerfileの作成

Dockerfileにインストールするパッケージを記述します。

パッケージ一覧
  • CUDA:12.1
  • Python:3.10
  • PyTorch:2.2.2
  • transformers:4.41.2
  • bitsandbytes
  • ninja
  • packaging
  • accelerate
  • datasets
  • bitsandbytes
  • evaluate
  • trl
  • peft
  • wheel
  • flash-attn
  • JupyterLab
  • huggingface_hub[cli]
  • wandb

CUDA、PyTorch、Transformer等は、バージョン依存関係によるエラーが起きやすいので、動作検証済のバージョン指定してインストールしています。

Ubuntuのコマンドラインから、Dockerfileを作成します。

mkdir llama3_sftqlora
cd llama3_sftqlora
nano Dockerfile

次の記述をコピーしてDockerfileに貼り付けます。

# ベースイメージ(CUDA)の指定
FROM nvidia/cuda:12.1.0-cudnn8-devel-ubuntu22.04

# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y python3-pip python3-venv git nano

# 作業ディレクトリを設定
WORKDIR /app

# アプリケーションコードをコピー
COPY . /app

# Python仮想環境の作成
RUN python3 -m venv /app/.venv

# 仮想環境をアクティベートするコマンドを.bashrcに追加
RUN echo "source /app/.venv/bin/activate" >> /root/.bashrc

# JupyterLab,HuggingFaceHub,WandBのインストール
RUN /app/.venv/bin/pip install Jupyter \
    jupyterlab \
    huggingface_hub[cli] \
    wandb

# PyTorchのインストール
RUN /app/.venv/bin/pip install torch==2.2.2 torchvision==0.17.2 torchaudio==2.2.2 --index-url https://download.pytorch.org/whl/cu121

# Transformer関連のインストール
RUN /app/.venv/bin/pip install \
    transformers==4.41.2 \
    ninja \
    packaging \
    accelerate \
    datasets \
    bitsandbytes \
    evaluate \
    trl \
    peft \
    wheel

# Flash attentionをインストール
RUN /app/.venv/bin/pip install flash-attn --no-build-isolation

# コンテナの起動時にbashを実行
CMD ["/bin/bash"]

[Ctrl + S]キーで変更内容を保存し、[Ctrl + X]キーで編集モードから抜けます。

STEP
docker-compose.ymlファイルの作成

docker-compose.ymlファイルを使ってDockerコンテナの設定をします。

docker-compose.ymlファイルを作成します。

nano docker-compose.yml

次の記述をコピーしてdocker-compose.ymlに貼り付けます。

services:
  llama3_sftqlora:
    build:
      context: .
      dockerfile: Dockerfile
    image: llama3_sftqlora
    runtime: nvidia
    container_name: llama3_sftqlora
    ports:
      - "8888:8888"
    volumes:
      - .:/app/llama3_sftqlora
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    command: >
      bash -c '/app/.venv/bin/jupyter lab --ip="*" --port=8888 --NotebookApp.token="" --NotebookApp.password="" --no-browser --allow-root'

[Ctrl + S]キーで変更内容を保存し、[Ctrl + X]キーで編集モードから抜けます。

STEP
Dockerコンテナを起動

Dockerfileからビルドしてコンテナを起動します。   

docker compose up

Dockerの起動後にブラウザの検索窓に”localhost:8888″を入力すると、Jupyter Labをブラウザで表示できます。

localhost:8888

Llama3ファインチューニングの実装

見出し画像

Dokcerコンテナ上で起動したJupyter Labを使って、Llama3のファインチューニングを実装していきます。

Jupyter Labのコードセルで以下のコマンドを実行します。

STEP
ライブラリのインポート

必要なライブラリをインポートします。

import torch
from torch import cuda, bfloat16
from transformers import (
    AutoTokenizer,
    AutoModelForCausalLM,
    BitsAndBytesConfig,
    HfArgumentParser,
    TrainingArguments,
    pipeline,
    logging
)
from datasets import load_dataset
from peft import LoraConfig, PeftModel
from trl import SFTTrainer
from huggingface_hub import login
import wandb
STEP
外部連携の設定

モデルをダウンロードするために、HuggingFaceにログインします。

token = "*************"
login(token)

token = “*************”には、HuggingFaceで発行したアクセストークンが入ります。

HuggingFaceでアクセストークンを発行する方法は以下の記事で解説しています。

学習ログの管理をするために、WandBにログインします。(WandBを使用しない場合は省略してください。)

API_KEY = "*************"
wandb.login(key=API_KEY)

API_KEY = “*************”には、Wandbで発行したAPIキーが入ります。

WandbでAPIキーを発行する方法を以下の記事で解説しています。

STEP
モデルの設定

モデルとトークナイザーのダウンロードをして読み込みます。

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)
model_id="meta-llama/Meta-Llama-3-8B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
    model_id, 
    trust_remote_code=True,
    token=token,
    quantization_config=bnb_config,
    device_map='auto',
    torch_dtype=torch.bfloat16,
    attn_implementation="flash_attention_2"
)
tokenizer = AutoTokenizer.from_pretrained(
    model_id,
    padding_side="right",
    add_eos_token=True
)
if tokenizer.pad_token_id is None:
  tokenizer.pad_token_id = tokenizer.eos_token_id
コードの解説

BitsAndBytesConfig

4bit量子化の定義をしています。モデルの精度を下げて、GPUメモリの節約ができます。

meta-llama/Meta-Llama-3-8B-Instruct

llama3 8B Instructモデルを指定しています。

AutoModelForCausalLM.from_pretrained

モデルの読み込む設定をしています。

torch.bfloat16

Bfloat16は、FP32と同じ数値範囲を持ちながら、GPUメモリを節約でき、計算速度も向上します。

attn_implementation

Transformerの処理を効率化して学習を高速するライブラリを指定しています。

AutoTokenizer.from_pretrained

トークナイザーの読み込み設定をしています。

モデルの読み込み時点で、GPUメモリを消費します。

STEP
ファインチューニング前のモデルでテキスト生成テスト

ファインチューニングをする前のモデルでテキスト生成のテストをしてみます。

messages = [
    {"role": "system", "content": "あなたは日本語で回答するアシスタントです。"},
    {"role": "user", "content": "GDPとは何ですか?"},
]

input_ids = tokenizer.apply_chat_template(
    messages,
    add_generation_prompt=True,
    return_tensors="pt"
).to(model.device)

terminators = [
    tokenizer.eos_token_id,
    tokenizer.convert_tokens_to_ids("<|eot_id|>")
]

outputs = model.generate(
    input_ids,
    max_new_tokens=256,
    eos_token_id=terminators,
    do_sample=True,
    temperature=0.6,
    top_p=0.9,
)
response = outputs[0][input_ids.shape[-1]:]

print(tokenizer.decode(response, skip_special_tokens=True))

GDPとは何ですか?

GDP(Gross Domestic Product)は、国の経済活動の総計を示す指標です。国のGDPは、一定の期間(例えば1年)に生じた全ての商品やサービスの生産額、また輸出入の差額を加算して算出されます。

GDPには、以下の3つの要素が含まれます。
Consumption(消費):国民が購入した商品やサービスの額
Investment(投資):企業や政府が行った投資の額
Government spending(政府支出):政府が行った支出の額
Net exports(輸出入の差額):輸出額と輸入額の差額

GDPは、国の経済の規模や成長率を示す指標として、国際的に比較されることが多いです。また、GDPを使用して、国の経済状況を分析し、政策決定に役立つ情報を得ることができます。

ファインチューニング前は、日本語の標準的な口調になっています。

STEP
データセットの設定

モデルの学習に使用するデータセット「bbz662bbz/databricks-dolly-15k-ja-ojousama」を読み込みます。

dataset = load_dataset("bbz662bbz/databricks-dolly-15k-ja-ojousama", split="train")

データセットから「instruction(指示)」と「output(応答)」を抽出して、チャットテンプレートに変換して、データセットを更新します。

def formatting_func(example):
        messages = [
            {'role': "system",'content': "あなたは日本語で回答するアシスタントです。"},
            {'role': "user",'content': example["instruction"]},
            {'role': "assistant",'content': example["output"]}
        ]
        return tokenizer.apply_chat_template(messages, tokenize=False)

def update_dataset(example):
    example["text"] = formatting_func(example)
    for field in ["index", "category", "instruction", "input", "output"]:
        example.pop(field, None)
    return example

dataset = dataset.map(update_dataset)

print(dataset[293]["text"])
チャットテンプレート変換後のデータセットの例

<|begin_of_text|><|start_header_id|>system<|end_header_id|>
あなたは日本語で回答するアシスタントです。<|eot_id|><|start_header_id|>user<|end_header_id|>
石けんはどうやって作るの?<|eot_id|><|start_header_id|>assistant<|end_header_id|>

ワタクシ思いますの、 石けんは、天然またはオーガニックのオイル、水酸化ナトリウム、蒸留水を混ぜて作られますわー。  石けんを作るときは、オリーブオイルやココナッツオイルのような軽い油と重い油の組み合わせを混ぜ、水酸化ナトリウムと蒸留水と混ぜ合わせる必要がありますわー。  この混合物をよく混ぜた後、石けん型に流し込んで何週間もかけて固めますわー。  鹸化と呼ばれるこのプロセスは、水酸化ナトリウムが混合物から蒸発するのにかかる時間であり、石鹸が石鹸型の中で硬化する原因となりますわー。  石鹸を作る際に、ラベンダーやグレープフルーツのようなエッセンシャルオイルを加えて、石鹸に良い香りをつけるという方法もありますわー。  これは、鹸化の前に追加する必要がありますわー。ξ゚⊿゚)ξ<|eot_id|>

モデル学習の前には、データセットをチャットテンプレートに変換しておく必要があります。

STEP
LoRAパラメータの設定

LoRAのパラメータ設定をします。

peft_config = LoraConfig(
    r=8,
    lora_alpha=16,
    lora_dropout=0.05,
    target_modules = ["q_proj", "k_proj", "v_proj", "o_proj","gate_proj", "up_proj", "down_proj",],
    bias="none",
    task_type="CAUSAL_LM",
    modules_to_save=["embed_tokens"],
)
コードの解説

target_modules = [“q_proj”,…

LoRAを適用する対象のトランスフォーマの層(Target modules)を指定します。

すべての線形層にLoRAを対象にすることでモデルの適応品質が向上すると言われています。

r=8

rはファインチューニングの過程で学習される低ランク行列のサイズを表します。

rを大きくするとモデルの適応品質が向上する傾向がありますが、必ずしも直線的な関係ではありません。

rの値が大きくなると更新されるパラメータが増えるため、メモリ使用量が増加します。

lora_alpha:16

LoRaスケーリングのAlphaパラメータは、学習した重みをスケーリングします。

多くの文献では、Alphaを調整可能なパラメータとして扱っておらず、Alphaを16に固定してます。

STEP
学習パラメータの設定

学習パラメータを設定しています。

training_arguments = TrainingArguments(
    output_dir="./results",
    bf16=True,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=16,
    num_train_epochs=3,
    optim="adamw_torch_fused",
    learning_rate=2e-4, 
    lr_scheduler_type="cosine",
    weight_decay=0.01,
    warmup_steps=100,
    logging_steps=10,
    group_by_length=True,
    report_to="wandb" 
)
コードの解説

group_by_length=True

同じ長さのシーケンスをまとめてバッチ化して、メモリを節約しています。

per_device_train_batch_size

一度に処理するバッチのサイズを指定します。GPUメモリが不足する場合は、値を小さくします。

gradient_accumulation_steps

勾配累積。この値を大きくすることで、擬似的にミニバッチのサイズを大きくすることができます。

report_to=”wandb”

WandBにログを出力します。WandBを使用しない場合は、コメントアウトしてください。

STEP
SFTTrainerの設定、学習の実行

SFTTrainerを使って教師ありファインチューニング(Supervised Fine-tuning)を実行します。

new_model="llama-3-8b-ft"

trainer = SFTTrainer(
    model=model,
    tokenizer=tokenizer,
    train_dataset=dataset,
    dataset_text_field="text",
    peft_config=peft_config,
    args=training_arguments,
    max_seq_length=1024,
    packing=True,
)

wandb.init(project="llama3_sftqlora")
trainer.train()
trainer.model.save_pretrained(new_model)
コードの解説

SFTTrainer(…

事前に設定したモデルやトークナイザー、データセット、LoRAパラメータ、学習パラメータ等をSFTTrainerに渡しています。

wandb.init(…

WandBの記録を開始します。WandBと連携しない場合はコメントアウトしてください。

STEP
メトリクスの確認

WandBに保存したファインチューニング実行中のメトリクスを確認します。

Loss(損失)は最初の60ステップで急速に減少し、その後は緩やかに小さくなり収束しています。

loss
(出典:https://wandb.ai/)

GPUメモリは、常に88%(70GB)を使用しています。

GPU使用率
(出典:https://wandb.ai/)

ファインチューニング後のモデルでテキスト生成

見出し画像

ファインチューニング後のモデルでテキスト生成をしていきます。

ファインチューニング後のモデルを読み込みます。

torch.cuda.empty_cache()

ft_model = AutoModelForCausalLM.from_pretrained(
    new_model, 
    device_map='auto',
    torch_dtype=torch.bfloat16,
    attn_implementation="flash_attention_2"
)

tokenizer = AutoTokenizer.from_pretrained(
    model_id
) 

if tokenizer.pad_token_id is None:
  tokenizer.pad_token_id = tokenizer.eos_token_id
コードの解説

torch.cuda.empty_cache()

テキスト生成を実行する前に、ファインチューニングで使用していたGPUメモリをリセットしています。

AutoModelForCausalLM.from_pretrained(new_model,…

ファインチューニング後のモデルを読み込んでいます。

“GDPとは何ですか?”というプロンプトを実行します。

messages = [
    {"role": "system", "content": "あなたは日本語で回答するアシスタントです。"},
    {"role": "user", "content": "GDPとは何ですか?"},
]

input_ids = tokenizer.apply_chat_template(
    messages,
    add_generation_prompt=True,
    return_tensors="pt"
).to(ft_model.device)

terminators = [
    tokenizer.eos_token_id,
    tokenizer.convert_tokens_to_ids("<|eot_id|>")
]

outputs = ft_model.generate(
    input_ids,
    max_new_tokens=256,
    eos_token_id=terminators,
    do_sample=True,
    temperature=0.6,
    top_p=0.9,
)

response = outputs[0][input_ids.shape[-1]:]
print(tokenizer.decode(response, skip_special_tokens=True))

GDPとは何ですか?

ワタクシ思いますの、 国民総生産(GDP)は、国民の総消費、投資、輸出、輸入を合計した額なのですわー。
GDPは、国民経済の規模を測る最も一般的な指標で、各国の経済の大きさを比較するために使用されますわー。
GDPは、1年間の経済活動を測るものであり、経済の成長を測るために使用されますわー。ξ゚⊿゚)ξ

データセットの特徴である「お嬢様の口調」が反映されています。

生成AI・LLMのコストでお困りなら

GPUのスペック不足で生成AIの開発が思うように進まないことはありませんか?

そんなときには、高性能なGPUをリーズナブルな価格で使えるGPUクラウドサービスがおすすめです!

GPUSOROBAN
GPUSOROBAN

GPUSOROBANは、高性能GPU「NVIDIA H200」を業界最安級の料金で使用することができます。

NVIDIA H200は、生成AI・LLMの計算にかかる時間を大幅に短縮することが可能です。

クラウドで使えるため、大規模な設備投資の必要がなく、煩雑なサーバー管理からも解放されます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
EdgeHUBロゴ

メールマガジン登録

Stable Diffusion・LLM・RAGに関する最新情報をいち早くお届けします。

無料メルマガの配信をご希望の方は、下記フォームよりご登録ください。

    EdgeHUB編集部からのお知らせ

    無料ウェビナーのお知らせ

    11/27ウェビナー

    RAG×日本語LLMの無料オンラインウェビナー第2弾!

    開催日時:
    2024年11月27日(水) 14:00~15:00

    内容:

    • 「LlamaIndex」と「Llama-3.1-Swallow」でRAG環境を構築
    • 生成AI開発の強い味方「GPUSOROBAN」の活用方法

    このウェビナーでは、オープンソース「LlamaIndex」で構築したRAGの使い方や、デモを紹介します。

    生成AIに関心のある方、AI技術をビジネスに活かしたい方は、ぜひこの貴重な機会にご参加ください!

    こんな方におすすめ!

    • 日本語LLMの利用を検討している方
    • AI開発に携わっている方
    • 最新のAI技術やトレンドに興味がある方
    • GPUスペック不足に悩んでいる方

    \簡単1分で申し込み!/

    この記事を書いた人

    EdgeHUBは、NVIDIAクラウドパートナーである株式会社ハイレゾが運営しています。「AIと共にある未来へ繋ぐ」をテーマに、画像生成AI、文章生成AI、動画生成AI、機械学習・LLM、Stable Diffusionなど、最先端の生成AI技術の使い方をわかりやすく紹介します。

    目次