diff --git a/ja/3_parameter_efficient_finetuning/README.md b/ja/3_parameter_efficient_finetuning/README.md new file mode 100644 index 00000000..f375fc32 --- /dev/null +++ b/ja/3_parameter_efficient_finetuning/README.md @@ -0,0 +1,39 @@ +# パラメータ効率の良い微調整 (PEFT) + +言語モデルが大きくなるにつれて、従来の微調整はますます困難になります。1.7Bパラメータのモデルの完全な微調整には、かなりのGPUメモリが必要であり、別々のモデルコピーを保存することが高価であり、モデルの元の能力を破壊的に忘れるリスクがあります。パラメータ効率の良い微調整(PEFT)メソッドは、モデルパラメータの小さなサブセットのみを変更し、ほとんどのモデルを固定したままにすることで、これらの課題に対処します。 + +従来の微調整は、トレーニング中にすべてのモデルパラメータを更新しますが、これは大規模なモデルには実用的ではありません。PEFTメソッドは、元のモデルサイズの1%未満のパラメータを使用してモデルを適応させるアプローチを導入します。この劇的な学習可能なパラメータの削減により、次のことが可能になります: + +- 限られたGPUメモリを持つ消費者向けハードウェアでの微調整 +- 複数のタスク固有の適応を効率的に保存 +- 低データシナリオでのより良い一般化 +- より速いトレーニングと反復サイクル + +## 利用可能なメソッド + +このモジュールでは、2つの人気のあるPEFTメソッドを紹介します: + +### 1️⃣ LoRA (低ランク適応) + +LoRAは、効率的なモデル適応のためのエレガントなソリューションを提供する最も広く採用されているPEFTメソッドとして浮上しました。モデル全体を変更する代わりに、**LoRAはモデルの注意層に学習可能な行列を注入します。**このアプローチは、通常、完全な微調整と比較して約90%の学習可能なパラメータを削減しながら、同等の性能を維持します。[LoRA (低ランク適応)](./lora_adapters.md)セクションでLoRAを詳しく見ていきます。 + +### 2️⃣ プロンプトチューニング + +プロンプトチューニングは、モデルの重みを変更するのではなく、**入力に学習可能なトークンを追加する**さらに軽量なアプローチを提供します。プロンプトチューニングはLoRAほど人気はありませんが、モデルを新しいタスクやドメインに迅速に適応させるための便利な技術です。[プロンプトチューニング](./prompt_tuning.md)セクションでプロンプトチューニングを詳しく見ていきます。 + +## 演習ノートブック + +| タイトル | 説明 | 演習 | リンク | Colab | +|-------|-------------|----------|------|-------| +| LoRA微調整 | LoRAアダプタを使用してモデルを微調整する方法を学ぶ | 🐢 LoRAを使用してモデルをトレーニング
🐕 異なるランク値で実験
🦁 完全な微調整と性能を比較 | [ノートブック](./notebooks/finetune_sft_peft.ipynb) | Open In Colab | +| LoRAアダプタの読み込み | トレーニングされたLoRAアダプタを読み込んで使用する方法を学ぶ | 🐢 事前学習されたアダプタを読み込む
🐕 アダプタをベースモデルと統合
🦁 複数のアダプタ間を切り替える | [ノートブック](./notebooks/load_lora_adapter.ipynb) | Open In Colab | + + +## リソース +- [PEFTドキュメント](https://huggingface.co/docs/peft) +- [LoRA論文](https://arxiv.org/abs/2106.09685) +- [QLoRA論文](https://arxiv.org/abs/2305.14314) +- [プロンプトチューニング論文](https://arxiv.org/abs/2104.08691) +- [Hugging Face PEFTガイド](https://huggingface.co/blog/peft) +- [2024年にHugging FaceでLLMを微調整する方法](https://www.philschmid.de/fine-tune-llms-in-2024-with-trl) +- [TRL](https://huggingface.co/docs/trl/index) diff --git a/ja/3_parameter_efficient_finetuning/lora_adapters.md b/ja/3_parameter_efficient_finetuning/lora_adapters.md new file mode 100644 index 00000000..1eef41eb --- /dev/null +++ b/ja/3_parameter_efficient_finetuning/lora_adapters.md @@ -0,0 +1,124 @@ +# LoRA (低ランク適応) + +LoRAは最も広く採用されているPEFTメソッドとなっています。これは、注意重み行列に小さなランク分解行列を追加することで機能し、通常、学習可能なパラメータを約90%削減します。 + +## LoRAの理解 + +LoRA(低ランク適応)は、事前学習されたモデルの重みを固定し、学習可能なランク分解行列をモデルの層に注入するパラメータ効率の良い微調整技術です。微調整中にすべてのモデルパラメータを学習する代わりに、LoRAは低ランク分解を通じて重みの更新を小さな行列に分解し、学習可能なパラメータの数を大幅に削減しながらモデルの性能を維持します。例えば、GPT-3 175Bに適用した場合、LoRAは学習可能なパラメータを10,000倍、GPUメモリ要件を3倍削減しました。LoRAについての詳細は[LoRA論文](https://arxiv.org/pdf/2106.09685)を参照してください。 + +LoRAは、通常、注意重みに焦点を当てて、トランスフォーマーレイヤーにランク分解行列のペアを追加することで機能します。推論中に、これらのアダプタ重みはベースモデルと統合され、追加の遅延オーバーヘッドが発生しません。LoRAは、大規模な言語モデルを特定のタスクやドメインに適応させるのに特に役立ち、リソース要件を管理可能に保ちます。 + +## LoRAアダプタの読み込み + +アダプタは、load_adapter()を使用して事前学習されたモデルに読み込むことができ、これは重みが統合されていない異なるアダプタを試すのに便利です。set_adapter()関数を使用してアクティブなアダプタ重みを設定します。ベースモデルに戻るには、unload()を使用してすべてのLoRAモジュールをアンロードできます。これにより、異なるタスク固有の重み間の切り替えが容易になります。 + +```python +from transformers import AutoModelForCausalLM +from peft import PeftModel + +base_model = AutoModelForCausalLM.from_pretrained("") +peft_model_id = "" +model = PeftModel.from_pretrained(base_model, peft_model_id) +``` + +![lora_load_adapter](./images/lora_adapter.png) + +## LoRAアダプタの統合 + +LoRAでトレーニングした後、アダプタ重みをベースモデルに統合して、デプロイを容易にすることができます。これにより、統合された重みを持つ単一のモデルが作成され、推論中にアダプタを別々に読み込む必要がなくなります。 + +統合プロセスには、メモリ管理と精度に注意が必要です。ベースモデルとアダプタ重みの両方を同時に読み込む必要があるため、十分なGPU/CPUメモリが利用可能であることを確認してください。`transformers`の`device_map="auto"`を使用すると、自動メモリ管理が容易になります。トレーニング中に使用した精度(例:float16)を一貫して維持し、統合されたモデルを同じ形式で保存してデプロイします。デプロイ前に、アダプタベースのバージョンと出力および性能メトリックを比較して、統合されたモデルを検証してください。 + +アダプタは、異なるタスクやドメイン間の切り替えにも便利です。ベースモデルとアダプタ重みを別々に読み込むことができます。これにより、異なるタスク固有の重み間の迅速な切り替えが可能になります。 + +## 実装ガイド + +`notebooks/`ディレクトリには、さまざまなPEFTメソッドを実装するための実践的なチュートリアルと演習が含まれています。基本的な紹介には`load_lora_adapter_example.ipynb`を、LoRAとSFTを使用したモデルの微調整について詳しく知りたい場合は`lora_finetuning.ipynb`を参照してください。 + +PEFTメソッドを実装する際は、LoRAのランク値(4-8)を小さく設定し、トレーニング損失を監視します。検証セットを使用して過学習を防ぎ、可能であればフルファインチューニングのベースラインと結果を比較します。異なるメソッドの有効性はタスクによって異なるため、実験が重要です。 + +## OLoRA + +[OLoRA](https://arxiv.org/abs/2406.01775)は、QR分解を使用してLoRAアダプタを初期化します。OLoRAは、QR分解の係数によってモデルのベース重みを変換します。つまり、トレーニングを行う前に重みを変換します。このアプローチは、安定性を大幅に向上させ、収束速度を加速し、最終的に優れた性能を達成します。 + +## TRLとPEFTの使用 + +PEFTメソッドは、TRL(Transformers Reinforcement Learning)と組み合わせて効率的な微調整を行うことができます。この統合は、RLHF(Reinforcement Learning from Human Feedback)に特に有用であり、メモリ要件を削減します。 + +```python +from peft import LoraConfig +from transformers import AutoModelForCausalLM + +# PEFT設定でモデルを読み込む +lora_config = LoraConfig( + r=16, + lora_alpha=32, + lora_dropout=0.05, + bias="none", + task_type="CAUSAL_LM" +) + +# 特定のデバイスにモデルを読み込む +model = AutoModelForCausalLM.from_pretrained( + "your-model-name", + load_in_8bit=True, # オプション: 8ビット精度を使用 + device_map="auto", + peft_config=lora_config +) +``` + +上記では、`device_map="auto"`を使用してモデルを自動的に適切なデバイスに割り当てました。また、`device_map={"": device_index}`を使用してモデルを特定のデバイスに手動で割り当てることもできます。メモリ使用量を効率的に保ちながら、複数のGPUにトレーニングをスケールすることもできます。 + +## 基本的な統合実装 + +LoRAアダプタをトレーニングした後、アダプタ重みをベースモデルに統合することができます。以下はその方法です: + +```python +import torch +from transformers import AutoModelForCausalLM +from peft import PeftModel + +# 1. ベースモデルを読み込む +base_model = AutoModelForCausalLM.from_pretrained( + "base_model_name", + torch_dtype=torch.float16, + device_map="auto" +) + +# 2. アダプタを持つPEFTモデルを読み込む +peft_model = PeftModel.from_pretrained( + base_model, + "path/to/adapter", + torch_dtype=torch.float16 +) + +# 3. アダプタ重みをベースモデルと統合 +try: + merged_model = peft_model.merge_and_unload() +except RuntimeError as e: + print(f"統合に失敗しました: {e}") + # フォールバック戦略またはメモリ最適化を実装 + +# 4. 統合されたモデルを保存 +merged_model.save_pretrained("path/to/save/merged_model") +``` + +保存されたモデルのサイズに不一致がある場合は、トークナイザーも保存していることを確認してください: + +```python +# モデルとトークナイザーの両方を保存 +tokenizer = AutoTokenizer.from_pretrained("base_model_name") +merged_model.save_pretrained("path/to/save/merged_model") +tokenizer.save_pretrained("path/to/save/merged_model") +``` + +## 次のステップ + +⏩ [プロンプトチューニング](prompt_tuning.md)ガイドに進み、プロンプトチューニングでモデルを微調整する方法を学びましょう。 +⏩ [LoRAアダプタの読み込みチュートリアル](./notebooks/load_lora_adapter.ipynb)に進み、LoRAアダプタを読み込む方法を学びましょう。 + +# リソース + +- [LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS](https://arxiv.org/pdf/2106.09685) +- [PEFTドキュメント](https://huggingface.co/docs/peft) +- [Hugging FaceのPEFTに関するブログ記事](https://huggingface.co/blog/peft) diff --git a/ja/3_parameter_efficient_finetuning/notebooks/finetune_sft_peft.ipynb b/ja/3_parameter_efficient_finetuning/notebooks/finetune_sft_peft.ipynb new file mode 100644 index 00000000..8fea097a --- /dev/null +++ b/ja/3_parameter_efficient_finetuning/notebooks/finetune_sft_peft.ipynb @@ -0,0 +1,516 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "z-6LLOPZouLg" + }, + "source": [ + "# Hugging Face TRLを使用してLoRAアダプタでLLMを微調整する方法\n", + "\n", + "このノートブックでは、LoRA(低ランク適応)アダプタを使用して大規模言語モデルを効率的に微調整する方法を示します。LoRAは、次のようなパラメータ効率の良い微調整技術です:\n", + "- 事前学習されたモデルの重みを固定\n", + "- 注意層に小さな学習可能なランク分解行列を追加\n", + "- 通常、学習可能なパラメータを約90%削減\n", + "- メモリ効率を維持しながらモデル性能を維持\n", + "\n", + "以下の内容をカバーします:\n", + "1. 開発環境のセットアップとLoRA設定\n", + "2. アダプタトレーニング用のデータセットの作成と準備\n", + "3. `trl`と`SFTTrainer`を使用してLoRAアダプタで微調整\n", + "4. モデルのテストとアダプタの統合(オプション)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fXqd9BXgouLi" + }, + "source": [ + "## 1. 開発環境のセットアップ\n", + "\n", + "最初のステップは、Hugging FaceライブラリとPytorchをインストールすることです。`trl`、`transformers`、`datasets`を含みます。`trl`について聞いたことがない場合でも心配ありません。これは`transformers`と`datasets`の上に構築された新しいライブラリで、微調整、RLHF、オープンLLMの調整を容易にします。\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tKvGVxImouLi" + }, + "outputs": [], + "source": [ + "# Google Colabでの要件のインストール\n", + "# !pip install transformers datasets trl huggingface_hub\n", + "\n", + "# Hugging Faceへの認証\n", + "\n", + "from huggingface_hub import login\n", + "\n", + "login()\n", + "\n", + "# 便利のため、Hugging Faceのトークンを環境変数HF_TOKENとして設定できます" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XHUzfwpKouLk" + }, + "source": [ + "## 2. データセットの読み込み" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "id": "z4p6Bvo7ouLk" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "DatasetDict({\n", + " train: Dataset({\n", + " features: ['full_topic', 'messages'],\n", + " num_rows: 2260\n", + " })\n", + " test: Dataset({\n", + " features: ['full_topic', 'messages'],\n", + " num_rows: 119\n", + " })\n", + "})" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# サンプルデータセットの読み込み\n", + "from datasets import load_dataset\n", + "\n", + "# TODO: パスと名前のパラメータを使用してデータセットと設定を定義\n", + "dataset = load_dataset(path=\"HuggingFaceTB/smoltalk\", name=\"everyday-conversations\")\n", + "dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9TOhJdtsouLk" + }, + "source": [ + "## 3. `trl`と`SFTTrainer`を使用してLLMをLoRAで微調整\n", + "\n", + "`trl`の[SFTTrainer](https://huggingface.co/docs/trl/sft_trainer)は、[PEFT](https://huggingface.co/docs/peft/en/index)ライブラリを通じてLoRAアダプタとの統合を提供します。このセットアップの主な利点は次のとおりです:\n", + "\n", + "1. **メモリ効率**:\n", + " - アダプタパラメータのみがGPUメモリに保存されます\n", + " - ベースモデルの重みは固定され、低精度で読み込むことができます\n", + " - 大規模モデルの消費者向けGPUでの微調整が可能\n", + "\n", + "2. **トレーニング機能**:\n", + " - 最小限のセットアップでネイティブPEFT/LoRA統合\n", + " - さらにメモリ効率を向上させるためのQLoRA(量子化LoRA)サポート\n", + "\n", + "3. **アダプタ管理**:\n", + " - チェックポイント中のアダプタ重みの保存\n", + " - ベースモデルにアダプタを統合する機能\n", + "\n", + "例としてLoRAを使用します。これは、LoRAと4ビット量子化を組み合わせて、性能を犠牲にせずにメモリ使用量をさらに削減します。セットアップには次の手順が必要です:\n", + "1. LoRA設定(ランク、アルファ、ドロップアウト)を定義\n", + "2. PEFT設定でSFTTrainerを作成\n", + "3. アダプタ重みをトレーニングして保存\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 必要なライブラリをインポート\n", + "from transformers import AutoModelForCausalLM, AutoTokenizer\n", + "from datasets import load_dataset\n", + "from trl import SFTConfig, SFTTrainer, setup_chat_format\n", + "import torch\n", + "\n", + "device = (\n", + " \"cuda\"\n", + " if torch.cuda.is_available()\n", + " else \"mps\" if torch.backends.mps.is_available() else \"cpu\"\n", + ")\n", + "\n", + "# モデルとトークナイザーを読み込む\n", + "model_name = \"HuggingFaceTB/SmolLM2-135M\"\n", + "\n", + "model = AutoModelForCausalLM.from_pretrained(\n", + " pretrained_model_name_or_path=model_name\n", + ").to(device)\n", + "tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name_or_path=model_name)\n", + "\n", + "# チャット形式を設定\n", + "model, tokenizer = setup_chat_format(model=model, tokenizer=tokenizer)\n", + "\n", + "# 微調整の名前を設定\n", + "finetune_name = \"SmolLM2-FT-MyDataset\"\n", + "finetune_tags = [\"smol-course\", \"module_1\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZbuVArTHouLk" + }, + "source": [ + "`SFTTrainer`は、`peft`とのネイティブ統合をサポートしており、例えばLoRAを使用してLLMを効率的に微調整するのが非常に簡単です。`LoraConfig`を作成し、トレーナーに提供するだけです。\n", + "\n", + "
\n", + "

