このガイドでは、ウェブデータを使ったGPT-OSSの微調整について学びます:
- Unslothとは何か、なぜ微調整をスピードアップするのか
- Bright DataのスクレイピングAPIを使って質の高いトレーニングデータを収集する方法
- 効率的な微調整のための環境設定方法
- 完全なステップバイステップのチュートリアルによるGPT-OSSの微調整方法
始めよう
Unslothとは何か、なぜ微調整に使うのか?
Unslothは、Hugging Faceエコシステム(ハブ、トランスフォーマー、PEFT、TRL)と完全な互換性を持ちながら、LLM微調整を大幅に高速化する軽量ライブラリです。このライブラリは、GTX 1070からH100まで、ほとんどのNVIDIA GPUをサポートし、TRLライブラリのトレーナー・スイート全体とシームレスに動作します。

Unslothのパフォーマンス向上は印象的です。ベンチマークでは、標準的なトランスフォーマーの実装と比較して2倍速いトレーニング速度を達成し、同時に40%少ないメモリを使用します。これは、同じハードウェアで、より大きなモデルのトレーニングや、より大きなバッチサイズを使用できることを意味します。おそらく最も重要なことは、精度の劣化が0%であることです。そのため、モデルの品質を犠牲にすることなく、これらすべての利点を得ることができます。
GPT-OSSモデルを理解する
OpenAIのGPT-OSSのリリースは、AI開発へのアプローチに大きな変化をもたらします。初めて、API制限、使用ベースの課金、料金制限のない本物のGPTモデルにアクセスできるようになりました。
GPT-OSSには主に2つのバリエーションがあります:
- GPT-OSS-120B:この大型モデルはGPT-4の品質に匹敵するが、少なくとも80GBのGPUメモリを必要とする。
- GPT-OSS-20B:GPT-3.5の性能に匹敵し、このモデルは16GBのGPUで効率的に動作します(チュートリアルに最適です)。
GPT-OSSを他のオープンモデルと一線を画すユニークな特徴の一つは、推論努力のコントロールです。推論レベルを “低”、”中”、”高 “に設定することで、モデルがどの程度深く問題を考えるかを調整することができます。これにより、特定のユースケースに応じて、スピードと精度のバランスを取ることができます。
微調整に高品質データが重要な理由
ファインチューニングは、あなたが与えたデータと同じくらい良いものです。最も洗練されたトレーニング・セットアップができても、データにノイズがあったり、一貫性がなかったり、フォーマットが悪かったりすれば、モデルは同じ問題を学習してしまいます。そのため、私たちはBright DataのウェブスクレイパーAPIを使用して、クリーンでフォーマットされた正確なデータを提供します。
Bright Dataは、カスタムソリューションをしばしばつまずかせるウェブスクレイピングの複雑な部分を処理します。IPローテーションを管理してレート制限を回避し、CAPTCHAを自動的に解決し、ダイナミックなJavaScriptレンダリングコンテンツを処理し、何百万ものリクエストに一貫したデータ品質を維持します。
このチュートリアルでは、Bright DataのAPIを使ってPythonドキュメントを収集し、それをモデルのトレーニングデータに変換します。
前提条件と環境のセットアップ
始める前に、微調整を成功させるために必要なものがすべて揃っていることを確認しよう。Google Colabは無料でGPUにアクセスできるため、ここではGoogle Colabを使用しますが、少なくとも16GBのVRAMを搭載したマシンであれば、同じプロセスを実行できます。
必要なハードウェア
このチュートリアルには以下が必要です:
- 少なくとも16GBのVRAMを搭載したGPU(T4、V100、またはそれ以上)
- モデルのウェイトとチェックポイント用に25GBのディスク空き容量
- モデルと依存関係をダウンロードするための安定したインターネット接続
Google Colabでは、以下の方法でT4 GPUに無料でアクセスできます:
- 新しいノートブックを開く
- ランタイム → ランタイムタイプの変更
- ハードウェアアクセラレータとしてGPUを選択
- Saveをクリックして変更を適用する

Unsloth と依存関係のインストール
GPUランタイムの準備ができたら、Unslothと必要なすべての依存関係をインストールします。インストールプロセスは、異なるパッケージバージョン間の競合を避けるために最適化されています:
キャプチャー
# Unsloth とコアの依存関係のインストール
pip install --upgrade -qqq uv
try: import numpy; get_numpy = f "numpy=={numpy.__version__}"
except: get_numpy = "numpy" です。
!uv pip install -qqq
"torch>=2.8.0" "triton>=3.4.0" {get_numpy} torchvision bitsandbytes "transformers>=4.55.3"
"unsloth_zoo[base]"@git+https://github.com/unslothai/unsloth-zoo"୧⃛(๑⃙⃘◡̈︎๑⃙⃘)
"unsloth[ベース]"unsloth_zoo[base]@git+https://github.com/unslothai/unsloth"
git+https://github.com/triton-lang/triton.git@05b2c186c1b6c9a08375389d5efe9cb4c401c075#subdirectory=python/triton_kernels
uv pip install --upgrade --no-deps transformers==4.56.2 tokenizers
uv pip install --no-deps trl==0.22.2
!pip install -q brightdata-sdk
このインストール・スクリプトは、いくつかの重要な詳細を処理します。まず、uvを使用してパッケージの解決を高速化します。また、互換性の問題を避けるために特定のバージョンをピン留めし、パフォーマンスを最適化するためにUnslothのカスタムTritonカーネルをインストールし、データ収集ステップのためにBright Data SDKを含めます。
GPUセットアップの確認
インストール後、GPUが正しく検出され、十分なメモリがあることを確認しましょう:
インポート torch
# GPU 情報を得る
gpu_stats = torch.cuda.get_device_properties(0)
max_memory = round(gpu_stats.total_memory / 1024 / 1024 / 1024, 3)
print(f "GPU = {gpu_stats.名前}")
print(f "最大メモリ = {max_memory} GB")
print(f "CUDAバージョン = {torch.version.cuda}")
print(f "PyTorchバージョン = {torch.__version__}")
# 最小要件の確認
if max_memory < 15:
print("⚠️ Warning: GPT-OSS-20B 用の十分なメモリが GPU にないかもしれません")
else:
print("✅ GPU には微調整に十分なメモリがあります")
少なくとも 15GB の利用可能な GPU メモリがあるはずです。フリーColabのT4 GPUは16GBを提供しており、Unslothの最適化で私たちのニーズにぴったりです。
UnslothでGPT-OSSをロードする
それでは、Unslothの最適化ローダーを使ってGPT-OSSモデルをロードします。Unslothはすべての最適化の詳細を自動的に処理するため、このプロセスは標準的なトランスフォーマーと比較して驚くほど簡単です。

