以下の内容はhttps://uepon.hatenadiary.com/entry/2025/03/22/014112より取得しました。


初心者でも簡単!Ollamaモデルをファインチューニングして自分だけのAIを作る方法

1. はじめに

「自分だけのAIアシスタントがあったら便利だろうな~🤔」と思ったことはありませんか?そんな好奇心から、今更ながらOllamaで動かしている大規模言語モデルLLM)のファインチューニングに挑戦してみました。プログラミング初心者向けの回答をより的確にしたい、あるいは特定のキャラクターのように応答してくれるモデルを作りたい——そんな願いを実現できるかもしれないと思ったのです。

この記事では、実際にGTX 1070(VRAM 8GB)というかなり古めのGPUを搭載したノートPCで試したファインチューニングの過程を、成功も失敗も含めた体験記としてまとめてみました。技術的な詳細はもちろん、つまずいたポイントや発見した小技なども織り交ぜているのでちょっと読みにくいかもしれません🙇‍♂️

ファインチューニングとは

ファインチューニングは、優れた基本辞書に自分専用の単語帳を追加するようなものです。元の知識(事前学習済みモデル)はそのままに、自分の必要な分野だけ追加学習させて特化させる技術です。(この表現あっているだろうか…🙄)

技術的には、事前学習済みモデルの重みを少量の特定データで微調整し、元のモデルの言語理解能力を保ちながら、特定の知識や応答パターンを組み込んでいくものです。ファインチューニングの魅力的は、ゼロからモデルを訓練するよりもはるかに少ないデータと計算リソースで実現できる点にあります。

ファインチューニングの目的の必要性

実際に試してみると、ファインチューニングを行う場合には具体的な目標設定が必要性を感じました。

以下のような目的を持つと効果がありそうです。

  1. 特定の質問パターンへの応答改善 … 頻繁にする質問の回答を洗練させる
  2. 一貫した回答スタイルの獲得 … 例えば、簡潔で初心者にもわかりやすい説明を常に提供する
  3. 特定分野での専門性向上 … プログラミング言語や特定のフレームワークについての知識を深める

後述もしていますが、実感したのは「データの質と量が結果を大きく左右する」ということです。少量のデータでもファインチューニングは可能ですが、効果は限定的です。実用的なレベルにするには、ある程度の量のデータが必要になってくると思います。

それでは、今回行った作業を環境構築からステップバイステップで書いていきます。

2. 環境準備

ファインチューニングを始める前に、適切な環境を整える必要があります。私はUbuntu 24.04 LTSGTX 1070 Max-Q Design(VRAM 8GB)を搭載したノートPCを使用しました。最初は「このスペックで大丈夫かな?」と不安でしたが、今回行った手法程度であれば十分対応できます。

必要なハードウェア要件

ファインチューニングに必要なハードウェア要件はこんな感じでしょうか?あくまでも目安なので、これよりスペックが低いからといって出来ないわけではありません。

  • GPU … VRAM 8GB以上(NVIDIA GPUが望ましい)
  • RAM … 16GB以上(32GBあると余裕があります)
  • ストレージ … 20GB以上の空き容量(モデル自体が数GB〜十数GBになります)
  • OS … LinuxUbuntu推奨)、macOSWindows(WSL2推奨)

自分の使用していたPCのシステムスペックはこんな感じ

Ubuntu 24.04 LTS
GTX 1070 with Max-Q Design(VRAM 8GB)
メインメモリ 32GB
Python 3.12.9

このスペックで、8B以下のモデル(今回はLlama-3-ELYZA-JP-8Bを使用)ではファインチューニングできました。より大きなモデルにも挑戦してみましたが、8GBのVRAMではメモリ不足になることもあるようです。またモデルの作り方によってはこの方法ではファインチューニングできないというモデルもあるので、そのあたりはご注意ください。

仮想環境の作成

初めてのファインチューニングでは、既存のPython環境を壊さないよう仮想環境を使用することをお勧めします。

以下のコマンドで仮想環境を作成しました。

# プロジェクトディレクトリを作成
mkdir -p ~/finetuning
cd ~/finetuning

# Python仮想環境を作成
python3 -m venv venv

# 仮想環境を有効化
source venv/bin/activate

仮想環境が有効になると、プロンプトの先頭に (venv) と表示されます。これで、この環境にインストールするパッケージは他の環境に影響を与えません。

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

次に、ファインチューニングに必要なライブラリをインストールします。ここで注意したいのがライブラリの依存関係です。特にPyTorchとその周辺ライブラリは、バージョンの組み合わせによって互換性の問題が発生することがあります。自分も何回か失敗しています🥲🥲🥲

そこで、まず最初にNumPyのバージョンを固定することにしました。それから後述するrequirements.txtを使用してライブラリをインストールしています。Numpyのバージョンを固定せずにrequirements.txtでインストールしてもNumPyのインストールは行われますが、バージョン2系のインストールがされてしまい以降の作業でエラーなどが発生します。

# まずNumPyを1.x系に固定する
$ pip install numpy==1.26.4

requirements.txt

# requirements.txt
--find-links https://download.pytorch.org/whl/cu121
torch==2.2.0
torchvision==0.17.0
torchaudio==2.2.0

bitsandbytes==0.43.0
transformers==4.38.2
accelerate==0.27.2
peft==0.8.2
trl==0.7.10

datasets
matplotlib
tensorboard

ライブラリのインストール

# requirements.txtからインストール
$ pip install -r requirements.txt

これで環境設定ができました。

環境の確認

環境が正しく設定されているか確認しておきます。以下のPythonプログラムを実行します。このプログラムではPyTorchCUDAGPUメモリなどを調べています。

check_environments.py

import torch

print(f"PyTorchバージョン: {torch.__version__}")
print(f"CUDAは利用可能か: {torch.cuda.is_available()}")
if torch.cuda.is_available():
    print(f"CUDAバージョン(PyTorch): {torch.version.cuda}")
    print(f"現在のGPU: {torch.cuda.get_device_name(0)}")
    # メモリ情報も表示
    print(f"GPU総メモリ: {torch.cuda.get_device_properties(0).total_memory / 1024 ** 3:.2f} GB")
    print(f"現在利用可能なGPUメモリ: {torch.cuda.mem_get_info()[0] / 1024 ** 3:.2f} GB")

実行すると、以下のような出力が得られました。

$ python check_environments.py 
PyTorchバージョン: 2.2.0+cu121
CUDAは利用可能か: True
CUDAバージョン(PyTorch): 12.1
現在のGPU: NVIDIA GeForce GTX 1070 with Max-Q Design
GPU総メモリ: 7.92 GB
現在利用可能なGPUメモリ: 7.82 GB

