# TRL (обучение RLHF/DPO)

**TRL** (Transformer Reinforcement Learning) — официальная библиотека HuggingFace для обучения языковых моделей с использованием методов обучения с подкреплением. Имея более 10K звёзд на GitHub, она предоставляет передовые реализации RLHF, DPO, PPO, GRPO и других алгоритмов выравнивания для больших языковых моделей.

{% hint style="success" %}
Все примеры можно запускать на GPU-серверах, арендуемых через [CLORE.AI Маркетплейс](https://clore.ai/marketplace).
{% endhint %}

***

## Что такое TRL?

TRL — это библиотека, стоящая за многими из современных моделей с хорошим выравниванием. Она предоставляет:

* **SFT (Supervised Fine-Tuning)** — стандартную донастройку по инструкциям в формате ChatML
* **RLHF/PPO** — классический Proximal Policy Optimization с моделью вознаграждения
* **DPO** — Direct Preference Optimization (модель вознаграждения не требуется!)
* **GRPO** — Group Relative Policy Optimization (метод DeepSeek-R1)
* **KTO** — Kahneman-Tversky Optimization (работает с непарными предпочтениями)
* **Моделирование вознаграждения** — обучение модели вознаграждения на данных с человеческими предпочтениями
* **IterativeSFT** — онлайн RL с более простой настройкой
* **ORPO** — Odds Ratio Preference Optimization

TRL нативно интегрируется с экосистемой HuggingFace: `transformers`, `peft`, `datasets`, `accelerate`, и `bitsandbytes`.

{% hint style="info" %}
**Какой алгоритм вам следует использовать?**

* **DPO** — самый простой и стабильный. Используйте, когда у вас есть парные данные предпочтений (выбрано/отклонено).
* **PPO** — самый мощный, но сложный. Используйте, когда у вас есть модель вознаграждения или функция оценки.
* **GRPO** — отлично подходит для задач, требующих рассуждений/математики. Метод обучения DeepSeek-R1.
* **SFT** — всегда начинайте с этого перед применением любых методов RL.
  {% endhint %}

***

## Требования к серверу

| Компонент | Минимум                   | Рекомендуется                 |
| --------- | ------------------------- | ----------------------------- |
| GPU       | RTX 3090 (24 ГБ)          | A100 80 ГБ / H100             |
| VRAM      | 16 ГБ (SFT/DPO 7B + LoRA) | 80 ГБ (полная донастройка 7B) |
| ОЗУ       | 32 ГБ                     | 64 ГБ+                        |
| CPU       | 8 ядер                    | 16+ ядер                      |
| Хранилище | 100 ГБ                    | 300 ГБ+                       |
| ОС        | Ubuntu 20.04+             | Ubuntu 22.04                  |
| Python    | 3.9+                      | 3.11                          |
| CUDA      | 11.8+                     | 12.1+                         |

### VRAM по задаче

| Задача | Модель      | Метод       | VRAM             |
| ------ | ----------- | ----------- | ---------------- |
| SFT    | Llama 3 8B  | QLoRA 4-бит | \~8 ГБ           |
| DPO    | Llama 3 8B  | LoRA        | \~20 ГБ          |
| PPO    | Llama 3 8B  | Полная      | \~80 ГБ (2×A100) |
| GRPO   | Qwen 7B     | LoRA        | \~24 ГБ          |
| SFT    | Llama 3 70B | QLoRA 4-бит | \~48 ГБ          |
| DPO    | Llama 3 70B | LoRA        | \~80 ГБ          |

***

## Порты

| Порт | Сервис | Примечания                                      |
| ---- | ------ | ----------------------------------------------- |
| 22   | SSH    | Доступ к терминалу, передача файлов, мониторинг |

TRL — это библиотека для обучения — она запускается как CLI/Python-скрипт, веб-сервер не требуется.

***

## Установка на Clore.ai

### Шаг 1 — Арендуйте сервер

1. Перейдите на [Clore.ai Marketplace](https://clore.ai/marketplace)
2. Отфильтруйте по **VRAM ≥ 24 ГБ** (RTX 3090, A100 или H100)
3. Выберите **PyTorch** или **CUDA 12.1** базовый образ
4. Выберите **Хранилище ≥ 200 ГБ** для моделей и наборов данных
5. Откройте порт **22** для доступа по SSH

### Шаг 2 — Подключитесь по SSH

```bash
ssh root@<server-ip> -p <ssh-port>
```

### Шаг 3 — Установите TRL

```bash
# Создайте виртуальное окружение Python
python3 -m venv /opt/trl
source /opt/trl/bin/activate

# Установите TRL со всеми зависимостями
pip install trl

# Установите дополнительные зависимости для полноценных рабочих процессов
pip install \
    transformers \
    datasets \
    peft \
    accelerate \
    bitsandbytes \
    wandb \
    scipy \
    sentencepiece \
    protobuf

# Проверьте поддержку GPU
python3 -c "import torch; print(f'CUDA: {torch.cuda.is_available()}, GPU: {torch.cuda.get_device_name(0)}')"
```

### Шаг 4 — Аутентификация HuggingFace

```bash
# Войдите, чтобы получить доступ к закрытым моделям (Llama, Gemma)
huggingface-cli login
# Введите ваш HF токен с https://huggingface.co/settings/tokens

# Или установите переменную окружения
export HF_TOKEN=hf_your-token-here
```

### Шаг 5 — По желанию: отслеживание Weights & Biases

```bash
# Настройте трекинг экспериментов (настоятельно рекомендуется)
pip install wandb
wandb login  # Введите ваш W&B API ключ с https://wandb.ai/settings

# Или отключите W&B
export WANDB_DISABLED=true
```

***

## Супервизированная донастройка (SFT)

SFT всегда является первым шагом перед любой техникой RL.

### Подготовьте ваш набор данных

```python
# Формат: библиотека datasets с колонкой 'messages' или 'text'
# Формат ChatML (рекомендуется)
from datasets import Dataset

data = [
    {
        "messages": [
            {"role": "system", "content": "Вы являетесь полезным помощником в облаке GPU."},
            {"role": "user", "content": "Как арендовать GPU на Clore.ai?"},
            {"role": "assistant", "content": "Посетите clore.ai/marketplace, отфильтруйте по спецификациям GPU, выберите сервер и нажмите Rent. Доступ по SSH предоставляется сразу после оплаты."}
        ]
    },
    # ... ещё примеры
]

dataset = Dataset.from_list(data)
dataset.save_to_disk("data/sft_dataset")
dataset.push_to_hub("your-username/my-sft-dataset")  # опционально
```

### Скрипт обучения SFT

```python
# sft_train.py
from trl import SFTTrainer, SFTConfig
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model
from datasets import load_dataset
import torch

# Конфигурация модели
model_name = "meta-llama/Llama-3.2-8B-Instruct"

# QLoRA: конфигурация 4-битной квантизации
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
)

# Загрузите модель
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True,
)

# Загрузите токенизатор
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"

# Конфигурация LoRA
lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
)

# Загрузите набор данных
dataset = load_dataset("trl-lib/ultrachat_200k", split="train_sft[:10%]")

# Конфигурация обучения
training_config = SFTConfig(
    output_dir="./sft_output",
    num_train_epochs=3,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=4,
    learning_rate=2e-4,
    warmup_ratio=0.05,
    lr_scheduler_type="cosine",
    fp16=False,
    bf16=True,
    max_seq_length=2048,
    dataset_text_field="messages",
    logging_steps=10,
    save_steps=100,
    save_total_limit=3,
    push_to_hub=False,
    report_to="wandb",  # или "none"
)

# Инициализация тренера
trainer = SFTTrainer(
    model=model,
    args=training_config,
    train_dataset=dataset,
    peft_config=lora_config,
    tokenizer=tokenizer,
)

# Обучение
trainer.train()
trainer.save_model("./sft_final")
```

```bash
# Запуск обучения
python3 sft_train.py
```

***

## DPO (Direct Preference Optimization)

DPO — самый популярный метод выравнивания — модель вознаграждения не нужна, достаточно пар предпочтений.

### Подготовьте набор данных для DPO

```python
# Формат: каждый пример содержит 'prompt', 'chosen', 'rejected'
from datasets import Dataset

data = [
    {
        "prompt": "Объясните, как оптимизировать использование GPU",
        "chosen": "Чтобы оптимизировать использование GPU: 1) Используйте большие батчи для максимизации загрузки, 2) Включите смешанную точность (bf16/fp16), 3) Профилируйте с помощью nvidia-smi для выявления узких мест, 4) Используйте CUDA-потоки для параллельных операций.",
        "rejected": "Просто используйте больше GPU."
    },
    # ... ещё пар предпочтений
]

dataset = Dataset.from_list(data)
```

### Скрипт обучения DPO

```python
# dpo_train.py
from trl import DPOTrainer, DPOConfig
from transformers import AutoTokenizer, AutoModelForCausalLM
from datasets import load_dataset
import torch

model_name = "./sft_final"  # Начните с вашей SFT-модели!

# Загрузите SFT-модель (политика для выравнивания)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
    device_map="auto",
)

# Референсная модель (замороженная копия SFT-модели)
ref_model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
    device_map="auto",
)

tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token

# Загрузите датасет предпочтений
dataset = load_dataset("trl-lib/ultrafeedback_binarized", split="train[:5%]")

# Конфигурация DPO
dpo_config = DPOConfig(
    output_dir="./dpo_output",
    num_train_epochs=1,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=4,
    learning_rate=5e-7,           # Значительно ниже, чем для SFT
    beta=0.1,                     # Коэффициент штрафа KL
    loss_type="sigmoid",          # Стандартная DPO-функция потерь
    max_length=2048,
    max_prompt_length=512,
    bf16=True,
    logging_steps=10,
    save_steps=50,
    report_to="wandb",
)

trainer = DPOTrainer(
    model=model,
    ref_model=ref_model,
    args=dpo_config,
    train_dataset=dataset,
    tokenizer=tokenizer,
)

trainer.train()
trainer.save_model("./dpo_final")
```

***

## PPO (Proximal Policy Optimization)

PPO — классический подход RLHF — используйте, когда у вас есть сигнал вознаграждения:

```python
# ppo_train.py
from trl import PPOTrainer, PPOConfig, AutoModelForCausalLMWithValueHead
from transformers import AutoTokenizer, pipeline
from datasets import load_dataset
import torch

model_name = "./sft_final"

# Модель политики (с value head для PPO)
model = AutoModelForCausalLMWithValueHead.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
)

tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token

# Модель вознаграждения (может быть любой функцией оценки)
sentiment_pipe = pipeline(
    "sentiment-analysis",
    model="distilbert/distilbert-base-uncased-finetuned-sst-2-english",
    device=0,
)

def reward_fn(texts):
    """Оцените каждый ответ. Верните список тензоров вознаграждений."""
    results = sentiment_pipe(texts)
    rewards = []
    for result in results:
        score = result["score"] if result["label"] == "POSITIVE" else -result["score"]
        rewards.append(torch.tensor(score))
    return rewards

ppo_config = PPOConfig(
    output_dir="./ppo_output",
    learning_rate=1.41e-5,
    mini_batch_size=1,
    batch_size=4,
    gradient_accumulation_steps=4,
    kl_penalty="kl",
    target_kl=6.0,
    cliprange=0.2,
    vf_coef=0.1,
)

trainer = PPOTrainer(
    config=ppo_config,
    model=model,
    ref_model=None,  # Автокопирует начальную модель как референс
    tokenizer=tokenizer,
)

# Цикл обучения
dataset = load_dataset("imdb", split="train[:1000]")
for epoch in range(3):
    for batch in trainer.dataloader:
        queries = batch["input_ids"]
        
        # Генерация ответов
        responses = trainer.generate(queries, max_new_tokens=100)
        
        # Оценка ответов
        texts = tokenizer.batch_decode(responses, skip_special_tokens=True)
        rewards = reward_fn(texts)
        
        # Обновление PPO
        stats = trainer.step(queries, responses, rewards)
        trainer.log_stats(stats, batch, rewards)
```

***

## GRPO (Group Relative Policy Optimization)

GRPO используется в DeepSeek-R1 для обучения рассуждению:

```python
# grpo_train.py
from trl import GRPOTrainer, GRPOConfig
from transformers import AutoTokenizer, AutoModelForCausalLM
from datasets import Dataset
import re, torch

model_name = "Qwen/Qwen2.5-7B-Instruct"

model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
    device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained(model_name)

# Набор данных по математике
def make_math_dataset():
    examples = [
        {"prompt": "Сколько будет 2+2?", "answer": "4"},
        {"prompt": "Сколько будет 15 * 7?", "answer": "105"},
        # ... ещё математических задач
    ]
    return Dataset.from_list(examples)

dataset = make_math_dataset()

def correctness_reward(completions, answer, **kwargs):
    """Вознаграждение 1.0 если ответ правильный, 0.0 в противном случае."""
    rewards = []
    for completion in completions:
        # Извлечь окончательное число из completion
        numbers = re.findall(r'\d+', completion[-1]["content"])
        if numbers and numbers[-1] == answer:
            rewards.append(1.0)
        else:
            rewards.append(0.0)
    return rewards

grpo_config = GRPOConfig(
    output_dir="./grpo_output",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    num_generations=8,       # GRPO генерирует G ответов на подсказку
    learning_rate=5e-7,
    bf16=True,
    logging_steps=10,
)

trainer = GRPOTrainer(
    model=model,
    args=grpo_config,
    train_dataset=dataset,
    reward_funcs=correctness_reward,
    tokenizer=tokenizer,
)

trainer.train()
```

***

## Обучение на нескольких GPU

Используйте `accelerate` для распределённого обучения:

```bash
# Настройте accelerate для multi-GPU
accelerate config

# Пример конфигурации для 4 GPU:
# - compute_environment: LOCAL_MACHINE
# - distributed_type: MULTI_GPU
# - num_processes: 4
# - mixed_precision: bf16

# Запустите обучение на всех GPU
accelerate launch sft_train.py
accelerate launch dpo_train.py

# Или укажите GPU явно
CUDA_VISIBLE_DEVICES=0,1,2,3 accelerate launch \
  --num_processes 4 \
  --mixed_precision bf16 \
  sft_train.py
```

***

## Использование TRL CLI

TRL предоставляет удобные команды CLI:

```bash
# SFT через CLI
trl sft \
  --model_name_or_path meta-llama/Llama-3.2-8B-Instruct \
  --dataset_name trl-lib/ultrachat_200k \
  --dataset_text_field messages \
  --output_dir ./cli_sft_output \
  --num_train_epochs 3 \
  --per_device_train_batch_size 2 \
  --gradient_accumulation_steps 4 \
  --learning_rate 2e-4 \
  --bf16 \
  --use_peft \
  --lora_r 16 \
  --lora_alpha 32

# DPO через CLI
trl dpo \
  --model_name_or_path ./cli_sft_output \
  --dataset_name trl-lib/ultrafeedback_binarized \
  --output_dir ./cli_dpo_output \
  --num_train_epochs 1 \
  --beta 0.1 \
  --bf16
```

***

## Мониторинг обучения

```bash
# Наблюдайте за загрузкой GPU
watch -n 1 nvidia-smi

# Мониторьте loss при использовании W&B
# Откройте https://wandb.ai/your-username в браузере

# Проверьте директорию с контрольными точками
ls -lh sft_output/checkpoint-*/

# Возобновление с контрольной точки
python3 sft_train.py --resume_from_checkpoint sft_output/checkpoint-500/
```

***

## Рекомендации Clore.ai по GPU

Обучение TRL — одна из самых требовательных по VRAM рабочих нагрузок. Выбирайте GPU в зависимости от размера модели и метода:

| Задача                                           | GPU                | Примечания                                                                                       |
| ------------------------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------ |
| SFT / DPO на 7–8B (QLoRA)                        | **RTX 3090** 24 ГБ | \~8 ГБ для QLoRA 4-бит; помещается с запасом; \~0.12$/ч на Clore.ai                              |
| SFT / DPO на 7–8B (LoRA bf16)                    | **RTX 4090** 24 ГБ | Такая же память, как у 3090, но вычисления на 30% быстрее; отлично для ускорения итераций        |
| Полная SFT на 7B или DPO на 13B                  | **A100 40 ГБ**     | 40 ГБ хватает для обучения 7B в полноточной точности; ECC-память предотвращает молчаливые ошибки |
| PPO / полная донастройка 7B, или любой 70B QLoRA | **A100 80 ГБ**     | PPO требует 2× модели (политика+референт) в VRAM; 80 ГБ позволяет запустить обе без OOM          |

**Практический совет:** Начните на RTX 3090 с QLoRA для экспериментов — обучите Llama 3 8B примерно за \~2 часа на 10K примерах. После проверки конвейера переходите на A100 80GB для полноточных прогонов или моделей 70B.

**Показатели скорости (Llama 3 8B SFT, QLoRA, batch=4, seq=2048):**

* RTX 3090: \~1 100 токенов/сек пропускная способность при обучении
* RTX 4090: \~1 450 токенов/сек
* A100 80GB: \~2 800 токенов/сек (полный bf16, без квантизации)

***

## Устранение неполадок

### CUDA: недостаточно памяти (Out of Memory)

```bash
# Уменьшите размер батча
per_device_train_batch_size=1
gradient_accumulation_steps=16  # Сохранить одинаковый эффективный размер батча

# Использовать 4-битную квантизацию (QLoRA)
# Добавить BitsAndBytesConfig с load_in_4bit=True

# Включить gradient checkpointing
gradient_checkpointing=True

# Уменьшить длину последовательности
max_seq_length=1024  # вместо 2048+

# Проверить память GPU
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
```

### Loss равен NaN

```bash
# Распространённая причина: слишком высокий learning rate
learning_rate=1e-5  # Попробуйте меньше

# Распространённая причина: плохие данные (пустые строки, значения None)
# Проверить датасет:
python3 -c "
from datasets import load_from_disk
ds = load_from_disk('data/sft_dataset')
print(ds[0])
print(f'Length: {len(ds)}')
# Проверить на наличие None
none_count = sum(1 for x in ds if x.get('messages') is None)
print(f'None count: {none_count}')
"

# Включить bf16 вместо fp16 (более стабильно)
bf16=True
fp16=False
```

### DPO: `chosen_rewards > rejected_rewards` равно False

```bash
# Это значит, что модель предпочитает отклонённые ответы — переобучение или плохие данные
# Решения:
# 1. Проверьте качество вашего датасета
# 2. Уменьшите beta (меньше штрафа KL)
# 3. Уменьшите learning rate
# 4. Добавьте больше SFT обучения перед DPO
beta=0.05  # Попробуйте меньшие значения
```

### Обучение очень медленное

```bash
# Включить Flash Attention 2
pip install flash-attn --no-build-isolation

# В вашем коде:
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    attn_implementation="flash_attention_2",
    torch_dtype=torch.bfloat16,
)

# Используйте bf16 вместо fp16 на GPU Ampere+ (A100, RTX 3000+)
bf16=True

# Увеличьте количество worker'ов DataLoader
dataloader_num_workers=4

# Проверьте, действительно ли используется GPU
nvidia-smi  # Должно показывать высокую загрузку GPU
```

### `tokenizer.pad_token` предупреждение

```bash
# Стандартное исправление для токенизаторов Llama/Mistral
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"  # Важно для стабильности обучения
```

### Доступ запрещён / HuggingFace 401

```bash
# Повторно войдите в систему
huggingface-cli login

# Установите токен в окружении
export HF_TOKEN=hf_your-token

# Для приватных моделей/датасетов убедитесь, что у вас есть доступ:
# Перейдите на https://huggingface.co/meta-llama/Llama-3.2-8B-Instruct
# Нажмите «Request access» и примите лицензию
```

***

## Сохранение и публикация вашей модели

```bash
# Слить веса LoRA в базовую модель
python3 << 'EOF'
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

base_model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.2-8B-Instruct",
    torch_dtype=torch.bfloat16,
    device_map="auto"
)
model = PeftModel.from_pretrained(base_model, "./sft_final")
merged = model.merge_and_unload()
merged.save_pretrained("./merged_model")

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-8B-Instruct")
tokenizer.save_pretrained("./merged_model")
print("Merged model saved!")
EOF

# Отправить на HuggingFace
huggingface-cli upload your-username/my-trl-model ./merged_model
```

***

## Полезные ссылки

* **GitHub**: <https://github.com/huggingface/trl> ⭐ 10K+
* **Документация**: <https://huggingface.co/docs/trl>
* **Статья DPO**: <https://arxiv.org/abs/2305.18290>
* **GRPO / DeepSeek-R1**: <https://arxiv.org/abs/2501.12599>
* **Статья PPO (RLHF)**: <https://arxiv.org/abs/2203.02155>
* **HuggingFace PEFT**: <https://github.com/huggingface/peft>
* **Weights & Biases**: <https://wandb.ai>
* **Flash Attention**: <https://github.com/Dao-AILab/flash-attention>
* **Clore.ai Marketplace**: <https://clore.ai/marketplace>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-ru/obuchenie/trl.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
