言語モデルの微調整は、困難である必要はありません。Docker Offload と Unsloth を使用した モデルの微調整 に関する前回の投稿では、Docker の使い慣れたワークフローを使用して、小さなローカル モデルを効率的にトレーニングする方法を説明しました。今回は焦点を絞り込んでいます。
モデルにすべてに長けているように求めるのではなく、それを 専門化して、テキスト内の個人を特定できる情報 (PII) を一貫してマスクするなど、狭いながらも価値のあるスキルをモデルに教えることができます。LoRA (Low-Rank Adaptation) などの技術のおかげで、このプロセスは適度なリソースで実現できるだけでなく、高速かつ効率的です。
さらに良いことに、Docker のエコシステムを使用すると、トレーニング、パッケージ化、共有などの微調整パイプライン全体が親しみやすくなります。特注の ML セットアップや研究室のワークステーションは必要ありません。すばやく反復し、ワークフローを移植可能に保ち、結果を公開して、他のユーザーが既に知っているのと同じ Docker コマンドで試すことができます。
この投稿では、Gemma 3 270M モデルを PII を確実にマスキングできるコンパクトなアシスタントに適応させるという、実践的な微調整実験について説明します。
低ランク適応(LoRA)とは何ですか?
微調整は、言語の一般的な構造とパターンをすでに学習した事前トレーニング済みモデルから始まります。
ゼロからトレーニングする代わりに (大量のコンピューティングを消費し、モデルが事前知識を失う壊滅的な忘却のリスクがあります)、LoRA (Low-Rank Adaptation) と呼ばれるより効率的な方法を使用できます。
LoRAを使用すると、ベースモデルをフリーズしたまま、トレーニング可能な小さなアダプターレイヤーを追加することで、モデルがすでに知っていることを上書きすることなく、モデルに新しいタスクや動作を教えることができます。
LoRAはどのように機能しますか?
大まかに言うと、LoRA は次のように機能します。
- 基本モデルをフリーズする: モデルの元の重み (言語のコア知識) は変更されません。
- アダプターレイヤーを追加する: 小さくてトレーニング可能な「サイドモジュール」がモデルの特定の部分に挿入されます。これらのアダプターは、教えたい新しい動作またはスキルのみを学習します。
- 効率的なトレーニング: 微調整中は、アダプター パラメーターのみが更新されます。モデルの残りの部分は静的なままであるため、コンピューティングとメモリの要件が大幅に削減されます。
LoRA実験:PIIをマスクするためにGemma 3 270Mを微調整する
この実験では、モデルは指示の読み書き方法、従う方法をすでに知っています。私たちの仕事は、私たちが関心のある特定のパターンを教えることです。
「テキストが与えられた場合、PII を標準化されたプレースホルダーに置き換え、他のすべてはそのままにしておきます。」
微調整プロセスは、次の 4 つのステップで構成されます。
- データセットを準備する
- LoRAアダプターの準備
- モデルのトレーニング
- 結果のモデルをエクスポートする

