LLMファインチューニングのチュートリアル

投稿更新日: 2025/6/5

サムネイル

近年、自然言語処理(NLP)の分野では大規模言語モデル(LLM)の活用が急速に進んでいます。特に、特定のタスク向けにモデルの性能を向上させる「ファインチューニング」は、プロジェクトでの需要が増えつつあります。

本記事では、初心者の方でも理解しやすいように、ファインチューニングの基本的な流れを体験できるチュートリアルを用意しました。


環境設定と事前準備

このチュートリアルは、macOS環境(例: M2 MacBook Pro)をベースにしています。以下のコマンドで仮想環境を作成し、必要なライブラリをインストールしましょう。

仮想環境のセットアップ

python -m venv .env
source .env/bin/activate

必要なライブラリのインストール

以下のコマンドを実行して、必要なPythonライブラリをインストールします。

pip install transformers datasets evaluate accelerate scikit-learn torch

動作確認

ライブラリが正しくインストールされたか確認するために、以下のテストコードを実行します。

python -c "from transformers import pipeline; print(pipeline('sentiment-analysis')('we love you'))"

期待される出力:

[{'label': 'POSITIVE', 'score': 0.9998704195022583}]

ファインチューニングスクリプトの作成と実行

ファインチューニングの手順を示したPythonスクリプト(finetune.py)を以下に示します。このスクリプトでは、Yelpレビューのデータセットを使用します。

finetune.py

from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer
import numpy as np
import evaluate

# データセットの準備
dataset = load_dataset("yelp_review_full")

# トークナイザーによるトークナイズ
tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased")
def tokenize_function(examples):
    return tokenizer(examples["text"], padding="max_length", truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)

# 学習データとテストデータ作成
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(500))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(500))

# モデルのロード
model = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-cased", num_labels=5, torch_dtype="auto")

# 評価関数
metric = evaluate.load("accuracy")
def compute_metrics(eval_pred):
    logits, labels = eval_pred
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)

# 学習設定
training_args = TrainingArguments(output_dir="test_trainer", eval_strategy="epoch")
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=small_train_dataset,
    eval_dataset=small_eval_dataset,
    compute_metrics=compute_metrics,
)

# 学習の実行
trainer.train()

# モデルとトークナイザーの保存
save_dir = './finetuned/bert-base-cased'
tokenizer.save_pretrained(save_dir)
model.save_pretrained(save_dir)

実行結果

スクリプトを実行すると、以下のような評価結果が表示されます。

python3 finetune.py

{'eval_loss': 1.5970934629440308, 'eval_accuracy': 0.242, 'eval_runtime': 39.0603, 'eval_samples_per_second': 12.801, 'eval_steps_per_second': 1.613, 'epoch': 1.0}                                                                               
{'eval_loss': 1.468526840209961, 'eval_accuracy': 0.346, 'eval_runtime': 36.1194, 'eval_samples_per_second': 13.843, 'eval_steps_per_second': 1.744, 'epoch': 2.0}                                                                                
{'eval_loss': 1.4037985801696777, 'eval_accuracy': 0.368, 'eval_runtime': 35.7517, 'eval_samples_per_second': 13.985, 'eval_steps_per_second': 1.762, 'epoch': 3.0}                                                                               
{'train_runtime': 467.6126, 'train_samples_per_second': 3.208, 'train_steps_per_second': 0.404, 'train_loss': 1.5324497121982474, 'epoch': 3.0}                                                                                                   
100%|██████████████████████████████████████████████████████████████████████████████████| 189/189 [07:47<00:00,  2.47s/it]

ファインチューニングスクリプトの解説

以下は、ファインチューニングスクリプト finetune.py の各パートを詳しく解説しまとめたものです。


1. データセットのロード

dataset = load_dataset("yelp_review_full")

解説:

  • Hugging Faceのdatasetsライブラリを使用して、Yelpのレビューコメントデータセットをロードします。このデータセットにはレビューコメントと1~5の評価スコアが含まれています。

ポイント:

  • データセットの種類:Hugging Faceのload_datasetを使えば、豊富な事前構築データセットに簡単にアクセス可能。
  • カスタムデータの利用:自分のデータを使いたい場合、CSVやJSON形式で読み込むことも可能です。

2. トークナイズ

tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased")

def tokenize_function(examples):
    return tokenizer(examples["text"], padding="max_length", truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)

解説:

  • トークナイザーを使ってテキストを数値データに変換します。これにより、モデルが扱える形式に整えられます。
  • padding="max_length"で固定長の入力に揃え、truncation=Trueで最大トークン数を超えた部分をカットします。

ポイント:

  • トークナイザーの選択:モデルに対応するトークナイザーを使用することが重要(例: BERTモデルならBERT用のトークナイザー)。
  • 効率化batched=Trueを指定することで、複数のデータを一度にトークナイズして処理速度を向上。

3. データの分割と縮小

small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(500))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(500))

