# インストラクションデータセットの生成

[インストラクションチューニングの章](../1_instruction_tuning/README.md)では、教師あり微調整によるモデルの微調整について学びました。このセクションでは、SFTのためのインストラクションデータセットの生成方法を探ります。基本的なプロンプトを使用してインストラクションチューニングデータセットを作成する方法や、論文から得られたより洗練された技術を使用する方法を探ります。インストラクションチューニングデータセットは、SelfInstructやMagpieのような方法を使用して、インコンテキスト学習のためのシードデータを使用して作成できます。さらに、EvolInstructを通じたインストラクションの進化についても探ります。最後に、distilabelパイプラインを使用してインストラクションチューニングのためのデータセットを生成する方法を探ります。

## プロンプトからデータへ

合成データは一見複雑に見えますが、モデルから知識を抽出するための効果的なプロンプトを作成することに簡略化できます。つまり、特定のタスクのためのデータを生成する方法と考えることができます。課題は、データが多様で代表的であることを保証しながら、効果的にプロンプトを作成することです。幸いなことに、多くの論文がこの問題を探求しており、このコースではいくつかの有用なものを探ります。まずは、手動でプロンプトを作成して合成データを生成する方法を探ります。

### 基本的なプロンプト

基本的な例から始めましょう。[HuggingFaceTB/SmolLM2-1.7B-Instruct](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct)モデルを`distilabel`ライブラリの`transformers`統合を使用してロードします。`TextGeneration`クラスを使用して合成`プロンプト`を生成し、それを使用して`completion`を生成します。

次に、`distilabel`ライブラリを使用してモデルをロードします。

```python
from distilabel.llms import TransformersLLM
from distilabel.steps.tasks import TextGeneration

llm = TransformersLLM(model="HuggingFaceTB/SmolLM2-1.7B-Instruct")
gen = TextGeneration(llm=llm)
gen.load()
```

!!! note
    Distilabelは`llm`をメモリにロードするため、ノートブックで作業する場合は、メモリの問題を避けるために使用後に`gen.unload()`する必要があります。

次に、`llm`を使用してインストラクションチューニングのための`プロンプト`を生成します。

```python
next(gen.process([{"instruction": "Hugging Faceの小規模AIモデルに関するSmol-Courseについての質問を生成してください。"}]))
# Smol-Courseの目的は何ですか？
```

最後に、同じ`プロンプト`を入力として使用して`completion`を生成します。

```python
next(gen.process([{"instruction": "Smol-Courseの目的は何ですか？"}]))
# Smol-Courseはコンピュータサイエンスの概念を学ぶためのプラットフォームです。
```

素晴らしい！合成`プロンプト`と対応する`completion`を生成できました。このシンプルなアプローチをスケールアップして、より多くのデータを生成することができますが、データの品質はそれほど高くなく、コースやドメインのニュアンスを考慮していません。さらに、現在のコードを再実行すると、データがそれほど多様でないことがわかります。幸いなことに、この問題を解決する方法があります。

### SelfInstruct