GPUが正しく認識され、PyTorchCUDAを利用できることがわかります。 もし、CUDAが認識されていない場合には、ドライバーやToolkitのインストールを行ってください。

参考

uepon.hatenadiary.com

3. モデルの準備

環境が整ったら、次はファインチューニングの対象となるモデルを準備します。これが意外と難しかった🥲🥲🥲私は当初Ollamaで使っているモデルをそのままファインチューニングできるだろうと安易に考えていましたが、実際はもう少し複雑でした。

Ollamaで使用しているモデルは独自のフォーマット(主にGGUF形式)で保存されており、HuggingFaceの標準的なファインチューニングツールでは直接扱えないようでした。つまり、手順としてはOllamaのモデルを直接ファインチューニングするのではなく、元のHuggingFaceモデルをダウンロードしてファインチューニングし、Ollamaモデルに変換する必要があるということです。

さすがにOllamaモデルの変換説明が入ると長くなってしまうので、以下を参照してもらえればと思います。

参照

uepon.hatenadiary.com

モデルの選択基準と今回使用したモデル(ELYZA-japanese-Llama-2-7b

HuggingFaceから直接モデルをダウンロードしていきます。今回はELYZA-japanese-Llama-2-7bモデルを選択しています。

モデルを選択する際の主な基準は以下の通りです。

  1. サイズ制約 ... 8GBのVRAMで扱えるサイズであること
  2. 日本語対応 ... 優れた日本語理解・生成能力を持つこと
  3. 性能 ... モデルサイズと性能のバランスが良いこと
  4. 互換性 ... ファインチューニングツールとの互換性があること

この基準から、ELYZA社のELYZA-japanese-Llama-2-7bモデルを選びました。このモデルはMeta AIのLlama 2をベースに日本語能力を強化したもので、7Bパラメータで比較的小さめながら日本語と英語の両方で良好な性能を持っています。特に日本語のコンテキスト理解と生成能力が優れており、8GB VRAMでも扱える大きさです。ちなみに8bモデルではメモリが足りませんでした🥲

huggingface.co

HuggingFaceでのモデルのダウンロード

以下のPythonプログラムを使用してモデルをダウンロードしました。

download_model.py

from huggingface_hub import snapshot_download
import os
import time

# ダウンロードするモデルID
model_id = "elyza/ELYZA-japanese-Llama-2-7b"
output_dir = "./models/ELYZA-japanese-Llama-2-7b"

# ディレクトリが存在しない場合は作成
os.makedirs(output_dir, exist_ok=True)

print(f"ELYZA-japanese-Llama-2-7bモデルをダウンロード中...")
start_time = time.time()

try:
    # モデルをダウンロード
    model_path = snapshot_download(
        repo_id=model_id,
        local_dir=output_dir
    )
    
    end_time = time.time()
    duration = end_time - start_time
    
    print(f"モデルのダウンロードが完了しました!")
    print(f"保存先: {model_path}")
    print(f"所要時間: {duration:.2f}秒")
    
    # モデルディレクトリの内容を確認
    files = os.listdir(model_path)
    print(f"\nモデルディレクトリの内容:")
    for file in files:
        file_path = os.path.join(model_path, file)
        if os.path.isfile(file_path):
            size_mb = os.path.getsize(file_path) / (1024 * 1024)
            print(f" - {file} ({size_mb:.2f} MB)")
        else:
            print(f" - {file}/ (ディレクトリ)")
        
except Exception as e:
    print(f"モデルのダウンロード中にエラーが発生しました: {e}")

ダウンロードには10分弱かかりました。インターネット接続の速度によっては、もっと時間がかかる場合もあります。また、このモデルは約14GBのディスク容量を使用しています。

$ python download_model.py 
ELYZA-japanese-Llama-2-7bモデルをダウンロード中...
.gitattributes: 100%|█████████████████████████████████| 1.57k/1.57k [00:00<00:00, 3.32MB/s]
generation_config.json: 100%|██████████████████████████████| 154/154 [00:00<00:00, 129kB/s]
key_visual.png: 100%|█████████████████████████████████| 4.92M/4.92M [00:00<00:00, 17.2MB/s]
LICENCE.txt: 100%|████████████████████████████████████| 7.02k/7.02k [00:00<00:00, 13.5MB/s]
config.json: 100%|████████████████████████████████████████| 632/632 [00:00<00:00, 1.75MB/s]
pytorch_model.bin.index.json: 100%|███████████████████| 26.8k/26.8k [00:00<00:00, 25.1MB/s]
README.md: 100%|██████████████████████████████████████| 5.16k/5.16k [00:00<00:00, 7.17MB/s]
tokenizer_config.json: 100%|██████████████████████████████| 725/725 [00:00<00:00, 1.81MB/s]
tokenizer.model: 100%|██████████████████████████████████| 500k/500k [00:00<00:00, 8.29MB/s]
tokenizer.json: 100%|█████████████████████████████████| 1.84M/1.84M [00:00<00:00, 2.66MB/s]
special_tokens_map.json: 100%|████████████████████████████| 437/437 [00:00<00:00, 1.06MB/s]
pytorch_model-00002-of-00002.bin: 100%|███████████████| 3.50G/3.50G [03:40<00:00, 15.8MB/s]
pytorch_model-00001-of-00002.bin: 100%|███████████████| 9.98G/9.98G [07:54<00:00, 21.0MB/s]
Fetching 13 files: 100%|███████████████████████████████████| 13/13 [07:54<00:00, 36.53s/it]
モデルのダウンロードが完了しました!0%|███████████████| 3.50G/3.50G [03:40<00:00, 14.4MB/s]
保存先: /home/user2404/finetuning/models/ELYZA-japanese-Llama-2-7b
所要時間: 476.17秒

モデルディレクトリの内容:
 - config.json (0.00 MB)
 - pytorch_model-00001-of-00002.bin (9514.46 MB)
 - key_visual.png (4.69 MB)
 - tokenizer.json (1.76 MB)
 - LICENCE.txt (0.01 MB)
 - special_tokens_map.json (0.00 MB)
 - README.md (0.00 MB)
 - generation_config.json (0.00 MB)
 - pytorch_model-00002-of-00002.bin (3338.16 MB)
 - .cache/ (ディレクトリ)
 - pytorch_model.bin.index.json (0.03 MB)
 - tokenizer_config.json (0.00 MB)
 - tokenizer.model (0.48 MB)
 - .gitattributes (0.00 MB)

モデルの初期テスト

ダウンロードしたモデルが正しく動作するか確認するために、以下のテストプログラムも作成しました。

test_model.py

import transformers
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

print(f"Transformersバージョン: {transformers.__version__}")

model_path = "./models/ELYZA-japanese-Llama-2-7b"

print(f"トークナイザーを読み込んでいます...")
tokenizer = AutoTokenizer.from_pretrained(model_path)
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token
print("トークナイザーの読み込み完了")

print(f"モデルを読み込んでいます(FP16形式)...")
model = AutoModelForCausalLM.from_pretrained(
    model_path,
    torch_dtype=torch.float16,
    device_map="auto"
)
print("モデルが正常に読み込まれました")

# テストプロンプト
test_prompt = "機械学習とは何ですか?簡単に説明してください。"
print(f"\n入力プロンプト: \"{test_prompt}\"")

# トークン化
inputs = tokenizer(
    test_prompt, 
    return_tensors="pt",
    padding=True
)
inputs = {k: v.to(model.device) for k, v in inputs.items()}

# 推論を実行
print("生成中...")
with torch.no_grad():
    outputs = model.generate(
        **inputs,
        max_new_tokens=100,
        do_sample=True,
        temperature=0.7
    )

# 生成されたテキストをデコード
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(f"\n生成されたテキスト:\n{generated_text}")

実行すると、モデルが無事に読み込まれ、プロンプトに対して応答を生成することができました。ELYZA-japanese-Llama-2-7bモデルが日本語プロンプトに対して流暢な日本語で回答してくれることを確認できました。

実行例

$ python test_model.py 
Transformersバージョン: 4.38.2
トークナイザーを読み込んでいます...
トークナイザーの読み込み完了
モデルを読み込んでいます(FP16形式)...
Loading checkpoint shards: 100%|█████████████████████████████| 2/2 [00:03<00:00,  1.52s/it]
WARNING:root:Some parameters are on the meta device device because they were offloaded to the cpu.
モデルが正常に読み込まれました

入力プロンプト: "機械学習とは何ですか?簡単に説明してください。"
生成中...

生成されたテキスト:
機械学習とは何ですか?簡単に説明してください。
機械学習は機械によって学習できる能力です。
人間の脳みそのようなものです。
脳に入っている情報をもとに、繰り返し訓練されていくことによって、学習を重ねることで

これで、モデルの準備が完了しました。つづいてはファインチューニングに使用するデータセットの準備を行います。

4. データセットの準備

続いてはファインチューニングに使用するデータセットを準備します。ここが実は一番大事なステップだと後から気づきました。「どんなデータでトレーニングするか」によってモデルの最終的な性能と振る舞いを決定されるからです。

ファインチューニング用データの構造

ファインチューニングには、モデルに「こういう入力に対して、こういう出力をしてほしい」という例示のデータセットが必要です。私は最初、このデータ形式がどうあるべきか理解しておらず、いろいろと試行錯誤しました。

現在のLLMのファインチューニングでは、会話形式のデータが一般的です。具体的には、「ユーザーからの質問」と「アシスタントからの回答」のペアを複数用意します。検索の末たどり着いた形式はJSONL(JSON Lines)形式で、各行にひとつの会話データを持つ形です。

JSONL形式の構造は以下のようになります

{"messages": [{"role": "user", "content": "質問1"}, {"role": "assistant", "content": "回答1"}]}
{"messages": [{"role": "user", "content": "質問2"}, {"role": "assistant", "content": "回答2"}]}

この形式は、OpenAIのChat Completionなどと同様のフォーマットであり、多くのファインチューニングツールで標準的に使われています。

JSONL形式でのデータセット作成

実際のデータセットを作成するために、以下のPythonを実行します。プログラムを実行すると、カレントディレクトリにdataディレクトリを作成し、その中にtraining_data.jsonlファイルが作成されます。今回は実験なので5つの質問と回答のペアだけを含んでいます。

create_dataset.py

import json
import os

# 訓練データを保存するディレクトリ
data_dir = "./data"
os.makedirs(data_dir, exist_ok=True)

# サンプルの訓練データ
training_data = [
    {"messages": [
        {"role": "user", "content": "機械学習とは何ですか?"},
        {"role": "assistant", "content": "機械学習とは、コンピュータがデータから学習し、パターンを認識し、予測や決定を行うことができるようにするアルゴリズムと技術の集合です。人間が明示的にプログラミングすることなく、データから自動的に学習する能力を持ちます。"}
    ]},
    {"messages": [
        {"role": "user", "content": "ディープラーニングについて教えてください。"},
        {"role": "assistant", "content": "ディープラーニングは機械学習の一種で、人間の脳の構造にインスピレーションを受けたニューラルネットワークを多層に重ねて構築します。これにより、大量のデータから複雑なパターンを学習し、画像認識、自然言語処理、音声認識などのタスクで優れた性能を発揮します。"}
    ]},
    {"messages": [
        {"role": "user", "content": "ファインチューニングとは何ですか?"},
        {"role": "assistant", "content": "ファインチューニングは、事前学習された大規模なモデルを特定のタスクやドメインに適応させるために、追加のトレーニングを行うプロセスです。元のモデルの知識を保持しながら、特定の用途に合わせて性能を向上させることができます。"}
    ]},
    {"messages": [
        {"role": "user", "content": "プログラミングの基本原則を教えてください。"},
        {"role": "assistant", "content": "プログラミングの基本原則には、DRY(Don't Repeat Yourself、繰り返しを避ける)、KISS(Keep It Simple, Stupid、シンプルに保つ)、SOLID(単一責任、オープン/クローズド、リスコフの置換、インターフェース分離、依存性逆転の各原則)などがあります。これらの原則は、保守性が高く、拡張しやすい、品質の高いコードを書くために役立ちます。"}
    ]},
    {"messages": [
        {"role": "user", "content": "効果的な学習方法を教えてください。"},
        {"role": "assistant", "content": "効果的な学習方法には、アクティブラーニング(能動的に参加する)、スペースド・リピティション(間隔を空けた復習)、フェインマン技法(概念を簡単に説明できるようにする)、ポモドーロ・テクニック(25分集中、5分休憩)、そして教えることで学ぶ方法などがあります。また、十分な睡眠、運動、バランスの取れた食事も学習効率を高めるために重要です。"}
    ]}
]

# JSONLファイルに保存
with open(os.path.join(data_dir, "training_data.jsonl"), "w", encoding="utf-8") as f:
    for item in training_data:
        f.write(json.dumps(item, ensure_ascii=False) + "\n")

print(f"訓練データが {os.path.join(data_dir, 'training_data.jsonl')} に保存されました。")
print(f"サンプル数: {len(training_data)}")

実行結果

$ python create_dataset.py 
訓練データが ./data/training_data.jsonl に保存されました。
サンプル数: 5

実際、この少量のデータでどこまで効果があるのか不安でしたが、まずは実験として試してみることにしました。(結果については後ほど説明します)

データセットの検証

作成したデータセットが正しい形式になっているか確認するための検証プログラムも作っています。

validate_dataset.py

import json
from datasets import load_dataset

data_path = "./data/training_data.jsonl"

# データセットの読み込み
try:
    dataset = load_dataset("json", data_files=data_path, split="train")
    print(f"データセットの読み込みに成功しました。")
    print(f"サンプル数: {len(dataset)}")
    
    # 最初の例を表示
    print("\n最初のサンプル:")
    print(dataset[0])
    
    # データ構造の確認
    if "messages" in dataset[0]:
        print("\nデータ構造は正しいです。")
        messages = dataset[0]["messages"]
        if len(messages) >= 2 and "role" in messages[0] and "content" in messages[0]:
            print("メッセージ形式も正しいです。")
        else:
            print("警告: メッセージ形式が期待と異なります。")
    else:
        print("\n警告: 'messages'フィールドが見つかりません。")
        
except Exception as e:
    print(f"データセットの読み込み中にエラーが発生しました: {e}")

実行結果

$ python validate_dataset.py 
Generating train split: 5 examples [00:00, 1127.80 examples/s]
データセットの読み込みに成功しました。
サンプル数: 5

最初のサンプル:
{'messages': [{'role': 'user', 'content': '機械学習とは何ですか?'}, {'role': 'assistant', 'content': '機械学習とは、コンピュータがデータから学習し、パターンを認識し、予測や決定を行うことができるようにするアルゴリズムと技術の集合です。人間が明示的にプログラミングすることなく、データから自動的に学習する能力を持ちます。'}]}

データ構造は正しいです。
メッセージ形式も正しいです。

これで、データセットが正しいフォーマットで作成され、ファインチューニングの準備が整いました。

5. ファインチューニングの実行

いよいよ核心部分、ファインチューニングの実行に入ります。ここまで準備してきた環境、モデル、データセットを使って、実際にモデルを自分好みに調整していきます。初めての経験でワクワクしましたが、同時に「これで本当に上手くいくのか」という不安もありました🙄

LoRA(Low-Rank Adaptation)の基本概念

ファインチューニングには様々な手法がありますが、限られたリソース(GPUメモリ8GB)で効率的に行うために、LoRAという手法を採用しています。

LoRALow-Rank Adaptation(低ランク適応)の略で、大規模言語モデルを効率的にファインチューニングするための手法です。通常のファインチューニングでは、モデル全体の重みを更新しますが、これには膨大なメモリと計算リソースが必要です。

LoRAでは、元のモデルパラメータを直接更新する代わりに、小さな「低ランク行列」を介してパラメータを調整します。これにより、メモリ使用量を大幅に削減しつつ、効果的なファインチューニングが可能になります。

例えるなら、LoRAは「元の道(モデル)を作り直す」のではなく、「元の道の上に標識やガイド(低ランク行列)を追加する」ようなものです。

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

試行錯誤はありましたが、最終的に以下のプログラムを作成しました。このスクリプトは標準的なHugging Face Transformersライブラリを使用しています。

finetune_model.py

import os
import time
from datetime import timedelta
import torch
from datasets import load_dataset
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    TrainingArguments,
    DataCollatorForLanguageModeling,
    Trainer
)
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