解説:

  • 訓練データと評価データを分割し、ファインチューニング用にサブセット(500件)を作成します。
  • shuffle(seed=42)はデータをランダムに並び替えますが、同じ結果を再現するためシード値を設定しています。

ポイント:

  • データサイズの調整:学習時間やリソースの制約に合わせてデータサイズを選択できます。
  • 小規模学習のメリット:小規模データでもモデルの動作確認や理解が可能。

4. モデルのロード

model = AutoModelForSequenceClassification.from_pretrained(
    "google-bert/bert-base-cased",
    num_labels=5,
    torch_dtype="auto"
)

解説:

  • Hugging Faceから事前学習済みのBERTモデルをロードし、分類タスク用に調整します。
  • num_labels=5は分類クラスの数(Yelpの評価スコア1~5)を指定しています。

ポイント:

  • 事前学習モデルの再利用:大規模な事前学習済みモデルを活用することで、少ないデータでも良い結果が得られる。
  • モデルのカスタマイズ:分類クラス数や出力層を調整して特定のタスクに適応。

5. 評価関数の設定

metric = evaluate.load("accuracy")
def compute_metrics(eval_pred):
    logits, labels = eval_pred
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)

解説:

  • evaluateライブラリを使用して、モデルの精度(accuracy)を評価する関数を定義しています。
  • 推論結果(logits)をargmaxで予測ラベルに変換し、実際のラベルと比較してスコアを計算します。

ポイント:

  • 評価指標の選択:分類タスクではaccuracyが一般的ですが、タスクに応じてprecisionrecallも検討すべきです。
  • 簡単な実装evaluateライブラリを使うと、一般的な評価指標を簡単に利用可能。

6. 学習設定

training_args = TrainingArguments(
    output_dir="test_trainer",
    eval_strategy="epoch"
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=small_train_dataset,
    eval_dataset=small_eval_dataset,
    compute_metrics=compute_metrics,
)

解説:

  • TrainingArgumentsで学習パラメータを指定します。ここでは、モデル保存先(output_dir)と評価タイミング(eval_strategy="epoch")を設定。
  • Trainerクラスは、学習プロセスを簡略化するための高レベルAPIです。

ポイント:

  • 簡単な学習管理Trainerを使うと、ループの作成や勾配計算の実装を気にする必要がありません。
  • 設定の柔軟性:学習率やバッチサイズなどの詳細設定も可能です。

7. モデルの保存

tokenizer.save_pretrained('./finetuned/bert-base-cased')
model.save_pretrained('./finetuned/bert-base-cased')

解説:

  • ファインチューニング後のトークナイザーとモデルを保存します。この保存済みモデルは、将来的に推論やさらなる調整に使用できます。

ポイント:

  • 再利用性の向上:モデルを保存しておくことで、他のプロジェクトやデプロイメントに活用可能。
  • Hugging Face互換:保存形式はHugging Faceライブラリでの再利用に最適化されています。

チューニング前後のモデル性能比較

ファインチューニングによる効果を確認するため、同じ入力データを使ってモデルの予測結果を比較します。

チューニング前のモデルで推論

以下のスクリプトで推論を行います。

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import numpy as np

model_path = 'google-bert/bert-base-cased'
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForSequenceClassification.from_pretrained(model_path)

text = "I visit this bar at first time. Service is good. Making drinks and talking with Bartender is also good. I spend good 2 hours. I will come again next month."
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True)
outputs = model(**inputs)
predicted_class = np.argmax(outputs.logits.detach().numpy(), axis=-1)
print(predicted_class)

出力結果:

[0] # 配列なので0始まり。0は評価1。

inputのtextからは4か5の高評価を期待しますが、評価が「1」となりました。

チューニング後のモデルで推論

同じテキストを以下のスクリプトで推論します。

model_path = './finetuned/bert-base-cased'
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForSequenceClassification.from_pretrained(model_path)

text = "I visit this bar at first time. Service is good. Making drinks and talking with Bartender is also good. I spend good 2 hours. I will come again next month."
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True)
outputs = model(**inputs)
predicted_class = np.argmax(outputs.logits.detach().numpy(), axis=-1)
print(predicted_class)

出力結果:

[4] # 配列なので0始まり。4は評価5。

この結果は、より正確な評価となっており、モデルが改善されたことを示しています。

まとめ

このチュートリアルでは、transformersライブラリを使用してLLMのファインチューニングを行い、わずか500件のデータでもモデルの性能が向上することを確認できました。

本記事で紹介した手順は、あくまで学習目的です。

本格的な案件では、より大規模なデータセットや高性能なハードウェアを利用することを推奨します。

学びのポイント

  1. LLMの基本操作とトークナイズの仕組みが理解できる。
  2. Hugging Faceのエコシステムを活用する方法が習得できる。
  3. 簡単なファインチューニングによるモデルの改善効果を確認できる。

今後のプロジェクトや個人学習にぜひ役立ててください。


参考


この記事をシェアする

合同会社raisexでは一緒に働く仲間を募集中です。

ご興味のある方は以下の採用情報をご確認ください。