演習: 微調整のためのLoRAパラメータを定義

\n", + "

Hugging Faceのハブからデータセットを取得し、それを使用してモデルを微調整します。

\n", + "

難易度レベル

\n", + "

🐢 一般的なパラメータを使用して任意の微調整を行う

\n", + "

🐕 パラメータを調整し、重みとバイアスでレビューする

\n", + "

🦁 パラメータを調整し、推論結果の変化を示す

\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "blDSs9swouLk" + }, + "outputs": [], + "source": [ + "from peft import LoraConfig\n", + "\n", + "# TODO: LoRAパラメータを設定\n", + "# r: LoRA更新行列のランク次元(小さいほど圧縮率が高い)\n", + "rank_dimension = 6\n", + "# lora_alpha: LoRA層のスケーリングファクター(高いほど適応が強い)\n", + "lora_alpha = 8\n", + "# lora_dropout: LoRA層のドロップアウト確率(過学習を防ぐのに役立つ)\n", + "lora_dropout = 0.05\n", + "\n", + "peft_config = LoraConfig(\n", + " r=rank_dimension, # ランク次元 - 通常4-32の範囲\n", + " lora_alpha=lora_alpha, # LoRAスケーリングファクター - 通常ランクの2倍\n", + " lora_dropout=lora_dropout, # LoRA層のドロップアウト確率\n", + " bias=\"none\", # LoRAのバイアスタイプ。対応するバイアスはトレーニング中に更新されます。\n", + " target_modules=\"all-linear\", # LoRAを適用するモジュール\n", + " task_type=\"CAUSAL_LM\", # モデルアーキテクチャのタスクタイプ\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "l5NUDPcaouLl" + }, + "source": [ + "トレーニングを開始する前に、使用するハイパーパラメータ(`TrainingArguments`)を定義する必要があります。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NqT28VZlouLl" + }, + "outputs": [], + "source": [ + "# トレーニング設定\n", + "# QLoRA論文の推奨に基づくハイパーパラメータ\n", + "args = SFTConfig(\n", + " # 出力設定\n", + " output_dir=finetune_name, # モデルチェックポイントを保存するディレクトリ\n", + " # トレーニング期間\n", + " num_train_epochs=1, # トレーニングエポック数\n", + " # バッチサイズ設定\n", + " per_device_train_batch_size=2, # GPUごとのバッチサイズ\n", + " gradient_accumulation_steps=2, # 大きな効果的なバッチのための勾配蓄積\n", + " # メモリ最適化\n", + " gradient_checkpointing=True, # メモリ節約のための計算トレードオフ\n", + " # オプティマイザ設定\n", + " optim=\"adamw_torch_fused\", # 効率のために融合されたAdamWを使用\n", + " learning_rate=2e-4, # 学習率(QLoRA論文)\n", + " max_grad_norm=0.3, # 勾配クリッピングの閾値\n", + " # 学習率スケジュール\n", + " warmup_ratio=0.03, # ウォームアップのステップの割合\n", + " lr_scheduler_type=\"constant\", # ウォームアップ後に学習率を一定に保つ\n", + " # ロギングと保存\n", + " logging_steps=10, # Nステップごとにメトリックをログ\n", + " save_strategy=\"epoch\", # 各エポックごとにチェックポイントを保存\n", + " # 精度設定\n", + " bf16=True, # bfloat16精度を使用\n", + " # 統合設定\n", + " push_to_hub=False, # HuggingFace Hubにプッシュしない\n", + " report_to=\"none\", # 外部ロギングを無効化\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cGhR7uFBouLl" + }, + "source": [ + "すべてのビルディングブロックが揃ったので、`SFTTrainer`を作成してモデルのトレーニングを開始します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "M00Har2douLl" + }, + "outputs": [], + "source": [ + "max_seq_length = 1512 # モデルとデータセットのパッキングの最大シーケンス長\n", + "\n", + "# LoRA設定でSFTTrainerを作成\n", + "trainer = SFTTrainer(\n", + " model=model,\n", + " args=args,\n", + " train_dataset=dataset[\"train\"],\n", + " peft_config=peft_config, # LoRA設定\n", + " max_seq_length=max_seq_length, # 最大シーケンス長\n", + " tokenizer=tokenizer,\n", + " packing=True, # 効率のために入力パッキングを有効化\n", + " dataset_kwargs={\n", + " \"add_special_tokens\": False, # テンプレートで処理される特殊トークン\n", + " \"append_concat_token\": False, # 追加のセパレータは不要\n", + " },\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zQ_kRN24ouLl" + }, + "source": [ + "トレーニングを開始するには、`Trainer`インスタンスの`train()`メソッドを呼び出します。これにより、トレーニングループが開始され、モデルが3エポックにわたってトレーニングされます。PEFTメソッドを使用しているため、適応されたモデルの重みのみを保存し、完全なモデルは保存しません。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Tq4nIYqKouLl" + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "300e5dfbb4b54750b77324345c7591f9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/72 [00:00\n", + "

ボーナス演習: LoRAアダプタの読み込み

\n", + "

例のノートブックから学んだことを使用して、トレーニングされたLoRAアダプタを推論のために読み込みます。

\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "id": "I5B494OdouLl" + }, + "outputs": [], + "source": [ + "# メモリを再度解放\n", + "del model\n", + "del trainer\n", + "torch.cuda.empty_cache()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "P1UhohVdouLl" + }, + "outputs": [], + "source": [ + "import torch\n", + "from peft import AutoPeftModelForCausalLM\n", + "from transformers import AutoTokenizer, pipeline\n", + "\n", + "# PEFTアダプタでモデルを読み込む\n", + "tokenizer = AutoTokenizer.from_pretrained(finetune_name)\n", + "model = AutoPeftModelForCausalLM.from_pretrained(\n", + " finetune_name, device_map=\"auto\", torch_dtype=torch.float16\n", + ")\n", + "pipe = pipeline(\n", + " \"text-generation\", model=merged_model, tokenizer=tokenizer, device=device\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "99uFDAuuouLl" + }, + "source": [ + "いくつかのプロンプトサンプルをテストし、モデルの性能を確認しましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "id": "-shSmUbvouLl", + "outputId": "16d97c61-3b31-4040-c780-3c4de75c3824" + }, + "outputs": [], + "source": [ + "prompts = [\n", + " \"ドイツの首都はどこですか?その理由と過去に異なっていたかどうかを説明してください。\",\n", + " \"数の階乗を計算するPython関数を書いてください。\",\n", + " \"長さ25フィート、幅15フィートの長方形の庭があります。庭全体を囲むフェンスを作りたい場合、何フィートのフェンスが必要ですか?\",\n", + " \"果物と野菜の違いは何ですか?それぞれの例を挙げてください。\",\n", + "]\n", + "\n", + "\n", + "def test_inference(prompt):\n", + " prompt = pipe.tokenizer.apply_chat_template(\n", + " [{\"role\": \"user\", \"content\": prompt}],\n", + " tokenize=False,\n", + " add_generation_prompt=True,\n", + " )\n", + " outputs = pipe(\n", + " prompt,\n", + " )\n", + " return outputs[0][\"generated_text\"][len(prompt) :].strip()\n", + "\n", + "\n", + "for prompt in prompts:\n", + " print(f\" prompt:\\n{prompt}\")\n", + " print(f\" response:\\n{test_inference(prompt)}\")\n", + " print(\"-\" * 50)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/ja/3_parameter_efficient_finetuning/notebooks/load_lora_adapter.ipynb b/ja/3_parameter_efficient_finetuning/notebooks/load_lora_adapter.ipynb new file mode 100644 index 00000000..6ef9dd8c --- /dev/null +++ b/ja/3_parameter_efficient_finetuning/notebooks/load_lora_adapter.ipynb @@ -0,0 +1,161 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "z-6LLOPZouLg" + }, + "source": [ + "# LoRAアダプタの読み込み\n", + "\n", + "このノートブックでは、事前学習されたモデルにLoRAアダプタを読み込む方法を示します。LoRA(低ランク適応)は、事前学習されたモデルの重みを固定し、学習可能なランク分解行列をモデルの層に注入するパラメータ効率の良い微調整技術です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fXqd9BXgouLi" + }, + "source": [ + "## 1. 開発環境のセットアップ\n", + "\n", + "最初のステップは、Hugging FaceライブラリとPytorchをインストールすることです。`trl`、`transformers`、`datasets`を含みます。`trl`について聞いたことがない場合でも心配ありません。これは`transformers`と`datasets`の上に構築された新しいライブラリで、微調整、RLHF、オープンLLMの調整を容易にします。\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tKvGVxImouLi" + }, + "outputs": [], + "source": [ + "# Google Colabでの要件のインストール\n", + "# !pip install transformers datasets trl huggingface_hub\n", + "\n", + "# Hugging Faceへの認証\n", + "\n", + "from huggingface_hub import login\n", + "\n", + "login()\n", + "\n", + "# 便利のため、Hugging Faceのトークンを環境変数HF_TOKENとして設定できます" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XHUzfwpKouLk" + }, + "source": [ + "## 2. モデルとアダプタの読み込み" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "id": "z4p6Bvo7ouLk" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Downloading (…)okenizer_config.json: 100%|██████████| 28.0/28.0 [00:00<00:00, 28.0kB/s]\n", + "Downloading (…)lve/main/config.json: 100%|██████████| 1.11k/1.11k [00:00<00:00, 1.11MB/s]\n", + "Downloading (…)olve/main/vocab.json: 100%|██████████| 1.06M/1.06M [00:00<00:00, 1.06MB/s]\n", + "Downloading (…)olve/main/merges.txt: 100%|██████████| 525k/525k [00:00<00:00, 525kB/s]\n", + "Downloading (…)/main/tokenizer.json: 100%|██████████| 2.13M/2.13M [00:00<00:00, 2.13MB/s]\n", + "Downloading (…)/main/adapter_config.json: 100%|██████████| 472/472 [00:00<00:00, 472kB/s]\n", + "Downloading (…)/adapter_model.bin: 100%|██████████| 1.22G/1.22G [00:10<00:00, 120MB/s]\n", + "Downloading pytorch_model.bin: 100%|██████████| 1.22G/1.22G [00:10<00:00, 120MB/s]\n" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# モデルとアダプタの読み込み\n", + "from transformers import AutoModelForCausalLM\n", + "from peft import PeftModel\n", + "\n", + "base_model = AutoModelForCausalLM.from_pretrained(\"HuggingFaceTB/SmolLM2-135M\")\n", + "peft_model_id = \"HuggingFaceTB/SmolLM2-135M-LoRA\"\n", + "model = PeftModel.from_pretrained(base_model, peft_model_id)\n", + "model.eval()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9TOhJdtsouLk" + }, + "source": [ + "## 3. モデルのテスト\n", + "\n", + "モデルが正しく読み込まれたことを確認するために、いくつかのサンプル入力でテストします。" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "id": "z4p6Bvo7ouLk" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Downloading (…)okenizer_config.json: 100%|██████████| 28.0/28.0 [00:00<00:00, 28.0kB/s]\n", + "Downloading (…)lve/main/config.json: 100%|██████████| 1.11k/1.11k [00:00<00:00, 1.11MB/s]\n", + "Downloading (…)olve/main/vocab.json: 100%|██████████| 1.06M/1.06M [00:00<00:00, 1.06MB/s]\n", + "Downloading (…)olve/main/merges.txt: 100%|██████████| 525k/525k [00:00<00:00, 525kB/s]\n", + "Downloading (…)/main/tokenizer.json: 100%|██████████| 2.13M/2.13M [00:00<00:00, 2.13MB/s]\n" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# トークナイザーの読み込み\n", + "from transformers import AutoTokenizer\n", + "\n", + "tokenizer = AutoTokenizer.from_pretrained(\"HuggingFaceTB/SmolLM2-135M\")\n", + "\n", + "# サンプル入力の生成\n", + "inputs = tokenizer(\"こんにちは、元気ですか?\", return_tensors=\"pt\")\n", + "outputs = model.generate(**inputs, max_new_tokens=50)\n", + "print(tokenizer.decode(outputs[0], skip_special_tokens=True))" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/ja/3_parameter_efficient_finetuning/prompt_tuning.md b/ja/3_parameter_efficient_finetuning/prompt_tuning.md new file mode 100644 index 00000000..2676bbf6 --- /dev/null +++ b/ja/3_parameter_efficient_finetuning/prompt_tuning.md @@ -0,0 +1,74 @@ +# プロンプトチューニング + +プロンプトチューニングは、モデルの重みではなく入力表現を変更するパラメータ効率の良いアプローチです。従来の微調整がすべてのモデルパラメータを更新するのに対し、プロンプトチューニングはベースモデルを固定したまま、少数の学習可能なトークンを追加して最適化します。 + +## プロンプトチューニングの理解 + +プロンプトチューニングは、学習可能な連続ベクトル(ソフトプロンプト)を入力テキストの前に追加するパラメータ効率の良い微調整方法です。従来の離散テキストプロンプトとは異なり、これらのソフトプロンプトはベースモデルを固定したまま、逆伝播を通じて学習されます。この方法は、["The Power of Scale for Parameter-Efficient Prompt Tuning"](https://arxiv.org/abs/2104.08691)(Lester et al., 2021)で紹介され、モデルサイズが大きくなるにつれてプロンプトチューニングがモデル微調整と競争力を持つようになることが示されました。論文内では、約100億パラメータのモデルで、プロンプトチューニングがモデル微調整の性能に匹敵し、タスクごとに数百のパラメータしか変更しないことが示されています。 + +これらのソフトプロンプトは、トレーニング中に最適化されるモデルの埋め込み空間内の連続ベクトルです。従来の自然言語トークンを使用する離散プロンプトとは異なり、ソフトプロンプトは固有の意味を持たず、勾配降下を通じて固定モデルから望ましい動作を引き出すことを学習します。この技術は、各タスクに対して小さなプロンプトベクトル(通常は数百パラメータ)を保存するだけで済むため、マルチタスクシナリオに特に効果的です。このアプローチは、最小限のメモリフットプリントを維持するだけでなく、モデルの再読み込みなしにプロンプトベクトルを交換するだけで迅速なタスク切り替えを可能にします。 + +## トレーニングプロセス + +ソフトプロンプトは通常、8〜32トークンで構成され、ランダムに初期化するか、既存のテキストから初期化されます。初期化方法はトレーニングプロセスにおいて重要な役割を果たし、テキストベースの初期化はランダム初期化よりも優れた性能を発揮することがよくあります。 + +トレーニング中は、プロンプトパラメータのみが更新され、ベースモデルは固定されたままです。この集中アプローチは標準的なトレーニング目標を使用しますが、プロンプトトークンの学習率と勾配の挙動に注意を払う必要があります。 + +## PEFTを使用した実装 + +PEFTライブラリを使用すると、プロンプトチューニングの実装が簡単になります。以下は基本的な例です: + +```python +from peft import PromptTuningConfig, TaskType, get_peft_model +from transformers import AutoModelForCausalLM, AutoTokenizer + +# ベースモデルを読み込む +model = AutoModelForCausalLM.from_pretrained("your-base-model") +tokenizer = AutoTokenizer.from_pretrained("your-base-model") + +# プロンプトチューニングを設定 +peft_config = PromptTuningConfig( + task_type=TaskType.CAUSAL_LM, + num_virtual_tokens=8, # 学習可能なトークンの数 + prompt_tuning_init="TEXT", # テキストから初期化 + prompt_tuning_init_text="このテキストがポジティブかネガティブかを分類してください:", + tokenizer_name_or_path="your-base-model", +) + +# プロンプトチューニング可能なモデルを作成 +model = get_peft_model(model, peft_config) +``` + +## 他の方法との比較 + +他のPEFTアプローチと比較すると、プロンプトチューニングはその効率性で際立っています。LoRAは低パラメータ数とメモリ使用量を提供しますが、タスク切り替えにはアダプタの読み込みが必要です。プロンプトチューニングはさらに低いリソース使用量を達成し、即時のタスク切り替えを可能にします。対照的に、フルファインチューニングは多くのリソースを必要とし、異なるタスクごとに別々のモデルコピーが必要です。 + +| 方法 | パラメータ | メモリ | タスク切り替え | +|--------|------------|---------|----------------| +| プロンプトチューニング | 非常に低い | 最小限 | 簡単 | +| LoRA | 低い | 低い | 読み込みが必要 | +| フルファインチューニング | 高い | 高い | 新しいモデルコピー | + +プロンプトチューニングを実装する際は、最初に少数の仮想トークン(8〜16)を使用し、タスクの複雑さが要求する場合にのみ増やします。タスクに関連するテキストを使用したテキスト初期化は、ランダム初期化よりも優れた結果をもたらすことがよくあります。初期化戦略は、ターゲットタスクの複雑さを反映する必要があります。 + +トレーニングには、フルファインチューニングとは異なる考慮事項が必要です。高い学習率が効果的なことが多いですが、プロンプトトークンの勾配を注意深く監視することが重要です。多様な例で定期的に検証することで、さまざまなシナリオでの堅牢な性能を確保します。 + +## 応用 + +プロンプトチューニングは、次のようなシナリオで優れた効果を発揮します: + +1. マルチタスク展開 +2. リソース制約のある環境 +3. 迅速なタスク適応 +4. プライバシーに敏感なアプリケーション + +モデルが小さくなるにつれて、プロンプトチューニングはフルファインチューニングと比較して競争力が低下します。例えば、SmolLM2のようなモデルでは、プロンプトチューニングはフルファインチューニングよりも関連性が低くなります。 + +## 次のステップ + +⏭️ [LoRAアダプタのチュートリアル](./notebooks/finetune_sft_peft.ipynb)に進み、LoRAアダプタでモデルを微調整する方法を学びましょう。 + +## リソース +- [PEFTドキュメント](https://huggingface.co/docs/peft) +- [プロンプトチューニング論文](https://arxiv.org/abs/2104.08691) +- [Hugging Face Cookbook](https://huggingface.co/learn/cookbook/prompt_tuning_peft) diff --git a/ja/4_evaluation/README.md b/ja/4_evaluation/README.md new file mode 100644 index 00000000..bb3bfafd --- /dev/null +++ b/ja/4_evaluation/README.md @@ -0,0 +1,39 @@ +# 評価 + +評価は、言語モデルの開発と展開において重要なステップです。評価は、モデルがさまざまな能力にわたってどれだけうまく機能するかを理解し、改善の余地を特定するのに役立ちます。このモジュールでは、標準ベンチマークとドメイン固有の評価アプローチの両方をカバーし、smolモデルを包括的に評価します。 + +Hugging Faceが開発した強力な評価ライブラリである[`lighteval`](https://github.com/huggingface/lighteval)を使用します。評価の概念とベストプラクティスについて詳しく知りたい場合は、評価[ガイドブック](https://github.com/huggingface/evaluation-guidebook)を参照してください。 + +## モジュール概要 + +徹底した評価戦略は、モデル性能の複数の側面を検討します。質問応答や要約などのタスク固有の能力を評価し、モデルがさまざまなタイプの問題にどれだけうまく対処できるかを理解します。出力の品質を一貫性や事実の正確性などの要素で測定します。安全性評価は、潜在的な有害な出力やバイアスを特定するのに役立ちます。最後に、ドメインの専門知識テストは、ターゲット分野でのモデルの専門知識を検証します。 + +## コンテンツ + +### 1️⃣ [自動ベンチマーク](./automatic_benchmarks.md) + +標準化されたベンチマークとメトリクスを使用してモデルを評価する方法を学びます。MMLUやTruthfulQAなどの一般的なベンチマークを探求し、主要な評価メトリクスと設定を理解し、再現可能な評価のベストプラクティスをカバーします。 + +### 2️⃣ [カスタムドメイン評価](./custom_evaluation.md) + +特定のユースケースに合わせた評価パイプラインを作成する方法を学びます。カスタム評価タスクの設計、専門的なメトリクスの実装、要件に合った評価データセットの構築について説明します。 + +### 3️⃣ [ドメイン評価プロジェクト](./project/README.md) + +ドメイン固有の評価パイプラインを構築する完全な例を紹介します。評価データセットの生成、データ注釈のためのArgillaの使用、標準化されたデータセットの作成、LightEvalを使用したモデルの評価方法を学びます。 + +### 演習ノートブック + +| タイトル | 説明 | 演習 | リンク | Colab | +|-------|-------------|----------|------|-------| +| LLMの評価と分析 | LightEvalを使用して特定のドメインでモデルを評価および比較する方法を学びます | 🐢 医療ドメインタスクを使用してモデルを評価する
🐕 異なるMMLUタスクで新しいドメイン評価を作成する
🦁 ドメイン固有のカスタム評価タスクを作成する | [ノートブック](./notebooks/lighteval_evaluate_and_analyse_your_LLM.ipynb) | Open In Colab | + +## リソース + +- [評価ガイドブック](https://github.com/huggingface/evaluation-guidebook) - LLM評価の包括的なガイド +- [LightEvalドキュメント](https://github.com/huggingface/lighteval) - LightEvalライブラリの公式ドキュメント +- [Argillaドキュメント](https://docs.argilla.io) - Argillaアノテーションプラットフォームについて学ぶ +- [MMLU論文](https://arxiv.org/abs/2009.03300) - MMLUベンチマークを説明する論文 +- [カスタムタスクの作成](https://github.com/huggingface/lighteval/wiki/Adding-a-Custom-Task) +- [カスタムメトリクスの作成](https://github.com/huggingface/lighteval/wiki/Adding-a-New-Metric) +- [既存のメトリクスの使用](https://github.com/huggingface/lighteval/wiki/Metric-List) diff --git a/ja/4_evaluation/automatic_benchmarks.md b/ja/4_evaluation/automatic_benchmarks.md new file mode 100644 index 00000000..81df5393 --- /dev/null +++ b/ja/4_evaluation/automatic_benchmarks.md @@ -0,0 +1,131 @@ +# 自動ベンチマーク + +自動ベンチマークは、さまざまなタスクや能力にわたって言語モデルを評価するための標準化されたツールとして機能します。これらはモデルの性能を理解するための有用な出発点を提供しますが、包括的な評価戦略の一部に過ぎないことを認識することが重要です。 + +## 自動ベンチマークの理解 + +自動ベンチマークは通常、事前に定義されたタスクと評価指標を持つキュレーションされたデータセットで構成されます。これらのベンチマークは、基本的な言語理解から複雑な推論まで、モデルのさまざまな側面を評価することを目的としています。自動ベンチマークの主な利点はその標準化にあります。これにより、異なるモデル間で一貫した比較が可能となり、再現性のある結果が得られます。 + +ただし、ベンチマークの性能が必ずしも実世界での有効性に直結するわけではないことを理解することが重要です。学術的なベンチマークで優れた成績を収めたモデルでも、特定のドメインアプリケーションや実際のユースケースでは苦労することがあります。 + +## ベンチマークとその限界 + +### 一般知識ベンチマーク + +MMLU(Massive Multitask Language Understanding)は、科学から人文科学まで57の科目にわたる知識をテストします。包括的ではありますが、特定のドメインに必要な専門知識の深さを反映しているわけではありません。TruthfulQAは、モデルが一般的な誤解を再現する傾向を評価しますが、すべての形態の誤情報を捉えることはできません。 + +### 推論ベンチマーク +BBH(Big Bench Hard)とGSM8Kは、複雑な推論タスクに焦点を当てています。BBHは論理的思考と計画をテストし、GSM8Kは特に数学的な問題解決を対象としています。これらのベンチマークは分析能力を評価するのに役立ちますが、実世界のシナリオで必要とされる微妙な推論を完全に捉えることはできません。 + +### 言語理解 +HELMは包括的な評価フレームワークを提供し、WinoGrandeは代名詞の曖昧性解消を通じて常識をテストします。これらのベンチマークは言語処理能力に関する洞察を提供しますが、自然な会話やドメイン固有の用語の複雑さを完全に表現することはできません。 + +## 代替評価アプローチ + +多くの組織は、標準ベンチマークの限界に対処するために代替評価方法を開発しています: + +### LLM-as-Judge +ある言語モデルを使用して別のモデルの出力を評価する方法がますます人気を集めています。このアプローチは、従来の指標よりも微妙なフィードバックを提供することができますが、それ自体のバイアスと限界も伴います。 + +### 評価アリーナ +AnthropicのConstitutional AI Arenaのようなプラットフォームでは、モデルが相互に対話し、制御された環境で互いを評価することができます。これにより、従来のベンチマークでは明らかにならない強みと弱みが明らかになります。 + +### カスタムベンチマークスイート +組織は、特定のニーズやユースケースに合わせた内部ベンチマークスイートを開発することがよくあります。これには、ドメイン固有の知識テストや実際の展開条件を反映した評価シナリオが含まれることがあります。 + +## 独自の評価戦略の作成 + +LightEvalを使用して標準ベンチマークを実行するのは簡単ですが、ユースケースに特化した評価方法の開発にも時間を投資する必要があります。 + +標準ベンチマークは有用なベースラインを提供しますが、それだけが評価方法であってはなりません。より包括的なアプローチを開発する方法は次のとおりです: + +1. 関連する標準ベンチマークから始めて、ベースラインを確立し、他のモデルとの比較を可能にします。 + +2. ユースケースの特定の要件と課題を特定します。モデルが実際に実行するタスクは何ですか?どのようなエラーが最も問題になりますか? + +3. 実際のユースケースを反映したカスタム評価データセットを開発します。これには次のようなものが含まれるかもしれません: + - ドメインからの実際のユーザークエリ + - 遭遇した一般的なエッジケース + - 特に挑戦的なシナリオの例 + +4. 多層評価戦略の実装を検討します: + - クイックフィードバックのための自動指標 + - 微妙な理解のための人間の評価 + - 専門家によるレビュー + - 制御された環境でのA/Bテスト + +## LightEvalを使用したベンチマーク + +LightEvalタスクは特定の形式を使用して定義されます: +``` +{suite}|{task}|{num_few_shot}|{auto_reduce} +``` + +- **suite**: ベンチマークスイート(例:'mmlu', 'truthfulqa') +- **task**: スイート内の特定のタスク(例:'abstract_algebra') +- **num_few_shot**: プロンプトに含める例の数(ゼロショットの場合は0) +- **auto_reduce**: プロンプトが長すぎる場合に少数ショットの例を自動的に削減するかどうか(0または1) + +例:`"mmlu|abstract_algebra|0|0"`は、ゼロショット推論でMMLUの抽象代数学タスクを評価します。 + +### 評価パイプラインの例 + +特定のドメインに関連する自動ベンチマークの評価を設定して実行する完全な例を次に示します: + +```python +from lighteval.tasks import Task, Pipeline +from transformers import AutoModelForCausalLM + +# 評価するタスクを定義 +domain_tasks = [ + "mmlu|anatomy|0|0", + "mmlu|high_school_biology|0|0", + "mmlu|high_school_chemistry|0|0", + "mmlu|professional_medicine|0|0" +] + +# パイプラインパラメータを設定 +pipeline_params = { + "max_samples": 40, # 評価するサンプル数 + "batch_size": 1, # 推論のバッチサイズ + "num_workers": 4 # ワーカープロセスの数 +} + +# 評価トラッカーを作成 +evaluation_tracker = EvaluationTracker( + output_path="./results", + save_generations=True +) + +# モデルを読み込み、パイプラインを作成 +model = AutoModelForCausalLM.from_pretrained("your-model-name") +pipeline = Pipeline( + tasks=domain_tasks, + pipeline_parameters=pipeline_params, + evaluation_tracker=evaluation_tracker, + model=model +) + +# 評価を実行 +pipeline.evaluate() + +# 結果を取得して表示 +results = pipeline.get_results() +pipeline.show_results() +``` + +結果は次のような表形式で表示されます: +``` +| Task |Version|Metric|Value | |Stderr| +|----------------------------------------|------:|------|-----:|---|-----:| +|all | |acc |0.3333|± |0.1169| +|leaderboard:mmlu:_average:5 | |acc |0.3400|± |0.1121| +|leaderboard:mmlu:anatomy:5 | 0|acc |0.4500|± |0.1141| +|leaderboard:mmlu:high_school_biology:5 | 0|acc |0.1500|± |0.0819| +``` + +結果をpandas DataFrameで処理し、視覚化や表現を自由に行うこともできます。 + +# 次のステップ + +⏩ [カスタムドメイン評価](./custom_evaluation.md)を探索し、特定のニーズに合わせた評価パイプラインの作成方法を学びましょう diff --git a/ja/4_evaluation/custom_evaluation.md b/ja/4_evaluation/custom_evaluation.md new file mode 100644 index 00000000..4dc41642 --- /dev/null +++ b/ja/4_evaluation/custom_evaluation.md @@ -0,0 +1,132 @@ +# カスタムドメイン評価 + +標準ベンチマークは貴重な洞察を提供しますが、多くのアプリケーションでは特定のドメインやユースケースに合わせた評価アプローチが必要です。このガイドでは、ターゲットドメインでモデルの性能を正確に評価するためのカスタム評価パイプラインを作成する方法を説明します。 + +## 評価戦略の設計 + +成功するカスタム評価戦略は、明確な目標から始まります。ドメインでモデルが示すべき具体的な能力を考慮してください。これには、技術的な知識、推論パターン、ドメイン固有の形式が含まれるかもしれません。これらの要件を慎重に文書化してください。これがタスク設計とメトリック選択の両方を導きます。 + +評価は、標準的なユースケースとエッジケースの両方をテストする必要があります。例えば、医療ドメインでは、一般的な診断シナリオと稀な状態の両方を評価するかもしれません。金融アプリケーションでは、通常の取引と複数の通貨や特別な条件を含む複雑なエッジケースの両方をテストするかもしれません。 + +## LightEvalを使用した実装 + +LightEvalは、カスタム評価を実装するための柔軟なフレームワークを提供します。カスタムタスクを作成する方法は次のとおりです: + +```python +from lighteval.tasks import Task, Doc +from lighteval.metrics import SampleLevelMetric, MetricCategory, MetricUseCase + +class CustomEvalTask(Task): + def __init__(self): + super().__init__( + name="custom_task", + version="0.0.1", + metrics=["accuracy", "f1"], # 選択したメトリック + description="カスタム評価タスクの説明" + ) + + def get_prompt(self, sample): + # 入力をプロンプトにフォーマット + return f"質問: {sample['question']}\n回答:" + + def process_response(self, response, ref): + # モデルの出力を処理し、参照と比較 + return response.strip() == ref.strip() +``` + +## カスタムメトリック + +ドメイン固有のタスクには、専門的なメトリックが必要なことがよくあります。LightEvalは、ドメインに関連する性能の側面を捉えるカスタムメトリックを作成するための柔軟なフレームワークを提供します: + +```python +from aenum import extend_enum +from lighteval.metrics import Metrics, SampleLevelMetric, SampleLevelMetricGrouping +import numpy as np + +# サンプルレベルのメトリック関数を定義 +def custom_metric(predictions: list[str], formatted_doc: Doc, **kwargs) -> dict: + """サンプルごとに複数のスコアを返す例のメトリック""" + response = predictions[0] + return { + "accuracy": response == formatted_doc.choices[formatted_doc.gold_index], + "length_match": len(response) == len(formatted_doc.reference) + } + +# サンプルごとに複数の値を返すメトリックを作成 +custom_metric_group = SampleLevelMetricGrouping( + metric_name=["accuracy", "length_match"], # サブメトリックの名前 + higher_is_better={ # 各メトリックで高い値が良いかどうか + "accuracy": True, + "length_match": True + }, + category=MetricCategory.CUSTOM, + use_case=MetricUseCase.SCORING, + sample_level_fn=custom_metric, + corpus_level_fn={ # 各メトリックを集計する方法 + "accuracy": np.mean, + "length_match": np.mean + } +) + +# LightEvalにメトリックを登録 +extend_enum(Metrics, "custom_metric_name", custom_metric_group) +``` + +サンプルごとに1つのメトリック値のみが必要な場合の簡単なケース: + +```python +def simple_metric(predictions: list[str], formatted_doc: Doc, **kwargs) -> bool: + """サンプルごとに単一のスコアを返す例のメトリック""" + response = predictions[0] + return response == formatted_doc.choices[formatted_doc.gold_index] + +simple_metric_obj = SampleLevelMetric( + metric_name="simple_accuracy", + higher_is_better=True, + category=MetricCategory.CUSTOM, + use_case=MetricUseCase.SCORING, + sample_level_fn=simple_metric, + corpus_level_fn=np.mean # サンプル全体で集計する方法 +) + +extend_enum(Metrics, "simple_metric", simple_metric_obj) +``` + +カスタムメトリックを評価タスクで参照することで、評価タスクで自動的に計算され、指定された関数に従って集計されます。 + +より複雑なメトリックの場合、次のことを検討してください: +- フォーマットされたドキュメントのメタデータを使用してスコアを重み付けまたは調整 +- コーパスレベルの統計のためのカスタム集計関数の実装 +- メトリック入力の検証チェックの追加 +- エッジケースと期待される動作の文書化 + +これらの概念を実装する完全な例については、[ドメイン評価プロジェクト](./project/README.md)を参照してください。 + +## データセットの作成 + +高品質の評価には、慎重にキュレーションされたデータセットが必要です。データセット作成のアプローチを次に示します: + +1. 専門家のアノテーション:ドメインの専門家と協力して評価例を作成および検証します。[Argilla](https://github.com/argilla-io/argilla)のようなツールを使用すると、このプロセスがより効率的になります。 + +2. 実世界のデータ:実際の使用データを収集し、匿名化して、実際の展開シナリオを反映します。 + +3. 合成生成:LLMを使用して初期例を生成し、専門家がそれを検証および洗練します。 + +## ベストプラクティス + +- 評価方法論を徹底的に文書化し、仮定や制限を含める +- ドメインのさまざまな側面をカバーする多様なテストケースを含める +- 自動メトリックと人間の評価の両方を適用する +- 評価データセットとコードをバージョン管理する +- 新しいエッジケースや要件を発見するたびに評価スイートを定期的に更新する + +## 参考文献 + +- [LightEvalカスタムタスクガイド](https://github.com/huggingface/lighteval/wiki/Adding-a-Custom-Task) +- [LightEvalカスタムメトリック](https://github.com/huggingface/lighteval/wiki/Adding-a-New-Metric) +- データセットアノテーションのための[Argillaドキュメント](https://docs.argilla.io) +- 一般的な評価原則のための[評価ガイドブック](https://github.com/huggingface/evaluation-guidebook) + +# 次のステップ + +⏩ これらの概念を実装する完全な例については、[ドメイン評価プロジェクト](./project/README.md)を参照してください。 diff --git a/ja/4_evaluation/notebooks/lighteval_evaluate_and_analyse_your_LLM.ipynb b/ja/4_evaluation/notebooks/lighteval_evaluate_and_analyse_your_LLM.ipynb new file mode 100644 index 00000000..3e4f34a9 --- /dev/null +++ b/ja/4_evaluation/notebooks/lighteval_evaluate_and_analyse_your_LLM.ipynb @@ -0,0 +1,210 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "z-6LLOPZouLg" + }, + "source": [ + "# LightEvalを使用してLLMを評価および分析する方法\n", + "\n", + "このノートブックでは、LightEvalを使用して大規模言語モデル(LLM)を評価および分析する方法を示します。LightEvalは、さまざまなタスクにわたってモデルの性能を評価するための柔軟で使いやすいフレームワークです。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fXqd9BXgouLi" + }, + "source": [ + "## 1. 開発環境のセットアップ\n", + "\n", + "最初のステップは、必要なライブラリをインストールすることです。`lighteval`、`transformers`、`datasets`を含みます。\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tKvGVxImouLi" + }, + "outputs": [], + "source": [ + "# Google Colabでの要件のインストール\n", + "# !pip install lighteval transformers datasets\n", + "\n", + "# Hugging Faceへの認証\n", + "\n", + "from huggingface_hub import login\n", + "\n", + "login()\n", + "\n", + "# 便利のため、Hugging Faceのトークンを環境変数HF_TOKENとして設定できます" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XHUzfwpKouLk" + }, + "source": [ + "## 2. モデルとデータセットの読み込み" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "id": "z4p6Bvo7ouLk" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Downloading (…)okenizer_config.json: 100%|██████████| 28.0/28.0 [00:00<00:00, 28.0kB/s]\n", + "Downloading (…)lve/main/config.json: 100%|██████████| 1.11k/1.11k [00:00<00:00, 1.11MB/s]\n", + "Downloading (…)olve/main/vocab.json: 100%|██████████| 1.06M/1.06M [00:00<00:00, 1.06MB/s]\n", + "Downloading (…)olve/main/merges.txt: 100%|██████████| 525k/525k [00:00<00:00, 525kB/s]\n", + "Downloading (…)/main/tokenizer.json: 100%|██████████| 2.13M/2.13M [00:00<00:00, 2.13MB/s]\n", + "Downloading (…)/main/adapter_config.json: 100%|██████████| 472/472 [00:00<00:00, 472kB/s]\n", + "Downloading (…)/adapter_model.bin: 100%|██████████| 1.22G/1.22G [00:10<00:00, 120MB/s]\n", + "Downloading pytorch_model.bin: 100%|██████████| 1.22G/1.22G [00:10<00:00, 120MB/s]\n" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# モデルとデータセットの読み込み\n", + "from transformers import AutoModelForCausalLM, AutoTokenizer\n", + "from datasets import load_dataset\n", + "\n", + "model_name = \"HuggingFaceTB/SmolLM2-135M\"\n", + "model = AutoModelForCausalLM.from_pretrained(model_name)\n", + "tokenizer = AutoTokenizer.from_pretrained(model_name)\n", + "\n", + "# サンプルデータセットの読み込み\n", + "dataset = load_dataset(\"lighteval\", \"mmlu\")\n", + "dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9TOhJdtsouLk" + }, + "source": [ + "## 3. LightEvalを使用した評価\n", + "\n", + "LightEvalを使用してモデルを評価するために、評価タスクを定義し、パイプラインを設定します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# LightEvalのタスクとパイプラインをインポート\n", + "from lighteval.tasks import Task, Pipeline\n", + "from lighteval.metrics import EvaluationTracker\n", + "\n", + "# 評価するタスクを定義\n", + "tasks = [\n", + " \"mmlu|anatomy|0|0\",\n", + " \"mmlu|high_school_biology|0|0\",\n", + " \"mmlu|high_school_chemistry|0|0\",\n", + " \"mmlu|professional_medicine|0|0\"\n", + "]\n", + "\n", + "# パイプラインパラメータを設定\n", + "pipeline_params = {\n", + " \"max_samples\": 40, # 評価するサンプル数\n", + " \"batch_size\": 1, # 推論のバッチサイズ\n", + " \"num_workers\": 4 # ワーカープロセスの数\n", + "}\n", + "\n", + "# 評価トラッカーを作成\n", + "evaluation_tracker = EvaluationTracker(\n", + " output_path=\"./results\",\n", + " save_generations=True\n", + ")\n", + "\n", + "# パイプラインを作成\n", + "pipeline = Pipeline(\n", + " tasks=tasks,\n", + " pipeline_parameters=pipeline_params,\n", + " evaluation_tracker=evaluation_tracker,\n", + " model=model\n", + ")\n", + "\n", + "# 評価を実行\n", + "pipeline.evaluate()\n", + "\n", + "# 結果を取得して表示\n", + "results = pipeline.get_results()\n", + "pipeline.show_results()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-yO6E9quouLl" + }, + "source": [ + "## 4. 結果の分析\n", + "\n", + "評価結果を分析し、モデルの性能を理解します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# pandasを使用して結果を分析\n", + "import pandas as pd\n", + "\n", + "# 結果をDataFrameに変換\n", + "df = pd.DataFrame(results)\n", + "\n", + "# 結果を表示\n", + "print(df)\n", + "\n", + "# 結果を視覚化\n", + "df.plot(kind=\"bar\", x=\"Task\", y=\"Value\", legend=False)\n", + "plt.ylabel(\"Accuracy\")\n", + "plt.title(\"Model Performance on Different Tasks\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/ja/4_evaluation/project/README.md b/ja/4_evaluation/project/README.md new file mode 100644 index 00000000..ad6ec93f --- /dev/null +++ b/ja/4_evaluation/project/README.md @@ -0,0 +1,83 @@ +# Argilla、Distilabel、LightEvalを使用したドメイン固有の評価 + +ほとんどの人気のあるベンチマークは、一般的な能力(推論、数学、コード)を評価しますが、より具体的な能力を評価する必要がある場合はどうすればよいでしょうか? + +カスタムドメインに関連するモ���ルを評価する必要がある場合はどうすればよいでしょうか?(例えば、金融、法務、医療のユースケース) + +このチュートリアルでは、[Argilla](https://github.com/argilla-io/argilla)、[distilabel](https://github.com/argilla-io/distilabel)、および[LightEval](https://github.com/huggingface/lighteval)を使用して、関連するデータを作成し、サンプルに注釈を付け、モデルを評価するための完全なパイプラインを示します。例として、複数のドキュメントから試験問題を生成することに焦点を当てます。 + +## プロジェクト構造 + +このプロセスでは、データセットの生成、注釈の追加、評価のためのサンプルの抽出、実際のモデル評価の4つのステップを実行します。各ステップにはスクリプトがあります。 + +| スクリプト名 | 説明 | +|-------------|-------------| +| generate_dataset.py | 指定された言語モデルを使用して複数のテキストドキュメントから試験問題を生成します。 | +| annotate_dataset.py | 生成された試験問題の手動注釈のためのArgillaデータセットを作成します。 | +| create_dataset.py | Argillaから注釈付きデータを処理し、Hugging Faceデータセットを作成します。 | +| evaluation_task.py | 試験問題データセットの評価のためのカスタムLightEvalタスクを定義します。 | + +## ステップ + +### 1. データセットの生成 + +`generate_dataset.py`スクリプトは、distilabelライブラリを使用して複数のテキストドキュメントに基づいて試験問題を生成します。指定されたモデル(デフォルト:Meta-Llama-3.1-8B-Instruct)を使用して質問、正しい回答、および誤った回答(ディストラクター)を作成します。独自のデータサンプルを追加し、異なるモデルを使用することもできます。 + +生成を実行するには: + +```sh +python generate_dataset.py --input_dir path/to/your/documents --model_id your_model_id --output_path output_directory +``` + +これにより、入力ディレクトリ内のすべてのドキュメントに対して生成された試験問題を含む[Distiset](https://distilabel.argilla.io/dev/sections/how_to_guides/advanced/distiset/)が作成されます。 + +### 2. データセットの注釈 + +`annotate_dataset.py`スクリプトは、生成された質問を取得し、注釈のためのArgillaデータセットを作成します。データセットの構造を設定し、生成された質問と回答をランダムな順序で入力します。これにより、バイアスを避けることができます。Argillaでは、正しい回答が提案として表示されます。 + +LLMからの提案された正しい回答がランダムな順序で表示され、正しい回答を承認するか、別の回答を選択できます。このプロセスの所要時間は、評価データセットの規模、ドメインデータの複雑さ、およびLLMの品質によって異なります。例えば、Llama-3.1-70B-Instructを使用して、転移学習のドメインで150サンプルを1時間以内に作成できました。 + +注釈プロセスを実行するには: + +```sh +python annotate_dataset.py --dataset_path path/to/distiset --output_dataset_name argilla_dataset_name +``` + +これにより、手動レビューと注釈のためのArgillaデータセットが作成されます。 + +![argilla_dataset](./images/domain_eval_argilla_view.png) + +Argillaを使用していない場合は、この[クイックスタートガイド](https://docs.argilla.io/latest/getting_started/quickstart/)に従ってローカルまたはスペースにデプロイしてください。 + +### 3. データセットの作成 + +`create_dataset.py`スクリプトは、Argillaから注釈付きデータを処理し、Hugging Faceデータセットを作成します。提案された回答と手動で注釈された回答の両方を処理します。このスクリプトは、質問、可能な回答、および正しい回答の列名を含むデータセットを作成します。最終データセットを作成するには: + +```sh +huggingface_hub login +python create_dataset.py --dataset_path argilla_dataset_name --dataset_repo_id your_hf_repo_id +``` + +これにより、指定されたリポジトリにデータセットがHugging Face Hubにプッシュされます。サンプルデータセットは[こちら](https://huggingface.co/datasets/burtenshaw/exam_questions/viewer/default/train)で確認できます。データセットのプレビューは次のようになります: + +![hf_dataset](./images/domain_eval_dataset_viewer.png) + +### 4. 評価タスク + +`evaluation_task.py`スクリプトは、試験問題データセットの評価のためのカスタムLightEvalタスクを定義します。プロンプト関数、カスタム精度メトリック、およびタスク構成が含まれます。 + +カスタム試験問題タスクを使用してlightevalでモデルを評価するには: + +```sh +lighteval accelerate \ + --model_args "pretrained=HuggingFaceH4/zephyr-7b-beta" \ + --tasks "community|exam_questions|0|0" \ + --custom_tasks domain-eval/evaluation_task.py \ + --output_dir "./evals" +``` + +詳細なガイドはlighteval wikiで確認できます: + +- [カスタムタスクの作成](https://github.com/huggingface/lighteval/wiki/Adding-a-Custom-Task) +- [カスタムメトリックの作成](https://github.com/huggingface/lighteval/wiki/Adding-a-New-Metric) +- [既存のメトリックの使用](https://github.com/huggingface/lighteval/wiki/Metric-List) diff --git a/ja/4_evaluation/project/annotate_dataset.py b/ja/4_evaluation/project/annotate_dataset.py new file mode 100644 index 00000000..f57a4fb6 --- /dev/null +++ b/ja/4_evaluation/project/annotate_dataset.py @@ -0,0 +1,129 @@ +import argparse +import json +from random import choices, sample + +import argilla as rg +from distilabel.distiset import Distiset + +################################################################################ +# スクリプトのパラメータ +################################################################################ + +parser = argparse.ArgumentParser( + description="Argillaを使用して試験問題データセットに注釈を付けます。" +) +parser.add_argument( + "--argilla_api_key", + type=str, + default="argilla.apikey", + help="ArgillaのAPIキー", +) +parser.add_argument( + "--argilla_api_url", + type=str, + default="http://localhost:6900", + help="ArgillaのAPI URL", +) +parser.add_argument( + "--dataset_path", + type=str, + default="exam_questions", + help="試験問題データセットのパス", +) +parser.add_argument( + "--dataset_config", + type=str, + default="default", + help="データセットの構成", +) +parser.add_argument( + "--dataset_split", + type=str, + default="train", + help="使用するデータセットの分割", +) +parser.add_argument( + "--output_dataset_name", + type=str, + default="exam_questions", + help="出力Argillaデータセットの名前", +) + +args = parser.parse_args() + +################################################################################ +# 検証のためのフィードバックタスクを使用してArgillaデータセットを作成 +################################################################################ + +client = rg.Argilla(api_key=args.argilla_api_key, api_url=args.argilla_api_url) + +if client.datasets(args.output_dataset_name): + print(f"既存のデータセット '{args.output_dataset_name}' を削除します") + client.datasets(args.output_dataset_name).delete() + +settings = rg.Settings( + fields=[ + rg.TextField("question"), + rg.TextField("answer_a"), + rg.TextField("answer_b"), + rg.TextField("answer_c"), + rg.TextField("answer_d"), + ], + questions=[ + rg.LabelQuestion( + name="correct_answer", + labels=["answer_a", "answer_b", "answer_c", "answer_d"], + ), + rg.TextQuestion( + name="improved_question", + description="質問を改善できますか?", + ), + rg.TextQuestion( + name="improved_answer", + description="最良の回答を改善できますか?", + ), + ], +) + +dataset = rg.Dataset(settings=settings, name=args.output_dataset_name) +dataset.create() + +################################################################################ +# Distisetを読み込み、Argillaデータセットにレコードを処理して追加 +# バイアスを避けるために質問がランダムな順序で表示されることを確認します +# ただし、Argilla UIでは正しい回答を提案として表示します。 +################################################################################ + +distiset = Distiset.load_from_disk(args.dataset_path) +answer_names = ["answer_a", "answer_b", "answer_c", "answer_d"] +dataset_records = [] + +for exam in distiset[args.dataset_config][args.dataset_split]: + exam_json = json.loads(exam["generation"])["exam"] + + for question in exam_json: + answer = question["answer"] + distractors = question["distractors"] + distractors = choices(distractors, k=3) + answers = distractors + [answer] + answers = sample(answers, len(answers)) + suggestion_idx = answers.index(answer) + fields = dict(zip(answer_names, answers)) + fields["question"] = question["question"] + + record = rg.Record( + fields=fields, + suggestions=[ + rg.Suggestion( + question_name="correct_answer", + value=answer_names[suggestion_idx], + ) + ], + ) + dataset_records.append(record) + +dataset.records.log(dataset_records) + +print( + f"データセット '{args.output_dataset_name}' がArgillaに作成され、入力されました。" +) diff --git a/ja/4_evaluation/project/create_dataset.py b/ja/4_evaluation/project/create_dataset.py new file mode 100644 index 00000000..f3b5c812 --- /dev/null +++ b/ja/4_evaluation/project/create_dataset.py @@ -0,0 +1,72 @@ +import argparse + +import argilla as rg +from datasets import Dataset + +################################################################################ +# スクリプトのパラメータ +################################################################################ + +parser = argparse.ArgumentParser( + description="注釈付きArgillaデータからHugging Faceデータセットを作成します。" +) +parser.add_argument( + "--argilla_api_key", + type=str, + default="argilla.apikey", + help="ArgillaのAPIキー", +) +parser.add_argument( + "--argilla_api_url", + type=str, + default="http://localhost:6900", + help="ArgillaのAPI URL", +) +parser.add_argument( + "--dataset_path", + type=str, + default="exam_questions", + help="Argillaデータセットのパス", +) +parser.add_argument( + "--dataset_repo_id", + type=str, + default="burtenshaw/exam_questions", + help="Hugging FaceデータセットリポジトリID", +) + +args = parser.parse_args() + +################################################################################ +# Argillaクライアントを初期化し、データセットを読み込む +################################################################################ + +client = rg.Argilla(api_key=args.argilla_api_key, api_url=args.argilla_api_url) +dataset = client.datasets(args.dataset_path) + +################################################################################ +# Argillaレコードを処理 +################################################################################ + +dataset_rows = [] + +for record in dataset.records(with_suggestions=True, with_responses=True): + row = record.fields + + if len(record.responses) == 0: + answer = record.suggestions["correct_answer"].value + row["correct_answer"] = answer + else: + for response in record.responses: + if response.question_name == "correct_answer": + row["correct_answer"] = response.value + dataset_rows.append(row) + +################################################################################ +# Hugging Faceデータセットを作成し、Hubにプッシュ +################################################################################ + +hf_dataset = Dataset.from_list(dataset_rows) +hf_dataset.push_to_hub(repo_id=args.dataset_repo_id) + +print(f"データセットが{args.dataset_repo_id}に正常にプッシュされました") diff --git a/ja/4_evaluation/project/evaluation_task.py b/ja/4_evaluation/project/evaluation_task.py new file mode 100644 index 00000000..9c643c9f --- /dev/null +++ b/ja/4_evaluation/project/evaluation_task.py @@ -0,0 +1,87 @@ +import numpy as np + +from lighteval.tasks.lighteval_task import LightevalTaskConfig +from lighteval.tasks.requests import Doc +from lighteval.metrics.utils.metric_utils import ( + SampleLevelMetric, + MetricCategory, + MetricUseCase, +) + +################################################################################ +# データセットの構造に基づいてプロンプト関数を定義 +################################################################################ + + +def prompt_fn(line, task_name: str = None): + """データセットの行を評価用のDocオブジェクトに変換します。""" + instruction = "次の試験問題に対して正しい答えを選んでください:" + return Doc( + task_name=task_name, + query=f"{instruction} {line['question']}", + choices=[ + f" {line['answer_a']}", + f" {line['answer_b']}", + f" {line['answer_c']}", + f" {line['answer_d']}", + ], + gold_index=["answer_a", "answer_b", "answer_c", "answer_d"].index( + line["correct_answer"] + ), + instruction=instruction, + ) + + +################################################################################ +# カスタムメトリックを定義 +# ガイドに基づいて https://github.com/huggingface/lighteval/wiki/Adding-a-New-Metric +# または既存のメトリックを使用 https://github.com/huggingface/lighteval/wiki/Metric-List +# 既存のメトリックは lighteval.metrics.metrics からインポート可能 +################################################################################ + + +def sample_level_fn(formatted_doc: Doc, **kwargs) -> bool: + response = np.argmin(kwargs["choices_logprob"]) + return response == formatted_doc.gold_index + + +custom_metric = SampleLevelMetric( + metric_name="exam_question_accuracy", + higher_is_better=True, + category=MetricCategory.MULTICHOICE, + use_case=MetricUseCase.NONE, + sample_level_fn=sample_level_fn, + corpus_level_fn=np.mean, +) + +################################################################################ +# プロンプト関数とカスタムメトリックに基づいてタスクを定義 +# ガイドに基づいて https://github.com/huggingface/lighteval/wiki/Adding-a-Custom-Task +################################################################################ + +task = LightevalTaskConfig( + name="example", + prompt_function=prompt_fn, + suite=["community"], + hf_repo="burtenshaw/exam_questions", + hf_subset="default", + hf_avail_splits=["train"], + evaluation_splits=["train"], + few_shots_split=None, + few_shots_select=None, + metric=[custom_metric], +) + +# TASKS_TABLEにタスクを追加 +TASKS_TABLE = [task] + +# モジュールロジック +if __name__ == "__main__": + print([t.name for t in TASKS_TABLE]) + print(len(TASKS_TABLE)) + +# lighteval accelerate \ +# "pretrained=HuggingFaceTB/SmolLM2-135M-Instruct" \ +# "community|example|0|0" \ +# --custom-tasks "submitted_tasks/example.py" \ +# --output-dir "results" diff --git a/ja/4_evaluation/project/generate_dataset.py b/ja/4_evaluation/project/generate_dataset.py new file mode 100644 index 00000000..3bd42bfd --- /dev/null +++ b/ja/4_evaluation/project/generate_dataset.py @@ -0,0 +1,168 @@ +import argparse +import os +from pydantic import BaseModel, Field +from datasets import Dataset +from typing import List + +from distilabel.llms import InferenceEndpointsLLM +from distilabel.pipeline import Pipeline +from distilabel.steps.tasks import TextGeneration + + +################################################################################ +# スクリプトのパラメータ +################################################################################ + +parser = argparse.ArgumentParser( + description="ディレクトリ内のテキストファイルから試験問題を生成します。" +) +parser.add_argument( + "--model_id", + type=str, + default="Qwen/Qwen2.5-7B-Instruct", + help="テキスト生成のためのモデルID", +) +parser.add_argument( + "--tokenizer_id", + type=str, + default="Qwen/Qwen2.5-7B-Instruct", + help="テキスト生成のためのトークナイザーID", +) +parser.add_argument( + "--input_dir", + type=str, + help="入力テキストファイルを含むディレクトリ", + default="data", +) +parser.add_argument( + "--max_new_tokens", + type=int, + default=2048, + help="生成する新しいトークンの最大数", +) +parser.add_argument( + "--output_path", + type=str, + default="exam_questions_output", + help="生成されたデータセットを保存するディレクトリ", +) + +args = parser.parse_args() + +################################################################################ +# ドキュメントの読み込み +# ドキュメントは入力ディレクトリにあり、各ファイルが同じトピックに関する +# 個別のドキュメントであると仮定します。 +################################################################################ + +# 入力ディレクトリ内のすべてのテキストファイルを処理 +documents = [] +for filename in os.listdir(args.input_dir): + if filename.endswith(".txt"): + file_path = os.path.join(args.input_dir, filename) + with open(file=file_path, mode="r", encoding="utf-8") as file: + document_content = file.read() + documents.append(document_content) + +# すべてのドキュメント内容から単一のデータセットを作成 +dataset = Dataset.from_dict({"document": documents}) + +################################################################################ +# プロンプトの定義 +# モデルが正しい出力形式を生成するようにシステムプロンプトを使用します。 +# テンプレートを使用してドキュメントをプロンプトに挿入します。 +################################################################################ + +SYSTEM_PROMPT = """\ +あなたは学生のための試験問題を作成する専門家です。 +提供されたドキュメントに基づいて質問と回答を作成し、 +質問に対する正しい回答と、誤っているが妥当な回答のリストを作成してください。 +回答は次の形式に従う必要があります: +``` +[ + { + "question": "質問内容", + "answer": "質問に対する正しい回答", + "distractors": ["誤った回答1", "誤った回答2", "誤った回答3"] + }, + ... (必要に応じてさらに質問と回答を追加) +] +``` +""".strip() + +INSTRUCTION_TEMPLATE = """\ + ドキュメントに関する質問と回答のリストを生成してください。 + ドキュメント:\n\n{{ instruction }}""" + +################################################################################ +# 出力構造の定義 +# パイプラインの出力のデータモデルを定義し、評価タスクの正しい形式で +# 出力があることを確認します。 +################################################################################ + + +class ExamQuestion(BaseModel): + question: str = Field(..., description="回答すべき質問") + answer: str = Field(..., description="質問に対する正しい回答") + distractors: List[str] = Field( + ..., description="質問に対する誤っているが妥当な回答のリスト" + ) + + +class ExamQuestions(BaseModel): + exam: List[ExamQuestion] + + +################################################################################ +# パイプラインの作成 +# ドキュメントに基づいて試験問題を生成し、正しい形式で出力する単一のタスクを +# 持つパイプラインを作成します。Hugging Face InferenceEndpointsと +# 引数で指定されたモデルを使用します。 +################################################################################ + +with Pipeline( + name="Domain-Eval-Questions", + description="提供されたドキュメントに基づいて試験問題を生成します。", +) as pipeline: + # テキスト生成タスクの設定 + text_generation = TextGeneration( + name="exam_generation", + llm=InferenceEndpointsLLM( + model_id=args.model_id, + tokenizer_id=args.model_id, + api_key=os.environ["HF_TOKEN"], + structured_output={ + "schema": ExamQuestions.model_json_schema(), + "format": "json", + }, + ), + input_batch_size=8, + output_mappings={"model_name": "generation_model"}, + input_mappings={"instruction": "document"}, + system_prompt=SYSTEM_PROMPT, + template=INSTRUCTION_TEMPLATE, + ) + + +################################################################################ +# パイプラインの実行 +# すべてのドキュメントに対してパイプラインを実行し、結果を出力パスに保存します。 +################################################################################ + +if __name__ == "__main__": + # すべてのドキュメントに対してパイプラインを実行 + distiset = pipeline.run( + parameters={ + "exam_generation": { + "llm": { + "generation_kwargs": { + "max_new_tokens": args.max_new_tokens, + } + } + } + }, + use_cache=False, + dataset=dataset, + ) + + distiset.save_to_disk(args.output_path) diff --git a/ja/README.md b/ja/README.md new file mode 100644 index 00000000..b7f0e6e8 --- /dev/null +++ b/ja/README.md @@ -0,0 +1,90 @@ +![smolcourse image](../banner.png) + +# スモールコース + +これは、特定のユースケースに合わせて言語モデルを調整するための実践的なコースです。ほとんどのローカルマシンで実行できるため、言語モデルの調整を始めるのに便利です。GPUの要件は最小限で、有料サービスは必要ありません。このコースは[SmolLM2](https://github.com/huggingface/smollm/tree/main)シリーズのモデルに基づいていますが、ここで学んだスキルを大規模なモデルや他の小型言語モデルに転用することができます。 + + + + + +
+

参加は無料で、今すぐ始められます!

+

このコースはオープンでピアレビューされています。コースに参加するには、プルリクエストを開くことで、あなたの作業をレビューに提出してください。以下の手順に従ってください:

+
    +
  1. リポジトリをフォークします こちら
  2. +
  3. 資料を読み、変更を加え、演習を行い、自分の例を追加します。
  4. +
  5. december_2024ブランチでプルリクエストを開きます
  6. +
  7. レビューを受けてマージされます
  8. +
+

これにより、学習を助け、常に改善されるコミュニティ主導のコースを構築することができます。

+
+ +このプロセスについては、この[ディスカッションスレッド](https://github.com/huggingface/smol-course/discussions/2#discussion-7602932)で議論できます。 + +## コース概要 + +このコースは、小型言語モデルを使用した実践的なアプローチを提供し、初期のトレーニングから本番展開までをカバーします。 + +| モジュール | 説明 | ステータス | リリース日 | +|--------|-------------|---------|--------------| +| [インストラクションチューニング](./1_instruction_tuning) | 教師あり微調整、チャットテンプレート、および基本的な指示に従う方法を学びます | ✅ 準備完了 | 2024年12月3日 | +| [選好整合](./2_preference_alignment) | DPOおよびORPO技術を探求し、人間の選好にモデルを整合させる方法を学びます | ✅ 準備完了 | 2024年12月6日 | +| [パラメータ効率の良い微調整](./3_parameter_efficient_finetuning) | LoRA、プロンプトチューニング、および効率的な適応方法を学びます | ✅ 準備完了 | 2024年12月9日 | +| [評価](./4_evaluation) | 自動ベンチマークを使用し、カスタムドメイン評価を作成する方法を学びます | ✅ 準備完了 | 2024年12月13日 | +| [ビジョン言語モデル](./5_vision_language_models) | マルチモーダルモデルをビジョン言語タスクに適応させる方法を学びます | ✅ 準備完了 | 2024年12月16日 | +| [合成データセット](./6_synthetic_datasets) | トレーニング用の合成データセットを作成し、検証する方法を学びます | ✅ 準備完了 | 2024年12月20日 | +| [推論](./7_inference) | モデルを効率的に推論する方法を学びます | [🚧 作業中](https://github.com/huggingface/smol-course/pull/150) | 2025年1月8日 | +| [エージェント](./8_agents) | 自分のエージェントAIを構築する方法を学びます | ✅ 準備完了 | 2025年1月13日 || +| キャップストーンプロジェクト | 学んだことを使ってリーダーボードを登りましょう! | [🚧 作業中](https://github.com/huggingface/smol-course/pull/97) | 2025年1月10日 | + +## なぜ小型言語モデルなのか? + +大規模な言語モデルは印象的な能力を示していますが、しばしば多くの計算リソースを必要とし、特定のアプリケーションには過剰な場合があります。小型言語モデルは、ドメイン固有のアプリケーションに対していくつかの利点を提供します: + +- **効率性**:トレーニングと展開に必要な計算リソースが大幅に少ない +- **カスタマイズ**:特定のドメインに簡単に微調整および適応可能 +- **制御**:モデルの動作をよりよく理解し、制御できる +- **コスト**:トレーニングと推論の運用コストが低い +- **プライバシー**:データを外部APIに送信せずにローカルで実行可能 +- **グリーンテクノロジー**:リソースの効率的な使用を推進し、炭素排出量を削減 +- **学術研究の容易さ**:最先端のLLMを使用した学術研究のための簡単なスターターを提供し、物流の制約を減らす + +## 前提条件 + +開始する前に、以下を確認してください: +- 機械学習と自然言語処理の基本的な理解 +- Python、PyTorch、および`transformers`ライブラリに精通していること +- 事前学習された言語モデルとラベル付きデータセットへのアクセス + +## インストール + +コースをパッケージとして維持しているため、パッケージマネージャーを使用して依存関係を簡単にインストールできます。`uv`をお勧めしますが、`pip`や`pdm`などの代替手段も使用できます。 + +### `uv`を使用する場合 + +`uv`がインストールされている場合、次のようにしてコースをインストールできます: + +```bash +uv venv --python 3.11.0 +uv sync +``` + +### `pip`を使用する場合 + +すべての例は**python 3.11**環境で実行されるため、次のように環境を作成し、依存関係をインストールします: + +```bash +# python -m venv .venv +# source .venv/bin/activate +pip install -r requirements.txt +``` + +### Google Colab + +**Google Colabから**は、使用するハードウェアに基づいて柔軟に依存関係をインストールする必要があります。次のようにします: + +```bash +pip install transformers trl datasets huggingface_hub +``` +