← Назад к вопросам

Что такое fine-tuning языковых моделей?

2.7 Senior🔥 171 комментариев
#NLP и обработка текста#Глубокое обучение

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Fine-tuning языковых моделей

Fine-tuning — это процесс адаптации предварительно обученной языковой модели к конкретной задаче или домену путём доподнительного обучения на специализированных данных. Вместо обучения модели с нуля, мы берём уже обученную модель и приспосабливаем её веса к новой задаче.

Общая архитектура процесса

Все современные методы fine-tuning следуют одной схеме:

1. Загружаем предобученную модель (BERT, GPT, T5, LLaMA и т.д.)
2. Инициализируем веса из предобучения
3. Добавляем task-specific слои (если нужно)
4. Обучаем на целевом датасете
5. Оцениваем на тестовом наборе

Почему fine-tuning работает лучше, чем обучение с нуля?

1. Transfer Learning Предобученная модель уже изучила основные признаки языка: синтаксис, семантику, факты. Нам остаётся адаптировать эти знания к конкретной задаче.

2. Меньше данных Для fine-tuning нужно меньше данных, чем для полного обучения, так как модель уже знает язык.

3. Быстрее обучение Можно использовать более низкий learning rate и меньше эпох, так как веса уже хорошо инициализированы.

4. Лучше производительность Модель получает лучший результат на малых датасетах благодаря знаниям из предобучения.

Простой пример fine-tuning с BERT

import torch
from torch.utils.data import DataLoader, Dataset
from transformers import BertTokenizer, BertForSequenceClassification
from torch.optim import AdamW

# 1. Загружаем предобученную модель
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForSequenceClassification.from_pretrained(
    'bert-base-uncased',
    num_labels=2  # Бинарная классификация
)

# 2. Подготовка данных
class TextDataset(Dataset):
    def __init__(self, texts, labels, tokenizer, max_len=128):
        self.texts = texts
        self.labels = labels
        self.tokenizer = tokenizer
        self.max_len = max_len
    
    def __len__(self):
        return len(self.texts)
    
    def __getitem__(self, idx):
        text = self.texts[idx]
        label = self.labels[idx]
        
        encoding = self.tokenizer(
            text,
            add_special_tokens=True,
            max_length=self.max_len,
            padding='max_length',
            truncation=True,
            return_tensors='pt'
        )
        
        return {
            'input_ids': encoding['input_ids'].squeeze(),
            'attention_mask': encoding['attention_mask'].squeeze(),
            'label': torch.tensor(label)
        }

# 3. Подготовка data loader
train_texts = ["This movie is great!", "I hated this film."]
train_labels = [1, 0]  # 1 = положительный, 0 = отрицательный

train_dataset = TextDataset(train_texts, train_labels, tokenizer)
train_loader = DataLoader(train_dataset, batch_size=8)

# 4. Настройка обучения
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)

optimizer = AdamW(model.parameters(), lr=2e-5)
epochs = 3

# 5. Обучение
for epoch in range(epochs):
    model.train()
    total_loss = 0
    
    for batch in train_loader:
        input_ids = batch['input_ids'].to(device)
        attention_mask = batch['attention_mask'].to(device)
        labels = batch['label'].to(device)
        
        optimizer.zero_grad()
        
        outputs = model(
            input_ids=input_ids,
            attention_mask=attention_mask,
            labels=labels
        )
        
        loss = outputs.loss
        loss.backward()
        optimizer.step()
        
        total_loss += loss.item()
    
    print(f"Epoch {epoch + 1}, Loss: {total_loss / len(train_loader):.4f}")

# 6. Инференс
model.eval()
with torch.no_grad():
    test_text = "This movie is wonderful!"
    encoding = tokenizer(test_text, return_tensors='pt')
    encoding = {k: v.to(device) for k, v in encoding.items()}
    
    outputs = model(**encoding)
    logits = outputs.logits
    prediction = torch.argmax(logits, dim=1).item()
    print(f"Prediction: {prediction}")  # 1 = положительный

Различные стратегии fine-tuning

1. Полный fine-tuning (Full Fine-tuning)

Обновляются все параметры модели:

# Все параметры требуют градиентов
for param in model.parameters():
    param.requires_grad = True

# Обучаем со слегка пониженным learning rate
optimizer = AdamW(model.parameters(), lr=2e-5)

Преимущества: Лучшая производительность, полная адаптация модели Недостатки: Много параметров, высокий риск переобучения

2. Partial Fine-tuning (Замораживание слоёв)