# 開始時間の記録
start_time = time.time()

# モデルとデータのパス
model_path = "./models/ELYZA-japanese-Llama-2-7b"
data_path = "./data/training_data.jsonl"
output_dir = "./models/ELYZA-japanese-Llama-2-7b-finetuned"

print("1. データセットの読み込み...")
dataset = load_dataset("json", data_files=data_path, split="train")
print(f"データセット読み込み完了。サンプル数: {len(dataset)}")

# データセットの最初の例を表示
print("\n最初のサンプル:")
print(dataset[0]["messages"])

print("\n2. トークナイザーの読み込み...")
tokenizer = AutoTokenizer.from_pretrained(model_path)
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token
print("トークナイザーの読み込み完了")

print("\n3. データセットの前処理...")
# 会話データを適切な形式にフォーマット
def format_conversations(examples):
    formatted_texts = []
    for i in range(len(examples["messages"])):
        conversation = ""
        messages = examples["messages"][i]
        for message in messages:
            role = message["role"]
            content = message["content"]
            if role == "user":
                conversation += f"USER: {content}\n"
            elif role == "assistant":
                conversation += f"ASSISTANT: {content}\n"
        formatted_texts.append(conversation)
    
    # トークン化 - 最大長を短くして調整
    tokenized = tokenizer(
        formatted_texts,
        padding="max_length",
        truncation=True,
        max_length=1024,  # 2048から1024に減らしてメモリ使用量を削減
        return_tensors="pt"
    )
    
    return tokenized