SelfInstructは、シードデータセットに基づいて新しいインストラクションを生成するプロンプトです。このシードデータは単一のインストラクションやコンテキストの一部である場合があります。プロセスは、初期のシードデータのプールから始まります。言語モデルは、インコンテキスト学習を使用してこのシードデータに基づいて新しいインストラクションを生成するようにプロンプトされます。このプロンプトは[distilabelで実装されています](https://github.com/argilla-io/distilabel/blob/main/src/distilabel/steps/tasks/templates/self-instruct.jinja2)が、簡略化されたバージョンは以下の通りです：

```
# タスクの説明
与えられたAIアプリケーションが受け取ることができる{{ num_instructions }}のユーザークエリを開発してください。モデルのテキスト能力内で動詞と言語構造の多様性を強調してください。

# コンテキスト
{{ input }}

# 出力
```

これを使用するには、`llm`を[SelfInstructクラス](https://distilabel.argilla.io/dev/components-gallery/tasks/selfinstruct/)に渡す必要があります。[プロンプトからデータセクション](#prompt-to-data)のテキストをコンテキストとして使用し、新しいインストラクションを生成してみましょう。

```python
from distilabel.steps.tasks import SelfInstruct

self_instruct = SelfInstruct(llm=llm)
self_instruct.load()

context = "<prompt_to_data_section>"

next(self_instruct.process([{"input": text}]))["instructions"][0]
# 手動プロンプトを使用して合成データを生成するプロセスは何ですか？
```

生成されたインストラクションはすでにかなり良くなっており、実際のコンテンツやドメインに適しています。しかし、プロンプトを進化させることでさらに良くすることができます。

### EvolInstruct

EvolInstructは、入力インストラクションを進化させて、同じインストラクションのより良いバージョンにするプロンプト技術です。このより良いバージョンは、一連の基準に従って定義され、元のインストラクションに制約、深化、具体化、推論、または複雑化を追加します。このプロセスは、元のインストラクションのさまざまな進化を作成するために複数回繰り返すことができ、理想的には元のインストラクションのより良いバージョンに導きます。このプロンプトは[distilabelで実装されています](https://github.com/argilla-io/distilabel/tree/main/src/distilabel/steps/tasks/evol_instruct)が、簡略化されたバージョンは以下の通りです：

```
プロンプトリライターとして行動してください。
与えられたプロンプトを、より複雑なバージョンに書き換えてください。
以下の基準に基づいてプロンプトを複雑化してください：
{{ criteria }}

# プロンプト
{{ input }}

# 出力
```

これを使用するには、`llm`を[EvolInstructクラス](https://distilabel.argilla.io/dev/components-gallery/tasks/evolinstruct/)に渡す必要があります。[SelfInstructセクション](#selfinstruct)の合成プロンプトを入力として使用し、それをより良いバージョンに進化させてみましょう。この例では、1世代だけ進化させます。

```python
from distilabel.steps.tasks import EvolInstruct

evol_instruct = EvolInstruct(llm=llm, num_evolutions=1)
evol_instruct.load()

text = "手動プロンプトを使用して合成データを生成するプロセスは何ですか？"

next(evol_instruct.process([{"instruction": text}]))
# 手動プロンプトを使用して合成データを生成するプロセスは何ですか？
# そして、人工知能システムであるGPT4が機械学習アルゴリズムを使用して入力データを合成データに変換する方法は？
```

インストラクションはより複雑になりましたが、元の意味を失っています。したがって、進化させることは両刃の剣であり、生成するデータの品質に注意する必要があります。

### Magpie

Magpieは、言語モデルの自己回帰的要因と、インストラクションチューニングプロセス中に使用されていた[チャットテンプレート](../1_instruction_tuning/chat_templates.md)に依存する技術です。覚えているかもしれませんが、チャットテンプレートは、システム、ユーザー、アシスタントの役割を明確に示す形式で会話を構造化します。インストラクションチューニングフェーズ中に、言語モデルはこの形式を再現するように最適化されており、Magpieはそれを利用します。チャットテンプレートに基づいた事前クエリプロンプトから始めますが、ユーザーメッセージインジケーター、例：<|im_end|>ユーザー\nの前で停止し、その後、言語モデルを使用してユーザープロンプトを生成し、アシスタントインジケーター、例：<|im_end|>まで生成します。このアプローチにより、多くのデータを非常に効率的に生成でき、マルチターンの会話にもスケールアップできます。この生成されたデータは、使用されたモデルのインストラクションチューニングフェーズのトレーニングデータを再現すると仮定されています。

このシナリオでは、プロンプトテンプレートはチャットテンプレート形式に基づいているため、モデルごとに異なります。しかし、プロセスをステップバイステップで簡略化して説明できます。

```bash
# ステップ1：事前クエリプロンプトを提供する
<|im_end|>ユーザー\n

# ステップ2：言語モデルがユーザープロンプトを生成する
<|im_end|>ユーザー\n
Smol-Courseの目的は何ですか？

# ステップ3：生成を停止する
<|im_end|>
```

distilabelで使用するには、`llm`を[Magpieクラス](https://distilabel.argilla.io/dev/components-gallery/tasks/magpie/)に渡す必要があります。

```python
from distilabel.steps.tasks import Magpie

magpie = Magpie(llm=llm)
magpie.load()

next(magpie.process([{"system_prompt": "あなたは役立つアシスタントです。"}]))
# [{
#   "role": "user",
#   "content": "トップ3の大学のリストを提供できますか？"
# },
# {
#   "role": "assistant",
#   "content": "トップ3の大学は：MIT、イェール、スタンフォードです。"
# }]
```

すぐに`プロンプト`と`completion`を含むデータセットが得られます。ドメインに特化したパフォーマンスを向上させるために、`system_prompt`に追加のコンテキストを挿入できます。LLMが特定のドメインデータを生成するために、システムプロンプトでユーザーのクエリがどのようなものかを説明することが役立ちます。これは、ユーザープロンプトを生成する前の事前クエリプロンプトで使用され、LLMがそのドメインのユーザークエリを生成するようにバイアスをかけます。

```
あなたは数学の問題を解決するためにユーザーを支援するAIアシスタントです。
```

システムプロンプトに追加のコンテキストを渡すことは、一般的に言語モデルが最適化されていないため、カスタマイズには他の技術ほど効果的ではないことが多いです。

### プロンプトからパイプラインへ

これまで見てきたクラスはすべて、パイプラインで使用できるスタンドアロンのクラスです。これは良いスタートですが、`Pipeline`クラスを使用してデータセットを生成することでさらに良くすることができます。`TextGeneration`ステップを使用してインストラクションチューニングのための合成データセットを生成します。パイプラインは、データをロードするための`LoadDataFromDicts`ステップ、`プロンプト`を生成するための`TextGeneration`ステップ、およびそのプロンプトの`completion`を生成するためのステップで構成されます。ステップを接続し、`>>`演算子を使用してデータをパイプラインに流します。distilabelの[ドキュメント](https://distilabel.argilla.io/dev/components-gallery/tasks/textgeneration/#input-output-columns)には、ステップの入力および出力列に関する情報が記載されています。データがパイプラインを正しく流れるようにするために、`output_mappings`パラメータを使用して出力列を次のステップの入力列にマッピングします。

```python
from distilabel.llms import TransformersLLM
from distilabel.pipeline import Pipeline
from distilabel.steps import LoadDataFromDicts
from distilabel.steps.tasks import TextGeneration

with Pipeline() as pipeline:
    data = LoadDataFromDicts(data=[{"instruction": "Hugging Faceの小規模AIモデルに関するSmol-Courseについての短い質問を生成してください。"}])
    llm = TransformersLLM(model="HuggingFaceTB/SmolLM2-1.7B-Instruct")
    gen_a = TextGeneration(llm=llm, output_mappings={"generation": "instruction"})
    gen_b = TextGeneration(llm=llm, output_mappings={"generation": "response"})
    data >> gen_a >> gen_b

if __name__ == "__main__":
    distiset = pipeline.run(use_cache=False)
    print(distiset["default"]["train"][0])
# [{
#   "instruction": "Smol-Courseの目的は何ですか？",
#   "response": "Smol-Courseはコンピュータサイエンスの概念を学ぶためのプラットフォームです。"
# }]
```

このパイプラインの背後には多くのクールな機能があります。生成結果を自動的にキャッシュするため、生成ステップを再実行する必要がありません。フォールトトレランスが組み込まれており、生成ステップが失敗してもパイプラインは実行を続けます。また、すべての生成ステップを並行して実行するため、生成が高速です。`draw`メソッドを使用してパイプラインを視覚化することもできます。ここでは、データがパイプラインをどのように流れ、`output_mappings`が出力列を次のステップの入力列にどのようにマッピングするかを確認できます。

![Pipeline](./images/pipeline.png)

## ベストプラクティス

- 多様なシードデータを確保して、さまざまなシナリオをカバーする
- 生成されたデータが多様で高品質であることを定期的に評価する
- データの品質を向上させるために（システム）プロンプトを繰り返し改善する

## 次のステップ

👨🏽💻 コード - [演習ノートブック](./notebooks/instruction_sft_dataset.ipynb)でインストラクションチューニングのためのデータセットを生成する
🧑🏫 学ぶ - [preference datasetsの生成](./preference_datasets.md)について学ぶ

## 参考文献

- [Distilabel Documentation](https://distilabel.argilla.io/latest/)
- [Self-instruct](https://arxiv.org/abs/2212.10560)
- [Evol-Instruct](https://arxiv.org/abs/2304.12244)
- [Magpie](https://arxiv.org/abs/2406.08464)

A course on aligning smol models.


# 嗜好データセットの生成

[嗜好調整の章](../2_preference_alignment/README.md)では、直接嗜好最適化について学びました。このセクションでは、DPOのような方法のための嗜好データセットを生成する方法を探ります。[インストラクションデータセットの生成](./instruction_datasets.md)で紹介した方法を基に構築します。さらに、基本的なプロンプトを使用してデータセットに追加の完了を追加する方法や、EvolQualityを使用して応答の品質を向上させる方法を示します。最後に、UltraFeedbackを使用してスコアと批評を生成する方法を示します。

## 複数の完了を作成する

嗜好データは、同じ`インストラクション`に対して複数の`完了`を持つデータセットです。モデルにプロンプトを与えて追加の`完了`を生成することで、データセットにより多くの`完了`を追加できます。この際、2つ目の完了が全体的な品質や表現において最初の完了とあまり似ていないことを確認する必要があります。これは、モデルが明確な嗜好に最適化される必要があるためです。通常、`選ばれた`と`拒否された`と呼ばれる完了のどちらが好まれるかを知りたいのです。[スコアの作成セクション](#creating-scores)で、選ばれた完了と拒否された完了を決定する方法について詳しく説明します。

### モデルプーリング

異なるモデルファミリーからモデルを使用して2つ目の完了を生成することができます。これをモデルプーリングと呼びます。2つ目の完了の品質をさらに向上させるために、`温度`を調整するなど、異なる生成引数を使用することができます。最後に、異なるプロンプトテンプレートやシステムプロンプトを使用して2つ目の完了を生成し、特定の特性に基づいて多様性を確保することができます。理論的には、異なる品質の2つのモデルを使用し、より良いものを`選ばれた`完了として使用することができます。

まず、[Qwen/Qwen2.5-1.5B-Instruct](https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct)と[HuggingFaceTB/SmolLM2-1.7B-Instruct](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct)モデルを`transformers`統合の`distilabel`ライブラリを使用してロードします。これらのモデルを使用して、与えられた`プロンプト`に対して2つの合成`応答`を作成します。`LoadDataFromDicts`、`TextGeneration`、および`GroupColumns`を使用して別のパイプラインを作成します。最初にデータをロードし、次に2つの生成ステップを使用し、最後に結果をグループ化します。ステップを接続し、`>>`演算子と`[]`を使用してデータをパイプラインに流します。これは、前のステップの出力を次のステップの入力として使用することを意味します。

```python
from distilabel.llms import TransformersLLM
from distilabel.pipeline import Pipeline
from distilabel.steps import GroupColumns, LoadDataFromDicts
from distilabel.steps.tasks import TextGeneration

with Pipeline() as pipeline:
    data = LoadDataFromDicts(data=[{"instruction": "合成データとは何ですか？"}])
    llm_a = TransformersLLM(model="HuggingFaceTB/SmolLM2-1.7B-Instruct")
    gen_a = TextGeneration(llm=llm_a)
    llm_b = TransformersLLM(model="Qwen/Qwen2.5-1.5B-Instruct")
    gen_b = TextGeneration(llm=llm_b)
    group = GroupColumns(columns=["generation"])
    data >> [gen_a, gen_b] >> group

if __name__ == "__main__":
    distiset = pipeline.run()
    print(distiset["default"]["train"]["grouped_generation"][0])
# {[
#   '合成データは、実際の使用状況を模倣する人工的に生成されたデータです。',
#   '合成データとは、人工的に生成されたデータを指します。'
# ]}
```

ご覧のとおり、与えられた`プロンプト`に対して2つの合成`完了`があります。生成ステップを特定の`システムプロンプト`で初期化するか、`TransformersLLM`に生成引数を渡すことで、多様性をさらに向上させることができます。次に、EvolQualityを使用して`完了`の品質を向上させる方法を見てみましょう。

### EvolQuality

EvolQualityは、[EvolInstruct](./instruction_datasets.md#evolinstruct)に似ていますが、入力`プロンプト`の代わりに`完了`を進化させます。このタスクは、`プロンプト`と`完了`の両方を受け取り、一連の基準に基づいて`完了`をより良いバージョンに進化させます。このより良いバージョンは、役立ち度、関連性、深化、創造性、または詳細の基準に基づいて定義されます。これにより、データセットに追加の`完了`を自動的に生成できます。理論的には、進化したバージョンが元の完了よりも優れていると仮定し、それを`選ばれた`完了として使用することができます。

プロンプトは[distilabelで実装されています](https://github.com/argilla-io/distilabel/tree/main/src/distilabel/steps/tasks/evol_quality)が、簡略化されたバージョンは以下の通りです：

```bash
応答リライターとして行動してください。
与えられたプロンプトと応答を、より良いバージョンに書き換えてください。
以下の基準に基づいてプロンプトを複雑化してください：
{{ criteria }}

# プロンプト
{{ input }}

# 応答
{{ output }}

# 改善された応答
```

これを使用するには、[EvolQualityクラス](https://distilabel.argilla.io/dev/components-gallery/tasks/evolquality/)に`llm`を渡す必要があります。[モデルプーリングセクション](#model-pooling)の合成`プロンプト`と`完了`を使用して、より良いバージョンに進化させてみましょう。この例では、1世代だけ進化させます。

```python
from distilabel.llms import TransformersLLM
from distilabel.steps.tasks import EvolQuality

llm = TransformersLLM(model="HuggingFaceTB/SmolLM2-1.7B-Instruct")
evol_quality = EvolQuality(llm=llm, num_evolutions=1)
evol_quality.load()

instruction = "合成データとは何ですか？"
completion = "合成データは、実際の使用状況を模倣する人工的に生成されたデータです。"

next(evol_quality.process([{
    "instruction": instruction,
    "response": completion
}]))
# 手動プロンプトを使用して合成データを生成するプロセスは何ですか？
```

`応答`はより複雑で、`インストラクション`に特化しています。これは良いスタートですが、EvolInstructで見たように、進化した生成物が常に優れているわけではありません。したがって、データセットの品質を確保するために追加の評価技術を使用することが重要です。次のセクションでこれを探ります。

## スコアの作成

スコアは、ある応答が他の応答よりもどれだけ好まれるかを測定するものです。一般的に、これらのスコアは絶対的、主観的、または相対的です。このコースでは、最初の2つに焦点を当てます。これらは嗜好データセットの作成に最も価値があるためです。このスコアリングは、評価技術と重なる部分があり、[評価の章](../3_evaluation/README.md)で見たように、通常はより大きなモデルが必要です。

### UltraFeedback

UltraFeedbackは、与えられた`プロンプト`とその`完了`に対してスコアと批評を生成する技術です。

スコアは、一連の基準に基づいて`完了`の品質を評価します。基準には、`役立ち度`、`関連性`、`深化`、`創造性`の4つの細かい基準があります。これらは便利ですが、一般的には全体的な基準を使用するのが良いスタートです。これにより、スコアの生成プロセスが簡略化されます。スコアは、どの`完了`が`選ばれた`もので、どれが`拒否された`ものであるかを決定するために使用できます。絶対的なスコアであるため、データセットの外れ値を見つけるための興味深いフィルターとしても使用できます。たとえば、最悪の完了や差が大きいペアを見つけることができます。

批評はスコアの理由を提供するために追加されます。これにより、スコアの違いを理解するための追加のコンテキストが提供されます。言語モデルは広範な批評を生成しますが、これは非常に便利ですが、スコアを表す単一のトークンを生成するよりもコストと複雑さが増します。

プロンプトは[distilabelで実装されています](https://github.com/argilla-io/distilabel/tree/main/src/distilabel/steps/tasks/templates/ultrafeedback)が、簡略化されたバージョンは以下の通りです：

```bash
モデルの出力をさまざまな基準に基づいて評価してください：役立ち度、関連性、深化、創造性
上記の要因に基づいて全体的な評価を提供する役割を果たします。
出力の全体的な品質を1から5のスコアで評価してください。

以下の形式で回答してください：スコア - 理由

# 入力
{{ input }}

# 応答
{{ output }}

# スコア - 理由
```

これを使用するには、[UltraFeedbackクラス](https://distilabel.argilla.io/dev/components-gallery/tasks/ultrafeedback/)に`llm`を渡す必要があります。[モデルプーリングセクション](#model-pooling)の合成`プロンプト`と`完了`を評価してみましょう。

```python
from distilabel.llms import TransformersLLM
from distilabel.steps.tasks import UltraFeedback

llm = TransformersLLM(model="HuggingFaceTB/SmolLM2-1.7B-Instruct")
ultrafeedback = UltraFeedback(llm=llm)
ultrafeedback.load()

instruction = "合成データとは何ですか？"
completion_a = "合成データは、実際の使用状況を模倣する人工的に生成されたデータです。"
completion_b = "合成データとは、人工的に生成されたデータを指します。"

next(ultrafeedback.process([{
    "instruction": instruction,
    "generations": [completion_a, completion_b]
}]))
# [
#     {
#         'ratings': [4, 5],
#         'rationales': ['could have been more specific', 'good definition'],
#     }
# ]
```

## ベストプラクティス

- 全体的なスコアは、批評や特定のスコアよりも安価で生成が容易です
- スコアや批評を生成するために大きなモデルを使用する
- スコアや批評を生成するために多様なモデルセットを使用する
- `system_prompt`やモデルの構成を繰り返し改善する

## 次のステップ

👨🏽💻 コード -[演習ノートブック](./notebooks/preference_dpo_dataset.ipynb)でインストラクションチューニングのためのデータセットを生成する

## 参考文献

- [Distilabel Documentation](https://distilabel.argilla.io/latest/)
- [Deita](https://arxiv.org/abs/2312.15685)
- [UltraFeedback](https://arxiv.org/abs/2310.01377)

A course on aligning smol models.


# 지시 조정(Instruction Tuning)

이 모듈에서는 언어 모델의 지시 조정(instruction tuning) 방법을 설명합니다. 지시 조정이란, 사전 학습된 모델을 특정 태스크에 맞게 조정하기 위해 해당 태스크와 관련된 데이터셋으로 추가 학습시키는 과정을 의미합니다. 이를 통해 목표로 하는 작업에서 모델이 더 나은 성능을 발휘할 수 있습니다.

이 모듈에서는 다음 두 가지 주제를 다룰 예정입니다: 1) 대화 템플릿(Chat Templates) 2) 지도 학습 기반 미세 조정(Supervised Fine-Tuning)

## 1⃣ 대화 템플릿

채팅 템플릿(Chat Templates)은 사용자와 AI 모델 간의 상호작용을 구조화하여 모델의 일관되고 적절한 맥락의 응답을 보장합니다. 템플릿에는 시스템 프롬프트와 역할 기반 메시지와 같은 구성 요소가 포함됩니다. 더 자세한 내용은 [대화 템플릿](./chat_templates.md) 섹션을 참고하세요.

## 2⃣ 지도 학습 기반 미세 조정

지도 학습 기반 미세 조정(SFT)은 사전 학습된 언어 모델이 특정 작업에 적합하도록 조정하는 데 핵심적인 과정입니다. 이 과정에서는 레이블이 포함된 태스크별 데이터셋을 사용해 모델을 학습시킵니다. SFT의 주요 단계와 모범 사례를 포함한 자세한 가이드는 [지도 학습 기반 미세 조정](./supervised_fine_tuning.md) 섹션을 참고하세요.

## 실습 노트북
| 파일명 | 설명 | 실습 내용 | 링크 | Colab |
|-------|-------------|----------|------|-------|
| Chat Templates | SmolLM2를 활용한 대화 템플릿 사용법과 데이터셋을 ChatML 형식으로 변환하는 과정 학습 | 🐢 `HuggingFaceTB/smoltalk` 데이터셋을 chatml 형식으로 변환해보기
 🐕 `openai/gsm8k` 데이터셋을 chatml 형식으로 변환해보기 | [Notebook](./notebooks/chat_templates_example.ipynb) |  |
| Supervised Fine-Tuning | SFTTrainer를 이용해 SmolLM2를 파인튜닝하는 방법 학습 | 🐢 `HuggingFaceTB/smoltalk` 데이터셋 활용해보기
🐕 `bigcode/the-stack-smol` 데이터셋 활용해보기
🦁 실제 사용 사례에 맞는 데이터셋 선택해보기 | [Notebook](./notebooks/sft_finetuning_example.ipynb) |  |
## 참고

- [Transformers documentation on chat templates](https://huggingface.co/docs/transformers/main/en/chat_templating)
- [Script for Supervised Fine-Tuning in TRL](https://github.com/huggingface/trl/blob/main/examples/scripts/sft.py)
- [`SFTTrainer` in TRL](https://huggingface.co/docs/trl/main/en/sft_trainer)
- [Direct Preference Optimization Paper](https://arxiv.org/abs/2305.18290)
- [Supervised Fine-Tuning with TRL](https://huggingface.co/docs/trl/main/en/tutorials/supervised_finetuning)
- [How to fine-tune Google Gemma with ChatML and Hugging Face TRL](https://www.philschmid.de/fine-tune-google-gemma)
- [Fine-tuning LLM to Generate Persian Product Catalogs in JSON Format](https://huggingface.co/learn/cookbook/en/fine_tuning_llm_to_generate_persian_product_catalogs_in_json_format)

A course on aligning smol models.


# 대화 템플릿(Chat Templates)

대화 템플릿은 언어 모델과 사용자 간 상호작용을 구조화하는 데 필수적입니다. 이 템플릿은 대화에 일관된 형식을 제공하여, 모델이 각 메시지의 맥락과 역할을 이해하고 적절한 응답 패턴을 유지할 수 있도록 합니다.

## 기본 모델 vs 지시 모델

기본 모델은 다음 토큰을 예측하기 위해 대량의 원시 텍스트 데이터로 학습되는 반면, 지시 모델은 특정 지시를 따르고 대화를 나눌 수 있도록 미세 조정된 모델입니다. 예를 들어, `SmolLM2-135M`은 기본 모델이고, `SmolLM2-135M-Instruct`는 이를 지시 조정한 변형 모델입니다.

기본 모델이 지시 모델처럼 동작하도록 만들기 위해서는 모델이 이해할 수 있는 방식으로 프롬프트를 일관되게 만들어야 합니다. 이때 대화 템플릿이 유용합니다. ChatML은 이러한 템플릿 형식 중 하나로 대화를 구조화하고 명확한 역할 지시자(시스템, 사용자, 어시스턴트)를 제공합니다.

기본 모델은 서로 다른 대화 템플릿으로 미세 조정될 수 있으므로, 지시 모델을 사용할 때는 반드시 해당 모델에 적합한 대화 템플릿을 사용하는 것이 중요합니다.

## 대화 템플릿 이해하기

대화 템플릿의 핵심은 언어 모델과 소통할 때 대화가 어떤 형식으로 이루어져야 하는지 정의하는 것입니다. 템플릿에는 시스템 수준의 지침, 사용자 메시지, 어시스턴트의 응답이 포함되어 있으며, 모델이 이해할 수 있는 구조화된 형식을 제공합니다. 이러한 구조는 상호작용의 일관성을 유지하고, 모델이 다양한 유형의 입력에 적절히 응답하도록 돕습니다. 아래는 채팅 템플릿의 예시입니다:

```sh
<|im_start|>user
안녕하세요!<|im_end|>
<|im_start|>assistant
만나서 반갑습니다!<|im_end|>
<|im_start|>user
질문을 해도 될까요?<|im_end|>
<|im_start|>assistant
```

`transformers` 라이브러리는 모델의 토크나이저와 관련하여 대화 템플릿을 자동으로 처리해줍니다. 대화 템플릿이 transformers에서 어떻게 구성되는지 자세히 알아보려면 [여기](https://huggingface.co/docs/transformers/en/chat_templating#how-do-i-use-chat-templates)를 참고하세요. 우리가 메시지를 올바른 형식으로 구조화하기만 하면 나머지는 토크나이저가 처리합니다. 아래는 기본적인 대화 예시입니다:
```python
messages = [
    {"role": "system", "content": "You are a helpful assistant focused on technical topics."},
    {"role": "user", "content": "Can you explain what a chat template is?"},
    {"role": "assistant", "content": "A chat template structures conversations between users and AI models..."}
]
```

위 예시를 자세히 보면서 대화 템플릿 형식에 어떻게 매핑되는지 살펴봅시다.

## 시스템 메시지

시스템 메시지는 모델의 기본 동작 방식을 설정합니다. 이는 이후 모든 상호작용에 영향을 미치는 지속적인 지침이 됩니다.

```python
system_message = {
    "role": "system",
    "content": "You are a professional customer service agent. Always be polite, clear, and helpful."
}
```

## 대화

대화 템플릿은 대화 기록을 통해 맥락을 유지하며, 사용자와 어시스턴트 간의 이전 대화를 저장합니다. 이를 통해 더욱 일관된 멀티 턴 대화가 가능해집니다:

```python
conversation = [
    {"role": "user", "content": "I need help with my order"},
    {"role": "assistant", "content": "I'd be happy to help. Could you provide your order number?"},
    {"role": "user", "content": "It's ORDER-123"},
]
```

## Transformers 기반 구현

transformers 라이브러리는 대화 템플릿을 위한 내장 기능을 지원합니다.사용 방법은 다음과 같습니다:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-135M-Instruct")

messages = [
    {"role": "system", "content": "You are a helpful coding assistant."},
    {"role": "user", "content": "Write a Python function to sort a list"},
]

# 대화 템플릿 적용
formatted_chat = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)
```

## 사용자 정의 형식

메시지 유형별로 원하는 형식을 설정할 수 있습니다. 예를 들어, 역할에 따라 특수 토큰을 추가하거나 형식을 지정할 수 있습니다:

```python
template = """
<|system|>{system_message}
<|user|>{user_message}
<|assistant|>{assistant_message}
""".lstrip()
```

## 멀티 턴 지원

템플릿은 문맥을 유지하면서 복잡한 멀티 턴 대화도 처리할 수 있습니다:

```python
messages = [
    {"role": "system", "content": "You are a math tutor."},
    {"role": "user", "content": "What is calculus?"},
    {"role": "assistant", "content": "Calculus is a branch of mathematics..."},
    {"role": "user", "content": "Can you give me an example?"},
]
```

⏭ [다음: Supervised Fine-Tuning](./supervised_fine_tuning.md)

## 참고

- [Hugging Face Chat Templating Guide](https://huggingface.co/docs/transformers/main/en/chat_templating)
- [Transformers Documentation](https://huggingface.co/docs/transformers)
- [Chat Templates Examples Repository](https://github.com/chujiezheng/chat_templates)

A course on aligning smol models.


# 지도 학습 기반 미세 조정(Supervised Fine-Tuning)

지도 학습 기반 미세 조정(SFT)은 사전 학습된 언어 모델을 특정 작업이나 도메인에 맞게 조정하는 데 핵심적인 과정입니다. 사전 학습된 모델의 능력은 일반적으로 뛰어나지만, 특정 사용 사례에서 우수한 성능을 발휘하려면 맞춤화가 필요할 때가 많습니다. SFT는 사람이 검증한 예제를 포함하여 신중하게 선별된 데이터셋으로 모델을 추가 학습시켜 이러한 격차를 해소합니다.

## SFT 이해하기

SFT의 핵심은 레이블이 있는 토큰을 통해 사전 학습된 모델이 특정 작업을 수행하도록 학습시키는 것입니다. 이 과정에서 원하는 입력-출력 동작 예제를 모델에 많이 보여줌으로써, 해당 사용 사례에 특화된 패턴을 학습하도록 합니다.

SFT가 효과적인 이유는 사전 학습 과정에서 습득한 기초 지식을 활용하면서도 사용자의 특정 요구에 맞게 모델의 동작을 조정할 수 있기 때문입니다.

## SFT를 언제 사용해야 하나요?

SFT 사용 여부는 주로 모델이 현재 가지고 있는 능력과 특정 요구 사항 간의 차이에 따라 결정됩니다. SFT는 특히 모델 출력에 대한 정교한 제어가 필요하거나 전문적인 도메인에서 작업할 때 매우 유용합니다.

예를 들어, 고객 서비스 애플리케이션을 개발하는 경우 모델이 회사 지침을 일관되게 따르고 기술적인 질문을 표준화된 방식으로 처리하도록 만들고 싶을 것입니다. 마찬가지로 의료나 법률 애플리케이션에서는 정확성과 도메인 특화 용어를 사용하는 것이 중요합니다. SFT는 이러한 경우에서 모델의 응답이 전문적인 표준을 따르고 모델 응답을 도메인 전문 지식에 맞게 조정하는 데 도움을 줄 수 있습니다.

## 미세 조정 과정

지도 학습 기반 미세 조정은 특정 태스크에 맞춘 데이터셋으로 모델 가중치를 학습시키는 과정을 포함합니다.

먼저, 목표 태스크를 대표할 수 있는 데이터셋을 준비하거나 선택해야 합니다. 이 데이터셋은 모델이 마주하게 될 다양한 시나리오를 포괄할 수 있는 예제를 포함하고 있어야 합니다. 데이터 품질이 매우 중요하며 각 예제는 모델이 생성하길 원하는 출력 유형을 명확히 보여줘야 합니다. 이후 Hugging Face의 `transformers` 및 `trl`과 같은 프레임워크를 사용하여 데이터셋을 기반으로 모델을 학습시키는 실제 미세 조정 단계가 이어집니다.

이 과정 전반에서 지속적인 평가가 필수적입니다. 모델이 원하는 동작을 학습하면서도 일반적인 능력을 잃지 않도록 검증 데이터셋을 사용해 성능을 모니터링해야 합니다. 모델을 평가하는 방법에 대해서는 [모듈 4](../4_evaluation)에서 다룰 예정입니다.

## 선호도 조정에서 SFT의 역할

SFT는 언어 모델을 인간의 선호도에 맞추는 데 기본적인 역할을 합니다. 인간 피드백을 통한 강화 학습(Reinforcement Learning from Human Feedback, RLHF) 및 직접 선호도 최적화(Direct Preference Optimization, DPO)와 같은 기술은 SFT를 사용하여 기본적인 수준으로 태스크를 이해하게 한 후 모델의 응답을 원하는 결과에 맞게 조정합니다. 사전 학습된 모델은 일반적인 언어 능력을 가지고 있음에도 불구하고 항상 사람의 선호도와 일치하는 결과를 생성하지 못할 수 있습니다. SFT는 도메인별 데이터와 지침을 도입하여 이러한 격차를 해소함으로써 모델이 인간의 기대에 부합하는 응답을 생성할 수 있도록 합니다.

## TRL을 활용한 SFT

지도 학습 기반 미세 조정에서 중요한 소프트웨어 패키지 중 하나는 Transformer 강화 학습(Transformer Reinforcement Learning, TRL)입니다. TRL은 강화 학습(Reinforcement Learning, RL)을 사용하여 트랜스포머 언어 모델을 학습시키기 위한 도구입니다.

Hugging Face Transformers 라이브러리를 기반으로 구축된 TRL은 사용자가 사전 학습된 언어 모델을 직접 로드할 수 있게 하며, 대부분의 디코더 및 인코더-디코더 아키텍처를 지원합니다. 이 라이브러리는 언어 모델링에 사용되는 주요 RL 프로세스를 지원하고 지도 학습 기반 미세 조정(SFT), 보상 모델링(Reward Modeling, RM), 근접 정책 최적화(Proximal Policy Optimization, PPO), 직접 선호 최적화(Direct Preference Optimization, DPO)와 같은 과정을 용이하게 만듭니다.

이 레포지토리의 여러 모듈에서 TRL을 활용할 예정입니다.

# 다음 단계

다음 튜토리얼을 통해 TRL을 활용한 SFT 실습 경험을 쌓아 보세요:

⏭ [Chat Templates 튜토리얼](./notebooks/chat_templates_example.ipynb)

⏭ [Supervised Fine-Tuning 튜토리얼](./notebooks/supervised_fine_tuning_tutorial.ipynb)

A course on aligning smol models.


# Parameter-Efficient Fine-Tuning, PEFT

언어 모델이 커지면서 전통적인 미세 조정 방식을 적용하는 것이 점점 어려워지고 있습니다. 1.7B 모델조차도 전체 미세 조정을 수행하려면 상당한 GPU 메모리가 필요하며, 모델 사본을 별도로 저장하기 위한 비용이 많이 들고, 모델의 원래 능력을 상실하는 위험이 존재합니다. Parmeter-Efficient Fine-Tuning(PEFT) 방법은 대부분의 모델 파라미터가 고정된 상태에서 모델 파라미터의 일부만 수정하여 전체 미세 조정 과정에서 발생하는 문제를 해결헙니다.

학습 과정에서 모델의 모든 파라미터를 업데이트하는 전통적인 미세 조정 방법을 대형 언어 모델에 적용하는 것은 현실적으로 어렵습니다. PEFT는 원래 모델 크기의 1% 미만에 해당하는 파라미터만 학습시켜 모델을 조정하는 방법입니다. 학습 가능한 파라미터를 크게 줄이는 것은 다음과 같은 이점을 제공합니다:

- 제한된 GPU 메모리를 가진 하드웨어에서도 미세 조정 가능
- 효율적인 태스크별 적응 모델 저장
- 데이터가 적은 상황에서도 뛰어난 일반화 성능 제공
- 더 빠른 학습 및 반복 가능

## 사용 가능한 방법

이 모듈에서는 많이 사용되는 두 가지 PEFT 방법을 다룹니다:

### 1⃣ LoRA (Low-Rank Adaptation)

LoRA는 효율적인 모델 적응을 위한 멋진 솔루션을 제공하면서 가장 많이 사용되는 PEFT 방법으로 자리 잡았습니다. LoRA는 전체 모델을 수정하는 대신 **학습 가능한 파라미터를 모델의 어텐션 레이어에 주입**합니다. 이 접근법은 전체 미세 조정과 비슷한 성능을 유지하면서 학습 가능한 파라미터를 약 90%까지 줄입니다. [LoRA (Low-Rank Adaptation)](./lora_adapters.md) 섹션에서 LoRA에 대해 자세히 알아보겠습니다.

### 2⃣ 프롬프트 튜닝

프롬프트 튜닝은 모델 가중치를 수정하는 대신 **입력에 학습 가능한 토큰을 추가**하여 **더 경량화된** 접근법을 제공합니다. 프롬프트 튜닝은 LoRA만큼 유명하지는 않지만, 모델을 새로운 태스크나 도메인에 빠르게 적용할 때 유용하게 쓰일 수 있는 기술입니다. [프롬프트 튜닝](./prompt_tuning.md) 섹션에서 프롬프트 튜닝에 대해 탐구해볼 예정입니다.

## 실습 노트북

| 파일명 | 설명 | 실습 내용 | 링크 | Colab |
|-------|-------------|----------|------|-------|
| LoRA Fine-tuning | LoRA 어댑터를 사용해 모델을 미세 조정하는 방법 학습 | 🐢 LoRA를 사용해 모델 학습해보기
🐕 다양한 랭크 값으로 실험해보기
🦁 전체 미세 조정과 성능 비교해보기 | [Notebook](./notebooks/finetune_sft_peft.ipynb) |  |
| Load LoRA Adapters | LoRA 어댑터를 불러오고 학습시키는 방법 배우기 | 🐢 사전 학습된 어댑터 불러오기
🐕 기본 모델과 어댑터 합치기
🦁 여러 어댑터 간 전환해보기 | [Notebook](./notebooks/load_lora_adapter_example.ipynb) |  |

## 참고
- [Hugging Face 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)
- [How to Fine-Tune LLMs in 2024 with Hugging Face](https://www.philschmid.de/fine-tune-llms-in-2024-with-trl)
- [TRL](https://huggingface.co/docs/trl/index)

A course on aligning smol models.


# LoRA (Low-Rank Adaptation)

LoRA는 널리 쓰이는 PEFT 방법으로 자리 잡았습니다. 어텐션 가중치에 작은 랭크 분해 행렬을 추가하는 방식으로 동작작하며 일반적으로 학습 가능한 파라미터를 약 90% 줄여줍니다.

## LoRA 이해하기

LoRA(Low-Rank Adaptation)는 사전 학습된 모델 가중치를 고정한 상태에서 학습 가능한 랭크 분해 행렬을 모델 레이어에 주입하는 파라미터 효율적인 미세 조정 기법입니다. 미세 조정 과정에서 모든 모델 파라미터를 학습시키는 대신, LoRA는 저랭크 분해를 통해 가중치 업데이트를 더 작은 행렬로 나눠 모델 성능은 유지하면서 학습 가능한 파라미터 수를 크게 줄입니다. 예를 들어, GPT-3 175B에 LoRA를 적용했을 때 전체 미세 조정 대비 학습 가능한 파라미터 수는 10,000배, GPU 메모리 요구 사항은 3배 감소했습니다. [LoRA 논문](https://arxiv.org/pdf/2106.09685)에서 LoRA에 관한 자세한 내용을 확인할 수 있습니다.

LoRA는 일반적으로 트랜스포머 레이어 중 어텐션 가중치에 랭크 분해 행렬 쌍을 추가하는 방식으로 동작합니다. 어댑터 가중치는 추론 과정에서 기본 모델과 병합될 수 있고 추가적인 지연 시간이 발생하지 않습니다. LoRA는 자원 요구 사항을 적절한 수준으로 유지하면서 대형 언어 모델을 특정 태스크나 도메인에 맞게 조정하는 데 특히 유용합니다.

## LoRA 어댑터 불러오기

load_adapter()를 사용하여 사전 학습된 모델에 어댑터를 불러올 수 있으며 가중치가 병합되지 않은 다른 어댑터를 사용해 볼 때 유용합니다. set_adapter() 함수로 활성 어댑터 가중치를 설정합니다. 기본 모델을 반환하려면 unload()를 사용하여 불러온 모든 LoRA 모듈을 내릴 수 있습니다. 이렇게 하면 태스크별 가중치를 쉽게 전환할 수 있습니다.

```python
from transformers import AutoModelForCausalLM
from peft import PeftModel

base_model = AutoModelForCausalLM.from_pretrained("<base_model_name>")
peft_model_id = "<peft_adapter_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_finetuning.ipynb`를 통해 LoRA와 SFT를 사용한 모델 미세 조정 과정을 더 자세히 탐구해 보세요.

PEFT 방법을 구현할 때는 LoRA의 랭크를 4~8 정도의 작은 값으로 설정하고 학습 손실을 지속적으로 모니터링하는 것이 좋습니다. 과적합을 방지하기 위해 검증 세트를 활용하고 가능하다면 전체 미세 조정 기준선과 결과를 비교하세요. 다양한 태스크에서 각 방법의 효과는 다를 수 있으므로 실험을 통해 최적의 방법을 찾는 것이 중요합니다.

## OLoRA

[OLoRA](https://arxiv.org/abs/2406.01775)는 LoRA 어댑터 초기화를 위해 QR 분해를 활용합니다. OLoRA는 모델의 기본 가중치를 QR 분해 계수에 따라 변환합니다. 즉, 모델 학습 전에 가중치를 변경합니다. 이 접근 방식은 안정성을 크게 향상시키고 수렴 속도를 빠르게 하여, 궁극적으로 더 우수한 성능을 달성합니다.

## PEFT와 함께 TRL 사용하기

효율적인 미세 조정을 위해 PEFT 방법을 TRL(Transformers Reinforcements Learning)과 결합할 수 있습니다. 이러한 통합은 메모리 요구사항을 줄여주기 때문에 RLHF (Reinforcement Learning from Human Feedback)에 특히 유용합니다.

```python
from peft import LoraConfig
from transformers import AutoModelForCausalLM

# PEFT configuration 설정
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"Merging failed: {e}")
    # fallback 전략 또는 메모리 최적화 구현

# 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)
- [Hugging Face PEFT 문서](https://huggingface.co/docs/peft)
- [Hugging Face blog post on PEFT](https://huggingface.co/blog/peft)

A course on aligning smol models.


# 프롬프트 튜닝

프롬프트 튜닝은 모델 가중치 대신 입력 표현을 변경하는 파라미터 효율적인 접근법입니다. 모델의 모든 파라미터를 업데이트하는 전통적인 미세 조정과 다르게, 프롬프트 튜닝은 기본 모델의 파라미터가 고정된 상태를 유지하면서 소수의 학습 가능한 토큰을 추가하고 최적화합니다.

## 프롬프트 튜닝 이해하기

프롬프트 튜닝은 모델 미세 조정의 대안으로, 학습 가능한 연속 벡터(소프트 프롬프트)를 입력 텍스트에 추가하는 파라미터 효율적인 방식입니다. 소프트 프롬프트는 이산적인 텍스트 프롬프트와 다르게 모델 파라미터가 고정된 상태에서 역전파를 통해 학습됩니다. 이 방식은 ["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")

# 프롬프트 튜닝 configuration 설정
peft_config = PromptTuningConfig(
    task_type=TaskType.CAUSAL_LM,
    num_virtual_tokens=8,  # 학습 가능한 토큰 수
    prompt_tuning_init="TEXT",  # 텍스트 기반 초기화
    prompt_tuning_init_text="Classify if this text is positive or negative:",
    tokenizer_name_or_path="your-base-model",
)

# 프롬프트 튜닝 가능한 모델 생성
model = get_peft_model(model, peft_config)
```

## 프롬프트 튜닝과 다른 PEFT 방법 비교

다른 PEFT 접근 방식과 비교했을 때 프롬프트 튜닝은 효율성이 뛰어납니다. LoRA는 파라미터 수와 메모리 사용량이 적지만 태스크 전환을 위해 어댑터를 불러와야 하는 반면 프롬프트 튜닝은 자원을 더 적게 사용하면서 즉각적인 태스크 전환이 가능합니다. 반면에 전체 미세 조정은 상당한 양의 자원을 필요로 하고 서로 다른 태스크를 수행하기 위해 태스크에 맞는 각각의 모델을 필요로 합니다.

| 방법 | 파라미터 수 | 메모리 사용량 | 태스크 전환 |
|--------|------------|---------|----------------|
| 프롬프트 튜닝 | 매우 적음 | 메모리 사용량 최소화 | 쉬움 |
| LoRA | 적음 | 적음 | 모델을 다시 불러와야 함 |
| 전체 미세 조정 | 많음 | 많음 | 새로운 모델 복사 필요 |

프롬프트 튜닝을 구현할 때는 적은 수(8~16개)의 가상 토큰으로 시작한 후 태스크의 복잡성에 따라 토큰 수를 점차 늘려가세요. 태스크와 관련된 텍스트로 초기화를 수행하는 것이 무작위 초기화보다 일반적으로 더 나은 결과를 가져옵니다. 초기화 전략은 목표 태스크의 복잡성을 반영해야 합니다.

학습 과정은 전체 미세 조정과 약간의 차이가 있습니다. 보통 높은 학습률이 효과적이지만 프롬프트 토큰 경사를 신중하게 모니터링해야 합니다. 다양한 예제를 활용한 주기적인 검증은 서로 다른 시나리오에서 강력한 성능을 보장하는 데 도움이 됩니다.

## 응용

프롬프트 튜닝은 다음과 같은 시나리오에서 특히 뛰어납니다:

1. 멀티 태스크 배포
2. 자원이 제한적인 환경
3. 신속한 태스크 적응
4. 프라이버시에 민감한 애플리케이션

그러나 모델 크기가 작아질수록 전체 미세 조정과 비교했을 때 프롬프트 튜닝의 경쟁력이 떨어집니다. 예를 들어, SmolLM2 크기의 모델은 프롬프트 튜닝보다 전체 미세 조정를 수행하는 것이 좀 더 의미있습니다.

## 다음 단계

⏭ [LoRA 어댑터 튜토리얼](./notebooks/finetune_sft_peft.ipynb)에서 LoRA 어댑터를 활용해 모델을 미세 조정하는 법을 배워봅시다.

## 참고
- [Hugging Face PEFT 문서](https://huggingface.co/docs/peft)
- [프롬프트 튜닝 논문](https://arxiv.org/abs/2104.08691)
- [Hugging Face Cookbook](https://huggingface.co/learn/cookbook/prompt_tuning_peft)

A course on aligning smol models.


# Instruction Tuning (Ajuste de Instrução)

Este módulo o guiará através de modelos de linguagem de ajuste de instrução. O ajuste de instrução envolve a adaptação de modelos pré-treinados a tarefas específicas, treinando-os ainda mais em conjuntos de dados específicos de tarefas. Esse processo ajuda os modelos a melhorar seu desempenho em certas tarefas específicas.

Neste módulo, exploraremos dois tópicos: 1) Modelos de bate-papo e 2) Ajuste fino supervisionado.

## 1⃣ Modelos de Bate-Papo

Modelos de bate-papo estruturam interações entre usuários e modelos de IA, garantindo respostas consistentes e contextualmente apropriadas. Eles incluem componentes como avisos de sistema e mensagens baseadas em funções. Para informações mais detalhadas, Consulte a seção [Chat Templates (Modelos de Bate-Papo)](./chat_templates.md).

## 2⃣ Ajuste Fino Supervisionado

Ajuste fino supervisionado (em inglês, SFT - Supervised Fine-Tuning) é um processo crítico para adaptar modelos de linguagem pré-treinados a tarefas específicas. O ajuste envolve treinar o modelo em um conjunto de dados de uma tarefa específica com exemplos rotulados. Para um guia detalhado sobre SFT, incluindo etapas importantes e práticas recomendadas, veja a página [Supervised Fine-Tuning (Ajuste Fino Supervisionado)](./supervised_fine_tuning.md).

## Cadernos de Exercícios

| Título | Descrição | Exercício | Link | Colab |
|-------|-------------|----------|------|-------|
| Modelos de Bate-Papo | Aprenda a usar modelos de bate-papo com SmolLM2 and a processar conjunto de dados para o formato chatml | 🐢 Converta o conjunto de dados `HuggingFaceTB/smoltalk` para o formato chatml
 🐕 Converta o conjunto de dados `openai/gsm8k` para o formato chatml | [Exercício](./notebooks/chat_templates_example.ipynb) |  |
| Ajuste Fino Supervisionado | Aprenda como fazer o ajuste fino no modelo SmolLM2 usando o SFTTrainer | 🐢 Use o conjunto de dados `HuggingFaceTB/smoltalk`
🐕 Experimente o conjunto de dados `bigcode/the-stack-smol`
🦁 Selecione um conjunto de dados para um caso de uso do mundo real | [Exercício](./notebooks/sft_finetuning_example.ipynb) |  |

## Referências

- [Documentação dos transformadores em modelos de bate-papo](https://huggingface.co/docs/transformers/main/en/chat_templating)
- [Script para ajuste fino supervisionado em TRL](https://github.com/huggingface/trl/blob/main/examples/scripts/sft.py)
- [`SFTTrainer` em TRL](https://huggingface.co/docs/trl/main/en/sft_trainer)
- [Artigo de otimização de preferência direta](https://arxiv.org/abs/2305.18290)
- [Ajuste fino supervisionado com TRL](https://huggingface.co/docs/trl/main/en/tutorials/supervised_finetuning)
- [Como ajustar o Google Gemma com ChatML e Hugging Face TRL](https://www.philschmid.de/fine-tune-google-gemma)
- [Fazendo o ajuste fino em uma LLM para gerar catálogos de produtos persas em formato JSON](https://huggingface.co/learn/cookbook/en/fine_tuning_llm_to_generate_persian_product_catalogs_in_json_format)

A course on aligning smol models.


# Modelos de bate-papo

Os modelos de bate-papo são essenciais para estruturar interações entre modelos de linguagem e usuários. Eles fornecem um formato consistente para conversas, garantindo que os modelos entendam o contexto e o papel de cada mensagem, enquanto mantêm os padrões de resposta apropriados.

## Modelos Base vs Modelos de Instrução

Um modelo base é treinado em dados de texto bruto para prever o próximo token, enquanto um modelo de instrução é ajustado especificamente para seguir as instruções e se envolver em conversas. Por exemplo, `SmolLM2-135M` é um modelo base, enquanto o `SmolLM2-135M-Instruct` é sua variante ajustada por instrução.

Para fazer um modelo base se comportar como um modelo de instrução, precisamos formatar nossos prompts de uma maneira consistente que o modelo possa entender. É aqui que entram os modelos de bate-papo. ChatML é um desses formatos de modelo que estrutura conversas com indicadores claros de papéis (sistema, usuário, assistente).

É importante notar que o modelo base pode ser ajustado finamente em diferentes modelos de bate-papo, então, quando estamos usando um modelo de instrução, precisamos garantir que estamos usando o modelo de bate-papo correto.

## Entendendo os Modelos de Bate-papo

Na sua essência, os modelos de bate-papo definem como as conversas devem ser formatadas ao se comunicar com um modelo de linguagem. Eles incluem instruções no nível do sistema, mensagens do usuário e respostas assistentes em um formato estruturado que o modelo pode entender. Essa estrutura ajuda a manter a consistência entre as interações e garante que o modelo responda adequadamente a diferentes tipos de input de dados. Abaixo está um exemplo de modelo de bate-papo:

```sh
<|im_start|>user
Hi there!<|im_end|>
<|im_start|>assistant
Nice to meet you!<|im_end|>
<|im_start|>user
Can I ask a question?<|im_end|>
<|im_start|>assistant
```

O módulo `transformers` cuidará dos modelos de bate-papo para você em relação ao tokenizador do modelo. Leia mais sobre como os transformadores criam modelos de bate-papo [aqui](https://huggingface.co/docs/transformers/en/chat_templating#how-do-i-use-chat-templates). Tudo o que precisamos fazer é estruturar nossas mensagens da maneira correta e o tokenizador cuidará do resto. Abaixo, você verá um exemplo básico de uma conversa:

```python
messages = [
    {"role": "system", "content": "You are a helpful assistant focused on technical topics."},
    {"role": "user", "content": "Can you explain what a chat template is?"},
    {"role": "assistant", "content": "A chat template structures conversations between users and AI models..."}
]
```

Vamos ir passo-a-passo no exemplo acima e ver como ele mapeia o formato do modelo de bate-papo.

## Mensagens do Sistema

As mensagens do sistema definem a base de como o modelo deve se comportar. Elas agem como instruções persistentes que influenciam todas as interações subsequentes. Por exemplo:

```python
system_message = {
    "role": "system",
    "content": "You are a professional customer service agent. Always be polite, clear, and helpful."
}
```

## Conversas

Os modelos de bate-papo mantêm o contexto através do histórico de conversas, armazenando conversas anteriores entre os usuários e o assistente. Isso permite que as conversas de multi-turno sejam mais coerentes:

```python
conversation = [
    {"role": "user", "content": "I need help with my order"},
    {"role": "assistant", "content": "I'd be happy to help. Could you provide your order number?"},
    {"role": "user", "content": "It's ORDER-123"},
]
```

## Implementação com Transformadores

O módulo transformers fornece suporte interno para modelos de bate-papo. Veja como usá-los:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-135M-Instruct")

messages = [
    {"role": "system", "content": "You are a helpful coding assistant."},
    {"role": "user", "content": "Write a Python function to sort a list"},
]

# Apply the chat template
formatted_chat = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)
```

## Formatação Personalizada

Você pode personalizar como tipos diferentes de mensagens são formatadas. Por exemplo, adicionando tokens especiais ou formatando para diferentes funções:

```python
template = """
<|system|>{system_message}
<|user|>{user_message}
<|assistant|>{assistant_message}
""".lstrip()
```

## Suporte do Multi-turno

Os modelos podem lidar com conversas multi-turno complexas enquanto mantêm o contexto:

```python
messages = [
    {"role": "system", "content": "You are a math tutor."},
    {"role": "user", "content": "What is calculus?"},
    {"role": "assistant", "content": "Calculus is a branch of mathematics..."},
    {"role": "user", "content": "Can you give me an example?"},
]
```

⏭ [Próxima: Supervised Fine-Tuning (Ajuste Fino Supervisionado)](./supervised_fine_tuning.md)

## Resources

- [Guia de modelos de bate-papo - Hugging Face](https://huggingface.co/docs/transformers/main/en/chat_templating)
- [Documentação do módulo Transformers](https://huggingface.co/docs/transformers)
- [Repositório de exemplos de modelos de bate-papo](https://github.com/chujiezheng/chat_templates)

A course on aligning smol models.


# Ajuste Fino Supervisionado

Ajuste fino supervisionado (em inglês, SFT - Supervised Fine-Tuning) é um processo crítico para adaptar modelos de linguagem pré-treinados a tarefas ou domínios específicos. Embora os modelos pré-treinados tenham recursos gerais impressionantes, eles geralmente precisam ser personalizados para se destacar em casos de usos específicos. O SFT preenche essa lacuna treinando ainda mais o modelo com conjuntos de dados cuidadosamente selecionados com exemplos validados por humanos.

## Entendendo o Ajuste Fino Supervisionado

Na sua essência, o ajuste fino supervisionado é sobre ensinar um modelo pré-treinado a executar tarefas específicas por meio de exemplos de tokens rotulados. O processo envolve mostrar muitos exemplos do comportamento desejado de input-output ao modelo, permitindo que ele aprenda os padrões específicos do seu caso de uso.

O SFT é eficaz porque usa o conhecimento fundamental adquirido durante o pré-treinamento, adaptando o comportamento do modelo para atender às suas necessidades específicas.

## Quando Usar o Ajuste Fino Supervisionado

A decisão de usar o SFT geralmente se resume à lacuna entre os recursos atuais do seu modelo e seus requisitos específicos. O SFT se torna particularmente valioso quando você precisa de controle preciso sobre os outputs do modelo ou ao trabalhar em domínios especializados.

Por exemplo, se você estiver desenvolvendo um aplicativo de atendimento ao cliente, você vai querer que seu modelo siga constantemente as diretrizes da empresa e lide com consultas técnicas de maneira padronizada. Da mesma forma, em aplicações médicas ou legais, a precisão e a adesão à terminologia específica do domínio se tornam cruciais. Nesses casos, o SFT pode ajudar a alinhar as respostas do modelo com padrões profissionais e experiência no campo de trabalho.

## O Processo de Ajuste Fino

O processo de ajuste fino supervisionado envolve o treinamento dos pesos do modelo em um conjunto de dados de tarefa específico.

Primeiro, você precisará preparar ou selecionar um conjunto de dados que represente sua tarefa. Esse conjunto de dados deve incluir diversos exemplos que cobrem a gama de cenários que seu modelo encontrará. A qualidade desses dados é importante - cada exemplo deve demonstrar o tipo de output que você deseja que seu modelo produza. Em seguida, vem a fase real de ajuste fino, onde você usará estruturas como os módulos do Hugging Face, `transformers` e `trl`, para treinar o modelo no seu conjunto de dados.

Ao longo do processo, a avaliação contínua é essencial. Você vai querer monitorar o desempenho do modelo em um conjunto de validação para garantir que ele esteja aprendendo os comportamentos desejados sem perder suas capacidades gerais. No [Módulo 4](../4_evaluation), abordaremos como avaliar seu modelo.

## O Papel do SFT no Alinhamento de Preferência

O SFT desempenha um papel fundamental no alinhamento de modelos de linguagem com preferências humanas. Técnicas como o aprendizado de reforço com o feedback humano (RLHF - Reinforcement Learning with Human Feedback) e a otimização de preferência direta (DPO - Direct Preference Optimization) dependem do SFT para formar um nível básico de entendimento da tarefa antes de alinhar ainda mais as respostas do modelo com os resultados desejados. Modelos pré-treinados, apesar de sua proficiência em linguagem geral, nem sempre podem gerar resultados que correspondam às preferências humanas. O SFT preenche essa lacuna introduzindo dados e orientações específicos de domínio, o que melhora a capacidade do modelo de gerar respostas que se alinham mais de perto com as expectativas humanas.

## Ajuste Fino Supervisionado com Aprendizado de Reforço de Transformadores

Um pacote de software importante para ajuste fino supervisionado é o aprendizado de reforço de transformadores (TRL - Transformer Reinforcement Learning). TRL é um kit de ferramentas usado para treinar modelos de linguagem de transformação usando o aprendizado de reforço.

Construído em cima do módulo de transformadores do Hugging Face, o TRL permite que os usuários carreguem diretamente modelos de linguagem pré-treinados e suporta a maioria das arquiteturas decodificadoras e codificador-decodificador. O módulo facilita os principais processos de RL usados na modelagem de linguagem, incluindo ajuste fino supervisionado (SFT), modelagem de recompensa (RM - Reward Modeling), otimização de políticas proximais (PPO - Proximal Policy Optimization) e otimização de preferência direta (DPO). Usaremos o TRL em vários módulos ao longo deste repositório.

# Próximos passos

Experimente os seguintes tutoriais para obter experiência com o SFT usando TRL:

⏭ [Tutorial de modelos de bate-papo](./notebooks/chat_templates_example.ipynb)

⏭ [Tutorial de ajuste fino supervisionado](./notebooks/sft_finetuning_example.ipynb)

A course on aligning smol models.


# Preference Alignment (Alinhamento de Preferência)

Este módulo abrange técnicas para alinhar modelos de linguagem com preferências humanas. Enquanto o ajuste fino supervisionado ajuda os modelos a aprender tarefas, o alinhamento de preferência incentiva os resultados a corresponder às expectativas e valores humanos.

## Visão Geral

Métodos de alinhamento típicos envolvem vários estágios:
1. Ajuste fino supervisionado (SFT) para adaptar os modelos a domínios específicos
2. Alinhamento de preferência (como RLHF ou DPO) para melhorar a qualidade da resposta

Abordagens alternativas como ORPO combinam ajuste de instrução e alinhamento de preferência em um único processo. Aqui, vamos focar nos algoritmos DPO e ORPO.

Se você quer aprender mais sobre as diferentes técnicas de alinhamento, você pode ler mais sobre isso no [Argilla Blog](https://argilla.io/blog/mantisnlp-rlhf-part-8).

### 1⃣ Otimização de Preferência Direta (DPO - Direct Preference Optimization)

Otimização de preferência direta (DPO) simplifica o alinhamento de preferência otimizando diretamente os modelos usando dados de preferência. Essa abordagem elimina a necessidade de usar modelos de recompensa que não fazem parte do sistema e aprendizado de reforço complexo, tornando-o mais estável e eficiente do que o tradicional aprendizado de reforço com o feedback humano (RLHF). Para mais detalhes,você pode ler mais em [Documentação sobre otimização de preferência direta (DPO)](./dpo.md).

### 2⃣ Otimização de Preferências de Razão de Chances (ORPO - Odds Ratio Preference Optimization)

ORPO introduz uma abordagem combinada para ajuste de instrução e alinhamento de preferência em um único processo. Ele modifica o objetivo padrão de modelagem de linguagem combinando a perda de log-verossimilhança negativa com um termo de razão de chances em um nível de token. A abordagem apresenta um processo de treinamento unificado de estágio único, arquitetura sem modelo de referência e eficiência computacional aprimorada. O ORPO apresentou resultados impressionantes em vários benchmarks, demonstrando melhor desempenho no AlpacaEval em comparação com os métodos tradicionais. Para obter mais detalhes, consulte a [Documentação sobre Otimização de Preferências de Razão de Chances (ORPO)](./orpo.md)

## Caderno de Exercícios

| Título | Descrição | Exercício | Link | Colab |
|-------|-------------|----------|------|-------|
| Treinamento em DPO | Aprenda a treinar modelos usando a Otimização Direta de Preferência | 🐢 Treine um modelo usando o conjunto de dados Anthropic HH-RLHF
🐕 Use seu próprio conjunto de dados de preferências
🦁 Experimente diferentes conjuntos de dados de preferências e tamanhos de modelos | [Exercício](./notebooks/dpo_finetuning_example.ipynb) |  |
| Treinamento em ORPO | Aprenda a treinar modelos usando a otimização de preferências de razão de chances | 🐢 Treine um modelo usando instruções e dados de preferências
🐕 Experimente com diferentes pesos de perda
🦁 Comparar os resultados de ORPO com DPO | [Exercício](./notebooks/orpo_finetuning_example.ipynb) |  |

## Referências

- [Documentação do TRL](https://huggingface.co/docs/trl/index) - Documentação do módulo Transformers Reinforcement Learning (TRL), que implementa várias técnicas de alinhamento, inclusive a DPO.
- [Artigo sobre DPO](https://arxiv.org/abs/2305.18290) - Documento de pesquisa original que apresenta a Otimização Direta de Preferência como uma alternativa mais simples ao RLHF que otimiza diretamente os modelos de linguagem usando dados de preferência.
- [Artigo sobre ORPO](https://arxiv.org/abs/2403.07691) - Apresenta a Otimização de preferências de razão de chances, uma nova abordagem que combina o ajuste de instruções e o alinhamento de preferências em um único estágio de treinamento.
- [Guia Argilla RLHF](https://argilla.io/blog/mantisnlp-rlhf-part-8/) - Um guia que explica diferentes técnicas de alinhamento, incluindo RLHF, DPO e suas implementações práticas.
- [Postagem de blog sobre DPO](https://huggingface.co/blog/dpo-trl) - Guia prático sobre a implementação de DPO usando a biblioteca TRL com exemplos de código e práticas recomendadas.
- [Exemplo de script TRL no DPO](https://github.com/huggingface/trl/blob/main/examples/scripts/dpo.py) - Script de exemplo completo que demonstra como implementar o treinamento em DPO usando a biblioteca TRL.
- [Exemplo de script TRL no ORPO](https://github.com/huggingface/trl/blob/main/examples/scripts/orpo.py) - Implementação de referência do treinamento ORPO usando a biblioteca TRL com opções de configuração detalhadas.
- [Manual de alinhamento do Hugging Face](https://github.com/huggingface/alignment-handbook) - Guias de recursos e base de código para alinhamento de modelos de linguagem usando várias técnicas, incluindo SFT, DPO e RLHF.

A course on aligning smol models.


# Otimização de Preferência Direta (DPO)

Otimização de Preferência Direta (DPO) oferece uma abordagem simplificada para alinhar modelos de linguagem com preferências humanas. Ao contrário dos métodos RLHF tradicionais que requerem modelos de recompensa separados e aprendizado de reforço complexo, o DPO otimiza diretamente o modelo usando dados de preferência.

## Entendendo sobre o DPO

O DPO reformula o alinhamento da preferência como um problema de classificação nos dados de preferência humana. As abordagens tradicionais de RLHF exigem treinamento de um modelo de recompensa separado e usando algoritmos de aprendizado de reforço complexos como o PPO para alinhar os outputs do modelo. O DPO simplifica esse processo, definindo uma função de perda que otimiza diretamente a política do modelo com base em outputs preferidos versus não preferidos.

Esta abordagem se mostrou altamente eficaz quando colocada em prática, sendo usada para treinar modelos como o Llama. Ao eliminar a necessidade de usar modelos de recompensa que não fazem parte do sistema e aprendizado de reforço complexo, DPO faz o alinhamento de preferência muito mais acessível e estável.

## Como o DPO Funciona

O processo do DPO exige que o ajuste fino supervisionado (SFT) adapte o modelo ao domínio-alvo. Isso cria uma base para a aprendizagem de preferências por meio do treinamento em conjuntos de dados padrão que seguem instruções. O modelo aprende a concluir tarefas básicas enquanto mantém suas capacidades gerais.

Em seguida, vem o aprendizado de preferências, em que o modelo é treinado em pares de outputs - um preferido e outro não preferido. Os pares de preferências ajudam o modelo a entender quais respostas se alinham melhor com os valores e as expectativas humanas.

A principal inovação do DPO está em sua abordagem de otimização direta. Em vez de treinar um modelo de recompensa separado, o DPO usa uma perda de entropia cruzada binária para atualizar diretamente os pesos do modelo com base nos dados de preferência. Esse processo simplificado torna o treinamento mais estável e eficiente e, ao mesmo tempo, obtém resultados comparáveis ou melhores do que a RLHF tradicional.

## Conjuntos de Dados para DPO

Os conjuntos de dados para DPO são normalmente criados anotando pares de respostas como preferidas ou não preferidas. Isso pode ser feito manualmente ou usando técnicas de filtragem automatizadas. Abaixo está um exemplo de estrutura de conjunto de dados de preferência de turno único para DPO:

| Prompt | Escolhido | Rejeitado |
|--------|-----------|-----------|
| ...    | ...       | ...       |
| ...    | ...       | ...       |
| ...    | ...       | ...       |

A coluna `Prompt` contém o prompt usado para gerar as respostas `Escolhido` e `Rejeitado`. As colunas `Escolhido` e `Rejeitado` contêm as respostas preferidas e não preferidas, respectivamente. Há variações nessa estrutura, por exemplo, incluindo uma coluna de prompt do sistema ou uma coluna `Input` que contém material de referência. Os valores de `Escolhido` e `Rejeitado` podem ser representados como cadeias de caracteres para conversas de turno único ou como listas de conversas.

Você pode encontrar uma coleção de conjuntos de dados DPO em Hugging Face [aqui](https://huggingface.co/collections/argilla/preference-datasets-for-dpo-656f0ce6a00ad2dc33069478).

## Implementação com TRL

O módulo Transformers Reinforcement Learning (TRL) facilita a implementação do DPO. As classes `DPOConfig` e `DPOTrainer` seguem a mesma API no estilo `transformers`.
Veja a seguir um exemplo básico de configuração de treinamento de DPO:

```python
from trl import DPOConfig, DPOTrainer

# Define arguments
training_args = DPOConfig(
    ...
)

# Initialize trainer
trainer = DPOTrainer(
    model,
    train_dataset=dataset,
    tokenizer=tokenizer,
    ...
)

# Train model
trainer.train()
```

Abordaremos mais detalhes sobre como usar as classes `DPOConfig` e `DPOTrainer` no [Tutotial sobre DPO](./notebooks/dpo_finetuning_example.ipynb).

## Práticas Recomendadas

A qualidade dos dados é fundamental para a implementação bem sucedida do DPO. O conjunto de dados de preferências deve incluir diversos exemplos que abranjam diferentes aspectos do comportamento desejado. Diretrizes claras de anotação garantem a rotulagem consistente das respostas preferidas e não preferidas. Você pode aprimorar o desempenho do modelo melhorando a qualidade do conjunto de dados de preferências. Por exemplo, filtrando conjuntos de dados maiores para incluir apenas exemplos de alta qualidade ou exemplos relacionados ao seu caso de uso.

Durante o treinamento, monitore cuidadosamente a convergência da perda e valide o desempenho em dados retidos. O parâmetro beta pode precisar de ajustes para equilibrar o aprendizado de preferências com a manutenção das capacidades gerais do modelo. A avaliação regular em diversos prompts ajuda a garantir que o modelo esteja aprendendo as preferências pretendidas sem se ajustar demais.

Compare os resultados do modelo com o modelo de referência para verificar a melhoria no alinhamento de preferências. Testar em uma variedade de prompts, incluindo casos extremos, ajuda a garantir um aprendizado de preferências robusto em diferentes cenários.

## Próximos Passos

⏩ Para obter experiência prática com o DPO, experimente fazer o [Tutorial DPO](./notebooks/dpo_finetuning_example.ipynb). Esse guia prático o orientará na implementação do alinhamento de preferências com seu próprio modelo, desde a preparação dos dados até o treinamento e a avaliação.

⏭ Depois de concluir o tutorial, você pode explorar a página do [ORPO](./orpo.md) para conhecer outra técnica de alinhamento de preferências.

A course on aligning smol models.


# Otimização de Preferências de Razão de Chances (ORPO)

A ORPO (Odds Ratio Preference Optimization) é uma nova técnica de ajuste fino que combina o ajuste fino e o alinhamento de preferências em um único processo unificado. Essa abordagem combinada oferece vantagens em termos de eficiência e desempenho em comparação com métodos tradicionais como RLHF ou DPO.

## Entendendo sobre o ORPO

O alinhamento com métodos como o DPO normalmente envolve duas etapas separadas: ajuste fino supervisionado para adaptar o modelo a um domínio e formato, seguido pelo alinhamento de preferências para alinhar com as preferências humanas. Embora o SFT adapte efetivamente os modelos aos domínios-alvo, ele pode aumentar inadvertidamente a probabilidade de gerar respostas desejáveis e indesejáveis. O ORPO aborda essa limitação integrando as duas etapas em um único processo, conforme ilustrado na comparação abaixo:

![Comparação das Técnicas de Alinhamento](https://argilla.io/images/blog/mantisnlp-rlhf/part-8-alignments.png)
*Comparação de diferentes técnicas de alinhamento de modelos*

## Como o ORPO Funciona

O processo de treinamento utiliza um conjunto de dados de preferências semelhante ao que usamos para o DPO, em que cada exemplo de treinamento contém um prompt de entrada com duas respostas: uma que é preferida e outra que é rejeitada. Diferentemente de outros métodos de alinhamento que exigem estágios e modelos de referência separados, o ORPO integra o alinhamento de preferências diretamente ao processo de ajuste fino supervisionado. Essa abordagem monolítica o torna livre de modelos de referência, computacionalmente mais eficiente e eficiente em termos de memória com menos FLOPs.

O ORPO cria um novo objetivo ao combinar dois componentes principais:

1. **Função de perda do SFT**: A perda padrão de log-verossimilhança negativa usada na modelagem de linguagem, que maximiza a probabilidade de gerar tokens de referência. Isso ajuda a manter as capacidades gerais de linguagem do modelo.

2. **Função de perda da Razão de Chances**: Um novo componente que penaliza respostas indesejáveis e recompensa as preferidas. Essa função de perda usa índices de probabilidade (razão de chances) para contrastar efetivamente entre respostas favorecidas e desfavorecidas no nível do token.

Juntos, esses componentes orientam o modelo a se adaptar às gerações desejadas para o domínio específico e, ao mesmo tempo, desencorajam ativamente as gerações do conjunto de respostas rejeitadas. O mecanismo de razão de chances oferece uma maneira natural de medir e otimizar a preferência do modelo entre os resultados escolhidos e rejeitados. Se quiser se aprofundar na matemática, você pode ler o [artigo sobre ORPO](https://arxiv.org/abs/2403.07691). Se quiser saber mais sobre o ORPO do ponto de vista da implementação, confira como a função de perda do ORPO é calculada no [módulo TRL](https://github.com/huggingface/trl/blob/b02189aaa538f3a95f6abb0ab46c0a971bfde57e/trl/trainer/orpo_trainer.py#L660).

## Desempenho e Resultados

O ORPO demonstrou resultados impressionantes em vários benchmarks. No MT-Bench, ele alcança pontuações competitivas em diferentes categorias:

![Resultados do MT-Bench](https://argilla.io/images/blog/mantisnlp-rlhf/part-8-mtbench.png)
*Resultados do MT-Bench por categoria para os modelos Mistral-ORPO*

Quando comparado a outros métodos de alinhamento, o ORPO apresenta desempenho superior no AlpacaEval 2.0:

![Resultados do AlpacaEval](https://argilla.io/images/blog/mantisnlp-rlhf/part-8-winrate.png)
*Pontuações do AlpacaEval 2.0 em diferentes métodos de alinhamento*

Em comparação com p SFT+DPO, o ORPO reduz os requisitos de computação, eliminando a necessidade de um modelo de referência e reduzindo pela metade o número de passagens de avanço por lote. Além disso, o processo de treinamento é mais estável em diferentes tamanhos de modelos e conjuntos de dados, exigindo menos hiperparâmetros para ajustar. Em termos de desempenho, o ORPO se iguala aos modelos maiores e mostra melhor alinhamento com as preferências humanas.

## Implementação

A implementação bem-sucedida do ORPO depende muito de dados de preferência de alta qualidade. Os dados de treinamento devem seguir diretrizes claras de anotação e fornecer uma representação equilibrada das respostas preferidas e rejeitadas em diversos cenários.

### Implementação com TRL

O ORPO pode ser implementado usando o módulo Transformers Reinforcement Learning (TRL). Aqui está um exemplo básico:

```python
from trl import ORPOConfig, ORPOTrainer

# Configure ORPO training
orpo_config = ORPOConfig(
    learning_rate=1e-5,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    max_steps=1000,
    orpo_alpha=1.0,  # Controls strength of preference optimization
    orpo_beta=0.1,   # Temperature parameter for odds ratio
)

# Initialize trainer
trainer = ORPOTrainer(
    model=model,
    args=orpo_config,
    train_dataset=dataset,
    tokenizer=tokenizer,
)

# Start training
trainer.train()
```

Principais parâmetros a serem considerados:
- `orpo_alpha`: Controla a força da otimização de preferências
- `orpo_beta`: Parâmetro de temperatura para o cálculo da razão de chances
- `learning_rate`: Deve ser relativamente pequeno para evitar o esquecimento catastrófico
- `gradient_accumulation_steps`: Ajuda na estabilidade do treinamento

## Próximos Passos

⏩ Experimente o [Tutorial do ORPO](./notebooks/orpo_tutorial.ipynb) para implementar essa abordagem unificada ao alinhamento de preferências.

## Referências
- [Artigo sobre ORPO](https://arxiv.org/abs/2403.07691)
- [Documentação TRL](https://huggingface.co/docs/trl/index)
- [Guia Argilla RLHF](https://argilla.io/blog/mantisnlp-rlhf-part-8/)

A course on aligning smol models.


# Parameter-Efficient Fine-Tuning (PEFT) (Ajuste Fino com Eficiência de Parâmetro)

À medida que os modelos de linguagem aumentam, o ajuste fino tradicional torna-se cada vez mais desafiador. O ajuste fino completo de um modelo com 1,7 bilhão de parâmetros requer uma quantidade considerável de memória da GPU, torna caro o armazenamento de cópias separadas do modelo e apresenta o risco de um esquecimento catastrófico das capacidades originais do modelo. Os métodos de ajuste fino com eficiência de parâmetros (PEFT) abordam esses desafios modificando apenas um pequeno subconjunto de parâmetros do modelo e mantendo a maior parte do modelo congelada.

O ajuste fino tradicional atualiza todos os parâmetros do modelo durante o treinamento, o que se torna impraticável para modelos grandes. Os métodos PEFT introduzem abordagens para adaptar modelos usando menos parâmetros treináveis, geralmente menos de 1% do tamanho do modelo original. Essa redução drástica nos parâmetros treináveis permite:

- Ajuste fino no hardware do consumidor com memória de GPU limitada
- Armazenamento eficiente de várias adaptações de tarefas específicas
- Melhor generalização em cenários com poucos dados
- Ciclos de treinamento e iteração mais rápidos

## Métodos Disponíveis

Neste módulo, abordaremos dois métodos populares de PEFT:

### 1⃣ LoRA (Low-Rank Adaptation - Adaptação de Baixa Classificação)

O LoRA surgiu como o método PEFT mais amplamente adotado, oferecendo uma solução elegante para a adaptação eficiente do modelo. Em vez de modificar o modelo inteiro, o **LoRA injeta matrizes treináveis nas camadas de atenção do modelo.**  Essa abordagem normalmente reduz os parâmetros treináveis em cerca de 90%, mantendo um desempenho comparável ao ajuste fino completo. Exploraremos o LoRA na seção [LoRA (Adaptação de Baixa Classificação)](./lora_adapters.md).

### 2⃣ Ajuste de Prompts

O ajuste de prompts oferece uma abordagem **ainda mais leve** ao **adicionar tokens treináveis à entrada** em vez de modificar os pesos do modelo. O ajuste de prompt é menos popular que o LoRA, mas pode ser uma técnica útil para adaptar rapidamente um modelo a novas tarefas ou domínios. Exploraremos o ajuste de prompt na seção [Ajuste de Prompt](./prompt_tuning.md).

## Cadernos de Exercícios

| Título | Descrição | Exercício | Link | Colab |
|-------|-------------|----------|------|-------|
| Ajuste fino do LoRA | Aprenda a fazer o ajuste fino de modelos usando adaptadores do LoRA | 🐢 Treine um modelo usando o LoRA
🐕 Experimente com diferentes valores de classificação
🦁 Compare o desempenho com o ajuste fino completo | [Exercício](./notebooks/finetune_sft_peft.ipynb) |  | |
| Carregue adaptadores LoRA | Aprenda como carregar e usar adaptadores LoRA treinados | 🐢 Carregar adaptadores pré-treinados
🐕 Mesclar adaptadores com o modelo de base
🦁 Alternar entre vários adaptadores | [Exercício](./notebooks/load_lora_adapter.ipynb) |  |

## Referências

- [Documentação PEFT](https://huggingface.co/docs/peft)
- [Artigo sobre LoRA](https://arxiv.org/abs/2106.09685)
- [Artigo sobre QLoRA](https://arxiv.org/abs/2305.14314)
- [Artigo sobre Ajuste de Prompts](https://arxiv.org/abs/2104.08691)
- [Guia PEFT do Hugging Face](https://huggingface.co/blog/peft)
- [Como ajustar os LLMs em 2024 com o Hugging Face](https://www.philschmid.de/fine-tune-llms-in-2024-with-trl)
- [TRL](https://huggingface.co/docs/trl/index)

A course on aligning smol models.


# LoRA (Low-Rank Adaptation - Adaptação de Baixa Classificação)

LoRA tornou-se o método PEFT mais amplamente adotado. Ele funciona adicionando pequenas matrizes de decomposição de classificação aos pesos de atenção, reduzindo tipicamente os parâmetros treináveis em cerca de 90%.

## Entendendo o LoRA

LoRA (Adaptação de Baixa Classificação) é uma técnica de ajuste fino eficiente em termos de parâmetros que congela os pesos do modelo pré-treinado e injeta matrizes treináveis de decomposição de classificação nas camadas do modelo. Em vez de treinar todos os parâmetros do modelo durante o ajuste fino, o LoRA decompõe as atualizações de peso em matrizes menores por meio da decomposição de baixa classificação, reduzindo significativamente o número de parâmetros treináveis enquanto mantém o desempenho do modelo. Por exemplo, quando aplicado ao GPT-3 175B, o LoRA reduziu os parâmetros treináveis em 10.000x e os requisitos de memória GPU em 3x em comparação ao ajuste fino completo. Você pode saber mais sobre o LoRA no [Artigo sobre LoRA](https://arxiv.org/pdf/2106.09685).

O LoRA funciona adicionando pares de matrizes de decomposição de classificação às camadas do transformer, geralmente focando nos pesos de atenção. Durante a inferência, esses pesos adaptadores podem ser mesclados com o modelo base, resultando em nenhuma sobrecarga adicional de latência. LoRA é particularmente útil para adaptar modelos de linguagem de grande porte a tarefas ou domínios específicos, mantendo os requisitos de recursos gerenciáveis.

## Carregando Adaptadores LoRA

Os adaptadores podem ser carregados em um modelo pré-treinado com load_adapter(), o que é útil para experimentar diferentes adaptadores cujos pesos não estão mesclados. Defina os pesos do adaptador ativo com a função set_adapter(). Para retornar ao modelo base, você pode usar unload() para descarregar todos os módulos LoRA. Isso facilita a alternância entre pesos específicos de tarefas.

```python
from transformers import AutoModelForCausalLM
from peft import PeftModel

base_model = AutoModelForCausalLM.from_pretrained("<base_model_name>")
peft_model_id = "<peft_adapter_id>"
model = PeftModel.from_pretrained(base_model, peft_model_id)
```

![lora_load_adapter](./images/lora_adapter.png)

## Mesclando Adaptadores LoRA

Após o treinamento com LoRA, você pode querer mesclar os pesos do adaptador de volta ao modelo base para facilitar a implantação. Isso cria um único modelo com os pesos combinados, eliminando a necessidade de carregar adaptadores separadamente durante a inferência.

processo de mesclagem requer atenção ao gerenciamento de memória e precisão. Como será necessário carregar o modelo base e os pesos do adaptador simultaneamente, garanta memória suficiente na GPU/CPU. Usar `device_map="auto"` no `transformers` ajudará no gerenciamento automático de memória. Mantenha uma precisão consistente (por exemplo, float16) durante todo o processo, correspondendo à precisão usada durante o treinamento e salvando o modelo mesclado no mesmo formato para implantação. Antes de implantar, sempre valide o modelo mesclado comparando suas saídas e métricas de desempenho com a versão baseada em adaptadores.

Os adaptadores também são convenientes para alternar entre diferentes tarefas ou domínios. Você pode carregar o modelo base e os pesos do adaptador separadamente, permitindo alternâncias rápidas entre pesos específicos de tarefas.

## Guia de Implementação

O diretório `notebooks/` contém tutoriais práticos e exercícios para implementar diferentes métodos PEFT. Comece com `load_lora_adapter_example.ipynb` para uma introdução básica e depois veja `lora_finetuning.ipynb` para um estudo mais detalhado sobre como ajustar um modelo com LoRA e SFT.

Ao implementar métodos PEFT, comece com valores baixos de classificação (4-8) para LoRA e monitore a perda de treinamento. Use conjuntos de validação para evitar overfitting e compare os resultados com as linhas de base de ajuste fino completo sempre que possível. A eficácia de diferentes métodos pode variar conforme a tarefa, então a experimentação é essencial.

## OLoRA

[OLoRA](https://arxiv.org/abs/2406.01775) utiliza decomposição QR para inicializar os adaptadores LoRA. OLoRA traduz os pesos base do modelo por um fator de suas decomposições QR, ou seja, altera os pesos antes de realizar qualquer treinamento sobre eles. Essa abordagem melhora significativamente a estabilidade, acelera a velocidade de convergência e, por fim, alcança um desempenho superior.

## Usando TRL com PEFT

Os métodos PEFT podem ser combinados com TRL (Reinforcement Learning com Transformers) para ajuste fino eficiente. Essa integração é particularmente útil para RLHF (Reinforcement Learning from Human Feedback), pois reduz os requisitos de memória.

```python
from peft import LoraConfig
from transformers import AutoModelForCausalLM

# Load model with PEFT config
lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

# Load model on specific device
model = AutoModelForCausalLM.from_pretrained(
    "your-model-name",
    load_in_8bit=True,  # Optional: use 8-bit precision
    device_map="auto",
    peft_config=lora_config
)
```

No exemplo acima, usamos `device_map="auto"` para atribuir automaticamente o modelo ao dispositivo correto. Você também pode atribuir manualmente o modelo a um dispositivo específico usando `device_map={"": device_index}`. Também é possível escalar o treinamento em várias GPUs enquanto mantém o uso de memória eficiente.

## Implementação Básica de Mesclagem

Após treinar um adaptador LoRA, você pode mesclar os pesos do adaptador de volta ao modelo base. Veja como fazer isso:

```python
import torch
from transformers import AutoModelForCausalLM
from peft import PeftModel

# 1. Load the base model
base_model = AutoModelForCausalLM.from_pretrained(
    "base_model_name",
    torch_dtype=torch.float16,
    device_map="auto"
)

# 2. Load the PEFT model with adapter
peft_model = PeftModel.from_pretrained(
    base_model,
    "path/to/adapter",
    torch_dtype=torch.float16
)

# 3. Merge adapter weights with base model
try:
    merged_model = peft_model.merge_and_unload()
except RuntimeError as e:
    print(f"Merging failed: {e}")
    # Implement fallback strategy or memory optimization

# 4. Save the merged model
merged_model.save_pretrained("path/to/save/merged_model")
```

Se você encontrar discrepâncias de tamanho no modelo salvo, garanta que está salvando também o tokenizador:

```python
# Save both model and tokenizer
tokenizer = AutoTokenizer.from_pretrained("base_model_name")
merged_model.save_pretrained("path/to/save/merged_model")
tokenizer.save_pretrained("path/to/save/merged_model")
```

## Próximos Passos

⏩ Prossiga para o guia de [Ajuste de Prompts](prompt_tuning.md) para aprender como ajustar um modelo com ajuste de prompts.
⏩ Veja o [Tutorial de Carregamento de Adaptadores LoRA](./notebooks/load_lora_adapter.ipynb) para aprender como carregar adaptadores LoRA.

# Referências

- [LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS](https://arxiv.org/pdf/2106.09685)
- [Documentação PEFT](https://huggingface.co/docs/peft)
- [Blog do Hugging Face sobre PEFT](https://huggingface.co/blog/peft)

A course on aligning smol models.


# Ajuste de Prompts

O ajuste de prompts (prompt tuning) é uma abordagem eficiente em termos de parâmetros que modifica as representações de entrada em vez dos pesos do modelo. Diferente do ajuste fino tradicional, que atualiza todos os parâmetros do modelo, o ajuste de prompts adiciona e otimiza um pequeno conjunto de tokens treináveis, mantendo o modelo base congelado.

## Entendendo o Ajuste de Prompts

O ajuste de prompts é uma alternativa eficiente ao ajuste fino de modelos que adiciona vetores contínuos treináveis (soft prompts) ao texto de entrada. Diferente dos prompts de texto discretos, esses soft prompts são aprendidos através de retropropagação enquanto o modelo de linguagem permanece congelado. O método foi introduzido em ["The Power of Scale for Parameter-Efficient Prompt Tuning"](https://arxiv.org/abs/2104.08691) (Lester et al., 2021), que demonstrou que o ajuste de prompts se torna mais competitivo em relação ao ajuste fino conforme o tamanho do modelo aumenta. No artigo, em torno de 10 bilhões de parâmetros, o ajuste de prompts iguala o desempenho do ajuste fino, modificando apenas algumas centenas de parâmetros por tarefa.

Esses soft prompts são vetores contínuos no espaço de embedding do modelo que são otimizados durante o treinamento. Diferente dos prompts discretos tradicionais que usam tokens de linguagem natural, os soft prompts não possuem significado inerente, mas aprendem a evocar o comportamento desejado do modelo congelado por meio de gradiente descendente. A técnica é particularmente eficaz em cenários multitarefa, pois cada tarefa exige apenas o armazenamento de um pequeno vetor de prompt (normalmente algumas centenas de parâmetros) em vez de uma cópia completa do modelo. Essa abordagem não apenas mantém um uso mínimo de memória, mas também possibilita a troca rápida de tarefas apenas trocando os vetores de prompt sem precisar recarregar o modelo.

## Processo de Treinamento

Os soft prompts geralmente têm entre 8 e 32 tokens e podem ser inicializados aleatoriamente ou a partir de texto existente. O método de inicialização desempenha um papel crucial no processo de treinamento, com inicializações baseadas em texto frequentemente apresentando melhor desempenho do que inicializações aleatórias.

Durante o treinamento, apenas os parâmetros do prompt são atualizados, enquanto o modelo base permanece congelado. Essa abordagem focada utiliza objetivos de treinamento padrão, mas exige atenção cuidadosa à taxa de aprendizado e ao comportamento do gradiente dos tokens do prompt.

## Implementação com PEFT

O módulo PEFT facilita a implementação do ajuste de prompts. Aqui está um exemplo básico:

```python
from peft import PromptTuningConfig, TaskType, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer

# Load base model
model = AutoModelForCausalLM.from_pretrained("your-base-model")
tokenizer = AutoTokenizer.from_pretrained("your-base-model")

# Configure prompt tuning
peft_config = PromptTuningConfig(
    task_type=TaskType.CAUSAL_LM,
    num_virtual_tokens=8,  # Number of trainable tokens
    prompt_tuning_init="TEXT",  # Initialize from text
    prompt_tuning_init_text="Classify if this text is positive or negative:",
    tokenizer_name_or_path="your-base-model",
)

# Create prompt-tunable model
model = get_peft_model(model, peft_config)
```

## Comparação com Outros Métodos

Quando comparado a outras abordagens PEFT, o ajuste de prompts se destaca por sua eficiência. Enquanto o LoRA oferece baixo uso de parâmetros e memória, mas exige o carregamento de adaptadores para troca de tarefas, o ajuste de prompts atinge um uso de recursos ainda menor e possibilita a troca imediata de tarefas. O ajuste fino completo, em contraste, demanda recursos significativos e requer cópias separadas do modelo para diferentes tarefas.

| Método | Parâmetros | Memória | Troca de Tarefas |
|--------|------------|---------|----------------|
| Ajuste de Prompts| Muito Baixo | Mínima | Fácil |
| LoRA | Baixo | Baixa | Requer Carregamento |
| Ajuste Fino | Alto | Alta | Nova Cópia do Modelo |

Ao implementar o ajuste de prompts, comece com um pequeno número de tokens virtuais (8-16) e aumente apenas se a complexidade da tarefa exigir. A inicialização baseada em texto geralmente apresenta melhores resultados do que a inicialização aleatória, especialmente ao usar texto relevante para a tarefa. A estratégia de inicialização deve refletir a complexidade da tarefa alvo.

O treinamento requer considerações ligeiramente diferentes do ajuste fino completo. Taxas de aprendizado mais altas geralmente funcionam bem, mas o monitoramento cuidadoso dos gradientes dos tokens do prompt é essencial. A validação regular com exemplos diversos ajuda a garantir um desempenho robusto em diferentes cenários.

## Aplicação

O ajuste de prompts se destaca em diversos cenários:

1. Implantação multitarefa
2. Ambientes com restrição de recursos
3. Adaptação rápida a tarefas
4. Aplicações sensíveis à privacidade

Conforme os modelos ficam menores, o ajuste de prompts se torna menos competitivo em comparação ao ajuste fino completo. Por exemplo, em modelos como SmolLM2, o ajuste de prompts é menos relevante do que o ajuste fino completo.

## Próximos Passos

⏭ Prossiga para o [Tutorial de Adaptadores LoRA](./notebooks/finetune_sft_peft.ipynb) para aprender como ajustar um modelo com adaptadores LoRA.

## Referências
- [Documentação PEFT](https://huggingface.co/docs/peft)
- [Artigo sobre Ajuste de Prompts](https://arxiv.org/abs/2104.08691)
- [Cookbook do Hugging Face](https://huggingface.co/learn/cookbook/prompt_tuning_peft)

A course on aligning smol models.


# Evaluation (Avaliação)

A avaliação é uma etapa crítica no desenvolvimento e implantação de modelos de linguagem. Ela nos ajuda a entender quão bem nossos modelos desempenham em diferentes capacidades e a identificar áreas para melhorias. Este módulo aborda benchmarks padrão e abordagens de avaliação específicas de domínio para avaliar de forma abrangente o seu modelo smol (miudinho).

Usaremos o [`lighteval`](https://github.com/huggingface/lighteval), uma poderosa biblioteca de avaliação desenvolvida pelo Hugging Face que se integra perfeitamente ao ecossistema Hugging Face. Para um aprendizado mais profundo nos conceitos e práticas recomendadas de avaliação, confira o [guia](https://github.com/huggingface/evaluation-guidebook).

## Visão Geral do Módulo

Uma estratégia de avaliação completa examina múltiplos aspectos do desempenho do modelo. Avaliamos capacidades específicas de tarefas, como responder a perguntas e sumarização, para entender como o modelo lida com diferentes tipos de problemas. Medimos a qualidade do output através de fatores como coerência e precisão factual. A avaliação de segurança ajuda a identificar outputs potencialmente prejudiciais ou biases. Por fim, os testes de especialização de domínio verificam o conhecimento especializado do modelo no campo-alvo.

## Conteúdo

### 1⃣ [Benchmarks Automáticos](./automatic_benchmarks.md)

Aprenda a avaliar seu modelo usando benchmarks e métricas padronizados. Exploraremos benchmarks comuns, como MMLU e TruthfulQA, entenderemos as principais métricas e configurações de avaliação e abordaremos as melhores práticas para avaliações reproduzíveis.

### 2⃣ [Avaliação de Domnínio Personalizado](./custom_evaluation.md)

Descubra como criar pipelines de avaliação adaptados ao seu caso de uso específico. Veremos o design de tarefas de avaliação personalizadas, implementação de métricas especializadas e construção de conjuntos de dados de avaliação que atendam às suas necessidades.

### 3⃣ [Projeto de Avaliação de Domínio](./project/README.md)

Siga um exemplo completo de construção de um pipeline de avaliação de domínio específico. Você aprenderá a gerar conjuntos de dados de avaliação, usar o Argilla para anotação de dados, criar conjuntos de dados padronizados e avaliar modelos usando o LightEval.

### Cadernos de Exercício

| Título | Descrição | Exercício | Link | Colab |
|-------|-------------|----------|------|-------|
| Avalie e Analise Seu LLM | Aprenda a usar o LightEval para avaliar e comparar modelos em domínios específicos | 🐢 Use tarefas do domínio da medicina para avaliar um modelo
 🐕 Crie uma nova avaliação de domínio com diferentes tarefas do MMLU
 🦁 Crie uma tarefa de avaliação personalizada para o seu domínio | [Notebook](./notebooks/lighteval_evaluate_and_analyse_your_LLM.ipynb) |  |

## Referências

- [Guia de Avaliação](https://github.com/huggingface/evaluation-guidebook) - Guia abrangente de avaliação de LLMs
- [Documentação do LightEval](https://github.com/huggingface/lighteval) - Documentação oficial do módulo LightEval
- [Documentação do Argilla](https://docs.argilla.io) - Saiba mais sobre a plataforma de anotação Argilla
- [Artigo do MMLU](https://arxiv.org/abs/2009.03300) - Artigo que descreve o benchmark MMLU
- [Criando uma Tarefa Personalizada](https://github.com/huggingface/lighteval/wiki/Adding-a-Custom-Task)
- [Criando uma Métrica Personalizada](https://github.com/huggingface/lighteval/wiki/Adding-a-New-Metric)
- [Usando Métricas Existentes](https://github.com/huggingface/lighteval/wiki/Metric-List)

A course on aligning smol models.


# Benchmarks Automáticos

Os benchmarks automáticos servem como ferramentas padronizadas para avaliar modelos de linguagem em diferentes tarefas e capacidades. Embora forneçam um ponto de partida útil para entender o desempenho do modelo, é importante reconhecer que eles representam apenas uma parte de uma estratégia abrangente de avaliação.

## Entendendo os Benchmarks Automáticos

Os benchmarks automáticos geralmente consistem em conjuntos de dados organizados com tarefas e métricas de avaliação predefinidas. Esses benchmarks têm como objetivo avaliar vários aspectos da capacidade do modelo, desde compreensão básica de linguagem até raciocínio complexo. A principal vantagem de usar benchmarks automáticos é a padronização, eles permitem comparações consistentes entre diferentes modelos e fornecem resultados reproduzíveis.

No entanto, é crucial entender que o desempenho em benchmarks nem sempre se traduz diretamente em eficácia no mundo real. Um modelo que se destaca em benchmarks acadêmicos pode ainda enfrentar dificuldades em aplicações específicas de domínio ou casos práticos.

## Benchmarks e Suas Limitações

### Benchmarks de Conhecimento Geral

O MMLU (Massive Multitask Language Understanding) testa conhecimentos em 57 disciplinas, de ciências a humanidades. Embora abrangente, este benchmark pode não refletir a profundidade de especialização necessária para domínios específicos. O TruthfulQA avalia a tendência de um modelo em reproduzir conceitos equivocados comuns, embora não capture todas as formas de desinformação.

### Benchmarks de Raciocínio

BBH (Big Bench Hard) e GSM8K focam em tarefas de raciocínio complexo. O BBH testa pensamento lógico e planejamento, enquanto o GSM8K visa especificamente a resolução de problemas matemáticos. Esses benchmarks ajudam a avaliar capacidades analíticas, mas podem não captar o raciocínio detalhado exigido em cenários do mundo real.

### Compreensão de Linguagem

HELM fornece uma estrutura de avaliação holística, enquanto o WinoGrande testa o senso comum através da desambiguação de pronomes. Esses benchmarks oferecem insights sobre capacidades de processamento de linguagem, mas podem não representar totalmente a complexidade da conversação natural ou terminologia específica de domínio.

## Abordagens Alternativas de Avaliação

Muitas organizações desenvolveram métodos alternativos de avaliação para lidar com as limitações dos benchmarks padrão:

### LLM como Juiz

Usar um modelo de linguagem para avaliar os outputs de outro tornou-se cada vez mais popular. Essa abordagem pode fornecer feedback mais detalhado do que métricas tradicionais, embora apresente seus próprios vieses e limitações.

### Arenas de Avaliação

Plataformas como a IA Arena Constitucional da Anthropic permitem que modelos interajam e avaliem uns aos outros em ambientes controlados. Isso pode revelar pontos fortes e fracos que podem não ser evidentes em benchmarks tradicionais.

### Conjuntos de Benchmarks Personalizados

As organizações frequentemente desenvolvem conjuntos de benchmarks internos adaptados às suas necessidades e casos de uso específicos. Esses conjuntos podem incluir testes de conhecimento de domínio ou cenários de avaliação que refletem condições reais de implantação.

## Criando Sua Própria Estratégia de Avaliação

Embora o LightEval facilite a execução de benchmarks padrão, você também deve investir tempo no desenvolvimento de métodos de avaliação específicos para o seu caso de uso.

Sabendo que benchmarks padrão fornecem uma linha de base útil, eles não devem ser seu único método de avaliação. Aqui está como desenvolver uma abordagem mais abrangente:

1. Comece com benchmarks padrão relevantes para estabelecer uma linha de base e permitir comparações com outros modelos.

2. Identifique os requisitos específicos e desafios do seu caso de uso. Quais tarefas seu modelo realmente executará? Que tipos de erros seriam mais problemáticos?

3. Desenvolva conjuntos de dados de avaliação personalizados que reflitam seu caso de uso real. Isso pode incluir:
    - Consultas reais de usuários do seu domínio
    - Casos extremos comuns que você encontrou
    - Exemplos de cenários particularmente desafiadores

4. Considere implementar uma estratégia de avaliação em camadas:
    - Métricas automatizadas para feedback rápido
    - Avaliação humana para entendimento mais detalhado
    - Revisão por especialistas no domínio para aplicações especializadas
    - Testes A/B em ambientes controlados

## Usando LightEval para Benchmarks

As tarefas do LightEval são definidas usando um formato específico:

```
{suite}|{task}|{num_few_shot}|{auto_reduce}
```

- **suite**: O conjunto de benchmarks (ex.: 'mmlu', 'truthfulqa')
- **task**: Tarefa específica dentro do conjunto (ex.: 'abstract_algebra')
- **num_few_shot**: Número de exemplos a incluir no prompt (0 para zero-shot)
- **auto_reduce**: Se deve reduzir automaticamente exemplos few-shot caso o prompt seja muito longo (0 ou 1)

Exemplo: `"mmlu|abstract_algebra|0|0"` avalia a tarefa de álgebra abstrata do MMLU com inferência zero-shot.

### Exemplo de Pipeline de Avaliação

Aqui está um exemplo completo de configuração e execução de uma avaliação em benchmarks automáticos relevantes para um domínio específico:

```python
from lighteval.tasks import Task, Pipeline
from transformers import AutoModelForCausalLM

# Define tasks to evaluate
domain_tasks = [
    "mmlu|anatomy|0|0",
    "mmlu|high_school_biology|0|0",
    "mmlu|high_school_chemistry|0|0",
    "mmlu|professional_medicine|0|0"
]

# Configure pipeline parameters
pipeline_params = {
    "max_samples": 40,  # Number of samples to evaluate
    "batch_size": 1,    # Batch size for inference
    "num_workers": 4    # Number of worker processes
}

# Create evaluation tracker
evaluation_tracker = EvaluationTracker(
    output_path="./results",
    save_generations=True
)

# Load model and create pipeline
model = AutoModelForCausalLM.from_pretrained("your-model-name")
pipeline = Pipeline(
    tasks=domain_tasks,
    pipeline_parameters=pipeline_params,
    evaluation_tracker=evaluation_tracker,
    model=model
)

# Run evaluation
pipeline.evaluate()

# Get and display results
results = pipeline.get_results()
pipeline.show_results()
```

Os resultados são exibidos em formato tabular, mostrando:
```
|                  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|
```

Você também pode manipular os resultados em um DataFrame do pandas e visualizá-los conforme necessário.

# Próximos Passos

⏩ Veja a [Avaliação Personalizada de Domínio](./custom_evaluation.md) para aprender a criar pipelines de avaliação adaptados às suas necessidades específicas.

A course on aligning smol models.


# Avaliação de Domínio Personalizado

Embora benchmarks padrão ofereçam insights valiosos, muitas aplicações requerem abordagens de avaliação especializadas adaptadas a domínios ou casos de uso específicos. Este guia ajudará você a criar pipelines de avaliação personalizados que avaliem com precisão o desempenho do seu modelo no domínio-alvo.

## Planejando Sua Estratégia de Avaliação

Uma estratégia de avaliação personalizada bem-sucedida começa com objetivos claros. Considere quais capacidades específicas seu modelo precisa demonstrar no seu domínio. Isso pode incluir conhecimento técnico, padrões de raciocínio ou formatos específicos de domínio. Documente esses requisitos cuidadosamente, eles guiarão tanto o design da tarefa quanto a seleção de métricas.

Sua avaliação deve testar tanto casos de uso padrão quanto casos extremos. Por exemplo, em um domínio de medicina, você pode avaliar cenários com diagnósticos comuns e condições raras. Em aplicações financeiras, pode-se testar transações rotineiras e casos extremos envolvendo múltiplas moedas ou condições especiais.

## Implementação com LightEval

O LightEval fornece um framework flexível para implementar avaliações personalizadas. Veja como criar uma tarefa personalizada:

```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"],  # Your chosen metrics
            description="Description of your custom evaluation task"
        )

    def get_prompt(self, sample):
        # Format your input into a prompt
        return f"Question: {sample['question']}\nAnswer:"

    def process_response(self, response, ref):
        # Process model output and compare to reference
        return response.strip() == ref.strip()
```

## Métricas Personalizadas

Tarefas específicas de domínio frequentemente exigem métricas especializadas. O LightEval fornece um framework flexível para criar métricas personalizadas que capturam aspectos relevantes do desempenho no domínio:

```python
from aenum import extend_enum
from lighteval.metrics import Metrics, SampleLevelMetric, SampleLevelMetricGrouping
import numpy as np

# Define a sample-level metric function
def custom_metric(predictions: list[str], formatted_doc: Doc, **kwargs) -> dict:
    """Example metric that returns multiple scores per sample"""
    response = predictions[0]
    return {
        "accuracy": response == formatted_doc.choices[formatted_doc.gold_index],
        "length_match": len(response) == len(formatted_doc.reference)
    }

# Create a metric that returns multiple values per sample
custom_metric_group = SampleLevelMetricGrouping(
    metric_name=["accuracy", "length_match"],  # Names of sub-metrics
    higher_is_better={  # Whether higher values are better for each metric
        "accuracy": True,
        "length_match": True
    },
    category=MetricCategory.CUSTOM,
    use_case=MetricUseCase.SCORING,
    sample_level_fn=custom_metric,
    corpus_level_fn={  # How to aggregate each metric
        "accuracy": np.mean,
        "length_match": np.mean
    }
)

# Register the metric with LightEval
extend_enum(Metrics, "custom_metric_name", custom_metric_group)
```

Para casos mais simples onde você precisa de apenas um valor por amostra:

```python
def simple_metric(predictions: list[str], formatted_doc: Doc, **kwargs) -> bool:
    """Example metric that returns a single score per sample"""
    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  # How to aggregate across samples
)

extend_enum(Metrics, "simple_metric", simple_metric_obj)
```

Você pode então usar suas métricas personalizadas em tarefas de avaliação referenciando-as na configuração da tarefa. As métricas serão automaticamente calculadas para todas as amostras e agregadas de acordo com as funções especificadas.

Para métricas mais complexas, considere:
- Usar metadados em seus documentos formatados para ponderar ou ajustar pontuações
- Implementar funções de agregação personalizadas para estatísticas em nível de corpus
- Adicionar verificações de validação para as entradas da métrica
- Documentar casos extremos e comportamentos esperados

Para um exemplo completo de métricas personalizadas em ação, veja nosso [projeto de avaliação de domínio](./project/README.md).

## Criação de Conjuntos de Dados

Uma avaliação de alta qualidade requer conjuntos de dados cuidadosamente selecionados. Considere estas abordagens para criação de conjuntos de dados:

1. Anotação Avançada: Trabalhe com especialistas no domínio para criar e validar exemplos de avaliação. Ferramentas como o [Argilla](https://github.com/argilla-io/argilla) tornam esse processo mais eficiente.

2. Dados do Mundo Real: Colete e anonimize dados de uso real, garantindo que representem cenários reais de implantação.

3. Geração Sintética: Use LLMs para gerar exemplos iniciais e, em seguida, peça para especialistas validarem e refinarem esses exemplos.

## Melhores Práticas

- Documente sua metodologia de avaliação detalhadamente, incluindo quaisquer suposições ou limitações
- Inclua casos de teste diversificados que cubram diferentes aspectos do seu domínio
- Considere métricas automatizadas e avaliação humana, quando apropriado
- Faça o controle de versão dos seus conjuntos de dados e código de avaliação
- Atualize regularmente seu conjunto de avaliação à medida que descobrir novos casos extremos ou requisitos

## Referências

- [Guia de Tarefas Personalizadas do LightEval](https://github.com/huggingface/lighteval/wiki/Adding-a-Custom-Task)
- [Métricas Personalizadas do LightEval](https://github.com/huggingface/lighteval/wiki/Adding-a-New-Metric)
- [Documentação do Argilla](https://docs.argilla.io) para anotação de conjuntos de dados
- [Guia de Avaliação](https://github.com/huggingface/evaluation-guidebook) para princípios gerais de avaliação

# Próximos Passos

⏩ Para um exemplo completo de implementação desses conceitos, veja nosso [projeto de avaliação de domínio](./project/README.md).

A course on aligning smol models.


# Tinh chỉnh theo chỉ thị (Instruction Tuning)

Trong chương này chúng ta sẽ học về quá trình tinh chỉnh mô hình ngôn ngữ theo chỉ thị. Tinh chỉnh theo chỉ thị là quá trình điều chỉnh *pre-trained models* cho các tác vụ cụ thể bằng cách tiếp tục huấn luyện chúng trên các tập dữ liệu đặc thù cho tác vụ. Quá trình này giúp các mô hình cải thiện hiệu suất trên những tác vụ đó.

Chúng ta sẽ cùng khám phá hai chủ đề chính: 1) Định dạng Chat (Chat Templates) và 2) Tinh chỉnh có giám sát (Supervised Fine-Tuning).

## 1⃣ Định dạng Chat (Chat Templates)

Định dạng Chat là cấu trúc giữa các tương tác giữa người dùng và mô hình ngôn ngữ, đảm bảo các phản hồi nhất quán và phù hợp với từng ngữ cảnh. Chúng bao gồm các thành phần như `system prompts` và các `message` theo vai trò (người dùng - `user` hoặc trợ lý - `assistant`). Để biết thêm thông tin chi tiết, hãy tham khảo phần [Chat Templates](./chat_templates.md).

## 2⃣ Huấn luyện có giám sát (Supervised Fine-Tuning)

Huấn luyện có giám sát (SFT) là một quá trình cốt lõi để điều chỉnh các mô hình ngôn ngữ đã *pre-trained* cho các tác vụ cụ thể. Quá trình này bao gồm việc huấn luyện mô hình trên tập dữ liệu có gán nhãn theo tác vụ cụ thể. Để đọc hướng dẫn chi tiết về SFT, bao gồm các bước quan trọng và các phương pháp thực hành tốt nhất, hãy xem tại trang [Supervised Fine-Tuning](./supervised_fine_tuning.md).

## Bài tập

| Tiêu đề | Mô tả | Bài tập | Đường dẫn | Google Colab |
|-------|-------------|----------|------|-------|
| Định dạng Chat | Học cách sử dụng *định dạng chat* với SmolLM2 và xử lý dữ liệu thành định dạng *chatml* | 🐢 Chuyển đổi tập dữ liệu `HuggingFaceTB/smoltalk` sang định dạng *chatml*
 🐕 Chuyển đổi tập dữ liệu `openai/gsm8k` sang định dạng *chatml* | [Notebook](./notebooks/chat_templates_example.ipynb) |  |
| Tinh chỉnh có giám sát | Học cách tinh chỉnh SmolLM2 sử dụng SFTTrainer | 🐢 Sử dụng tập dữ liệu `HuggingFaceTB/smoltalk`
🐕 Thử nghiệm với tập dữ liệu `bigcode/the-stack-smol`
🦁 Chọn một tập dữ liệu cho trường hợp sử dụng thực tế | [Notebook](./notebooks/sft_finetuning_example.ipynb) |  |

## Tài liệu tham khảo

- [Tài liệu Transformers về định dạng chat](https://huggingface.co/docs/transformers/main/en/chat_templating)
- [Script cho huấn luyện có giám sát bằng thư viện TRL](https://github.com/huggingface/trl/blob/main/examples/scripts/sft.py)
- [`SFTTrainer` trong thư viện TRL](https://huggingface.co/docs/trl/main/en/sft_trainer)
- [Bài báo Direct Preference Optimization (DPO)](https://arxiv.org/abs/2305.18290)
- [Huấn luyện có giám sát bằng thư viện TRL](https://huggingface.co/docs/trl/main/en/tutorials/supervised_finetuning)
- [Cách fine-tune Google Gemma với ChatML và Hugging Face TRL](https://www.philschmid.de/fine-tune-google-gemma)
- [Huấn luyện LLM để tạo danh mục sản phẩm tiếng Ba Tư ở định dạng JSON](https://huggingface.co/learn/cookbook/en/fine_tuning_llm_to_generate_persian_product_catalogs_in_json_format)

A course on aligning smol models.