図 1:LoRAによる微調整の4つのステップ
この例では、教師あり微調整 (SFT) を使用します: 各トレーニング例は、PII を含む生のテキストと正しく編集されたバージョンをペアにします。このような多くの例で、モデルはパターンを内面化し、編集ルールを一般化することを学習します。
データセットの品質は非常に重要であり、データセットがクリーンで代表的であればあるほど、微調整されたモデルのパフォーマンスが向上します。
手順に入る前に、チャット テンプレートを理解することが重要です。
チャットテンプレートを理解する
次のようなリクエストを Gemma 3 270M に送信しても、モデルにはこの JSON 構造が直接表示されません。
"messages": [
{
"role": "user",
"content": "Mask all PII in the following text. Replace each entity with the exact UPPERCASE label in square brackets (e.g., [PERSON], [EMAIL], [PHONE], [USERNAME], [ADDRESS], [CREDIT_CARD], [TIME], etc.). Preserve all non-PII text, whitespace, ' ' and punctuation exactly. Return ONLY the redacted text. Text: This is an example of text that contains some data. The author of this text is Ignacio López Luna, but everybody calls him Ignasi. His ID number is 123456789. He has a son named Arnau López, who was born on 21-07-2021"
}
]
代わりに、入力は特別なトークンを使用して チャット形式のプロンプト に変換されます。
<start_of_turn>user Mask all PII in the following text. Replace each entity with the exact UPPERCASE label in square brackets (e.g., [PERSON], [EMAIL], [PHONE], [USERNAME], [ADDRESS], [CREDIT_CARD], [TIME], etc.). Preserve all non-PII text, whitespace, ' ' and punctuation exactly. Return ONLY the redacted text. Text: This is an example of text that contains some data. The author of this text is Ignacio López Luna, but everybody calls him Ignasi. His ID number is 123456789. He has a son named Arnau López, who was born on 21-07-2021<end_of_turn>
メッセージがどのように再ラップされ、<start_of_turn> や <end_of_turn> などの追加のトークンが挿入されているかに注目してください。これらのトークンは、モデルのチャット テンプレートの一部であり、推論時に期待される標準化された構造です。
モデルが異なれば、使用するテンプレートも異なります。たとえば、Gemma は<start_of_turn>マーカーを使用しますが、他のモデルは <bos> などに依存する場合があります。
これがまさに、最初のステップが「データセットの準備」である理由です。微調整する場合は、モデルが推論中に使用するのと同じチャットテンプレートを使用してトレーニングデータをフォーマットする必要があります。この調整により、微調整されたモデルは、本番環境で遭遇するものとまったく同じデータでトレーニングされているため、堅牢性が確保されます。
データセットの準備: 例による教育
データセットは、汎用言語能力とタスク固有の専門知識の間の架け橋です。各例は、PII を含む生のテキストを含むプロンプトと、編集されたバージョンを示す応答など、モデルに何をさせたいかを示すデモンストレーションです。
スクリプトでは、モデルのチャットテンプレートを使用して元のデータセットがフォーマットされる方法です(apply_chat_template関数を参照)。
max_seq_length = 2048
model, tokenizer = FastModel.from_pretrained(
model_name="unsloth/gemma-3-270m-it",
max_seq_length=max_seq_length,
load_in_4bit=False,
load_in_8bit=False,
full_finetuning=False,
)
with open("pii_redaction_train.json", "r", encoding="utf-8") as f:
data = json.load(f)
ds = Dataset.from_list(data)
def to_text(ex):
resp = ex["response"]
if not isinstance(resp, str):
resp = json.dumps(resp, ensure_ascii=False)
msgs = [
{"role": "user", "content": ex["prompt"]},
{"role": "assistant", "content": resp},
]
return {
"text": tokenizer.apply_chat_template(
msgs, tokenize=False, add_generation_prompt=False
)
}
dataset = ds.map(to_text, remove_columns=ds.column_names)
いくつかのペアを印刷して、次の方法でどのように見えるかを確認できます。
for i in range(3):
print(dataset[i]["text"])
print("=" * 80)
データセットエントリの例:
<bos><start_of_turn>user
Mask all PII in the following text. Replace each entity with the exact UPPERCASE label in square brackets (e.g., [PERSON], [EMAIL], [PHONE], [USERNAME], [ADDRESS], [CREDIT_CARD], [TIME], etc.). Preserve all non-PII text, whitespace, and punctuation exactly. Return ONLY the redacted text.
Text:
<p>My child faozzsd379223 (DOB: May/58) will undergo treatment with Dr. faozzsd379223, office at Hill Road. Our ZIP code is 28170-6392. Consult policy M.UE.227995. Contact number: 0070.606.322.6244. Handle transactions with 6225427220412963. Queries? Email: faozzsd379223@outlook.com.</p><end_of_turn>
<start_of_turn>model
<p>My child [USERNAME_2] (DOB: [DATEOFBIRTH_1]) will undergo treatment with Dr. [USERNAME_1], office at [STREET_1]. Our ZIP code is [ZIPCODE_1]. Consult policy M.UE.227995. Contact number: [TELEPHONENUM_1]. Handle transactions with [CREDITCARDNUMBER_1]. Queries? Email: [EMAIL_1].</p><end_of_turn>
LoRAアダプターの準備:ベースモデルの肩の上に立つ
白紙の状態から始めるのではなく、小さいながらも有能な命令調整モデルである Gemma-3 270M-IT から始めます。重みとトークナイザーの両方をロードすることで、テキストを理解するモデルだけでなく、文章の分割と再構築に使用する正確なルールも取得できます。
微調整は言語を再発明するものではなく、読み書きの仕方をすでに知っている基盤の上に、タスク固有の専門知識を重ねることです。
そのために、LoRAテクニックを使用します。
LoRAを使用する理由
大規模な言語モデルをゼロからトレーニングすることは、数十億のパラメーターを調整することを意味するため、非常にコストがかかります。
しかし、良いニュースは、モデルに新しいスキルを教えるために通常、すべてを変更する必要がないということです。
そこで LoRA の出番です。LoRA は、モデル全体を再トレーニングする代わりに、「アドオン」などのいくつかの小さな追加コンポーネントを追加します。モデルを微調整するときは、これらのアドオンのみを調整し、メインモデルは同じままです。
from peft import LoraConfig, get_peft_model
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05
)
model = get_peft_model(base_model, lora_config)
これらの数行 は、パラメータを固定したままにしますが、低ランクのアダプターの小さなセットを通じて学習することをモデルに伝えます。そのため、微調整は効率的で手頃な価格です。
モデルのトレーニング: 実際の微調整
データセットの準備ができ、LoRAアダプターが配置されると、実際のトレーニングは従来の教師あり学習のようになります。
- 入力 (ユーザー プロンプト) をフィードします。
- モデルの出力を予想される応答と比較します。
- アダプターの重みを調整して、差を最小限に抑えます。
model = model,
tokenizer = tokenizer,
train_dataset = dataset,
eval_dataset = None, # Can set up evaluation!
args = SFTConfig(
dataset_text_field = "text",
per_device_train_batch_size = 8,
gradient_accumulation_steps = 1, # Use GA to mimic batch size!
warmup_steps = 5,
num_train_epochs = 1, # Set this for 1 full training run.
# max_steps = 100,
learning_rate = 5e-5, # Reduce to 2e-5 for long training runs
logging_steps = 1,
optim = "adamw_8bit",
weight_decay = 0.01,
lr_scheduler_type = "linear",
seed = 3407,
output_dir="outputs",
report_to = "none", # Use this for WandB etc
),
)
trainer_stats = trainer.train()
何度も反復する中で、このモデルは PII マスキングのルールを内部化し、電子メールを [EMAIL] に置き換えるだけでなく、句読点、空白、およびすべての非 PII コンテンツを指示どおりに保持することを学習します。
ここで重要なのは、微調整によってモデルの一般的な機能が上書きされないということです。モデルはまだ首尾一貫したテキストを生成する方法を知っていますが、もう 1 つのスキルに偏っているだけです。
結果のモデルをエクスポートする: 重みのマージ
トレーニングが終了すると、ベースモデルとLoRAアダプターのセットが完成します。これは実験には便利ですが、デプロイでは多くの場合、単一の統合モデルが好まれます。
アダプターをベースウェイトにマージして戻すことで、PII マスキングの専門知識が組み込まれていることを除いて、元のモデルと同じように動作するスタンドアロンのチェックポイントが作成されます。
model.save_pretrained_merged("result", tokenizer, save_method = "merged_16bit")
モデルを試して共有する
微調整後、次の自然なステップは、 モデルを実際に試し、 うまく機能する場合は 他のユーザーと共有することです。Docker Model Runner を使用すると、微調整されたモデルをパッケージ化して Docker Hub にプッシュし、どこでも即座に実行できるようにすることができます。面倒なセットアップや GPU 固有の頭痛の種はなく、AI モデルを配布およびテストするための使い慣れた Docker ワークフローだけです。
したがって、アダプターがトレーニングされてマージされたら、それだけにとどまらず、実行して公開し、他の人にも試してもらいましょう。前回の 投稿では、それを行うのがいかに簡単であるかを段階的に説明しました。
微調整によりモデルが 特殊になりますが、Docker はモデル にアクセスして共有できるようになります。彼らは協力して、地元の小さなモデルを珍品から、コミュニティですぐに使用し、再利用できる実用的なツールに変えます。
私たちはこれを一緒に構築しています!
Docker Model Runner は、その中核となるコミュニティ フレンドリーなプロジェクトであり、その将来はあなたのような貢献者によって形作られます。このツールが役立つと思われる場合は、 GitHubリポジトリにアクセスしてください。私たちに星を付けてサポートを示し、プロジェクトをフォークして独自のアイデアを試し、貢献してください。ドキュメントの改善、バグの修正、新機能のいずれであっても、すべての貢献が役に立ちます。モデル展開の未来を一緒に築きましょう!
さらに詳しく
- Docker Offload と Unsloth を使用して ローカル モデルを微調整する方法 を学習します
- Docker Model Runner の一般提供に関するお知らせを確認する
- Model Runner GitHub リポジトリにアクセスしてください。Docker Model Runner はオープンソースであり、コミュニティからのコラボレーションと貢献を歓迎します。
- シンプルなhello GenAIアプリケーションでModel Runnerを使い始める