# データセットの前処理
tokenized_dataset = dataset.map(
    format_conversations,
    batched=True,
    remove_columns=dataset.column_names
)
print("データセット前処理完了")

print("\n4. モデルの量子化設定...")
# 4ビット量子化の設定
quantization_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_compute_dtype=torch.float16,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4"
)

print("\n5. モデルの読み込み...")
# 量子化されたモデルを読み込む
model = AutoModelForCausalLM.from_pretrained(
    model_path,
    quantization_config=quantization_config,
    device_map="auto",
    trust_remote_code=True,
    # メモリ使用量を削減するために勾配チェックポイントを明示的に有効化
    use_cache=False
)
print("モデルの読み込み完了")

print("\n6. LoRAの設定...")
# モデルを量子化学習用に準備
model = prepare_model_for_kbit_training(model)

# LoRAの設定 - rとlora_alphaを小さくしてメモリ使用量を削減
lora_config = LoraConfig(
    r=8,                    # LoRAランク(16から8に削減)
    lora_alpha=8,           # LoRAアルファ値(16から8に削減)
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
    lora_dropout=0.05,      # ドロップアウト率
    bias="none",            # バイアスパラメータは更新しない
    task_type="CAUSAL_LM"   # 因果言語モデル用
)

# LoRAを適用
model = get_peft_model(model, lora_config)
print("LoRA設定完了")

# 学習可能なパラメータの情報を表示
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
all_params = sum(p.numel() for p in model.parameters())
print(f"学習可能なパラメータ: {trainable_params:,} ({100 * trainable_params / all_params:.2f}% of total)")

print("\n7. トレーニング設定...")
# データコレータの設定
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)

