# Дообучение LLM

Обучайте собственную кастомную LLM с помощью эффективных методов дообучения на GPU CLORE.AI.

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

## Аренда на CLORE.AI

1. Посетите [CLORE.AI Marketplace](https://clore.ai/marketplace)
2. Фильтровать по типу GPU, объёму VRAM и цене
3. Выберите **По запросу** (фиксированная ставка) или **Спотовая** (цена ставки)
4. Настройте ваш заказ:
   * Выберите Docker-образ
   * Установите порты (TCP для SSH, HTTP для веб-интерфейсов)
   * Добавьте переменные окружения при необходимости
   * Введите команду запуска
5. Выберите способ оплаты: **CLORE**, **BTC**, или **USDT/USDC**
6. Создайте заказ и дождитесь развертывания

### Доступ к вашему серверу

* Найдите данные для подключения в **Мои заказы**
* Веб-интерфейсы: используйте URL HTTP-порта
* SSH: `ssh -p <port> root@<proxy-address>`

## Что такое LoRA/QLoRA?

* **LoRA** (Low-Rank Adaptation) — обучайте небольшие адаптерные слои вместо всего моделя
* **QLoRA** — LoRA с квантованием для ещё меньшего потребления VRAM
* Обучение модели 7B на одной RTX 3090
* Обучение модели 70B на одной A100

## Требования

| Модель | Метод       | Мин. VRAM | Рекомендуется |
| ------ | ----------- | --------- | ------------- |
| 7B     | QLoRA       | 12ГБ      | RTX 3090      |
| 13B    | QLoRA       | 20ГБ      | RTX 4090      |
| 70B    | QLoRA       | 48ГБ      | A100 80GB     |
| 7B     | Полный LoRA | 24ГБ      | RTX 4090      |

## Быстрое развертывание

**Docker-образ:**

```
pytorch/pytorch:2.5.1-cuda12.4-cudnn9-devel
```

**Порты:**

```
22/tcp
8888/http
6006/http
```

**Команда:**

```bash
pip install "transformers>=4.45" "datasets>=2.20" accelerate "peft>=0.14" \
    bitsandbytes "trl>=0.12" wandb jupyterlab && \
jupyter lab --ip=0.0.0.0 --port=8888 --allow-root
```

## Доступ к вашему сервису

После развертывания найдите ваш `http_pub` URL в **Мои заказы**:

1. Перейдите на **Мои заказы** страницу
2. Кликните по вашему заказу
3. Найдите `http_pub` URL (например, `abc123.clorecloud.net`)

Используйте `https://YOUR_HTTP_PUB_URL` вместо `localhost` в примерах ниже.

## Подготовка набора данных

### Формат чата (рекомендуется)

```json
[
  {
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "What is Python?"},
      {"role": "assistant", "content": "Python is a programming language..."}
    ]
  }
]
```

### Формат инструкций

```json
[
  {
    "instruction": "Translate to French",
    "input": "Hello, how are you?",
    "output": "Bonjour, comment allez-vous?"
  }
]
```

### Формат Alpaca

```json
[
  {
    "instruction": "Give three tips for staying healthy.",
    "input": "",
    "output": "1. Eat balanced meals..."
  }
]
```

## Поддерживаемые современные модели (2025)

| Модель                      | HF ID                                     | Мин. VRAM (QLoRA) |
| --------------------------- | ----------------------------------------- | ----------------- |
| Llama 3.1 / 3.3 8B          | `meta-llama/Llama-3.1-8B-Instruct`        | 12ГБ              |
| Qwen 2.5 7B / 14B           | `Qwen/Qwen2.5-7B-Instruct`                | 12ГБ / 20ГБ       |
| DeepSeek-R1-Distill (7B/8B) | `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` | 12ГБ              |
| Mistral 7B v0.3             | `mistralai/Mistral-7B-Instruct-v0.3`      | 12ГБ              |
| Gemma 2 9B                  | `google/gemma-2-9b-it`                    | 14ГБ              |
| Phi-4 14B                   | `microsoft/phi-4`                         | 20ГБ              |

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

Современный пример с PEFT 0.14+, Flash Attention 2, поддержкой DoRA и совместимостью с Qwen2.5 / DeepSeek-R1:

```python
import torch
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    TrainingArguments,
)
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from datasets import load_dataset
from trl import SFTTrainer, SFTConfig

# === Конфигурация ===
# Выберите одну из: Qwen2.5, DeepSeek-R1-Distill, Llama 3.1, Mistral и т.д.
MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
# MODEL_NAME = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"
# MODEL_NAME = "meta-llama/Llama-3.1-8B-Instruct"

DATASET = "your_dataset.json"  # или имя набора данных HuggingFace
OUTPUT_DIR = "./output"
MAX_SEQ_LENGTH = 4096           # Qwen2.5 поддерживает до 32K контекста
USE_DORA = True                 # DoRA улучшает качество по сравнению со стандартной LoRA
USE_FLASH_ATTN = True           # Flash Attention 2 экономит VRAM и ускоряет

# === Загрузка модели с 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,  # Требуется для Qwen2.5 и DeepSeek
    # Flash Attention 2: требуется GPU Ampere+ (RTX 30/40, A100)
    attn_implementation="flash_attention_2" if USE_FLASH_ATTN else "eager",
)

tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"

# === Настройка LoRA с опциональным DoRA ===
# DoRA (Weight-Decomposed Low-Rank Adaptation) — требуется PEFT >= 0.14
# use_dora=True разлагает веса на величину и направление для лучшего качества
lora_config = LoraConfig(
    r=64,                    # Ранг (больше = большая ёмкость, больше VRAM)
    lora_alpha=16,           # Коэффициент масштабирования (держите равным или вдвое меньше r)
    target_modules=[
        "q_proj", "k_proj", "v_proj", "o_proj",  # Слои внимания
        "gate_proj", "up_proj", "down_proj",      # MLP-слои
    ],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
    use_dora=USE_DORA,        # DoRA: улучшенное качество (PEFT 0.14+)
    # use_rslora=True,        # Опционально: Rank-Stabilized LoRA
)

# Подготовка модели для QLoRA-тренировки
model = prepare_model_for_kbit_training(
    model,
    use_gradient_checkpointing=True,
    gradient_checkpointing_kwargs={"use_reentrant": False},
)
model = get_peft_model(model, lora_config)

# Вывести сводку обучаемых параметров
model.print_trainable_parameters()
# Пример вывода: trainable params: 42,991,616 || all params: 7,284,891,648 || trainable%: 0.59

# === Загрузить набор данных ===
dataset = load_dataset("json", data_files=DATASET)
# Или используйте публичный набор данных:
# dataset = load_dataset("HuggingFaceH4/ultrachat_200k")

# === Форматирование набора данных для Qwen2.5 / ChatML формата ===
def format_chat_qwen(example):
    """Формат для Qwen2.5 с использованием шаблона ChatML."""
    messages = example.get("messages", [])
    if not messages:
        # Обработка данных в стиле alpaca
        text = f"<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n"
        text += f"<|im_start|>user\n{example['instruction']}"
        if example.get("input"):
            text += f"\n{example['input']}"
        text += f"<|im_end|>\n<|im_start|>assistant\n{example['output']}<|im_end|>"
    else:
        # Обработка формата messages (ChatML)
        text = tokenizer.apply_chat_template(
            messages,
            tokenize=False,
            add_generation_prompt=False,
        )
    return {"text": text}

dataset = dataset.map(format_chat_qwen, remove_columns=dataset["train"].column_names)

# === Параметры обучения (PEFT 0.14+ / TRL 0.12+) ===
training_args = SFTConfig(
    output_dir=OUTPUT_DIR,
    num_train_epochs=3,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=8,         # Эффективный батч = 2 * 8 = 16
    learning_rate=2e-4,
    weight_decay=0.001,
    warmup_ratio=0.03,
    lr_scheduler_type="cosine",
    logging_steps=10,
    save_steps=100,
    save_total_limit=3,
    bf16=True,                             # Используйте bf16 для современных GPU (A100, RTX 30/40)
    # fp16=True,                           # Используйте fp16 для более старых GPU
    optim="paged_adamw_8bit",
    max_grad_norm=0.3,
    group_by_length=True,
    report_to="wandb",                     # или "tensorboard"
    # Специфично для SFTConfig:
    max_seq_length=MAX_SEQ_LENGTH,
    dataset_text_field="text",
    packing=True,                          # Упаковывать несколько примеров для эффективности
)

# === Обучение ===
trainer = SFTTrainer(
    model=model,
    train_dataset=dataset["train"],
    tokenizer=tokenizer,
    args=training_args,
)

trainer.train()

# === Сохранить адаптер LoRA ===
trainer.save_model(f"{OUTPUT_DIR}/final")
tokenizer.save_pretrained(f"{OUTPUT_DIR}/final")
print(f"Model saved to {OUTPUT_DIR}/final")
```

## Flash Attention 2

Flash Attention 2 значительно снижает использование VRAM и ускоряет обучение. Требует GPU Ampere+ (RTX 3090, RTX 4090, A100).

```bash
# Установите Flash Attention 2
pip install flash-attn --no-build-isolation
```

```python
# Включить при загрузке модели:
model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    attn_implementation="flash_attention_2",  # <-- добавьте это
    torch_dtype=torch.bfloat16,               # FA2 требует bf16 или fp16
    device_map="auto",
)
```

| Настройка                   | VRAM (7B) | Скорость        |
| --------------------------- | --------- | --------------- |
| Стандартное внимание (fp16) | \~22ГБ    | базовый уровень |
| Flash Attention 2 (bf16)    | \~16ГБ    | +30%            |
| Flash Attention 2 + QLoRA   | \~12ГБ    | +30%            |

## DoRA (Weight-Decomposed LoRA)

DoRA (PEFT >= 0.14) разлагает предварительно обученные веса на компоненты величины и направления. Это улучшает качество дообучения, особенно для меньших рангов.

```python
from peft import LoraConfig

# Стандартный LoRA
lora_config = LoraConfig(r=64, lora_alpha=16, use_dora=False, ...)

# DoRA — те же параметры, лучшее качество
lora_config = LoraConfig(r=64, lora_alpha=16, use_dora=True, ...)
# Примечание: DoRA добавляет примерно 5–10% оверхеда VRAM по сравнению со стандартным LoRA
# Примечание: Не во всех случаях совместимо с квантованными (4-бит/8-бит) моделями
```

## Примеры Qwen2.5 & DeepSeek-R1-Distill

### Дообучение Qwen2.5

```python
MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
# Для 14B: "Qwen/Qwen2.5-14B-Instruct" (требует 20ГБ+ VRAM с QLoRA)

model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True,          # Требуется для Qwen2.5
    attn_implementation="flash_attention_2",
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)

# Qwen2.5 использует формат ChatML — используйте apply_chat_template
messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Hello!"},
    {"role": "assistant", "content": "Hi there! How can I help?"},
]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)
```

### Дообучение DeepSeek-R1-Distill

Модели DeepSeek-R1-Distill (Qwen-7B, Qwen-14B, Llama-8B, Llama-70B) ориентированы на рассуждение. Дообучите их, чтобы адаптировать стиль цепочек рассуждений к вашей доменной области.

```python
# Варианты DeepSeek-R1-Distill
MODEL_NAME = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"   # 7B на базе Qwen2.5
# MODEL_NAME = "deepseek-ai/DeepSeek-R1-Distill-Llama-8B" # 8B на базе Llama3
# MODEL_NAME = "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B" # 14B (требует A100)

model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True,
    attn_implementation="flash_attention_2",
)

# DeepSeek-R1 использует теги <think>...</think> для рассуждений
# Сохраняйте это в обучающих данных, чтобы сохранить способность цепочек рассуждений
example_format = """<|im_start|>user
Solve: What is 15 * 23?<|im_end|>
<|im_start|>assistant
<think>
15 * 23 = 15 * 20 + 15 * 3 = 300 + 45 = 345
</think>
The answer is 345.<|im_end|>"""

# Целевые модули LoRA для DeepSeek-R1-Distill (на базе Qwen2.5)
lora_config = LoraConfig(
    r=32,
    lora_alpha=16,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    use_dora=True,
    task_type="CAUSAL_LM",
)
```

## Использование Axolotl (проще)

Axolotl упрощает дообучение с YAML-конфигами:

```bash
pip install axolotl

# Создать конфиг
cat > config.yml << 'EOF'
base_model: mistralai/Mistral-7B-v0.1
model_type: MistralForCausalLM
tokenizer_type: LlamaTokenizer

load_in_4bit: true
adapter: qlora
lora_r: 32
lora_alpha: 16

datasets:
  - path: your_data.json
    type: alpaca

sequence_len: 4096
sample_packing: true

gradient_accumulation_steps: 4
micro_batch_size: 2
num_epochs: 3
learning_rate: 2e-4

output_dir: ./output
EOF

# Обучение
accelerate launch -m axolotl.cli.train config.yml
```

## Примеры конфигураций Axolotl

### Чат-модель

```yaml
base_model: mistralai/Mistral-7B-Instruct-v0.2
load_in_4bit: true
adapter: qlora

datasets:
  - path: data.json
    type: sharegpt

chat_template: mistral
```

### Модель для кода

```yaml
base_model: codellama/CodeLlama-7b-hf
load_in_4bit: true
adapter: qlora

datasets:
  - path: code_data.json
    type: alpaca

sequence_len: 8192  # Длинный контекст для кода
```

## Слияние весов LoRA

После обучения влейте LoRA обратно в базовую модель:

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

# Загрузить базовую модель
base_model = AutoModelForCausalLM.from_pretrained(
    "mistralai/Mistral-7B-v0.1",
    torch_dtype=torch.float16,
    device_map="auto",
)

# Загрузить LoRA
model = PeftModel.from_pretrained(base_model, "./output/final")

# Слить
merged_model = model.merge_and_unload()

# Сохранить слитую модель
merged_model.save_pretrained("./merged_model")
tokenizer.save_pretrained("./merged_model")
```

## Конвертация в GGUF

Для использования с llama.cpp/Ollama:

```bash

# Клонировать llama.cpp
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp

# Конвертировать
python convert.py ../merged_model --outtype f16 --outfile model-f16.gguf

# Квантовать
./quantize model-f16.gguf model-q4_k_m.gguf q4_k_m
```

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

### Weights & Biases

```python
import wandb
wandb.init(project="llm-finetune", name="mistral-7b-lora")
```

### TensorBoard

```python

# В параметрах обучения
report_to="tensorboard"
logging_dir="./logs"

# Просмотр
tensorboard --logdir ./logs --port 6006 --bind_all
```

## Лучшие практики

### Гиперпараметры

| Параметр    | Модель 7B | Модель 13B | Модель 70B |
| ----------- | --------- | ---------- | ---------- |
| batch\_size | 4         | 2          | 1          |
| grad\_accum | 4         | 8          | 16         |
| lr          | 2e-4      | 1e-4       | 5e-5       |
| lora\_r     | 64        | 32         | 16         |
| epochs      | 3         | 2-3        | 1-2        |

### Размер набора данных

* Минимум: 1 000 примеров
* Хорошо: 10 000+ примеров
* Качество > Количество

### Избегание переобучения

```python
training_args = TrainingArguments(
    ...
    weight_decay=0.01,
    warmup_ratio=0.03,
    save_total_limit=3,
    load_best_model_at_end=True,
    evaluation_strategy="steps",
    eval_steps=100,
)
```

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

```bash

# С accelerate
accelerate launch --multi_gpu --num_processes 4 train.py

# С DeepSpeed
accelerate launch --use_deepspeed --num_processes 4 train.py
```

Конфиг DeepSpeed:

```json
{
  "bf16": {"enabled": true},
  "zero_optimization": {
    "stage": 2,
    "offload_optimizer": {"device": "cpu"}
  }
}
```

## Сохранение и экспорт

```bash

# Сохранить адаптер LoRA
trainer.save_model("./lora_adapter")

# Сохранить слитую модель
merged_model.save_pretrained("./full_model")

# Загрузить на HuggingFace
huggingface-cli login
merged_model.push_to_hub("username/my-model")
```

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

### Ошибки OOM

* Уменьшите размер батча
* Увеличьте накопление градиента
* Используйте `gradient_checkpointing=True`
* Уменьшите lora\_r

### Потеря при обучении не уменьшается

* Проверьте формат данных
* Увеличьте скорость обучения
* Проверьте наличие проблем с данными

### Потеря NaN

* Уменьшите скорость обучения
* Используйте fp32 вместо fp16
* Проверьте данные на повреждения

## Оценка стоимости

Типичные тарифы на рынке CLORE.AI (по состоянию на 2024 год):

| GPU       | Почасовая ставка | Дневная ставка | Сессия на 4 часа |
| --------- | ---------------- | -------------- | ---------------- |
| RTX 3060  | \~$0.03          | \~$0.70        | \~$0.12          |
| RTX 3090  | \~$0.06          | \~$1.50        | \~$0.25          |
| RTX 4090  | \~$0.10          | \~$2.30        | \~$0.40          |
| A100 40GB | \~$0.17          | \~$4.00        | \~$0.70          |
| A100 80GB | \~$0.25          | \~$6.00        | \~$1.00          |

*Цены зависят от провайдера и спроса. Проверьте* [*CLORE.AI Marketplace*](https://clore.ai/marketplace) *для актуальных тарифов.*

> 📚 См. также: [Как дообучить LLaMA 3 на облачном GPU — пошаговое руководство](https://blog.clore.ai/how-to-fine-tune-llama-3-cloud-gpu/)

**Экономьте деньги:**

* Используйте **Спотовая** рынок для гибких нагрузок (часто на 30–50% дешевле)
* Оплатите с помощью **CLORE** токенов
* Сравните цены у разных провайдеров