Обновляют только верхние слои, нижние слои заморожены:

# Замораживаем нижние слои BERT
for name, param in model.bert.encoder.layer[:10].named_parameters():
    param.requires_grad = False

# Обновляем только верхние слои и классификационный слой
optimizer = AdamW(
    [p for p in model.parameters() if p.requires_grad],
    lr=1e-4
)

Преимущества: Меньше параметров, быстрее обучение, меньше риск переобучения Недостатки: Может быть менее точным

3. LoRA (Low-Rank Adaptation)

Добавляют обучаемые матрицы низкого ранга к весам, вместо обновления всех весов:

from peft import get_peft_model, LoraConfig, TaskType

# Конфигурируем LoRA
lora_config = LoraConfig(
    r=8,  # Ранг матриц
    lora_alpha=16,
    target_modules=['q_proj', 'v_proj'],  # Какие слои адаптировать
    lora_dropout=0.05,
    bias='none',
    task_type=TaskType.SEQ_2_SEQ_LM
)

# Применяем LoRA к модели
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Output: trainable params: 2,097,152 || all params: 350,127,616 || trainable%: 0.5986

Преимущества: Очень мало параметров (~1% от исходных), быстро и дёшево Недостатки: Иногда менее точно чем полный fine-tuning

4. Prompt Tuning

Обучают только специальные prefix-токены (prompt), остальная модель заморожена:

class PromptTuning(torch.nn.Module):
    def __init__(self, model, prompt_len=10, hidden_dim=768):
        super().__init__()
        self.model = model
        self.prompt_embeddings = torch.nn.Embedding(prompt_len, hidden_dim)
        
        # Замораживаем исходную модель
        for param in model.parameters():
            param.requires_grad = False
    
    def forward(self, input_ids, attention_mask):
        batch_size = input_ids.shape[0]
        
        # Получаем эмбеддинги prompt
        prompt_embs = self.prompt_embeddings(
            torch.arange(self.prompt_embeddings.num_embeddings)
        ).unsqueeze(0).expand(batch_size, -1, -1)
        
        # Получаем эмбеддинги входа
        input_embs = self.model.embeddings(input_ids)
        
        # Конкатенируем prompt и input
        combined_embs = torch.cat([prompt_embs, input_embs], dim=1)
        
        return self.model.encoder(combined_embs)

Преимущества: Экстремально мало параметров, мультитасковое обучение Недостатки: Менее универсально, может быть нестабильным

Гиперпараметры fine-tuning

# Типичные гиперпараметры для fine-tuning

learning_rate = 2e-5  # Ниже чем обучение с нуля (обычно 1e-4 или 1e-3)
epochs = 3            # Обычно 2-5 эпох
warmup_steps = 500    # Постепенное увеличение lr
weight_decay = 0.01   # L2 регуляризация
batch_size = 8        # Может быть меньше
max_grad_norm = 1.0   # Обрезание градиентов

Лучшие практики fine-tuning

1. Выбор learning rate

  • Начните с 2e-5 для BERT, меньше для больших моделей
  • Используйте learning rate scheduler (linear decay, cosine annealing)

2. Обработка дисбаланса классов

from sklearn.utils.class_weight import compute_class_weight

class_weights = compute_class_weight(
    'balanced',
    classes=np.unique(labels),
    y=labels
)
class_weights = torch.FloatTensor(class_weights)

3. Регуляризация

  • Dropout, weight decay
  • Ранняя остановка (early stopping)

4. Валидация

# Валидируем каждую эпоху
for epoch in range(epochs):
    train_loss = train_epoch(model, train_loader)
    val_loss, val_acc = validate(model, val_loader)
    
    if val_loss < best_val_loss:
        best_val_loss = val_loss
        torch.save(model.state_dict(), 'best_model.pt')
        patience_counter = 0
    else:
        patience_counter += 1
        if patience_counter >= 3:
            break

Размеры датасетов для fine-tuning

  • Маленькие датасеты (100-1000): LoRA или Prompt Tuning
  • Средние датасеты (1000-10000): Partial Fine-tuning или Full Fine-tuning с регуляризацией
  • Большие датасеты (100000+): Full Fine-tuning или даже предобучение нового токенайзера

Заключение

Fine-tuning — это один из самых эффективных способов адаптации языковых моделей к новым задачам. Выбор стратегии зависит от размера датасета, доступных вычислительных ресурсов и требуемой точности. В 2024-2025 годах LoRA стал стандартом для эффективного fine-tuning больших моделей.

Что такое fine-tuning языковых моделей? | PrepBro