ベースモデルのロード
from unsloth import FastLanguageModel
インポートトーチ
# コンフィギュレーション
max_seq_length = 1024 # データに基づいて調整する
dtype = None # GPUに最適なdtypeを自動検出
# Unslothはロードを高速化するためにあらかじめ量子化されたモデルを提供する
fourbit_models = [
"unsloth/gpt-oss-20b-unsloth-bnb-4bit", # BitsAndBytes 4bit
"unsloth/gpt-oss-120b-unsloth-bnb-4bit"、
"unsloth/gpt-oss-20b", # MXFP4 フォーマット
"unsloth/gpt-oss-120b",
]
# モデルのロード
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "unsloth/gpt-oss-20b"、
dtype = dtype、
max_seq_length = max_seq_length、
load_in_4bit = True, # 16GBでのフィッティングに必須
full_finetuning = False, # 効率のためにLoRAを使用する。
)
print(f"✅ モデルのロードに成功しました!")
print(f "モデルサイズ:{model.num_parameters():,}パラメータ")
print(f "Using device: {model.device}")
FastLanguageModel.from_pretrained()メソッドは、裏でいくつかのことを行います。GPUの能力を自動的に検出し、それに応じて最適化し、4ビット量子化を適用してメモリ使用量を75%削減し、完全なファインチューニングの代わりにLoRAトレーニング用にモデルを設定し、メモリ効率の良いアテンションメカニズムを設定します。
LoRAアダプターの設定
LoRA(Low-Rank Adaptation)は、民生用ハードウェアでファインチューニングを実現可能にするものです。すべてのモデル・パラメータを更新する代わりに、主要なレイヤーに挿入される小さなアダプター行列のみを学習します:
モデル = FastLanguageModel.get_peft_model(
モデル
r = 8, # LoRAランク - 高いほど容量が大きいが遅い。
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj"、
「gate_proj", "up_proj", "down_proj"]、
lora_alpha = 16, # LoRAスケーリング係数
lora_dropout = 0, # 速いトレーニングのためにドロップアウトを無効にする。
bias = "none", # バイアス項をトレーニングしない
use_gradient_checkpointing = "unsloth", # メモリ節約のために重要。
random_state = 3407、
use_rslora = False, # ほとんどの場合、標準的なLoRAが最適。
loftq_config = なし、
)
# トレーニング統計の表示
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
all_params = sum(p.numel() for p in model.parameters())
trainable_percent = 100 * trainable_params / all_params
print(f"{all_params:,}のうち{trainable_params:,}のパラメータをトレーニング")
print(f "全パラメータのうち{trainable_percent:.2f}%しかない!")
print(f "保存メモリ: ~{(1 - trainable_percent/100) * 40:.1f}GB")
この構成は、学習効率とモデル容量のバランスをとっている。r=8では、全パラメータの1%未満しかトレーニングしていないにもかかわらず、優れた微調整結果を達成しています。勾配チェックポイントだけで約30%のメモリを節約でき、これはメモリ内でモデルをフィッティングするか、OOM(Out of Memory)エラーを起こすかの違いになります。
GPT-OSS推論努力制御のテスト
微調整を始める前に、GPT-OSSのユニークな推論努力機能を調べてみましょう。これにより、応答する前にモデルがどの程度 “考える “かを制御することができます:
from transformers import TextStreamer
# 数学的推論を必要とするテスト問題
messages = [
{"role":"user", "content":"x^5 + 3x^4 - 10 = 3 を解きなさい。あなたのアプローチを説明してください、}
]
# 低い推論努力でテストする
print("="*60)
print("LOW REASONING (速いが、あまり徹底していない)")
print("="*60)
inputs = tokenizer.apply_chat_template(
メッセージ
add_generation_prompt = True、
return_tensors = "pt"、
return_dict = True、
reasoning_effort = "low"、
).to("cuda")
text_streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
_ = model.generate(**inputs, max_new_tokens = 128, streamer = text_streamer)
# 高い推論努力でテストする
print("n" + "="*60)
print("HIGH REASONING (推論速度は遅いが正確)")
print("="*60)
inputs = tokenizer.apply_chat_template(
メッセージ
add_generation_prompt = True、
return_tensors = "pt"、
return_dict = True、
reasoning_effort = "high"、
).to("cuda")
_ = model.generate(**inputs, max_new_tokens = 512, streamer = text_streamer)
このコードを実行すると、”low “推論ではモデルが素早く近似解を出すのに対し、”high “推論ではステップ・バイ・ステップでより詳細な解を出すことがわかる。この機能は、プロダクション・デプロイメントにおけるスピードと正確さのバランスをとるために非常に貴重である。
Bright Dataでトレーニングデータを収集する
次に、Bright DataのWebスクレイパーAPIを使用して、高品質のトレーニングデータを収集します。このアプローチは、Bright Dataが大規模なウェブスクレイピングに必要な複雑なインフラをすべて処理するため、独自のスクレイパーを構築するよりもはるかに信頼性が高くなります。
データコレクターのセットアップ
from brightdata import bdclient
from typing import List, Dict
インポート re
インポート json
クラス DataCollector:
def __init__(self, api_token: str):
"""
ウェブスクレイピング用のBright Dataクライアントを初期化する。
引数
api_token:Bright Data API トークン。
"""
self.client = bdclient(api_token=api_token)
self.collected_data = [].
print("✅ Bright Dataクライアントが初期化されました")
def collect_documentation(self, urls: List[str]) -> List[Dict]:
"""
ドキュメントページをスクレイピングし、トレーニングデータに変換する。
このメソッドはバッチと個別のURLスクレイピングの両方を扱います、
バッチが失敗した場合、自動的に個別のリクエストにフォールバックします。
"""
print(f"{len(urls)}URLのスクレイピングを開始します...")
try:
# 効率のためにバッチスクレイピングを試みる
results = self.client.scrape(urls, data_format="markdown")
if isinstance(results, str):
# 単一結果が返される
print("単一の結果を処理中...")
training_data = self.process_single_result(results)
elif isinstance(results, list):
# 複数の結果が返される
print(f"{len(results)}の結果を処理中...")
training_data = [] (トレーニングデータ)
for i, content in enumerate(results, 1):
if content:
print(f" 結果{i}/{len(results)}を処理中")
examples = self.process_single_result(content)
training_data.extend(examples)
else:
print(f "Unexpected result type:{type(results)}")。
トレーニングデータ = [].
except Exception as e:
print(f "バッチスクレイピングに失敗しました: {e}")
print("個別のURLスクレイピングにフォールバックします...")
# フォールバック: URLを一つずつスクレイピングする
training_data = [] (トレーニングデータ)
for url in urls:
try:
print(f" スクレイピング: {url}")
content = self.client.scrape(url, data_format="markdown")
if content:
examples = self.process_single_result(content)
training_data.extend(examples)
print(f" ✓ 抽出された{len(examples)} examples")
except Exception as url_error:
print(f" ✗ 失敗しました:{url_error}")
self.collected_data = training_data
print(f"✅ 収集完了:{len(self.collected_data)}トレーニング例")
return self.collected_data
このコードは何をするのか?
- インテリジェントなフォールバック戦略:コレクターは、効率のためにまずバッチスクレイピングを試みる。ネットワークの問題やAPIの制限により)失敗した場合は、自動的に個別のURLスクレイピングにフォールバックする。
- 進捗追跡:リアルタイム更新により、スクレイピングプロセス中に何が起こっているかを正確に表示し、デバッグを容易にする。
- エラー耐性:各URLは、独自のtry-catchブロックでラップされているため、1つの失敗したURLで収集プロセス全体が停止することはありません。
- マークダウン形式:HTMLよりもきれいで、トレーニングデータに加工しやすいため、Markdown形式でデータを要求します。
Bright Dataクライアントは、いくつかの複雑なタスクを処理してくれます:
- レート制限を避けるためのIPアドレスのローテーション
- CAPTCHAの自動解決
- JavaScriptを多用したページのレンダリング
- 指数関数的バックオフによる失敗したリクエストの再試行
スクレイピングされたコンテンツをトレーニングデータに加工
優れた微調整の鍵は、クリーンで整形されたデータです。ここでは、スクレイピングされた生のコンテンツを質問と答えのペアに処理する方法を示します:
def process_single_result(self, content: str) -> List[Dict]:
"""
スクレイピングされたコンテンツをきれいなQ&Aトレーニングペアに処理します。
このメソッドは、すべての
このメソッドは、すべてのフォーマットアーチファクトを除去し、自然な響きの例を作成するために積極的なクリーニングを実行します。
"""
examples = [] (例)
# ステップ1:すべてのHTMLとMarkdownフォーマットを削除する
content = re.sub(r'<[^>]+>', '', content) # HTMLタグ
content = re.sub(r'!♪(.*?♪)', '', content) # 画像
content = re.sub(r'♪([^]]+)♪([^])♪([^])♪([^])♪([^])♪([^])♪([^])♪([^])♪([^])♪([^])♪)', r'♪1', content) # リンク
content = re.sub(r'``[^`]*``', '', content) # コードブロック
content = re.sub(r'`[^`]+`', '', content) # インラインコード
content = re.sub(r'[#*_~>`|-]+', ' ', content) # マークダウン記号
content = re.sub(r'♪(.)', r'♪1', content) # エスケープシーケンス
content = re.sub(r'https?://[^s]+', '', content) # URL
content = re.sub(r'¦S+¦w+', '', content) # ファイルパス
content = re.sub(r's+', ' ', content) # 空白文字の正規化
# ステップ2:文に分割する
sentences = re.split(r'(?<=[.!?])s+', content)
# ステップ3: ナビゲーションと定型文を除外する
clean_sentences = [].
skip_patterns = ['navigation', 'copyright', 'index'、
'table of contents', 'previous', 'next'、
'ここをクリック', 'ダウンロード', '共有'].
for sentences:
sent = sent.strip()
# 実質的な文章だけを残す
if (len(sent) > 30 and
not any(skip in sent.lower() for skip in skip_patterns)):
clean_sentences.append(sent)
# ステップ4:連続する文章からQ&Aのペアを作成する
for i in range(0, len(clean_sentences) - 1):
instruction = clean_sentences[i][:200].strip()
response = clean_sentences[i + 1][:300].strip()
# 両方の部分が充実していることを確認する
if len(instruction) > 20 and len(response) > 30:
examples.append({
"instruction": instruction、
"レスポンス": レスポンス
})
return examples
処理の仕組み
process_single_resultメソッドは、4つの重要なステップを通して、生のウェブコンテンツをクリーンなトレーニングデータに変換します:
- ステップ1 – 積極的なクリーニング:モデルを混乱させる可能性のある書式アーティファクトをすべて取り除きます:
- Markdown変換に耐えうるHTMLタグ
- テキスト理解に何の価値も与えない画像参照とリンク
- コードブロックとインラインコード(私たちはコードサンプルではなく、文章が欲しいのです)
- ノイズとなる特殊文字やエスケープシーケンス
- ステップ2 – センテンスのセグメンテーション:句読点マーカーを使って、コンテンツを個々のセンテンスに分割する。これにより、テキストの論理的な単位が得られます。
- ステップ3 – 品質フィルタリング:削除します:
- 実体のない短い文章(30文字以下
- “ここをクリック “や “次のページ “のようなナビゲーション要素
- 著作権表示のような定型文
- 一般的なウェブナビゲーションのパターンを含む文章
- ステップ4 – ペアの作成:連続する文章を質問と答えのペアとして扱うことで、トレーニング用のペアを作成します。ドキュメンテーションはしばしば、コンセプトを述べてからそれを説明するというパターンに従うため、これは有効です。
その結果、モデルに自然な流れと応答パターンを教える、きれいで文脈に沿ったトレーニングデータが得られます。
データの収集と検証
それでは、すべてをまとめてトレーニングデータを収集しましょう:
# APIトークンでコレクターを初期化する。
# トークンを取得する:/cp/api_tokens からトークンを取得する。
BRIGHTDATA_API_TOKEN = "your_brightdata_api_token_here(あなたのブライトデータAPIトークン)"
collector = DataCollector(api_token=BRIGHTDATA_API_TOKEN)
# スクレイピングするURL - Pythonドキュメントは優れたトレーニングデータになる
urls = [
"https://docs.python.org/3/tutorial/introduction.html"、
"https://docs.python.org/3/tutorial/controlflow.html"、
"https://docs.python.org/3/tutorial/datastructures.html"、
"https://docs.python.org/3/tutorial/modules.html"、
"https://docs.python.org/3/tutorial/classes.html"、
]
print("="*60)
print("データ収集開始")
print("="*60)
トレーニングデータ = collector.collect_documentation(urls)
# データを得たことを検証する
if len(training_data) == 0:
print("⚠️ ERROR: No training data collected!")
print("トラブルシューティングの手順:")
print("1.ブライトデータAPIトークンが正しいか確認する")
print("2.アカウントに十分なクレジットがあることを確認してください")
print("3.接続性をテストするために、まず単一のURLで試してください")
raise ValueError("トレーニングデータが収集されていません")
データ収集の設定を理解する
- APIトークン:APIトークンを取得するには、Bright Dataアカウントにサインアップする必要があります。ブライトデータでは無料トライアルを提供しています。
- URLの選択:Pythonのドキュメントを使用しています:
- 構造化されており、一貫性がある。
- コーディングアシスタントのトレーニングに最適な技術的コンテンツが含まれている。
- 説明のスタイルがQ&A形式にうまく変換されている。
- 一般に公開されており、倫理的に入手可能である。
- エラー処理:バリデーションチェックにより、空のデータセットで学習を進めないようにします。トラブルシューティングの手順は、よくある問題の診断に役立ちます。
最終的なデータの検証とクリーニング
トレーニングにデータを使用する前に、最後のクリーンアップを行います:
# 最終的な検証とクリーニング
def final_validation(examples: List[Dict]) -> List[Dict]:
"""
トレーニング例の最終的な検証と重複排除を行う。
"""
clean_data = [].
seen_instructions = set()
for ex in examples:
instruction = ex.get('instruction', '').strip()
response = ex.get('response', '').strip()
# 最終クリーニングパス
instruction = re.sub(r'[^a-zA-Z0-9s.˶'˶!]', '', instruction)
response = re.sub(r'[^a-zA-Z0-9s.˶?˶!]', '', response)
# 重複を削除し、品質を確保する
if (len(instruction) > 10 and
len(response) > 20 かつ
instruction not in seen_instructions):
seen_instructions.add(instruction)
clean_data.append({
"instruction": instruction、
「レスポンス": レスポンス
})
return clean_data
training_data = final_validation(training_data)
print(f"✅ 最終データセット:最終データセット:{len(training_data)}ユニークな例")
print("✅トレーニング例:")
print("="*60)
for i, example in enumerate(training_data[:3], 1):
print(f "学習例{i}:")
print(f "Q: {example['instruction']}")
print(f "A: {例題['応答']}")
バリデーションが達成すること
- 重複排除:
seen_instructionsセットは、トレーニング中にオーバーフィッティングを引き起こす可能性のある重複した質問を持たないことを保証します。 - 最終的な文字のクリーニング:基本的な句読点以外の特殊文字を削除し、テキストがきれいで一貫していることを保証します。
- 長さの検証:例題が充実していることを確認するために、最小限の長さを強制します:
- 指示は10文字以上
- 回答は20文字以上
- 品質保証:サンプル例を印刷することで、トレーニングを進める前にデータの品質を視覚的に確認することができます。
最終的な出力には、トレーニングデータとして意味のある、きれいで読みやすいQ&Aのペアが表示されるはずです。例題が無意味に見えたり、書式が悪い場合は、処理パラメータを調整するか、別のソースURLを選択する必要があるかもしれません。
プロからのアドバイス:本番環境で使用する場合は、Bright Dataの収集済みデータセットのマーケットプレイスの利用を検討してください。様々なドメインのデータセットを提供しており、時間を大幅に節約し、一貫した品質を確保することができます。
GPT-OSSトレーニングのためのデータのフォーマット
GPT-OSSは特定のチャット形式のデータを期待しています。Unslothのユーティリティを使用して、最適なトレーニング結果が得られるようにデータを適切にフォーマットします:
from unsloth.chat_templates import standardize_sharegpt
from データセット import データセット
def prepare_dataset(raw_data: List[Dict]):
"""
生のQ&Aペアを適切にフォーマットされたトレーニングデータセットに変換する。
この関数は以下の処理を行う:
1.メッセージ形式への変換
2.GPT-OSS チャットテンプレートの適用
3.フォーマットの問題の修正
"""
print("トレーニング用データセットを準備中...")
# ステップ1:チャットメッセージ形式に変換する
formatted_data = [].
for item in raw_data:
formatted_data.append({
"メッセージ": [
{"role":"user", "content": item["instruction"]}、
{"role":"アシスタント", "内容": item["応答"]}。
]
})
# ステップ2:HuggingFaceデータセットの作成
データセット = Dataset.from_list(formatted_data)
print(f"{len(データセット)}の例でデータセットを作成")
# ステップ3:ShareGPTフォーマットへの標準化
データセット = standardize_sharegpt(dataset)
この最初の部分で何が起こっているのか:
- メッセージフォーマットの変換:単純なQ&Aのペアを、GPTモデルが期待する会話形式に変換します。各トレーニング例は、ユーザーの質問とアシスタントの応答による2ターンの会話になります。
- データセット作成:HuggingFaceのデータセットクラスは、以下のような効率的なデータハンドリングを提供します:
- 大規模データセットのためのメモリマップアクセス
- 組み込みのバッチ処理とシャッフル
- HuggingFaceエコシステム全体との互換性
- ShareGPTの標準化:
standardize_sharegpt関数は、私たちのデータがチャットモデルのトレーニングのためのデファクトスタンダードとなっているShareGPTフォーマットと一致することを保証します。これはエッジケースを処理し、一貫性を保証します。
チャットテンプレートの適用
次に、GPT-OSS特有のフォーマット要件を適用します:
# ステップ4: GPT-OSS特有のチャットテンプレートを適用する
def formatting_prompts_func(examples):
"""GPT-OSSのチャットテンプレートを各例に適用します。"""
convos = examples["messages"]
テキスト = []"
for convo in convos:
# 生成プロンプトなしでテンプレートを適用 (トレーニング中)
text = tokenizer.apply_chat_template(
convo、
tokenize = False、
add_generation_prompt = False
)
text.append(text)
return {"text": texts}.
データセット = dataset.map(
formatting_prompts_func、
batched = True、
desc = "チャットテンプレートの適用"
)
テンプレートの適用を理解する
- チャットテンプレートの目的: 各モデルファミリーは、それ自身の特別なトークンとフォーマットを持っています。GPT-OSSは
<|start|>,<|message|>,<|channel|>のようなタグを使って、会話の異なる部分を区切ります。 - 世代プロンプトはありません:
add_generation_prompt=Falseに設定したのは、生成ではなく、トレーニングを行っているためです。トレーニング中、モデルには完了を待つプロンプトではなく、完了した会話を表示させたい。 - バッチ処理:
batched = Trueパラメータは、複数の例を一度に処理し、大規模データセットのフォーマット処理を大幅にスピードアップします。 - テキスト出力:トレーナーが独自の設定でトークン化を処理するため、この段階では(トークン化されない)テキストとして出力します。
フォーマットの問題の検証と修正
GPT-OSSには、チャンネルタグに関する特別な要件があり、それを検証する必要があります:
# ステップ 5: 必要であれば、チャンネルタグの検証と修正
sample_text = データセット[0]['text']
print("フォーマットチェック中...")
print(f "サンプル(最初の200文字):{sample_text[:200]}")
もし"<|channel|>"がsample_textにない場合:
print("⚠️ チャンネルタグがありません。書式を修正します。")
def fix_formatting(examples):
"""GPT-OSS互換のためにチャンネルタグを追加する。"""
fixed_texts = []
for text in examples["text"]:
# GPT-OSSはroleとmessageの間にチャンネルタグを期待する
text = text.replace(
"<|start|>assistant<|message|>"、
"<|start|>assistant<|channel|>final<|message|>"
)
fixed_texts.append(text)
return {"text": fixed_texts}.
データセット = dataset.map(
fix_formatting、
batched = True、
desc = "チャンネルタグの追加"
)
print("✅ フォーマットを修正しました")
print(f"✅ データセットができました:{len(データセット)}フォーマット例")
データセットを返す
# データセットを準備する
データセット = prepare_dataset(training_data)
チャンネル・タグが重要な理由
- チャンネルタグの機能:
<|channel|>finalタグはGPT-OSSに、これが最終的な応答であり、推論の中間ステップではないことを伝えます。これはGPT-OSS独自の推論努力制御システムの一部です。 - フォーマットの検証:タグが存在するかどうかを確認し、存在しない場合は追加します。これにより、フォーマットの不一致によるトレーニングの失敗を防ぎます。
- 自動修正:手動による介入を必要とせず、置換操作により互換性を確保します。これは、デフォルトの動作が異なる可能性のある異なるトークナイザー・バージョンを使用する場合に特に重要です。
データセット統計と検証
最後に、準備したデータセットを検証してみましょう:
# 統計情報を表示する
print("ⅳデータセットの統計:")
print(f "Number of examples:{len(データセット)}")
print(f "平均テキスト長: {sum(len(x['text']) for x in dataset) / len(dataset):.0f} chars")
# 完全にフォーマットされた例を表示する
print("Ⅾ整形された例:")
print("="*60)
print(dataset[0]['text'][:500])
print("="*60")
# すべての例が正しいフォーマットであることを確認する
format_checks = { 以下のようにします。
"has_user_tag": all("<|start|>user" in ex['text'] for ex in dataset)、
"has_assistant_tag": all("<|start|>assistant" in ex['text'] for ex in dataset)、
"has_channel_tag": all("<|channel|>" in ex['text'] for ex in dataset)、
"has_message_tags": all("<|message|>" in ex['text'] for ex in dataset)、
}
print("フォーマット検証:")
for check, passed in format_checks.items():
status = "✅" if passed else "❌"
print(f"{status} {check}: {passed}")
バリデーションで何を見るか
- 長さの統計:平均テキスト長は、トレーニングに適切なシーケンス長を設定するのに役立ちます。長すぎる場合は、切り詰めるか、より大きなmax_seq_lengthを使う必要があるかもしれません。
- フォーマットの完全性:4つのチェックはすべてパスする必要がある:
- ユーザータグは、ユーザー入力の開始位置を示します。
- アシスタントタグはモデルの応答をマークする
- Channelタグはレスポンスのタイプを指定する
- メッセージタグは実際のコンテンツを含む
- 視覚的な検査:印刷された例では、モデルが学習する内容を正確に見ることができます。以下のようになります:
<|start|>user<|message|>Your question here<|end|>
<|start|>assistant<|channel|>final<|message|>The response here<|end|> のようになります。
検証が失敗した場合、学習が正しく行われなかったり、モデルが誤ったパターンを学習してしまう可能性があります。自動修正によってほとんどの問題は処理されるはずですが、手動検査はエッジケースをキャッチするのに役立ちます。
UnslothとTRLを使ったトレーニングの設定
それではトレーニングの設定を行います。UnslothはHugging FaceのTRLライブラリとシームレスに統合されており、Unslothのスピード最適化とTRLの実績のあるトレーニングアルゴリズムという両方の長所を利用することができます。
from trl import SFTConfig, SFTTrainer
from unsloth.chat_templates import train_on_responses_only
# トレーニング設定を作成する
training_config = SFTConfig(
# 基本設定
per_device_train_batch_size = 2, # GPUメモリに応じて調整
gradient_accumulation_steps = 4, # 有効バッチサイズ = 2 * 4 = 8
warmup_steps = 5、
max_steps = 60, # クイックテスト用。
# 学習率の設定
learning_rate = 2e-4、
lr_scheduler_type = "linear"、
# 最適化の設定
optim = "adamw_8bit", # 8ビットオプティマイザでメモリを節約
weight_decay = 0.01、
# ロギングと保存
logging_steps = 1、
save_steps = 20、
output_dir = "outputs"、
# 高度な設定
seed = 3407, # 再現性のため
fp16 = True, # 混合精度トレーニング
report_to = "none", # 実験追跡のため "wandb "に設定
)
print("トレーニングの設定:")
print(f" Effective batch size: {training_config.per_device_train_batch_size * training_config.gradient_accumulation_steps}")
print(f" 総トレーニングステップ数:{training_config.max_steps}")
print(f" 学習率:{training_config.learning_rate}")
トレーナーのセットアップ
SFTTrainer (Supervised Fine-Tuning Trainer) はトレーニングの複雑さをすべて処理します:
# トレーナの初期化
trainer = SFTTrainer(
model = model、
tokenizer = tokenizer、
train_dataset = dataset、
args = training_config、
)
print("✅ トレーナーが初期化されました")
# アシスタントの回答のみを学習するように設定する
# モデル学習がユーザーの質問を生成しないようにします。
gpt_oss_kwargs = dict(
instruction_part = "<|start|>user<|message|>"、
response_part = "<|start|>assistant<|channel|>final<|message|>"
)
trainer = train_on_responses_only(
トレーナー
**gpt_oss_kwargs、
)
print("✅ 応答のみのトレーニングに設定しました")
トレーナーの設定を理解する
- SFTTrainer の統合:トレーナーはいくつかのコンポーネントを統合しています:
- LoRAで設定されたモデル
- テキストを処理するためのトークナイザー
- 準備したデータセット
- 学習設定パラメータ
- レスポンスのみのトレーニング:これはチャットモデルにとって重要です。
train_on_responses_onlyを使用することで、以下のことが保証されます:- モデルはアシスタントの応答に対してのみ損失を計算する
- ユーザーの質問を生成することを学習しない
- トレーニングがより効率的になる(最適化するトークンが少なくなる)
- モデルは多様なユーザー入力を理解する能力を維持する
- GPT-OSS固有のタグ:指示部分と応答部分は、フォーマットされたデータに含まれるものと正確に一致しなければなりません。これらのタグは、トレーナーに、無視すべきもの(ユーザー入力)とトレーニングすべきもの(アシスタント応答)の分岐点を教えます。
トレーニングマスクの検証
ユーザーの質問ではなく、アシスタントの応答のみをトレーニングしていることを確認することが重要です:
# トレーニングマスクが正しいか確認する
print("トレーニングマスクの検証...")
sample = trainer.train_dataset[0]を実行します。
# ラベルをデコードして何をトレーニングしているか確認する
# 100はトレーニングしていない(マスクされている)トークンを示す
visible_tokens = [].
for token_id, label_id in zip(sample["input_ids"], sample["labels"]):
if label_id != -100:
visible_tokens.append(token_id)
if visible_tokens:
decoded = tokenizer.decode(visible_tokens)
print(f "Training on: {decoded[:200]}...")
print("✅ マスクが確認されました - レスポンスに対するトレーニングのみ")
else:
print("⚠️ Warning: No visible training tokens detected")
マスク検証でわかること
- 100ラベル:PyTorchでは、-100は損失関数にこれらのトークンを無視するように指示する特別な値です。これが応答のみの学習を実装する方法です:
- ユーザー入力トークンは-100(無視)とラベル付けされます。
- アシスタント応答トークンは実際のトークンIDのまま(学習済み)
- 可視トークンのチェック:マスクされていないトークンだけを抽出することで、モデルが何から学習するかを正確に見ることができます。ユーザーの質問ではなく、アシスタントの応答テキストだけが見えるはずです。
- なぜこれが重要なのか:適切なマスキングがなければ
- モデルは回答ではなく、ユーザーの質問を生成することを学習するかもしれません。
- トレーニングの効率が悪くなります(不要なトークンを最適化します)。
- モデルはユーザの入力をエコーするような望ましくない振る舞いをする可能性がある。
- デバッグのヒントデコードされたテキストにユーザー入力があったら、チェックしてください:
instruction_partとresponse_partの文字列が正確に一致している。- データセットのフォーマットに必要なタグがすべて含まれている
- トークナイザーがチャットテンプレートを正しく適用しているか
トレーニングの開始
すべての設定が完了し、トレーニングを開始する準備ができました。GPUのメモリ使用量をモニターし、トレーニングの進捗を追跡してみましょう:
インポート時間
インポート torch
# トレーニングの前にGPUキャッシュをクリアする
torch.cuda.empty_cache()
# GPUの初期状態を記録する
start_gpu_memory = torch.cuda.max_memory_reserved() / 1024**3
start_time = time.time()
print("="*60)
print("STARTING TRAINING")
print("="*60)
print(f "初期GPUメモリ予約: {start_gpu_memory:.2f} GB")
print(f"{training_config.max_steps}ステップのトレーニング...")
print("トレーニングの進捗状況:")
# トレーニング開始
trainer_stats = trainer.train()
# トレーニング統計量を計算する
training_time = time.time() - start_time
final_gpu_memory = torch.cuda.max_memory_reserved() / 1024**3
memory_used = final_gpu_memory - start_gpu_memory
print("n" + "="*60)
print("TRAINING COMPLETE")
print("="*60)
print(f "かかった時間: {training_time/60:.1f} 分")
print(f "最終損失: {trainer_stats.metrics['train_loss']:.4f}")
print(f "トレーニングに使用したGPUメモリ: {memory_used:.2f} GB")
print(f "ピークGPUメモリ: {final_gpu_memory:.2f} GB")
print(f "トレーニング速度:{trainer_stats.metrics.get('train_steps_per_second', 0):.2f} steps/second")
トレーニングメトリクスを理解する
- GPUメモリ管理:
- トレーニング前にキャッシュをクリアすることで、未使用メモリが解放されます。
- メモリ使用量を監視することで、将来の実行のためにバッチサイズを最適化することができます。
- 開始と終了の差は実際のトレーニングオーバーヘッドを示します。
- ピークメモリは、OOMエラーにどれだけ近いかを示します。
- トレーニングの進捗を示します:
- 損失:時間の経過とともに減少するはずである。早期に停滞する場合は、学習率が低すぎる可能性がある。
- ステップ/秒: 大きなデータセットでの学習時間の見積もりに役立つ。
- 所要時間:T4 GPUの場合、60ステップで10-15分程度。
- トレーニング中の注意点
- 損失が着実に減少している(良好)
- 損失が不規則に跳ね上がる(学習率が高すぎる)
- 損失が変化しない(学習率が低すぎるか、データに問題がある)
- メモリーエラー(バッチサイズまたはシーケンス長を減らす)
- パフォーマンスへの期待
- T4 GPU:0.5~1.0ステップ/秒
- V100:1.5~2.5ステップ/秒
- A100:3~5ステップ/秒
トレーニングはエラーなく完了し、損失が当初の2-3程度から減少し、終了時には1.0以下になることが確認できるはずです。
微調整したモデルをテストする
いよいよ、ファインチューニングが実際にうまくいったかどうかをテストします!包括的なテスト関数を作成し、Python関連の様々な質問に対してモデルを評価します:
from transformers import TextStreamer
def test_model(prompt: str, reasoning_effort: str = "medium", max_length: int = 256):
"""
与えられたプロンプトで微調整されたモデルをテストします。
引数
prompt:質問または指示
reasoning_effort:"low"、"medium"、または "high"
max_length:生成するトークンの最大値
戻り値
生成されたレスポンス
"""
# メッセージフォーマットを作成する
メッセージ = [
{"role":"system", "content":「あなたはPythonエキスパートのアシスタントです、}
{"role":"user", "content": prompt}.
]
# チャットテンプレートを適用する
inputs = tokenizer.apply_chat_template(
メッセージ
add_generation_prompt = True、
return_tensors = "pt"、
return_dict = True、
reasoning_effort = reasoning_effort、
).to("cuda")
# リアルタイム出力のためのストリーミングのセットアップ
streamer = TextStreamer(
tokenizer、
skip_prompt=True、
skip_special_tokens=True
)
# レスポンスを生成する
outputs = model.generate(
**inputs、
max_new_tokens = max_length、
streamer = streamer、
temperature = 0.7、
top_p = 0.9、
do_sample = True、
)
# レスポンスをデコードして返す
レスポンス = tokenizer.decode(outputs[0], skip_special_tokens=True)
レスポンスを返す
# Pythonの様々なトピックに関するテスト
test_questions = [
"Pythonジェネレータとは何ですか、そしていつ使うべきですか?"、
"PythonでCSVファイルを読むには?"、
「Pythonのasync/awaitを簡単な例で説明してください、
「Pythonでリストとタプルの違いは何ですか?
「Pythonで例外を正しく処理するには?
]
print("="*60)
print("TESTING FINE-TUNED MODEL")
print("="*60)
for i, question in enumerate(test_questions, 1):
print(f"◆n{'='*60}")
print(f "Question {i}: {question}")
print(f"{'='*60}")
print("レスポンス:")
_ = test_model(question, reasoning_effort="medium")
print()

微調整前と比較して、モデルがより詳細でPython固有の回答を提供するようになったことに気づくはずです。回答はトレーニングデータのドキュメントスタイルと技術的な深さを反映しているはずです。
異なる推論レベルのテスト
推論の努力が回答にどのような影響を与えるかもテストしてみましょう:
complex_question = "エラトステネスのふるいを使ってnまでのすべての素数を見つけるPython関数を書く"
print("="*60)
print("TESTING REASONING EFFORT LEVELS")
print("="*60)
for effort in ["low", "medium", "high"]:
print(f"◆n{'='*40}")
print(f "推論努力:{effort.upper()}")。
print(f"{'='*40}")
_ = test_model(complex_question, reasoning_effort=effort, max_length=300)
print()
コードを実行すると、”low “は基本的な実装を提供し、”medium “は説明とコードの良いバランスを提供し、”high “は詳細な説明と最適化を含むことがわかります。
モデルの保存とデプロイ
微調整に成功したら、将来の使用のためにモデルを保存したいと思うでしょう。デプロイのニーズに応じて、いくつかのオプションがあります:
ローカルに保存する
インポートos
# 保存用のディレクトリを作成する
save_dir = "gpt-oss-python-expert"
os.makedirs(save_dir, exist_ok=True)
print("モデルをローカルに保存...")
# オプション 1: LoRA アダプタのみ保存 (小さい, ~200MB)
lora_save_dir = f"{save_dir}-lora"
model.save_pretrained(lora_save_dir)
tokenizer.save_pretrained(lora_save_dir)
print(f"✅ LoRAアダプタを{lora_save_dir}に保存")
# サイズをチェックする
lora_size = sum(
os.path.getsize(os.path.join(lora_save_dir, f))
for f in os.listdir(lora_save_dir)
)/ (1024**2)
print(f" サイズ: {lora_size:.1f} MB")
# オプション2: マージしたモデルを保存する (フルサイズ, ~20GB)
merged_save_dir = f"{save_dir}-merged"
model.save_pretrained_merged(
merged_save_dir、
tokenizer、
save_method = "merged_16bit" # オプション:"merged_16bit", "mxfp4"
)
print(f"✅ マージしたモデルを{merged_save_dir}に保存")
Hugging Face Hubにプッシュする
共有とデプロイを簡単にするために、モデルをHugging Faceにプッシュします:
from huggingface_hub import login
# Hugging Faceにログインする(トークンが必要です)
# トークンを取得する: https://huggingface.co/settings/tokens
login(token="hf_...") # あなたのトークンに置き換える
# LoRAアダプタをプッシュする(共有のために推奨)
model_name = "あなたのユーザー名/gpt-oss-python-expert-lora"
print(f "Pushing LoRA adapters to {model_name}...")
model.push_to_hub(
model_name、
use_auth_token=True、
commit_message="PythonドキュメントのGPT-OSSを微調整しました"
)
tokenizer.push_to_hub(
モデル名
use_auth_token=True
)
print(f"✅ モデルが利用可能な場所: https://huggingface.co/{model_name}")
# オプションでマージされたモデルをプッシュする(時間がかかる)
if False: # 完全なモデルをプッシュしたい場合はTrueをセットする
merged_model_name = "あなたのユーザー名/gpt-oss-python-expert"
model.push_to_hub_merged(
merged_model_name、
トークナイザー
save_method = "mxfp4", # サイズを小さくするための4ビット
use_auth_token=True
)
微調整したモデルのロード
推論のために後でモデルをロードする方法を示します:
from unsloth import FastLanguageModel
# ローカルディレクトリからロード
model, tokenizer = FastLanguageModel.from_pretrained()
model_name = "gpt-oss-python-expert-lora"、
max_seq_length = 1024、
dtype = None、
load_in_4bit = True、
)
# またはHugging Face Hubからロード
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "your-username/gpt-oss-python-expert-lora"、
max_seq_length = 1024、
dtype = None、
load_in_4bit = True、
)
print("✅ モデルがロードされ、推論の準備ができました!")
より良い結果を得るための最適化戦略
モデルのファインチューニングを最適化するために有用と思われる戦略をいくつか紹介します:
メモリ最適化テクニック
メモリ最適化テクニック 限られたGPUメモリで作業する場合、これらのテクニックが成功とOOMエラーの分かれ目になります:
# 1.勾配チェックポイント - メモリと計算を交換する
model.gradient_checkpointing_enable()
# 2.もしデータが許すのであれば、シーケンス長を短くする
max_seq_length = 512 # 1024の代わりに
# 3.より小さなバッチサイズでより多くの蓄積を行う
per_device_train_バッチサイズ = 1
gradient_accumulation_steps = 16 # それでも有効なバッチサイズは16
# 4.メモリ効率の良いアテンションを有効にする(サポートされている場合)
モデル.config.use_flash_attention_2 = True
# 5.トレーニング中に定期的にキャッシュをクリアする
インポート gc
gc.collect()
torch.cuda.empty_cache()
トレーニングのベストプラクティス
経験上、これらのプラクティスはより良い微調整結果につながります:
- 小さく始める:まず100例でテストする。それでうまくいったら、徐々に規模を拡大する。
- メトリクスを監視する:オーバーフィッティングに注意する。トレーニングの損失が下がっても検証の損失が上がるようなら、早めにやめる。
- データを混ぜる:ドメイン固有のデータと一般的なインストラクションデータを組み合わせることで、致命的な忘却を防ぐ。
- 学習率のスケジュール:デフォルトの2e-4から始める。小さいデータセットでは5e-5で良い結果を見たことがある。
- チェックポイント戦略:最適なチェックポイントからリカバリーできるように、Nステップごとに保存する:
training_config = SFTConfig(
save_steps = 50、
save_total_limit = 3, # 最良のチェックポイントを3つだけ保存する
load_best_model_at_end = True、
metric_for_best_model = "loss"、
)
スピードの最適化
学習速度を最大化する:
# 学習速度を上げるためにPyTorch 2.0のコンパイルを使う
if hasattr(torch, 'compile'):
model = torch.compile(model)
print("✅ モデルをコンパイルして学習を高速化")
# Ampere GPU (A100, RTX 30xx)でTF32を有効にする。
torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = True
# メモリが許す限り大きなバッチサイズを使う
# バッチサイズを大きくすると一般的に高速に学習できる
最適バッチサイズ = find_optimal_batch_size(model, max_memory=0.9)
本番環境への導入オプション
モデルを微調整したら、いくつかのデプロイメントオプションがあります:
FastAPIによる迅速なローカルAPI
ラピッドプロトタイピングのために、シンプルなAPIを作成します:
# 名前を付けて保存: api.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import uvicorn
from unsloth import FastLanguageModel
app = FastAPI()
# 起動時にモデルを一度ロード
model, tokenizer = None, None
app.on_event("startup")
非同期 def load_model():
global model, tokenizer
model, tokenizer = FastLanguageModel.from_pretrained(
"gpt-oss-python-expert-lora"、
max_seq_length = 1024、
load_in_4bit = True、
)
class GenerateRequest(BaseModel):
prompt: str
reasoning_effort: str = "medium"
max_tokens: int = 256
app.post("/generate")
async def generate(request: GenerateRequest):
if not model:
raise HTTPException(status_code=503, detail="モデルが読み込まれていません")
messages = [{"role":"user", "content": request.prompt}]。
inputs = tokenizer.apply_chat_template(
メッセージ
add_generation_prompt = True、
return_tensors = "pt"、
reasoning_effort = request.reasoning_effort、
).to("cuda")
outputs = model.generate(
**inputs、
max_new_tokens = request.max_tokens、
temperature = 0.7、
)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
return {"response": response}.
# Run with: uvicorn api:app --host 0.0.0.0 --port 8000
vLLMを使った本番環境での展開
高スループットの本番環境では、vLLMが優れたパフォーマンスを発揮する:
# vLLMのインストール
vLLMのインストール
# モデルの配信
python -m vllm.entrypoints.openai.api_server
--model gpt-oss-python-expert-expert-merged
--tensor-parallel-size 1 ㎟ -max-model-len 1024
-max-model-len 1024
--dtype float16
クラウド展開オプション
各クラウドプラットフォームには利点があります:
- 最も簡単なセットアップ – プッシュしてデプロイするだけ
- テストや小規模生産に最適
- 自動スケーリングが可能
- サーバーレスのデプロイに最適
- 実際の使用量に対してのみ支払い
- バースト的なワークロードに最適
- 24時間365日のサービス提供に最適なコスト効率
- 環境を完全にコントロール
- 高スループットのアプリケーションに最適
- AWSと完全に統合されたエンタープライズグレード
- 高度なモニタリングとロギング
- 大規模なプロダクションデプロイメントに最適
よくある問題のトラブルシューティング
Unsloth が最適化されていても、いくつかの問題が発生する可能性があります。ここでは、最も一般的な問題を解決する方法を説明します:
CUDA メモリ不足エラー
これは大規模なモデルを微調整する際に最も一般的な問題です:
# 解決策1:バッチサイズを小さくする
training_config = SFTConfig(
per_device_train_batch_size = 1, # 最小バッチサイズ
gradient_accumulation_steps = 8, # 累積で補正する
)
# 解決策2:シーケンス長を短くする
max_seq_length = 512 # 1024の代わりに
# 解決策3: より積極的な量子化を使う
モデル = FastLanguageModel.from_pretrained(
model_name = "unsloth/gpt-oss-20b"、
load_in_4bit = True、
use_double_quant = True, # さらにメモリを節約する
)
# 解決策4:すべてのメモリ最適化を有効にする
use_gradient_checkpointing = "unsloth"(勾配なしチェックポインティング)。
use_flash_attention = True
トレーニング速度を遅くする
トレーニングに時間がかかりすぎる場合:
# Unslothの完全な最適化スイートを使う
model = FastLanguageModel.get_peft_model(
モデル
use_gradient_checkpointing = "unsloth", # クリティカル
lora_dropout = 0, # 0はドロップアウトより速い
bias = "none", # "none "の方がトレーニングバイアスより速い
use_rslora = False, # 標準的なLoRAの方が速い
)
# 正しいd型を使用しているかチェックする
torch.set_float32_matmul_precision('medium') # または 'high'.
学習していないモデル
損失が減っていない場合
- データフォーマットをチェックしてください:データがGPT-OSSフォーマットと正確に一致しているか確認する
- 応答のマスキングを確認する:応答のみをトレーニングしていることを確認する
- 学習率を調整する:2e-4 の代わりに 5e-4 または 1e-4 を試す
- データの質を上げる:質の低い例を削除する
- データを増やす:通常、100例よりも500例以上の方が効果的
一貫性のない出力
モデルが一貫性のない、あるいは質の低い出力を生成する場合:
# より一貫性のある出力を得るために低い温度を使う
outputs = model.generate(
temperature = 0.3, # 低いほど一貫性がある
top_p = 0.9、
repetition_penalty = 1.1, # 繰り返しを減らす
)
# ステップ数を増やすための微調整
max_steps = 200 # 60の代わりに
# より質の高いデータフィルタリングを使用する
min_response_length = 50 # 30の代わりに
結論
結論
GPT-OSSのファインチューニングは、Unslothのスピードと、トップクラスのAIトレーニングデータ会社が提供する高品質で構造化されたトレーニングデータを組み合わせることで、より速く簡単になります。AIのためのBright Dataのソリューションを使用することで、効果的な微調整に必要な信頼性の高いデータへのアクセスが保証され、あらゆるユースケースに合わせたAIモデルを構築することができます。
AIのためのデータ抽出戦略についてさらに詳しくお知りになりたい方は、以下のリソースもご覧ください: