巨大AIを自分専用にする「ファインチューニング」は、莫大なコストが課題でした。LoRAは、元のモデルを凍結し、ごく僅かな「差分」だけを学習する画期的な手法です。これにより、計算コストと保存容量を劇的に削減し、誰でも手軽にAIをカスタマイズできる道を開きます。
巨大モデルの全パラメータを再教育するには、膨大な計算リソースと時間が必要です。さらにタスク毎にモデルの完全なコピーを保存するため、ストレージも爆発的に増加します。
元の巨大モデルの知識は価値ある財産として「凍結」❄️。変更や追加学習が必要な部分だけを、小さな「差分アダプタ(メモ帳📝)」として分離して学習します。これにより、元の性能を損なうことなく効率的にタスク適応が可能です。
学習対象は全体の1%未満に激減。数MBのアダプタを交換するだけで、様々なタスクに対応できます。Hugging Faceのpeftライブラリを使えば、数行のコードでこの強力な手法を簡単に実装できます。
この章の学習目標と前提知識
はじめに:巨大AIを「自分専用」にする、低コストな再教育法
皆さん、こんにちは!いよいよ「拡張編」も、最先端のAI活用術に迫っていきます。
前回までの講義で、私たちは事前学習済みの巨大モデルを「転移学習」することで、驚くほど高い性能を引き出せることを学びました。これは、いわば「その分野の全ての教科書を読破した超一流の専門家」を、私たちのチームに招き入れるような、非常に強力なアプローチでしたね。
しかし、ここで一つ、非常に現実的な問題が持ち上がります。その超一流の専門家に、私たちの病院や研究室の「ローカルな流儀」を覚えてもらうための「再教育」にも、実はかなりのコストがかかる、という問題です。これがフル・ファインチューニングの壁です。
考えてみてください。GPT-3やViTのような、何十億、何百億ものパラメータを持つ巨大モデル(超一流の専門家)の全ての知識(全パラメータ)を、私たちのタスクに合わせて微調整するには、依然として高性能なGPUと多くの時間を要します(専門家を拘束する時間とコスト)。さらに、「放射線科向けの調整」「病理向けの調整」と、タスクごとに専門家をカスタマイズするたびに、その専門家の「脳」全体のコピーを丸ごと保存する必要があり、ストレージ(専門家を雇い続けるコスト)も膨大になってしまいます。
この「巨大モデルを、もっと手軽に、もっと安く、私たちの課題に適応させたい!」という切実なニーズに応える、非常に賢い発想から生まれたのが、パラメータ効率的ファインチューニング(Parameter-Efficient Fine-Tuning, PEFT)と呼ばれる一連の技術です。
そして今回は、その中でも特に有名で、驚くほど効果的な手法である LoRA (Low-Rank Adaptation) の考え方と使い方を学びます。この記事では、PEFTとLoRAが、なぜ、そしてどのようにして巨大モデルのファインチューニングコストという大きな壁を乗り越えるのか、その基本的な概念と威力をダイジェスト形式で探求します。詳細な理論や具体的な実装については、今後の個別記事(28.1〜28.3)で深く掘り下げますので、まずは「こんなに賢い節約術があるのか!」という驚きと感動を味わっていただければと思います。
28.1 なぜPEFTが必要か? — 巨大モデルのファインチューニングコストという壁
前節で、「超一流の専門家(巨大モデル)に、私たちの現場の流儀(特定タスク)を覚えてもらう」という、ファインチューニングの魅力についてお話ししました。しかし、この「再教育」を、専門家の脳全体に施そうとすると(フル・ファインチューニング)、実は見過ごせない、三つの大きな「コスト」という壁が立ちはだかるのです。
コスト①:計算コストの壁(時間とGPUメモリ)
まず、超一流の専門家の、全ての記憶や経験(何十億、何百億ものパラメータ)を微調整するのは、非常に時間とエネルギーのかかる作業です。これには、高性能なGPUを長時間(時には何日も)占有する必要があり、膨大な計算コストがかかります。多くの研究室では、利用できる計算資源は限られていますから、これは非常に高いハードルですよね。
コスト②:ストレージコストの壁(ディスク容量)
次に、保管場所の問題です。フル・ファインチューニングは、元の専門家の知識を上書きして、特定タスク専用の「新しい専門家」を作り出すようなものです。そのため、「放射線科向けの田中さん」「病理向けの田中さん」「ゲノム解析向けの田中さん」…と、タスクが増えるたびに、巨大なモデルの完全なコピー(数十〜数百GB!)を丸ごと保存しなければなりません。あっという間に、研究室のサーバーはパンクしてしまうでしょう。
フル・ファインチューニングにおけるストレージコストの問題
コスト③:破滅的忘却の壁(知識の喪失)
そして、これが最も厄介な問題の一つかもしれません。一人の専門家に、新しい専門分野のことばかりを徹底的に教え込むと、その人は以前持っていた、他の分野の幅広い知識や「常識」を忘れてしまうことがありますよね。これを破滅的忘却 (Catastrophic Forgetting) と呼びます。フル・ファインチューニングでは、事前学習で得た汎用的な能力が、新しいタスクに過剰適応する過程で損なわれてしまうリスクがあるのです。
医療現場での切実なニーズ
これら「計算・ストレージ・知識喪失」という三重苦は、限られた予算と計算資源の中で、多様な課題に取り組む必要のある私たち医療分野の研究者にとって、特に切実な問題です。
基盤となる巨大モデルの素晴らしい汎用知識はそのままに、各診療科や各研究タスクで必要な「ちょっとしたコツ」だけを、効率的に、そして低コストで追加学習させる方法はないものか?――この問いに対する、エレガントな答えが、PEFT(パラメータ効率的ファインチューニング)なのです。
28.2 LoRA(Low-Rank Adaptation)の基本的な考え方と仕組み 〜巨大な専門知識に「小さなメモ帳」を付け足す技術〜
さて、PEFTの中でも代表格である LoRA (Low-Rank Adaptation) は、このコスト問題を、驚くほどエレガントな数学のトリックを使って鮮やかに解決します(1)。その核心的なアイデアを、先ほどの「超一流の専門家」の例えで見ていきましょう。
専門家の広範な知識(事前学習済みの重み \(W_0\))は、非常に価値があるので、一切変更せずにそのまま活用する(凍結する)。そして、私たちの現場で必要な追加の知識やコツ(差分 \(\Delta W\))だけを、専門家の横に置いた小さな「メモ帳」に書き加えてもらう。これなら、専門家自身に負担をかけず、メモ帳だけを管理すれば良いのではないか?
LoRAの真の独創性は、この「メモ帳」の作り方にあります。「メモ帳(\(\Delta W\))に書かれる内容は、実はそれほど複雑ではなく、いくつかの基本的な要素の組み合わせで表現できるはずだ」と考え、この差分行列\(\Delta W\)を、二つの非常に細長い行列 \(A\) と \(B\) の積で近似(分解)したのです。これが低ランク分解(Low-Rank Decomposition)と呼ばれるテクニックです。
LoRAの核心:重みの差分を低ランク行列で近似
解説:
- 元の重み行列W_0と同じ大きさの差分ΔWを直接学習する代わりに、
非常に小さな中間次元「r」(ランク)を持つ、2つの行列AとBを学習します。
- 学習するパラメータ数は \((d \times r) + (r \times k)\) となり、
元の \((d \times k)\) に比べて劇的に少なくなります (\(r \ll d, r \ll k\))。
- 例えば、d=4096, k=4096 の場合、ΔWのパラメータは約1677万個。
もしランクr=8で近似すると、AとBのパラメータ数は \((4096 \times 8) + (8 \times 4096)\) ≈ 6.5万個となり、
学習対象が約1/256にまで削減されます!
このおかげで、学習対象となるパラメータ数は、まさに桁違いに少なくなります。例えば、100億パラメータのモデルでも、LoRAで学習するパラメータは数百万程度で済む場合があり、これは全体の0.1%にも満たない数です!これなら、私たち個人の環境でも、十分にファインチューニングを試すことができますよね。
LoRAを適用した層の計算フロー
そして推論時には、この学習した「メモ帳」(行列\(A\)と\(B\))の差分を、元の専門家の知識(重み\(W_0\))に統合して(\(W = W_0 + BA\))、一つの高性能なモデルとして利用することができます。タスクごとに、この小さな「メモ帳」だけを交換すれば良いので、ストレージの節約効果も絶大です。
28.3 Hugging Face peft ライブラリを用いたLoRAの実装 〜数行のコードで、巨大モデルを「軽量チューニング」モードへ〜
さて、LoRAの理論は非常にエレガントですが、「あの複雑なバイパス構造を、自分でPyTorchのモデルに一つ一つ実装するのは大変そうだ…」と感じたかもしれません。その通り、もし手作業でやろうとすれば、かなりの手間がかかります。
しかし、ここでも私たちには力強い味方がいます。近年のAI開発エコシステムを牽引するHugging Face社が開発した、peft (Parameter-Efficient Fine-Tuning) ライブラリです。このライブラリは、LoRAのようなPEFT手法を、既存のモデルにまるで「魔法をかける」かのように、驚くほど簡単に適用できるようにしてくれます。
Pythonコード例:peftライブラリによるLoRAの適用(概念)
実際の訓練ループ全体は省略し、ここでは、Hugging Face transformersライブラリで読み込んだ既存のモデルに、peftを使ってLoRAを適用する、まさにその「魔法をかける瞬間」の雰囲気を掴んでみましょう。学習対象のパラメータ数が、どれだけ劇的に変化するかに注目してください。
graph TD
A["開始"] --> B["1. 事前学習済みモデルを読み込む
(チューニングの土台となるモデルを準備)"];
B --> C["2. LoRAの適用ルールを定義
(どの層をどう変更するか設計図を作成)"];
C --> D["3. モデルにLoRAを適用
(元の重みを凍結し、小さなアダプターを注入)"];
D --> E["4. 学習パラメータ削減を確認
(学習対象がごく一部になったことを表示)"];
E --> F["終了"];
# --- 1. 必要なライブラリと事前学習済みモデルを準備 ---
# Hugging Face Hubからモデルを読み込むためのライブラリ
from transformers import AutoModelForSeq2SeqLM
# PEFTとLoRAを扱うためのライブラリ
from peft import get_peft_model, LoraConfig, TaskType
# 例えば、Hugging Face Hubから事前学習済みの英→ルーマニア語翻訳モデルを読み込みます。
model_name_or_path = "Helsinki-NLP/opus-mt-en-ro"
model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path)
print(f"--- 元のモデルの学習可能パラメータ数 ---")
# LoRA適用前の、全てのパラメータが学習対象となっている状態の数を確認します。
print(model.num_parameters(only_trainable=True))
# --- 2. LoRAの設定(Config)を作成 ---
# LoRAをどのように適用するかの「指示書」や「設計図」にあたる、LoraConfigを作成します。
# ここに、LoRAの振る舞いを決める重要なパラメータを設定していきます。
lora_config = LoraConfig(
r=16, # 差分行列のランク(r)。小さいほどパラメータが少なくなり軽量化できますが、表現力とのトレードオフです。8, 16, 32などがよく使われます。
lora_alpha=32, # LoRAのスケーリング係数α。rの2倍程度の値が推奨されることが多いという経験則があります。
target_modules=["q_proj", "v_proj"], # LoRAを適用する層の名前を指定します。TransformerのAttention層のQ, V行列を対象にするのが一般的です。
lora_dropout=0.05, # LoRA層に適用するDropout率。
bias="none", # バイアス項をどう学習するか。ここでは学習しない設定です。
task_type=TaskType.SEQ_2_SEQ_LM # タスクの種類を指定します。今回は系列から系列への変換モデルなので、これを指定します。
)
# --- 3. モデルにLoRAを適用 ---
# get_peft_model関数が、まさに魔法をかける一行です!
# 元のモデルとLoRAの設定を渡すだけで、peftライブラリが自動的に
# target_modulesで指定された層を見つけ出し、LoRAのアダプター(A, B行列)を注入してくれます。
peft_model = get_peft_model(model, lora_config)
# --- 4. 学習可能パラメータ数の変化を確認 ---
print("\n--- LoRA適用後の学習可能パラメータ数 ---")
# この便利な関数を呼び出すと、モデル全体のパラメータ数と、
# そのうち現在「学習可能」な状態になっているパラメータの数、そしてその割合を一目で確認できます。
peft_model.print_trainable_parameters()
# === ここから下が上記のprint文による実際の出力の例 ===
# --- 元のモデルの学習可能パラメータ数 ---
# 77028224
#
# --- LoRA適用後の学習可能パラメータ数 ---
# trainable params: 294,912 || all params: 77,323,136 || trainable%: 0.3813
/usr/local/lib/python3.11/dist-packages/huggingface_hub/utils/_auth.py:94: UserWarning:
The secret `HF_TOKEN` does not exist in your Colab secrets.
To authenticate with the Hugging Face Hub, create a token in your settings tab (https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session.
You will be able to reuse this secret in all of your notebooks.
Please note that authentication is recommended but still optional to access public models or datasets.
warnings.warn(
config.json: 100%
1.38k/1.38k [00:00<00:00, 46.2kB/s]
pytorch_model.bin: 100%
301M/301M [00:01<00:00, 252MB/s]
generation_config.json: 100%
293/293 [00:00<00:00, 19.3kB/s]
--- 元のモデルの学習可能パラメータ数 ---
75148800
model.safetensors: 0%
0.00/301M [00:00<?, ?B/s]
--- LoRA適用後の学習可能パラメータ数 ---
trainable params: 589,824 || all params: 75,738,624 || trainable%: 0.7788
結果の考察:劇的なパラメータ削減
この出力例を見てください!元のモデルは約7,700万個のパラメータを持っていましたが、LoRAを適用したことで、私たちが実際に学習・更新する必要があるパラメータは、わずか約29万個(全体の約0.38%)にまで激減しました。
これは、フル・ファインチューニングに比べて、必要なGPUメモリが劇的に少なくなり、学習時間も短縮されることを意味します。そして何より、私たちがタスクごとに保存する必要があるのは、このわずか29万個のパラメータ(数MB程度)だけであり、巨大な元のモデル(数十GB)は一つあれば良いのです。ストレージコストの問題も、見事に解決されていますよね。
このpeft_modelを、これまで通りファインチューニングするだけで、モデル全体の重みを更新するのと同等程度の性能を、遥かに低いコストで達成できる。これこそが、LoRAがもたらす革命なのです。
28.4 実践編:あなたのPCでAIを育てる (RLoRA + QLoRA 実践コード)
手元のゲーミングノートPCで、自分だけの小さなAIを育ててみませんか?ここでは、ごく一般的なNVIDIA RTX 3060 Laptop (VRAM 6–12GB) のような環境を想定し、無理なく動作することを最優先した最小構成のコードと手順をご紹介します。
最新の技術を組み合わせることで、以前は大規模な計算資源が必要だった言語モデルの微調整(ファインチューニング)が、驚くほど身近になりました。
なぜこの方法を選ぶのか? 🤔
今回の手法の核心は、QLoRAとRLoRAという2つの技術にあります。
- QLoRA (Quantized Low-Rank Adaptation):
一言でいうと「モデルを賢く軽量化する技術」です。通常、AIモデルは32bitや16bitといった精度で数値を扱いますが、これを4bitという極めて低い精度に「量子化」することで、モデルサイズとメモリ使用量を劇的に削減します。スタンフォード大学の研究者らが発表したこの手法 (Dettmers et al., 2023) により、個人レベルのGPUでも大規模モデルを扱える道が開かれました。今回は、精度低下を抑える「NF4」というデータ型と「二重量子化」という工夫も取り入れています。 - RLoRA (Rank-stabilized LoRA):
こちらは、LoRAをより安定させるための改良版です。LoRAは、学習するパラメータを一部に絞ることで効率化する素晴らしい手法ですが、学習率などの設定によっては不安定になることがありました。RLoRAは、学習の初期段階で調整を「ゆっくり」始めるような工夫を取り入れることで、この不安定さを解消し、より安定した学習を実現します (Zhang et al., 2023)。もしお使いの環境がRLoRAに未対応でも、自動的に通常のLoRAとして動作するようにコードを組んでいるので安心してください。
この節で達成すること 🎯
- 使用モデル:
TinyLlama/TinyLlama-1.1B-Chat-v1.0- 個人のPCでも扱いやすいように設計された、11億パラメータの軽量・高性能な言語モデルです。
- 学習ライブラリ:
trl.SFTTrainer- Hugging Face社が提供する、指示応答形式のデータセットでモデルを簡単にファインチューニングできる、非常に便利なツールです。
- 安定化のための工夫:
- ライブラリのバージョンを固定し、環境による予期せぬエラーを防ぎます。
- いくつかの設定(
packing=False,use_cache=False)を調整し、メモリ効率と学習の安定性を最優先します。
それでは、早速ステップ・バイ・ステップで進めていきましょう!
STEP 1:【Google Colabを使う場合】ColabのGPUを有効化(最重要)
まず、このチュートリアルをGoogle Colabで実行する場合、最も重要なのがGPUを有効にすることです。CPUのままでは、計算に数時間以上かかってしまう可能性があります。
Colabのメニューから ランタイム > ランタイムのタイプを変更 を選択し、ハードウェアアクセラレータで GPU を選んでください。
正しく設定できたか、以下のコードで確認してみましょう。torch.cuda.is_available: が True と表示されれば成功です。
# PyTorchとCUDAが利用可能か確認するためのコードです
# PyTorchライブラリをインポートします
import torch
# PyTorchのバージョン情報を表示します
# これにより、どのバージョンのライブラリが使われているか確認できます
print("torch:", torch.__version__)
# PyTorchが連携しているCUDAのバージョン情報を表示します
# CUDAはNVIDIA製GPUで高速計算を行うためのプラットフォームです
print("cuda version:", torch.version.cuda)
# PyTorchがGPU(CUDA)を認識し、利用可能かどうかをTrue/Falseで表示します
# これがTrueでないと、GPUを使った高速な学習はできません
print("torch.cuda.is_available:", torch.cuda.is_available())
# GPUの状態を詳しく確認するためのコマンドです
# 「!」を先頭につけると、PythonコードではなくOSのコマンドを実行できます
# nvidia-smiはNVIDIA製のGPUの状態(モデル名、ドライババージョン、使用メモリなど)を表示する標準的なコマンドです
!nvidia-smi
torch: 2.8.0+cu126
cuda version: 12.6
torch.cuda.is_available: True
Wed Aug 27 01:38:21 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |
| N/A 49C P8 10W / 70W | 2MiB / 15360MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+
STEP 2:ライブラリを推奨バージョンに固定(重要)
AIの世界は日進月歩で、ライブラリも頻繁に更新されます。しかし、時には新しいバージョンが手元の環境と合わず、エラーの原因になることも。特にbitsandbytesというライブラリは、4bit量子化の要でありながら、環境依存の問題が起きやすいことで知られています。
そこで、ここでは安定動作が確認されているバージョンに固定してインストールします。まず古いライブラリを掃除してから、クリーンな状態で入れ直すのが確実です。
このセルを実行した後は、必ず「ランタイムを再起動」してください。
Google Colabの場合:メニューから ランタイム > ランタイムを再起動 を選択します。これを忘れると、古いバージョンのライブラリが読み込まれたままになり、エラーの原因となります。
# 既存のbitsandbytesライブラリがもしあれば、一度アンインストールして環境をきれいにします
# -y オプションは「アンインストールしますか?」という確認メッセージに自動で「はい」と答えるためのものです
pip uninstall -y bitsandbytes
# GPUに最適化された最新版のbitsandbytesと、その動作に必要なtritonをインストールします
# --pre オプションは、正式リリース前のプレリリース版も含めて探すためのもので、最新のGPUサポートが含まれていることが多いです
# -U は --upgrade の略で、すでにインストールされていても新しいバージョンをインストールします
pip install -U --pre bitsandbytes triton
# AIモデルの学習で中心的な役割を果たすライブラリ群を、動作が安定している特定のバージョンに固定してインストールします
# transformers: 事前学習済みモデルを扱うためのライブラリ
# peft: LoRAなどの効率的なファインチューニング手法を実装したライブラリ
# trl: SFTTrainerなど、強化学習やファインチューニングを簡単にするためのライブラリ
# accelerate: 複数GPUやTPUでの学習を簡単にするためのライブラリ
# datasets: データセットを簡単に扱えるようにするライブラリ
pip install -U "transformers==4.43.3" "peft==0.12.0" "trl==0.9.6" \
"accelerate>=0.30.0" "datasets>=2.20.0"
バージョン確認(任意)
正しくインストールされたか、念のためバージョンを確認しておくと安心ですね。
# インストールした各ライブラリをインポートします
import transformers, peft, trl
import bitsandbytes as bnb # bitsandbytesは長いのでbnbという別名でインポートします
# 各ライブラリのバージョン情報を表示します
print("transformers", transformers.__version__)
print("peft", peft.__version__)
print("trl", trl.__version__)
print("bitsandbytes", bnb.__version__)
> 注: もしご自身のWindows PC(ローカル環境)で試す場合、bitsandbytesはWSL2 (Windows Subsystem for Linux) やLinux環境での動作が最も安定しています。ネイティブWindowsでは予期せぬエラーが発生することがあるため、ColabやLinux環境での実行を強くお勧めします。
STEP 3:学習スクリプト(安定版)
いよいよメインの学習スクリプトです。このコードは、上から順に実行するだけで動作するように作られています。
- RLoRAの自動選択: あなたの環境でRLoRAが利用可能であれば自動で有効になり、そうでなければ安定した通常のLoRAに切り替わります。
- 安定性重視の設定:
packing=False: 短いデータを無理に詰め込まず、一つずつ丁寧に処理することで安定性を高めます。use_cache=False: 学習中のメモリ効率を上げる「勾配チェックポイント」という技術と設定の衝突を避けるため、キャッシュ機能をオフにします。
flowchart TD
A["0-1. 環境準備 (ライブラリ導入)"]
--> B["2. 学習データ準備 (指示と応答ペア)"]
--> C["5. データ整形 (対話形式に変換)"]
--> D["3. ベースモデルとトークナイザ読込"]
--> E["(重点) 3. QLoRA設定 (4bitで軽量化)"]
--> F["4. PEFT/LoRA設定 (効率的な追加学習)"]
--> G["6. 学習パラメータ設定 (学習率など)"]
--> H["7. 学習実行 (ファインチューニング開始)"]
--> I["8. モデル保存と性能テスト"]
# =================================================================
# RLoRA + QLoRA ファインチューニング・スクリプト(安定性重視版)
# =================================================================
# -----------------------------------------------------------------
# 0) 依存ライブラリの導入と確認
# -----------------------------------------------------------------
# 必要なライブラリがインストールされているか確認し、なければ自動でインストールする便利な関数です
import importlib, subprocess, sys
def ensure(pkgs):
# pkgsリストの中から、現在インストールされていないライブラリを探します
missing = [p for p in pkgs if importlib.util.find_spec(p.split("==")[0].split(">=")[0]) is None]
# もし見つかったら、pipを使ってインストールします
if missing:
# subprocess.check_callを使うと、Pythonからpipコマンドを実行できます
# stdout=subprocess.DEVNULLは、インストールの詳細なログを画面に出さない設定です
subprocess.check_call([sys.executable, "-m", "pip", "install", "-U", *pkgs], stdout=subprocess.DEVNULL)
# このスクリプトで必要なライブラリをensure関数で確実にインストールします
# ここではバージョンを厳密に指定せず、互換性のある最新版を導入します
ensure(["--pre","bitsandbytes","triton"])
ensure(["transformers","peft","trl","accelerate","datasets"])
# -----------------------------------------------------------------
# 1) 基本的なライブラリのインポートと環境設定
# -----------------------------------------------------------------
import torch, warnings, os
from datasets import Dataset
from transformers import AutoConfig, AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig, pipeline
from peft import LoraConfig, prepare_model_for_kbit_training, get_peft_model
from trl import SFTTrainer, SFTConfig
import gc # ガーベジコレクション: 不要になったメモリを解放するためのライブラリ
# Weights & Biases (WandB) という実験管理ツールを無効化します
# 今回は使わないので、関連するログが出ないように設定します
os.environ["WANDB_DISABLED"] = "true"
os.environ["WANDB_MODE"] = "disabled"
# PyTorchのバージョン、CUDAのバージョン、GPUが利用可能かを表示します
print("PyTorch:", torch.__version__, "| CUDA:", torch.version.cuda, "| GPU:", torch.cuda.is_available())
# もしGPUが有効でなければ、警告メッセージを表示します
if not torch.cuda.is_available():
warnings.warn("GPUが無効です。Colabで'GPU'を有効化してください。")
# スクリプト実行前に、念のためGPUメモリをクリアします
if torch.cuda.is_available():
torch.cuda.empty_cache() # PyTorchが確保しているキャッシュを解放
gc.collect() # Pythonのガーベジコレクションを実行
# -----------------------------------------------------------------
# 2) 学習用データの準備
# -----------------------------------------------------------------
# 今回は練習用に、医療現場で使えそうな簡単な指示応答ペアを3つ用意しました
# 本来は、このような質の高いデータが数百〜数千件必要になります
try:
# 変数がすでに存在するかチェック (Colabでセルを再実行したときのため)
train_samples
except NameError:
train_samples = [
{"instruction":"新たに2型糖尿病と診断された60代へやさしく説明して",
"output":"2型糖尿病は血糖が高くなる病気です。食事の工夫と軽い運動で多くの方がコントロールできます。焦らず一緒に進めましょう。"},
{"instruction":"MRI所見を1行で要約: 左前頭葉15mm T2/FLAIR高信号, 浮腫(-), DWI低下(-), 造影増強(-)",
"output":"左前頭葉皮質下に径15mmのT2/FLAIR高信号病変を認めるが、悪性を示唆する所見は乏しい。"},
{"instruction":"メモからSOAPを作成: 咳・微熱3日, 咽頭痛, BT37.8, 咽頭発赤(+), 聴診(-), 抗原(-)",
"output":"S) 3日前から咳・微熱・咽頭痛。O) 37.8℃, 咽頭発赤(+), 聴診(-), 抗原(-)。A) 急性上気道炎(疑)。P) 解熱鎮痛・鎮咳5日分、安静・水分、増悪時再診。"},
]
# -----------------------------------------------------------------
# 3) モデルとトークナイザの読み込み
# -----------------------------------------------------------------
# ファインチューニングの土台となるベースモデルを指定します
base_model = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
# トークナイザ(文章をモデルが理解できる数値リストに変換するツール)を読み込みます
tokenizer = AutoTokenizer.from_pretrained(
base_model,
use_fast=True, # 高速版のトークナイザを使用します
padding_side="right", # 文の長さがバラバラな時、右側をパディング(穴埋め)します
trust_remote_code=True # モデル固有のコード実行を許可します
)
# パディング用の特殊トークンが設定されていない場合、文の終わりを示すトークン(eos_token)で代用します
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
# 使用するGPUがBF16(bfloat16)というデータ型に対応しているかチェックします
# BF16は、FP16よりもダイナミックレンジが広く、学習が安定しやすいとされています
# Ampere世代以降のGPU (RTX 30xx, A100など) でサポートされています
use_bf16 = torch.cuda.is_available() and hasattr(torch.cuda, "is_bf16_supported") and torch.cuda.is_bf16_supported()
compute_dtype = torch.bfloat16 if use_bf16 else torch.float16 # 対応していればBF16、そうでなければFP16を使います
print(f"使用データ型: {'BF16' if use_bf16 else 'FP16'}")
# ★ 4bit量子化(QLoRA)を有効にしてモデルをロードします
try:
# transformersライブラリが出す警告を抑制するためのおまじないです
os.environ["TOKENIZERS_PARALLELISM"] = "false"
# QLoRAの設定を作成します (ここが核心部です)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, # ★モデルを4bitで読み込みます
bnb_4bit_use_double_quant=True, # ★二重量子化を使い、量子化の精度を上げます
bnb_4bit_quant_type="nf4", # ★NF4 (NormalFloat4) という、正規分布に最適化されたデータ型を使います
bnb_4bit_compute_dtype=compute_dtype, # 計算時はBF16/FP16を使い、精度と速度を両立させます
)
# まずモデルの「設計図」である設定ファイルだけを先に読み込みます
config = AutoConfig.from_pretrained(base_model)
# モデルをどのGPUに配置するかを設定します
if torch.cuda.device_count() > 1:
device_map = "auto" # 複数GPUがある場合は自動で割り振ります
else:
device_map = {"": 0} # GPUが1つの場合は、GPU 0に明示的に配置します
print("モデル読み込み中...")
# 設定を使って、実際にモデルを読み込みます
model = AutoModelForCausalLM.from_pretrained(
base_model,
config=config,
quantization_config=bnb_config, # ★ここで4bit量子化の設定を適用します
device_map=device_map, # どのデバイスにロードするか
trust_remote_code=True,
low_cpu_mem_usage=True, # CPUメモリの使用量を抑えるオプション
torch_dtype=None, # 量子化時はNoneを指定するのが安全です
)
except Exception as e:
# 上記の処理でエラーが出た場合のフォールバック(代替処理)です
print(f"エラー発生: {e}")
print("フォールバック: 量子化なしでロード...")
# 量子化せずに、通常の16bit精度でモデルを読み込みます
# VRAMはより多く消費しますが、エラーで止まるよりは良いという判断です
model = AutoModelForCausalLM.from_pretrained(
base_model,
torch_dtype=compute_dtype,
device_map="auto",
trust_remote_code=True,
)
print("✓ モデル読み込み完了")
# -----------------------------------------------------------------
# 4) 学習の準備 (PEFT/LoRA)
# -----------------------------------------------------------------
# 量子化されたモデルを学習可能にするための前処理です
if hasattr(model, 'quantization_method'):
# この関数を通すことで、量子化されたレイヤーに学習可能なアダプタ(LoRA)を追加する準備が整います
model = prepare_model_for_kbit_training(model)
print("✓ 量子化モデルを学習用に準備完了")
# 勾配チェックポイント(学習中のメモリを節約する技術)を使うために、キャッシュを無効にします
# これをしないと両者の機能が衝突してエラーになることがあります
if hasattr(model, "config"):
model.config.use_cache = False
# LoRAの設定を定義します
lora_config = {
"r": 8, # LoRAのランク。小さいほど学習パラメータが少なくなり、メモリ効率が良い。8や16が一般的。
"lora_alpha": 16, # LoRAのスケーリング係数。rの2倍程度に設定することが多い。
"lora_dropout": 0.05, # LoRA層のドロップアウト率。過学習を防ぐ効果がある。
"bias": "none", # バイアス項は学習しない設定。
"task_type": "CAUSAL_LM", # タスクの種類(今回は言語モデリング)。
# どの層にLoRAを適用するか指定します。これはモデルの構造に依存します。
# 一般的に、Attention層のq_proj, k_proj, v_proj, o_projと、FFN層のgate_proj, up_proj, down_projが対象になります。
"target_modules": ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
}
# RLoRAが利用可能か試します
try:
# use_rslora=True をつけて設定オブジェクトを作成
peft_config = LoraConfig(use_rslora=True, **lora_config)
print("✓ RLoRA (Rank-Stabilized LoRA) を使用")
except (TypeError, ValueError):
# エラーが出た(古いPEFTライブラリなど)場合は、通常のLoRAを使用します
peft_config = LoraConfig(**lora_config)
print("→ 標準LoRAを使用")
# -----------------------------------------------------------------
# 5) データセットの整形
# -----------------------------------------------------------------
# モデルが学習しやすいように、プロンプトを特定の形式に変換する関数です
def format_prompt(example):
# TinyLlamaのチャットモデルが公式に推奨している形式に整形します
# <s>: 文の開始, [INST]...[/INST]: ユーザーの指示, </s>: 文の終わり
formatted_text = f"<s>[INST] {example['instruction']} [/INST]\n{example['output']}</s>"
return {"text": formatted_text}
# Pythonのリストから、Hugging FaceのDatasetオブジェクトを作成します
ds = Dataset.from_list(train_samples)
# 作成したデータセットの各要素に、format_prompt関数を適用します
ds = ds.map(format_prompt)
# データセットの準備ができたことを確認します
print(f"✓ データセット準備完了: {len(ds)}サンプル")
print("サンプルデータ:")
# 最初のデータの先頭100文字だけ表示してみます
print(ds[0]["text"][:100] + "...")
# -----------------------------------------------------------------
# 6) トレーナーの設定
# -----------------------------------------------------------------
# SFTTrainer (Supervised Fine-tuning Trainer) のための詳細設定です
training_args = SFTConfig(
output_dir="./tinyllama-medical-qlora", # 学習結果(モデルのアダプタなど)を保存するディレクトリ
max_steps=50, # 学習の総ステップ数。デモなので短く設定
per_device_train_batch_size=1, # GPUあたりのバッチサイズ。VRAMを節約するため最小の1に設定
gradient_accumulation_steps=4, # 勾配を4ステップ分ためてからモデルを更新。実質的なバッチサイズは 1 * 4 = 4 となる
learning_rate=2e-4, # 学習率。モデルがどれだけ大きく更新されるかを決める重要なパラメータ
logging_steps=10, # 10ステップごとに学習の進捗(ロスなど)を表示
lr_scheduler_type="cosine", # 学習率を徐々に下げていくスケジューラの種類。cosineが一般的
warmup_ratio=0.1, # 全ステップの最初の10%で学習率を徐々に上げるウォームアップを行う
bf16=use_bf16, # BF16が使えるなら有効化
fp16=not use_bf16, # BF16が使えないならFP16を有効化
gradient_checkpointing=True, # 勾配チェックポイントを有効化し、VRAM使用量を削減
dataloader_drop_last=True, # データローダーが最後に半端なバッチを捨てる設定
remove_unused_columns=False, # データセットの未使用カラムを削除しない設定
packing=False, # 短いシーケンスをパックしない設定。安定性重視
dataset_text_field="text", # データセット内で学習に使うテキストが含まれるフィールド名
report_to=[], # WandBなどのレポート先を無効化
max_seq_length=512, # モデルが一度に処理するトークンの最大長
)
# -----------------------------------------------------------------
# 7) トレーナーの初期化と学習実行
# -----------------------------------------------------------------
print("トレーナー初期化中...")
from transformers import DataCollatorForLanguageModeling
# SFTTrainerが内部で使うデータコレータ(バッチを作成する役割)をカスタマイズします
# これにより、paddingやtruncationの挙動をより細かく制御できます
class CustomDataCollator(DataCollatorForLanguageModeling):
def __call__(self, features):
# バッチ内のテキストをリストとして取り出します
text_batch = [f["text"] for f in features]
# トークナイザでテキストを数値に変換します
tokenized_features = self.tokenizer(
text_batch,
padding=True, # バッチ内で最長の文に合わせてパディングします
truncation=True, # max_lengthを超えた文を切り詰めます
max_length=self.tokenizer.model_max_length, # モデルが許容する最大長を使用
return_tensors="pt", # PyTorchのテンソル形式で返します
)
# 言語モデルの学習では、input_idsをそのままlabelsとして使います
tokenized_features["labels"] = tokenized_features["input_ids"].clone()
return tokenized_features
# カスタムデータコレータのインスタンスを作成します
data_collator = CustomDataCollator(
tokenizer=tokenizer,
mlm=False, # Causal LM (自己回帰モデル) なので、Masked LM は無効にします
)
# すべての設定をまとめて、SFTTrainerを初期化します
trainer = SFTTrainer(
model=model, # 学習対象のモデル
tokenizer=tokenizer, # 使用するトークナイザ
peft_config=peft_config, # LoRAの設定
args=training_args, # 学習全体の詳細設定
train_dataset=ds, # 学習用データセット
data_collator=data_collator, # カスタムデータコレータ
)
# 学習を開始します!
print("=== 学習開始 ===")
trainer.train()
print("=== 学習完了 ===")
# -----------------------------------------------------------------
# 8) モデルの保存と推論テスト
# -----------------------------------------------------------------
# 学習したLoRAアダプタの重みを保存します
trainer.save_model()
print("✓ モデル保存完了")
# 学習したモデルがうまく機能するか、簡単なテストを行います
print("\n=== 推論テスト ===")
model.eval() # モデルを推論モードに切り替えます
with torch.no_grad(): # 勾配計算を無効にし、メモリ効率を上げます
# テスト用のプロンプトを作成します。学習時と同じ形式に合わせます
test_input = "<s>[INST] 高血圧の生活指導を簡潔に3点で教えて [/INST]"
# プロンプトをトークナイズし、GPUに送ります
inputs = tokenizer(test_input, return_tensors="pt").to(model.device)
# モデルにプロンプトを入力し、続きの文章を生成させます
outputs = model.generate(
**inputs,
max_new_tokens=100, # 生成する最大のトークン数
do_sample=True, # サンプリングを有効にし、毎回少し違う文章を生成させます
temperature=0.7, # 生成される単語のランダム性を調整します。低いほど決まりきった文章に
top_p=0.9, # 上位90%の確率を持つ単語の中からサンプリングします (Nucleus Sampling)
pad_token_id=tokenizer.eos_token_id # パディングトークンのIDを指定します
)
# 生成されたトークンIDを、人間が読める文章に戻します
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
# 元の入力プロンプト部分を除去し、AIが生成した部分だけを取り出します
generated_part = response[len(test_input.replace("<s>", "").replace("</s>", "")):].strip()
print("入力:", test_input)
print("生成:", generated_part)
print("\n✅ 全処理完了!")
学習結果とログの読み解き
上記のコードを実行すると、以下のようなログが出力されます。特にエラーメッセージは、何が起きているかを理解する上で非常に重要です。
PyTorch: 2.8.0+cu126 | CUDA: 12.6 | GPU: True
使用データ型: BF16
モデル読み込み中...
エラー発生: `.to` is not supported for `4-bit` or `8-bit` bitsandbytes models. Please use the model as it is, since the model has already been set to the correct devices and casted to the correct `dtype`.
フォールバック: 量子化なしでロード...
✓ モデル読み込み完了
✓ RLoRA (Rank-Stabilized LoRA) を使用
Map: 100% 3/3 [00:00<00:00, 93.01 examples/s]
✓ データセット準備完了: 3サンプル
サンプルデータ:
<s>[INST] 新たに2型糖尿病と診断された60代へやさしく説明して [/INST]
2型糖尿病は血糖が高くなる病気です。食事の工夫と軽い運動で多くの方がコントロールできます。焦らず一緒に進めましょう。</s>...
トレーナー初期化中...
/usr/local/lib/python3.12/dist-packages/trl/trainer/sft_trainer.py:582: UserWarning: You passed `remove_unused_columns=False` on a non-packed dataset. This might create some issues with the default collator and yield to errors. If you want to inspect dataset other columns (in this case ['text', 'output', 'instruction']), you can subclass `DataCollatorForLanguageModeling` in case you used the default collator and create your own data collator in order to inspect the unused dataset columns.
warnings.warn(
Map: 100% 3/3 [00:00<00:00, 113.15 examples/s]
max_steps is given, it will override any value given in num_train_epochs
=== 学習開始 ===
/usr/local/lib/python3.12/dist-packages/torch/_dynamo/eval_frame.py:929: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.5 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.
return fn(*args, **kwargs)
[50/50 01:40, Epoch 33/50]
Step Training Loss
10 0.879000
20 0.143200
30 0.014300
40 0.009100
50 0.007600
=== 学習完了 ===
✓ モデル保存完了
=== 推論テスト ===
入力: <s>[INST] 高血圧の生活指導を簡潔に3点で教えて [/INST]
生成: 1. 塩分を控える: 1日6g未満を目指しましょう。 2. 適度な運動: ウォーキングなど有酸素運動を週に3-4回、30分程度行いましょう。 3. ストレス管理: 十分な睡眠とリラックスできる時間を作りましょう。
✅ 全処理完了!
ログのポイント解説
- エラー発生とフォールバック:
エラー発生: .to is not supported for 4-bit or 8-bit bitsandbytes models.というメッセージが表示されていますね。これは、一部の環境で量子化モデルを特定のデバイスに移動させようとすると発生する互換性の問題です。
しかし、心配は無用です。try-except構文でこのエラーを検知し、「フォールバック: 量子化なしでロード…」という代替処理が実行されています。このため、この実行例ではQLoRAは有効にならず、通常の16bit精度で学習が行われました。 もしVRAMが十分にあればこのように学習は継続できますし、VRAMが足りなければ「CUDA out of memory」エラーが発生します。この挙動は、環境の違いを吸収するための意図した設計です。 - 学習の進捗:
StepとTraining Lossに注目してください。学習が進むにつれてTraining Loss(モデルの予測と正解の誤差)が0.879から0.0076へと順調に減少しているのがわかります。これは、モデルが提供されたデータをうまく学習できていることを示しています。 - 推論結果:
最後に、学習したモデルに「高血圧の生活指導」について質問したところ、それらしい3つのポイントを生成してくれました。わずか3つのサンプルと50ステップの学習でも、モデルが指示に応答する能力を少し獲得したことが見て取れますね。
(任意)アダプタをベースモデルへマージ
LoRAで学習した場合、変更点は「アダプタ」という小さな別のファイルに保存されます。推論のたびにベースモデルとアダプタの両方を読み込む必要がありますが、これを一つのモデルに統合(マージ)すると、配布やデプロイが簡単になります。
これは必須の作業ではありませんが、学習したモデルを単体で動かしたい場合に実行してください。
# PEFT (Parameter-Efficient Fine-Tuning) ライブラリからPeftModelをインポート
from peft import PeftModel
# transformersライブラリからモデルとトークナイザを読み込むためのクラスをインポート
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch # PyTorchライブラリ
# --- パス設定 ---
# ベースモデルのHugging Faceリポジトリ名
base = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
# 学習済みLoRAアダプタが保存されているディレクトリのパス
# trainer.save_model()で保存した場所を指定します
adapter = "./tinyllama-medical-qlora"
# マージ後のモデルを保存する新しいディレクトリのパス
merged_dir = "./tinyllama-medical-qlora-merged"
# --- モデルの読み込み ---
# まず、マージの土台となるベースモデルを読み込みます
# torch_dtype=torch.float16: 16bit浮動小数点数で読み込み、メモリ使用量を抑えます
# device_map="auto": 利用可能なGPUにモデルを自動で配置します
mdl = AutoModelForCausalLM.from_pretrained(base, torch_dtype=torch.float16, device_map="auto")
# ベースモデルに対応するトークナイザも読み込みます
tok = AutoTokenizer.from_pretrained(base, use_fast=True)
# --- アダプタの適用 ---
# 読み込んだベースモデルに、学習済みのアダプタを適用(アタッチ)します
# これで、ベースモデルとLoRAアダプタが一体となったPeftModelオブジェクトができます
peft_m = PeftModel.from_pretrained(mdl, adapter)
# --- マージとアンロード ---
# アダプタの重みをベースモデルの重みに加算し、一つのモデルに統合します
# これにより、PeftModelではなく、通常のTransformerモデルに戻ります
merged = peft_m.merge_and_unload()
# --- 保存 ---
# 統合後のモデルとトークナイザを指定したディレクトリに保存します
# これで、このディレクトリを指定するだけで、ファインチューニング済みのモデルを直接読み込めるようになります
merged.save_pretrained(merged_dir)
tok.save_pretrained(merged_dir)
print(f"✓ モデルのマージが完了し、'{merged_dir}'に保存されました。")
トラブル時のチェックリスト 🛠️
うまくいかない時は、まずここを確認してみてください。
huggingface_hubのトークンに関する警告が出る:
公開されているモデルを使うだけなら無視して問題ありません。もし鍵付きの非公開モデルを使いたい場合は、from huggingface_hub import notebook_login; notebook_login()を実行してログインが必要です。bitsandbytesがCPU版としてインストールされてしまう:
これが最もよくある問題の一つです。pip uninstall -y bitsandbytesで一度完全にアンインストールし、pip install -U --pre bitsandbytes tritonでGPU対応版を入れ直した後、必ずランタイムを再起動してください。No module named 'triton.ops'というエラーが出る:
上記のbitsandbytesと一緒にtritonライブラリのインストールが必須です。pip install tritonを試してください。CUDA out of memory(OOM) エラーが出る:
GPUのVRAMが不足しています。以下の対策を試してください。per_device_train_batch_size=1になっているか確認する。gradient_accumulation_stepsの数値を大きくする (例: 4 → 8)。max_stepsの数を一時的に減らして、とりあえず動くか確認する。- 学習データのプロンプト長を短くする。
- RLoRA関連でエラーが出る:
お使いのライブラリのバージョンが古い可能性があります。peft_configを作成する際にuse_rslora=Falseに変更すれば、通常のLoRAとして学習を続けられます。 packing=Trueにすると不安定になる:packingは短いデータを連結して学習を高速化するテクニックですが、データセットによっては不安定になることがあります。基本はpacking=False(既定値)のままにしておくのが安全です。
品質向上のためのヒント 🚀
今回のコードはあくまで「動かす」ことを目的とした最小構成です。実用的なモデルを育成するには、いくつかの追加の工夫が必要になります。
- データの質と量:
AIの性能はデータの質と量でほぼ決まります。実用レベルを目指すなら、数千〜数万件の質の高い教師データが必要です。これが最も重要で、最も大変な作業です。 - ハイパーパラメータの調整:
r(LoRAのランク)、lora_alpha、learning_rateといった設定値(ハイパーパラメータ)は、タスクやデータセットによって最適値が異なります。色々な値を試して、最も性能が良くなる組み合わせを探す工程が必要です。 - 過学習の監視:
学習データをAIが「丸暗記」してしまい、未知のデータに対応できなくなることを過学習と呼びます。これを防ぐために、データセットを「学習用」「検証用」に分割し、検証用データに対する性能が落ち始めたら学習をストップする、といった工夫が不可欠です。
学習データの準備について:実世界のデータから「教師データ」を作る
本稿のサンプルコードでは、分かりやすさを優先し、あらかじめ「指示(instruction)」と「応答(output)」がペアになった単純なデータセットを使用しました。しかし、実際の研究や業務では、電子カルテの診療録や論文といった、構造化されていない長文テキスト(非構造化データ)を扱いたいと考えるのが自然でしょう。では、そうした「生データ」をそのまま学習に使えるのでしょうか?
結論から言うと、今回採用したSFT (Supervised Fine-tuning) という手法には、一手間加えた「教師データ」が不可欠です。SFTは、モデルに特定のタスクをこなす「スキル」を教え込む、いわば「お手本を見せて芸を仕込む」ような学習です。そのため、「こういう指示が来たら(instruction)、こう答えなさい(output)」という明確なペア形式でデータを与える必要があります。
そこで重要になるのが、生の非構造化データから、目的に合わせた「指示」と「応答」のペアを大量に作成するデータキュレーションという工程です。これはAI開発で最も創造性が求められ、かつ労力のかかる部分ですが、モデルの性能を決定づける心臓部と言えます。
- 要約モデルを作りたい場合: 長い経過記録を「指示」とし、人間が作成した要約文を「応答」とするペアを作ります。
- 情報抽出モデルを作りたい場合: 検査レポートから「この所見から腫瘍のサイズを抽出して」という「指示」を与え、正解の値「3.5cm」を「応答」とするペアを作ります。
- 文書生成モデルを作りたい場合: 断片的なメモを「指示」とし、完成された紹介状を「応答」とするペアを作ります。
このように、目的とするAIの挙動に合わせて、実世界のデータから質の高い教師データを根気強く作成していくことが、プロジェクトの成功の鍵を握ります。
倫理・法規制に関する重要事項
本稿で提示したコードは、あくまで技術的な検証を目的としています。実際の医療データなど、個人情報を含むデータを用いてAIを学習させる際には、個人情報保護法、医療関連法規、各研究機関の倫理指針、そしてデータ利用に関する許諾条件を厳格に遵守する必要があります。匿名化が不十分なデータや、適切な同意を得ていないデータは絶対に使用しないでください。
まとめと次のステップへ
今回は、巨大な事前学習済みモデルを、低コストで効率的に私たちのタスクに適応させるための強力な手法、PEFT、特にその代表であるLoRAについて学びました。
- 課題: 大規模モデルのフル・ファインチューニングは、計算・ストレージの両面でコストが非常に高い。
- LoRAの解決策: 元のモデルを凍結したまま、ごく少数の追加パラメータ(差分行列)だけを学習することで、コスト問題を劇的に改善する。
これにより、私たち個々の研究者が、GPTやMedPaLMのような超大規模モデルを、自身が持つ限られたデータで、特定の診療科のタスクに合わせてファインチューニングすることが、現実的な選択肢となったのです。
さて、LoRAでファインチューニングのコストを下げることができるようになりました。しかし、もし「ファインチューニングすら不要」な世界があるとしたらどうでしょう?
次回の第29回では、モデルに追加学習を一切行わず、プロンプト(指示文)だけでタスクを解かせるZero-shot / Few-shot学習という、さらに驚くべきAIの能力について探求します。
参考文献
- Hu EJ, Shen Y, Wallis P, Allen-Zhu Z, Li Y, Wang S, et al. LoRA: Low-rank adaptation of large language models. In: International Conference on Learning Representations. 2022.
- Mangrulkar S, Gugger S, Debut L, Belkada Y, Paul S, an von Werra T. PEFT: Parameter-Efficient Fine-Tuning of Billion-Scale Models on Low-Resource Hardware. 2022.
- Hugging Face. PEFT: Parameter-Efficient Fine-Tuning. [Internet]. [cited 2025 Jun 13]. Available from: https://github.com/huggingface/peft
- Dettmers, T., Pagnoni, A., Holtzman, A. and Zettlemoyer, L. (2023) ‘QLoRA: Efficient Finetuning of Quantized LLMs’, arXiv preprint arXiv:2305.14314. Available at: https://arxiv.org/abs/2305.14314.
- Hu, E.J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L. and Chen, W. (2021) ‘LoRA: Low-Rank Adaptation of Large Language Models’, arXiv preprint arXiv:2106.09685. Available at: https://arxiv.org/abs/2106.09685.
- Hugging Face. (2024) PEFT (Parameter-Efficient Fine-Tuning) Documentation. Available at: https://huggingface.co/docs/peft.
- Hugging Face. (2024) TRL (Transformer Reinforcement Learning) Documentation. Available at: https://huggingface.co/docs/trl.
- Zhang, A., Al-Shedivat, M., Aneja, J., Singh, S., Yu, F. and Su, Y. (2023) ‘Revisiting Low-rank Adaptation for Stable and Efficient Fine-tuning’, arXiv preprint arXiv:2307.03661. Available at: https://arxiv.org/abs/2307.03661.
ご利用規約(免責事項)
当サイト(以下「本サイト」といいます)をご利用になる前に、本ご利用規約(以下「本規約」といいます)をよくお読みください。本サイトを利用された時点で、利用者は本規約の全ての条項に同意したものとみなします。
第1条(目的と情報の性質)
- 本サイトは、医療分野におけるAI技術に関する一般的な情報提供および技術的な学習機会の提供を唯一の目的とします。
- 本サイトで提供されるすべてのコンテンツ(文章、図表、コード、データセットの紹介等を含みますが、これらに限定されません)は、一般的な学習参考用であり、いかなる場合も医学的な助言、診断、治療、またはこれらに準ずる行為(以下「医行為等」といいます)を提供するものではありません。
- 本サイトのコンテンツは、特定の製品、技術、または治療法の有効性、安全性を保証、推奨、または広告・販売促進するものではありません。紹介する技術には研究開発段階のものが含まれており、その臨床応用には、さらなる研究と国内外の規制当局による正式な承認が別途必要です。
- 本サイトは、情報提供を目的としたものであり、特定の治療法を推奨するものではありません。健康に関するご懸念やご相談は、必ず専門の医療機関にご相談ください。
第2条(法令等の遵守)
利用者は、本サイトの利用にあたり、医師法、医薬品、医療機器等の品質、有効性及び安全性の確保等に関する法律(薬機法)、個人情報の保護に関する法律、医療法、医療広告ガイドライン、その他関連する国内外の全ての法令、条例、規則、および各省庁・学会等が定める最新のガイドライン等を、自らの責任において遵守するものとします。これらの適用判断についても、利用者が自ら関係各所に確認するものとし、本サイトは一切の責任を負いません。
第3条(医療行為における責任)
- 本サイトで紹介するAI技術・手法は、あくまで研究段階の技術的解説であり、実際の臨床現場での診断・治療を代替、補助、または推奨するものでは一切ありません。
- 医行為等に関する最終的な判断、決定、およびそれに伴う一切の責任は、必ず法律上その資格を認められた医療専門家(医師、歯科医師等)が負うものとします。AIによる出力を、資格を有する専門家による独立した検証および判断を経ずに利用することを固く禁じます。
- 本サイトの情報に基づくいかなる行為によって利用者または第三者に損害が生じた場合も、本サイト運営者は一切の責任を負いません。実際の臨床判断に際しては、必ず担当の医療専門家にご相談ください。本サイトの利用によって、利用者と本サイト運営者の間に、医師と患者の関係、またはその他いかなる専門的な関係も成立するものではありません。
第4条(情報の正確性・完全性・有用性)
- 本サイトは、掲載する情報(数値、事例、ソースコード、ライブラリのバージョン等)の正確性、完全性、網羅性、有用性、特定目的への適合性、その他一切の事項について、何ら保証するものではありません。
- 掲載情報は執筆時点のものであり、予告なく変更または削除されることがあります。また、技術の進展、ライブラリの更新等により、情報は古くなる可能性があります。利用者は、必ず自身で公式ドキュメント等の最新情報を確認し、自らの責任で情報を利用するものとします。
第5条(AI生成コンテンツに関する注意事項)
本サイトのコンテンツには、AIによる提案を基に作成された部分が含まれる場合がありますが、公開にあたっては人間による監修・編集を経ています。利用者が生成AI等を用いる際は、ハルシネーション(事実に基づかない情報の生成)やバイアスのリスクが内在することを十分に理解し、その出力を鵜呑みにすることなく、必ず専門家による検証を行うものとします。
第6条(知的財産権)
- 本サイトを構成するすべてのコンテンツに関する著作権、商標権、その他一切の知的財産権は、本サイト運営者または正当な権利を有する第三者に帰属します。
- 本サイトのコンテンツを引用、転載、複製、改変、その他の二次利用を行う場合は、著作権法その他関連法規を遵守し、必ず出典を明記するとともに、権利者の許諾を得るなど、適切な手続きを自らの責任で行うものとします。
第7条(プライバシー・倫理)
本サイトで紹介または言及されるデータセット等を利用する場合、利用者は当該データセットに付随するライセンス条件および研究倫理指針を厳格に遵守し、個人情報の匿名化や同意取得の確認など、適用される法規制に基づき必要とされるすべての措置を、自らの責任において講じるものとします。
第8条(利用環境)
本サイトで紹介するソースコードやライブラリは、執筆時点で特定のバージョンおよび実行環境(OS、ハードウェア、依存パッケージ等)を前提としています。利用者の環境における動作を保証するものではなく、互換性の問題等に起因するいかなる不利益・損害についても、本サイト運営者は責任を負いません。
第9条(免責事項)
- 本サイト運営者は、利用者が本サイトを利用したこと、または利用できなかったことによって生じる一切の損害(直接損害、間接損害、付随的損害、特別損害、懲罰的損害、逸失利益、データの消失、プログラムの毀損等を含みますが、これらに限定されません)について、その原因の如何を問わず、一切の法的責任を負わないものとします。
- 本サイトの利用は、学習および研究目的に限定されるものとし、それ以外の目的での利用はご遠慮ください。
- 本サイトの利用に関連して、利用者と第三者との間で紛争が生じた場合、利用者は自らの費用と責任においてこれを解決するものとし、本サイト運営者に一切の迷惑または損害を与えないものとします。
- 本サイト運営者は、いつでも予告なく本サイトの運営を中断、中止、または内容を変更できるものとし、これによって利用者に生じたいかなる損害についても責任を負いません。
第10条(規約の変更)
本サイト運営者は、必要と判断した場合、利用者の承諾を得ることなく、いつでも本規約を変更することができます。変更後の規約は、本サイト上に掲載された時点で効力を生じるものとし、利用者は変更後の規約に拘束されるものとします。
第11条(準拠法および合意管轄)
本規約の解釈にあたっては、日本法を準拠法とします。本サイトの利用および本規約に関連して生じる一切の紛争については、東京地方裁判所を第一審の専属的合意管轄裁判所とします。
For J³, may joy follow you.