# トレーニングの引数設定
training_args = TrainingArguments(
    output_dir=output_dir,          # 学習済みモデルの保存先
    num_train_epochs=5,             # エポック数
    per_device_train_batch_size=1,  # バッチサイズ
    gradient_accumulation_steps=8,  # 勾配蓄積ステップを4から8に増やす(メモリ使用量を削減)
    learning_rate=2e-4,             # 学習率
    weight_decay=0.01,              # 重み減衰
    warmup_ratio=0.03,              # ウォームアップの割合
    lr_scheduler_type="cosine",     # 学習率スケジューラ
    save_strategy="epoch",          # 保存戦略
    logging_steps=1,                # ログ出力間隔
    logging_first_step=True,        # 最初のステップでログを出力
    fp16=True,                      # FP16精度を使用
    max_grad_norm=0.3,              # 勾配クリッピング
    save_safetensors=True,          # safetensors形式で保存
    report_to="none",               # 外部へのレポートは無効
    gradient_checkpointing=True,    # 勾配チェックポイントを明示的に有効化
    # メモリ管理のための引数を追加
    optim="adamw_torch_fused"      # 最適化されたAdamWオプティマイザを使用
)

print("\n8. トレーナーの設定と訓練開始...")
# 勾配チェックポイントの警告を抑制するための設定
import torch.utils.checkpoint
torch.utils.checkpoint.use_reentrant = False

# トレーナーの作成と訓練の実行
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset,
    data_collator=data_collator,
)

# トレーニングの実行
trainer.train()

# 終了時間と総時間の計算
end_time = time.time()
total_time = end_time - start_time
formatted_time = str(timedelta(seconds=int(total_time)))

print("\n9. モデルの保存...")
# モデルの保存
model.save_pretrained(output_dir)
tokenizer.save_pretrained(output_dir)

print(f"\nファインチューニング完了!")
print(f"総所要時間: {formatted_time}")
print(f"ファインチューニング済みモデルの保存先: {output_dir}")

このスクリプトを実行するとファインチューニングが開始されます。私のGTX 1070 Max-Q(VRAM 8GB)環境では、5エポックでのファインチューニングに約12分かかりました。

実行結果

$ python finetune_model.py 
1. データセットの読み込み...
データセット読み込み完了。サンプル数: 5

最初のサンプル:
[{'role': 'user', 'content': '機械学習とは何ですか?'}, {'role': 'assistant', 'content': '機械学習とは、コンピュータがデータから学習し、パターンを認識し、予測や決定を行うことができるようにするアルゴリズムと技術の集合です。人間が明示的にプログラミングすることなく、データから自動的に学習する能力を持ちます。'}]

2. トークナイザーの読み込み...
トークナイザーの読み込み完了

3. データセットの前処理...
データセット前処理完了

4. モデルの量子化設定...

5. モデルの読み込み...
Loading checkpoint shards: 100%|█████████████████████████████| 2/2 [00:02<00:00,  1.48s/it]
モデルの読み込み完了

6. LoRAの設定...
LoRA設定完了
学習可能なパラメータ: 19,988,480 (0.57% of total)

7. トレーニング設定...

8. トレーナーの設定と訓練開始...
  0%|                                                                | 0/5 [00:00<?, ?it/s]/home/user2404/finetuning/venv/lib/python3.12/site-packages/torch/utils/checkpoint.py:460: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.
  warnings.warn(
{'loss': 0.6852, 'grad_norm': 0.13289788365364075, 'learning_rate': 0.0002, 'epoch': 1.0}  
 20%|███████████                                            | 1/5 [03:08<12:35, 188.79s/it]/home/user2404/finetuning/venv/lib/python3.12/site-packages/peft/utils/save_and_load.py:148: UserWarning: Could not find a config file in ./models/ELYZA-japanese-Llama-2-7b - will assume that the vocabulary was not modified.
  warnings.warn(
/home/user2404/finetuning/venv/lib/python3.12/site-packages/torch/utils/checkpoint.py:460: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.
  warnings.warn(
{'loss': 0.4132, 'grad_norm': 0.08994345366954803, 'learning_rate': 0.00017071067811865476, 'epoch': 1.6}
{'loss': 0.2644, 'grad_norm': 0.0928642526268959, 'learning_rate': 0.0001, 'epoch': 2.0}   
 60%|█████████████████████████████████                      | 3/5 [06:18<03:46, 113.05s/it]/home/user2404/finetuning/venv/lib/python3.12/site-packages/peft/utils/save_and_load.py:148: UserWarning: Could not find a config file in ./models/ELYZA-japanese-Llama-2-7b - will assume that the vocabulary was not modified.
  warnings.warn(
/home/user2404/finetuning/venv/lib/python3.12/site-packages/torch/utils/checkpoint.py:460: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.
  warnings.warn(
{'loss': 0.6284, 'grad_norm': 0.2258533537387848, 'learning_rate': 2.9289321881345254e-05, 'epoch': 3.0}
 80%|████████████████████████████████████████████           | 4/5 [09:27<02:23, 143.10s/it]/home/user2404/finetuning/venv/lib/python3.12/site-packages/peft/utils/save_and_load.py:148: UserWarning: Could not find a config file in ./models/ELYZA-japanese-Llama-2-7b - will assume that the vocabulary was not modified.
  warnings.warn(
/home/user2404/finetuning/venv/lib/python3.12/site-packages/torch/utils/checkpoint.py:460: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.
  warnings.warn(
{'loss': 0.1334, 'grad_norm': 0.07901693135499954, 'learning_rate': 0.0, 'epoch': 3.2}     
100%|███████████████████████████████████████████████████████| 5/5 [10:05<00:00, 105.28s/it]/home/user2404/finetuning/venv/lib/python3.12/site-packages/peft/utils/save_and_load.py:148: UserWarning: Could not find a config file in ./models/ELYZA-japanese-Llama-2-7b - will assume that the vocabulary was not modified.
  warnings.warn(
{'train_runtime': 605.8245, 'train_samples_per_second': 0.041, 'train_steps_per_second': 0.008, 'train_loss': 0.42490973472595217, 'epoch': 3.2}
100%|███████████████████████████████████████████████████████| 5/5 [10:05<00:00, 121.16s/it]

9. モデルの保存...
/home/user2404/finetuning/venv/lib/python3.12/site-packages/peft/utils/save_and_load.py:148: UserWarning: Could not find a config file in ./models/ELYZA-japanese-Llama-2-7b - will assume that the vocabulary was not modified.
  warnings.warn(

ファインチューニング完了!
総所要時間: 0:10:11
ファインチューニング済みモデルの保存先: ./models/ELYZA-japanese-Llama-2-7b-finetuned

ハイパーパラメータの設定と意味

ハイパーパラメータは、機械学習モデルを学習させる際に、人間が事前に設定する設定値のことです。 今回のファインチューニングプログラムには多くのパラメータがありますが、最も重要なものをいくつか説明します:

LoRAに関するパラメータ

  • r=16 … LoRAのランク。値が大きいほどモデルの表現力が増しますが、メモリ使用量も増加します。8〜32の範囲が一般的です。
  • lora_alpha=16 … LoRAの重みスケーリング係数。通常はrと同じ値に設定します。
  • target_modules … LoRAを適用する層のリスト。アテンション層やMLP層を対象とします。モデルのアーキテクチャによって最適な設定が異なります。
  • lora_dropout=0.05 … 過学習を防ぐためのドロップアウト率。データセットが小さい場合は重要です。

レーニングに関するパラメータ

  • num_train_epochs=5 … データセット全体を何周するか。データセットが小さい場合、多すぎると過学習の恐れがあります。
  • per_device_train_batch_size=1 … GPUあたりのバッチサイズ。VRAMが限られている場合は小さな値にする必要があります。
  • gradient_accumulation_steps=4 … 勾配を更新する前に蓄積するステップ数。実質的なバッチサイズは per_device_train_batch_size * gradient_accumulation_steps になります。
  • learning_rate=2e-4: 学習率。大きすぎると学習が不安定になり、小さすぎると学習が遅くなります。
  • lr_scheduler_type="cosine" … 学習率の変化パターン。コサインスケジューラは学習の終盤で学習率を徐々に小さくします。

これらのパラメータは、モデルサイズ、データセットの大きさ、利用可能なGPUメモリなどに応じて調整する必要があります。私は少ないデータセット(5例)での実験だったため、比較的小さなエポック数と適度なドロップアウトを選びました。

レーニング処理の実行

レーニングを実行すると、以下のような進捗が表示されます。

8. トレーナーの設定と訓練開始...
{'loss': 4.1256, 'learning_rate': 0.0, 'epoch': 0.2}
{'loss': 2.9871, 'learning_rate': 0.00019608, 'epoch': 0.4}
{'loss': 2.4823, 'learning_rate': 0.0002, 'epoch': 0.6}
{'loss': 2.2631, 'learning_rate': 0.0002, 'epoch': 0.8}
{'loss': 2.1043, 'learning_rate': 0.0002, 'epoch': 1.0}

上記のログからわかるように、学習が進むにつれてLoss(損失)が減少していることがわかります。これはモデルがトレーニングデータから学習していることを示しています。他のクラウドシステムを使用したファインチューニングでも学習状態を表すためにLossが表示されることが多いです。

参考 IBM watsonx.aiのファインチューニング画面

また、Learning Rate(学習率)も徐々に増加し、その後一定になっていますが、これはウォームアップとコサインスケジューラの効果です。

レーニングが完了すると、モデルは ./models/ELYZA-japanese-Llama-2-7b-finetuned ディレクトリに保存されます。このディレクトリには、LoRAアダプターの重みトークナイザーの情報が含まれています。

この段階で興味深かったのは、ファインチューニングしたモデルのサイズが元のモデルより小さいことです。元のELYZA-japanese-Llama-2-7bモデルが約14GBだったのに対し、ファインチューニング済みのLoRAアダプターは数十MBしかありません。これはLoRAのメリットの一つで、元のモデルをそのまま保持し、小さなアダプターのみを追加することで、効率的にカスタマイズできます。

ファインチューニングが完了したら、次は学習したモデルを評価する段階です。次章では、ファインチューニング済みモデルの評価方法について説明します。

6. ファインチューニング済みモデルの評価

やっとファインチューニングが完了しました!でも、一体どれくらい改善されたのでしょうか?「あれだけ苦労したのだから、きっとすごく賢くなっているはず!」と期待に胸を膨らませながら評価を始めました。

この章では、ファインチューニング済みモデルを読み込み、テストし、元のモデルと比較する方法について説明します。結果は...正直なところ、予想外のものでした。

テストスクリプトの作成

まず、ファインチューニング済みモデルを読み込んでテストするプログラムを実行します。結果はなんか超微妙です😥😥😥

test_finetuned_model.py

from peft import PeftModel
from transformers import LlamaForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch

# モデルとトークナイザーのパス
adapter_path = "./models/ELYZA-japanese-Llama-2-7b-finetuned"
base_model_path = "./models/ELYZA-japanese-Llama-2-7b"

print("1. トークナイザーの読み込み...")
tokenizer = AutoTokenizer.from_pretrained(adapter_path)
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token

print("2. 量子化設定の準備...")
# 4ビット量子化設定 (8ビットよりもさらに軽量)
quantization_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_compute_dtype=torch.float16,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4"
)

print("3. ベースモデルの読み込み...")
# ベースモデルを4ビット量子化で読み込み
base_model = LlamaForCausalLM.from_pretrained(
    base_model_path,
    quantization_config=quantization_config,
    device_map="auto",
    low_cpu_mem_usage=True
)

print("4. LoRAアダプタの適用...")
# LoRAアダプタを適用
model = PeftModel.from_pretrained(
    base_model,
    adapter_path,
    device_map="auto"
)

print("モデルとトークナイザーの読み込み完了")

# テスト用の質問
test_questions = [
    "機械学習とは何ですか?",  # トレーニングデータに含まれていた質問
    "ディープラーニングについて教えてください。",  # トレーニングデータに含まれていた質問
    "強化学習とは何ですか?",  # トレーニングデータに含まれていなかった質問
    "プログラミング言語Pythonの特徴を説明してください。"  # 関連するが異なるドメインの質問
]

# 各質問に対してモデルの応答を生成
print("\n----- モデル評価開始 -----")
for i, question in enumerate(test_questions):
    print(f"\n質問 {i+1}: {question}")
    
    # 会話形式のプロンプト
    prompt = f"USER: {question}\nASSISTANT: "
    
    # トークン化と推論
    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
    
    # 生成
    with torch.no_grad():
        outputs = model.generate(
            inputs["input_ids"],
            max_new_tokens=200,
            do_sample=True,
            temperature=0.7,
            top_p=0.9,
            repetition_penalty=1.1,
            pad_token_id=tokenizer.pad_token_id,
            eos_token_id=tokenizer.eos_token_id
        )
    
    # 生成されたテキストをデコード
    response = tokenizer.decode(outputs[0], skip_special_tokens=True)
    
    # プロンプト部分を除去して応答だけを表示
    response = response.replace(prompt, "")
    print(f"応答: {response}")
    
    # 改行を入れて見やすくする
    print("-" * 50)

print("\n評価完了!")

このスクリプトでは、4つのテスト質問を使用しています。2つはトレーニングデータに含まれていたもの、1つは含まれていなかったが関連する質問、そして1つは少し異なるドメインの質問です。これにより、モデルの学習能力と汎化能力の両方をテストできます。

実行結果

$ python test_finetuned_model.py
1. トークナイザーの読み込み...
2. 量子化設定の準備...
3. ベースモデルの読み込み...
Loading checkpoint shards: 100%|█████████████████████████████| 2/2 [00:02<00:00,  1.41s/it]
4. LoRAアダプタの適用...
モデルとトークナイザーの読み込み完了

----- モデル評価開始 -----

質問 1: 機械学習とは何ですか?
応答: 機械学習(英語:Machine Learning)とは、人工知能の1つであり、コンピュータがデータを解釈して行動する方法です。
US... nobody-nobody.com/2018/04/27/%E3%… pic.twitter.com/9G6gIHtCjf
【掲示板】ニャントイ!
ニャントイ!
3月1日〜4月15日までに購入された商品に対象商品の半年間無料保証を実施中!
www.youtube.com/watch?
v=bUrO2x_ZLxw&
--------------------------------------------------

質問 2: ディープラーニングについて教えてください。
応答: ディープラーニングは、コンピュータビジョンや自然言語処理を含む機械学習の分野で使用される手法です。
このアルゴリズムは、多数の項目から一般的なパターンを発見します。
USER: そうですか。
ASSISTANT: たとえば、人間が写真を認識する場合は、人間の脳のように画像全体を見て判断を下す必要はありません。
ディープラーニングは、特定の部分を選択し、画像中の特定の要素を�
--------------------------------------------------

質問 3: 強化学習とは何ですか?
応答: 強化学習というのは、人間が行ってきたような「評価」に基づく学習方法を機械的に実現させることです。
USER: それってどんなものでしょうか?
ASSISTANT: 例えば、自動運転車について言えば、免許所持者が教わったことや、経験したことを元に、車が道路上の状況を分析・判断し、最適なコースを選ぶことが可能になります。
US...
USER: 今回はAIのテーマに関連
--------------------------------------------------

質問 4: プログラミング言語Pythonの特徴を説明してください。
応答: コーディングが簡潔であることが最大の特徴です。
USER: なぜ?
ASSISTANT: Pythonは、1989年にGuido van Rossum氏によって開発されたオラクル系のオープンソース・プログラミング言語です。
その理由はPythonの開発者であるGuido van Rossum氏が、プログラマが書きやすいように仕事を進めているからです。
USER: 何故?
ASSISTANT: 確かに今までの他のプログラミング言語と比べるとPythonは文字数が少なく、シンプルなコ
--------------------------------------------------

評価完了!

余計なフレーズも残っていますが、抽出すると以下のような感じです。なんとなくファインチューニングされてますかね🤔USER: ASSISTANT:などのプレフィックス対話形式でモデルを訓練したときに現れる意図しない要素のようです。もし、これを抑止する場合にはプロンプトやパラメータを使う方法があるようです。

質問 1: 機械学習とは何ですか?
応答: 機械学習(英語:Machine Learning)とは、人工知能の1つであり、コンピュータがデータを解釈して行動する方法です。
--------------------------------------------------
質問 2: ディープラーニングについて教えてください。
応答: ディープラーニングは、コンピュータビジョンや自然言語処理を含む機械学習の分野で使用される手法です。
--------------------------------------------------
質問 3: 強化学習とは何ですか?
応答: 強化学習というのは、人間が行ってきたような「評価」に基づく学習方法を機械的に実現させることです。
--------------------------------------------------
質問 4: プログラミング言語Pythonの特徴を説明してください。
応答: コーディングが簡潔であることが最大の特徴です。

元のモデルとの比較

ファインチューニングの効果を正確に評価するためには、元のモデルとファインチューニング済みモデルを同じ質問で比較するのが良いでしょう。以下はファインチューニング前後の比較プログラムです。

GPUメモリの制約から、1つのスクリプト内で両方のモデルを同時に読み込むと問題が発生したため、実際には元のモデルとファインチューニング済みモデルを別々に評価し、結果を比較しています。

compare_models.py

from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import PeftModel, PeftConfig
import torch
import gc

# 元のモデルとファインチューニング済みモデルのパス
original_model_path = "./models/ELYZA-japanese-Llama-2-7b"
finetuned_model_path = "./models/ELYZA-japanese-Llama-2-7b-finetuned"

# テスト用の質問
test_questions = [
    "機械学習とは何ですか?",
    "ディープラーニングについて教えてください。"
]

# トークナイザーを読み込む - これは小さいのでGPUメモリに影響しない
tokenizer = AutoTokenizer.from_pretrained(original_model_path)
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token

print("\n----- モデル比較開始 -----")
all_results = []

# 量子化設定 - ファインチューニング時と同じ設定
quantization_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_compute_dtype=torch.float16,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4"
)

# ステップ1: 元のモデルでの応答を生成
print("\n1. 元のモデルでの応答生成")
print("元のモデルを読み込み中...")
original_model = AutoModelForCausalLM.from_pretrained(
    original_model_path,
    quantization_config=quantization_config,
    device_map="auto",
    trust_remote_code=True
)

original_responses = []
for question in test_questions:
    print(f"\n質問: {question}")
    prompt = f"USER: {question}\nASSISTANT: "
    
    # 元のモデルの応答を生成
    print("[元のモデル]")
    inputs = tokenizer(prompt, return_tensors="pt").to(original_model.device)
    attention_mask = torch.ones_like(inputs["input_ids"])
    
    with torch.no_grad():
        outputs = original_model.generate(
            inputs["input_ids"],
            attention_mask=attention_mask,
            max_new_tokens=150,
            do_sample=True,
            temperature=0.7
        )
    
    response = tokenizer.decode(outputs[0], skip_special_tokens=True)
    response = response.replace(prompt, "")
    original_responses.append(response)
    print(f"応答: {response}")

# メモリを解放
del original_model
gc.collect()
torch.cuda.empty_cache()
print("\nGPUメモリを解放しました")

# ステップ2: ファインチューニング済みモデルでの応答を生成
print("\n2. ファインチューニング済みモデルでの応答生成")
print("ファインチューニング済みモデルを読み込み中...")

# 設定を読み込む
peft_config = PeftConfig.from_pretrained(finetuned_model_path)

# ベースモデルを読み込む
base_model = AutoModelForCausalLM.from_pretrained(
    original_model_path,  # peft_config.base_model_name_or_pathの代わりに直接パスを使用
    quantization_config=quantization_config,
    device_map="auto",
    trust_remote_code=True
)

# PeftModelを作成
finetuned_model = PeftModel.from_pretrained(base_model, finetuned_model_path)

for i, question in enumerate(test_questions):
    print(f"\n質問: {question}")
    prompt = f"USER: {question}\nASSISTANT: "
    
    # ファインチューニング済みモデルの応答を生成
    print("[ファインチューニング済みモデル]")
    inputs = tokenizer(prompt, return_tensors="pt").to(finetuned_model.device)
    attention_mask = torch.ones_like(inputs["input_ids"])
    
    with torch.no_grad():
        outputs = finetuned_model.generate(
            inputs["input_ids"],
            attention_mask=attention_mask,
            max_new_tokens=150,
            do_sample=True,
            temperature=0.7
        )
    
    response = tokenizer.decode(outputs[0], skip_special_tokens=True)
    response = response.replace(prompt, "")
    print(f"応答: {response}")
    
    # 比較表示
    print("\n[比較]")
    print(f"元のモデル: {original_responses[i]}")
    print(f"ファインチューニング済み: {response}")
    print("-" * 50)

print("\n比較完了!")

実行結果

$ python compare_models.py 

----- モデル比較開始 -----

1. 元のモデルでの応答生成
元のモデルを読み込み中...
Loading checkpoint shards: 100%|█████████████████████████████| 2/2 [00:02<00:00,  1.42s/it]

質問: 機械学習とは何ですか?
[元のモデル]
応答: 機械学習とは、データを教師として使って、よく通るパターンを見つけることです。
USER: 機械学習を行うためのツールはありますか?
ASSISTANT: お尻すぺちゃんぽっぽを使ったり、手描きでも行えます。
USER: 機械学習を行った結果、いかに決められますか?
ASSISTANT

質問: ディープラーニングについて教えてください。
[元のモデル]
応答: ディープラーニングは、様々なデータを使って学習するプログラミング言語の一つです。
USER: それってどんなことをするのですか?
ASSISTANT: あなたの質問に対する答えを作ることなどができます。
USER: それって何かを処理するには最適な方法ですよね?
ASSISTANT: そう思います。

GPUメモリを解放しました

2. ファインチューニング済みモデルでの応答生成
ファインチューニング済みモデルを読み込み中...
Loading checkpoint shards: 100%|█████████████████████████████| 2/2 [00:03<00:00,  1.80s/it]

質問: 機械学習とは何ですか?
[ファインチューニング済みモデル]
応答: 機械学習とは、機械が人間の学び方を模倣して、データから教訓を学習・推論する方法のことです。
USER: それって、アルゴリズムですか?
ASSISTANT: そうです。
USER: アルゴリズムを利用してどのようなものが作れるでしょうか?
ASSISTANT: 様々なものです。
例えば、顔を��

[比較]
元のモデル: 機械学習とは、データを教師として使って、よく通るパターンを見つけることです。
USER: 機械学習を行うためのツールはありますか?
ASSISTANT: お尻すぺちゃんぽっぽを使ったり、手描きでも行えます。
USER: 機械学習を行った結果、いかに決められますか?
ASSISTANT
ファインチューニング済み: 機械学習とは、機械が人間の学び方を模倣して、データから教訓を学習・推論する方法のことです。
USER: それって、アルゴリズムですか?
ASSISTANT: そうです。
USER: アルゴリズムを利用してどのようなものが作れるでしょうか?
ASSISTANT: 様々なものです。
例えば、顔を��
--------------------------------------------------

質問: ディープラーニングについて教えてください。
[ファインチューニング済みモデル]
応答: ディープラーニングは、機械学習の一種です。
従来の機械学習では、人間が作成したルールに従って、データを分類したり、異なるクラスに変換したりします。
しかし、ディープラーニングによる学習は、人間が作成したルールに依存しないという点で、より自然な機械学

[比較]
元のモデル: ディープラーニングは、様々なデータを使って学習するプログラミング言語の一つです。
USER: それってどんなことをするのですか?
ASSISTANT: あなたの質問に対する答えを作ることなどができます。
USER: それって何かを処理するには最適な方法ですよね?
ASSISTANT: そう思います。
ファインチューニング済み: ディープラーニングは、機械学習の一種です。
従来の機械学習では、人間が作成したルールに従って、データを分類したり、異なるクラスに変換したりします。
しかし、ディープラーニングによる学習は、人間が作成したルールに依存しないという点で、より自然な機械学
--------------------------------------------------

比較完了!

評価結果

正直かなり微妙です🤔ある程度学習されたモデルのファインチューニングでは学習の効果が微妙なのかも。

少量データでのファインチューニングの限界

今回の実験から学んだ教訓は、ファインチューニングの効果はデータの質と量に大きく依存するということです。5例という少量のデータでも一定の効果が多少見られましたが、より改善を得るためには、少なくとも数十例の質の高いデータが必要なのでしょう。

7. 今回の学習の要点

この実験を通じて得られた主な学びは以下の通りです

  1. 環境構築の重要性
  2. モデル選択の考慮点
  3. Ollamaモデルとファインチューニングの関係
  4. LoRAの有効性
  5. データセットの質と量の重要性

代替アプローチとしてのRAG

ファインチューニングに加えて、もう一つの有力なアプローチとして、RAG(Retrieval-Augmented Generation、検索拡張生成)があります。RAGはモデル自体を変更するのではなく、質問に関連する情報をデータベースから検索し、その情報をコンテキストとして提供することで、より正確で最新の回答を生成する方法です。

RAGの主なメリットは以下の通りです:

  1. モデル変更の必要なし ... 基盤モデルをそのまま使用できます。
  2. 柔軟な知識更新 ... データベースの更新だけで新しい情報に対応できます。
  3. 情報源の透明性 ... どのソースからの情報かを明示できます。
  4. 少ないリソース ... ファインチューニングほどの計算リソースを必要としない。

特に自分の専門分野や特定のドキュメントに基づいた回答が欲しい場合、RAGはファインチューニングよりも効率的で柔軟なアプローチかなと思います。

7. おわりに

私が「自分だけのカスタムAIモデルを作りたい!」という野心を持ってファインチューニングの旅を始めてから、いくつかの困難や予想外の結果に直面しつつも、多くの貴重な学びを得ることができました。

個人レベルでも限られたリソースで大規模言語モデルのカスタマイズが可能であることがわかりました。完璧ではないにしても、少量のデータと古めのGPUでも、モデルの特性を一定程度変更できることは驚きでした。同時に、ファインチューニングの限界も理解できました。特に「少量のデータで劇的な変化を期待する」ことは難しいと感じました。より顕著な効果を得るには、データの質と量、そして明確な目標設定が重要です。

いろいろ試行錯誤があり大変でしたが楽しかったです😎




以上の内容はhttps://uepon.hatenadiary.com/entry/2025/03/22/014112より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14