← Назад к курсу

Описание процесса обучения больших языковых моделей (LLM)

Введение

Большие языковые модели (LLM) — это одно из самых значительных достижений в области ИИ. Современные модели с миллиардами параметров демонстрируют поразительные способности к пониманию и генерации естественного языка. В этом исследовании рассмотрим полный цикл обучения LLM.

1. Подготовка данных

1.1 Сбор и обработка данных

Основные источники данных:

  • Интернет-корпуса (Common Crawl, Wikipedia, новостные сайты)
  • Книги и научные публикации
  • Кодовые репозитории (GitHub, GitLab)
  • Специализированные доменные данные

Ключевые этапы обработки:

  1. Очистка данных (удаление дубликатов, спама)
  2. Нормализация (стандартизация форматов)
  3. Фильтрация (удаление контента с этическими проблемами)
  4. Балансировка (обеспечение разнообразия)

1.2 Токенизация и форматирование

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
from datasets import load_dataset
import pandas as pd
import numpy as np
from tqdm import tqdm
import re
import json
from collections import Counter

class DataProcessor:
    def __init__(self, model_name="bert-base-uncased"):
        self.tokenizer = AutoTokenizer.from_pretrained(model_name)
        self.model_name = model_name
        
    def clean_text(self, text):
        """Очистка текста"""
        text = re.sub(r'http\S+', '', text)  # Удаление URL
        text = re.sub(r'@\w+', '', text)     # Удаление упоминаний
        text = re.sub(r'#\w+', '', text)     # Удаление хэштегов
        text = re.sub(r'\s+', ' ', text)     # Нормализация пробелов
        text = text.strip()
        return text
    
    def tokenize_text(self, text, max_length=512):
        """Токенизация текста"""
        return self.tokenizer(
            text,
            max_length=max_length,
            truncation=True,
            padding=False,
            return_tensors='pt'
        )
    
    def process_dataset(self, dataset_path):
        """Обработка датасета"""
        # Загрузка данных
        dataset = load_dataset('text', data_files=dataset_path, split='train')
        
        processed_data = []
        
        for example in tqdm(dataset, desc="Обработка данных"):
            # Очистка текста
            cleaned_text = self.clean_text(example['text'])
            
            if len(cleaned_text) > 50:  # Фильтрация коротких текстов
                processed_data.append({
                    'original_text': example['text'],
                    'cleaned_text': cleaned_text,
                    'length': len(cleaned_text)
                })
        
        # Преобразование в DataFrame для анализа
        df = pd.DataFrame(processed_data)
        
        # Анализ распределения длин
        print(f"Обработано записей: {len(df)}")
        print(f"Средняя длина текста: {df['length'].mean():.2f}")
        print(f"Максимальная длина: {df['length'].max()}")
        
        return df, processed_data

2. Архитектура моделей

2.1 Трансформерная архитектура

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn import TransformerEncoder, TransformerEncoderLayer

class TransformerLLM(nn.Module):
    def __init__(self, vocab_size, d_model=512, nhead=8, num_layers=6, 
                 dim_feedforward=2048, dropout=0.1, max_seq_len=512):
        super(TransformerLLM, self).__init__()
        
        self.d_model = d_model
        self.embedding = nn.Embedding(vocab_size, d_model)
        self.pos_encoder = PositionalEncoding(d_model, dropout, max_seq_len)
        
        encoder_layer = TransformerEncoderLayer(
            d_model, nhead, dim_feedforward, dropout, batch_first=True
        )
        self.transformer_encoder = TransformerEncoder(encoder_layer, num_layers)
        
        self.fc_out = nn.Linear(d_model, vocab_size)
        
    def forward(self, x, mask=None):
        # Встраивание токенов
        x = self.embedding(x) * np.sqrt(self.d_model)
        
        # Добавление позиционного кодирования
        x = self.pos_encoder(x)
        
        # Прохождение через трансформер
        x = self.transformer_encoder(x, src_key_padding_mask=mask)
        
        # Выходной слой
        x = self.fc_out(x)
        
        return x

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, dropout=0.1, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * 
                           (-np.log(10000.0) / d_model))
        
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0).transpose(0, 1)
        
        self.register_buffer('pe', pe)
    
    def forward(self, x):
        x = x + self.pe[:x.size(0), :]
        return self.dropout(x)

# Пример использования
vocab_size = 50000
model = TransformerLLM(vocab_size, d_model=512, nhead=8, num_layers=12)
print(f"Количество параметров: {sum(p.numel() for p in model.parameters()):,}")

2.2 Современные архитектуры (GPT, BERT, T5)

from transformers import GPT2LMHeadModel, GPT2Config
from transformers import BertModel, BertConfig
from transformers import T5ForConditionalGeneration, T5Config

# GPT-2 модель
def create_gpt_model(vocab_size=50000, n_positions=1024, n_embd=768, 
                    n_layer=12, n_head=12):
    config = GPT2Config(
        vocab_size=vocab_size,
        n_positions=n_positions,
        n_embd=n_embd,
        n_layer=n_layer,
        n_head=n_head,
        gradient_checkpointing=True
    )
    model = GPT2LMHeadModel(config)
    return model

# BERT модель
def create_bert_model(vocab_size=30522, hidden_size=768, num_hidden_layers=12,
                     num_attention_heads=12, intermediate_size=3072):
    config = BertConfig(
        vocab_size=vocab_size,
        hidden_size=hidden_size,
        num_hidden_layers=num_hidden_layers,
        num_attention_heads=num_attention_heads,
        intermediate_size=intermediate_size
    )
    model = BertModel(config)
    return model

# T5 модель
def create_t5_model(vocab_size=32128, d_model=512, num_layers=6,
                   num_heads=8, d_ff=2048):
    config = T5Config(
        vocab_size=vocab_size,
        d_model=d_model,
        num_layers=num_layers,
        num_heads=num_heads,
        d_ff=d_ff,
        relative_attention_num_buckets=32
    )
    model = T5ForConditionalGeneration(config)
    return model

# Сравнение моделей
gpt_model = create_gpt_model()
bert_model = create_bert_model()
t5_model = create_t5_model()

print(f"GPT-2 параметры: {sum(p.numel() for p in gpt_model.parameters()):,}")
print(f"BERT параметры: {sum(p.numel() for p in bert_model.parameters()):,}")
print(f"T5 параметры: {sum(p.numel() for p in t5_model.parameters()):,}")

3. Процесс обучения

3.1 Pre-training (Обучение с учителем)

import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
from torch.cuda.amp import autocast, GradScaler
import time
from tqdm import tqdm

class TextDataset(Dataset):
    def __init__(self, texts, tokenizer, max_length=512):
        self.texts = texts
        self.tokenizer = tokenizer
        self.max_length = max_length
        
    def __len__(self):
        return len(self.texts)
    
    def __getitem__(self, idx):
        text = self.texts[idx]
        encoding = self.tokenizer(
            text,
            max_length=self.max_length,
            truncation=True,
            padding='max_length',
            return_tensors='pt'
        )
        
        return {
            'input_ids': encoding['input_ids'].flatten(),
            'attention_mask': encoding['attention_mask'].flatten(),
            'labels': encoding['input_ids'].flatten()  # Для языкового моделирования
        }

class LLMTrainer:
    def __init__(self, model, tokenizer, learning_rate=1e-4, 
                 weight_decay=0.01, device='cuda'):
        self.model = model.to(device)
        self.tokenizer = tokenizer
        self.device = device
        
        # Оптимизатор с weight decay
        self.optimizer = optim.AdamW(
            model.parameters(), 
            lr=learning_rate, 
            weight_decay=weight_decay
        )
        
        # Шедулер
        self.scheduler = optim.lr_scheduler.CosineAnnealingLR(
            self.optimizer, T_max=1000
        )
        
        # Mixed precision training
        self.scaler = GradScaler()
        
    def train_epoch(self, dataloader):
        self.model.train()
        total_loss = 0
        start_time = time.time()
        
        for batch in tqdm(dataloader, desc="Обучение"):
            input_ids = batch['input_ids'].to(self.device)
            attention_mask = batch['attention_mask'].to(self.device)
            labels = batch['labels'].to(self.device)
            
            self.optimizer.zero_grad()
            
            # Mixed precision
            with autocast():
                outputs = self.model(
                    input_ids=input_ids,
                    attention_mask=attention_mask,
                    labels=labels
                )
                loss = outputs.loss
            
            # Backpropagation
            self.scaler.scale(loss).backward()
            self.scaler.step(self.optimizer)
            self.scaler.update()
            
            total_loss += loss.item()
            
        avg_loss = total_loss / len(dataloader)
        epoch_time = time.time() - start_time
        
        return avg_loss, epoch_time
    
    def train(self, dataloaders, num_epochs=10):
        train_losses = []
        
        for epoch in range(num_epochs):
            print(f"\nЭпоха {epoch + 1}/{num_epochs}")
            
            # Обучение
            train_loss, train_time = self.train_epoch(dataloaders['train'])
            train_losses.append(train_loss)
            
            print(f"Train Loss: {train_loss:.4f} | Time: {train_time:.2f}s")
            
            # Шедулер
            self.scheduler.step()
            
            # Валидация (опционально)
            if 'val' in dataloaders:
                val_loss = self.validate_epoch(dataloaders['val'])
                print(f"Val Loss: {val_loss:.4f}")
        
        return train_losses
    
    def validate_epoch(self, dataloader):
        self.model.eval()
        total_loss = 0
        
        with torch.no_grad():
            for batch in dataloader:
                input_ids = batch['input_ids'].to(self.device)
                attention_mask = batch['attention_mask'].to(self.device)
                labels = batch['labels'].to(self.device)
                
                outputs = self.model(
                    input_ids=input_ids,
                    attention_mask=attention_mask,
                    labels=labels
                )
                loss = outputs.loss
                total_loss += loss.item()
        
        return total_loss / len(dataloader)

3.2 Fine-tuning (Дообучение)

class FineTuner(LLMTrainer):
    def __init__(self, model, tokenizer, num_labels=2, learning_rate=2e-5):
        super().__init__(model, tokenizer, learning_rate=learning_rate)
        
        # Адаптация модели для классификации
        if hasattr(model, 'bert'):
            # Для BERT-подобных моделей
            self.classifier = nn.Linear(model.config.hidden_size, num_labels).to(self.device)
            model.classifier = self.classifier
        else:
            # Для других моделей
            self.model.resize_token_embeddings(len(tokenizer))
    
    def compute_metrics(self, predictions, labels):
        """Вычисление метрик качества"""
        from sklearn.metrics import accuracy_score, f1_score
        
        preds = torch.argmax(predictions, dim=1)
        accuracy = accuracy_score(labels.cpu(), preds.cpu())
        f1 = f1_score(labels.cpu(), preds.cpu(), average='weighted')
        
        return {
            'accuracy': accuracy,
            'f1': f1
        }
    
    def fine_tune(self, dataloaders, num_epochs=3):
        """Дообучение модели"""
        for epoch in range(num_epochs):
            print(f"\nFine-tuning эпоха {epoch + 1}/{num_epochs}")
            
            self.model.train()
            total_loss = 0
            
            for batch in tqdm(dataloaders['train'], desc="Fine-tuning"):
                input_ids = batch['input_ids'].to(self.device)
                attention_mask = batch['attention_mask'].to(self.device)
                labels = batch['labels'].to(self.device)
                
                self.optimizer.zero_grad()
                
                outputs = self.model(
                    input_ids=input_ids,
                    attention_mask=attention_mask,
                    labels=labels
                )
                loss = outputs.loss
                
                loss.backward()
                self.optimizer.step()
                
                total_loss += loss.item()
            
            avg_loss = total_loss / len(dataloaders['train'])
            print(f"Fine-tuning Loss: {avg_loss:.4f}")

4. Вычислительные требования и оптимизация

4.1 Расчет ресурсов

import psutil
import GPUtil

def calculate_training_requirements(model_params, batch_size, sequence_length, 
                                 num_epochs, data_size):
    """Расчет требований для обучения модели"""
    
    # Примерные оценки
    bytes_per_parameter = 4  # float32
    memory_per_parameter = model_params * bytes_per_parameter / 1e9  # GB
    
    # Оценка VRAM для forward pass
    estimated_vram = (
        model_params * bytes_per_parameter +  # Веса модели
        batch_size * sequence_length * bytes_per_parameter +  # Активации
        batch_size * sequence_length * bytes_per_parameter * 0.1  # Градиенты
    ) / 1e9  # GB
    
    # Оценка общего времени обучения (очень приблизительная)
    estimated_time_hours = (num_epochs * data_size * sequence_length) / (1e12) * 0.1
    
    # Оценка общего дискового пространства
    estimated_disk_gb = model_params * bytes_per_parameter * 2 / 1e9  # + checkpoints
    
    return {
        'model_memory_gb': memory_per_parameter,
        'estimated_vram_gb': estimated_vram,
        'estimated_time_hours': estimated_time_hours,
        'estimated_disk_gb': estimated_disk_gb
    }

def check_system_resources():
    """Проверка доступных ресурсов системы"""
    # CPU информация
    cpu_count = psutil.cpu_count()
    cpu_memory = psutil.virtual_memory().total / 1e9  # GB
    
    # GPU информация
    gpus = GPUtil.getGPUs()
    gpu_info = []
    
    for i, gpu in enumerate(gpus):
        gpu_info.append({
            'id': i,
            'name': gpu.name,
            'memory_total': gpu.memoryTotal,
            'memory_used': gpu.memoryUsed,
            'memory_free': gpu.memoryFree,
            'temperature': gpu.temperature
        })
    
    return {
        'cpu': {
            'cores': cpu_count,
            'memory_gb': cpu_memory
        },
        'gpus': gpu_info
    }

# Пример использования
model_params = 125e6  # 125M параметров (GPT-2 small)
requirements = calculate_training_requirements(
    model_params=model_params,
    batch_size=32,
    sequence_length=512,
    num_epochs=10,
    data_size=1e6
)

system_info = check_system_resources()

print("Требования для обучения:")
for key, value in requirements.items():
    print(f"{key}: {value:.2f}")

print("\nСистемные ресурсы:")
print(f"CPU: {system_info['cpu']['cores']} ядер, {system_info['cpu']['memory_gb']:.1f} GB RAM")
for gpu in system_info['gpus']:
    print(f"GPU {gpu['id']}: {gpu['name']}, {gpu['memory_total']} MB VRAM")

4.2 Оптимизация использования памяти

class MemoryEfficientTrainer(LLMTrainer):
    def __init__(self, model, tokenizer, gradient_checkpointing=True, 
                 mixed_precision=True, gradient_accumulation_steps=4):
        super().__init__(model, tokenizer)
        
        # Включение gradient checkpointing
        if gradient_checkpointing:
            self.model.gradient_checkpointing_enable()
        
        # Mixed precision
        self.mixed_precision = mixed_precision
        if mixed_precision:
            self.scaler = GradScaler()
        
        # Gradient accumulation
        self.gradient_accumulation_steps = gradient_accumulation_steps
    
    def train_epoch(self, dataloader):
        self.model.train()
        total_loss = 0
        optimizer.zero_grad()
        
        for i, batch in enumerate(tqdm(dataloader, desc="Обучение (оптимизированное)")):
            input_ids = batch['input_ids'].to(self.device)
            attention_mask = batch['attention_mask'].to(self.device)
            labels = batch['labels'].to(self.device)
            
            # Forward pass
            with autocast() if self.mixed_precision else nullcontext():
                outputs = self.model(
                    input_ids=input_ids,
                    attention_mask=attention_mask,
                    labels=labels
                )
                loss = outputs.loss / self.gradient_accumulation_steps
            
            # Backward pass
            if self.mixed_precision:
                self.scaler.scale(loss).backward()
            else:
                loss.backward()
            
            # Gradient accumulation
            if (i + 1) % self.gradient_accumulation_steps == 0:
                if self.mixed_precision:
                    self.scaler.step(self.optimizer)
                    self.scaler.update()
                else:
                    self.optimizer.step()
                
                optimizer.zero_grad()
            
            total_loss += loss.item() * self.gradient_accumulation_steps
        
        avg_loss = total_loss / len(dataloader)
        return avg_loss

5. Оценка и развертывание

5.1 Оценка качества модели

from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt

class ModelEvaluator:
    def __init__(self, model, tokenizer, device='cuda'):
        self.model = model.to(device)
        self.tokenizer = tokenizer
        self.device = device
    
    def evaluate_classification(self, dataloader, class_names=None):
        """Оценка классификационной модели"""
        self.model.eval()
        all_predictions = []
        all_labels = []
        
        with torch.no_grad():
            for batch in dataloader:
                input_ids = batch['input_ids'].to(self.device)
                attention_mask = batch['attention_mask'].to(self.device)
                labels = batch['labels'].to(self.device)
                
                outputs = self.model(input_ids=input_ids, attention_mask=attention_mask)
                predictions = torch.argmax(outputs.logits, dim=1)
                
                all_predictions.extend(predictions.cpu().numpy())
                all_labels.extend(labels.cpu().numpy())
        
        # Метрики
        print("Classification Report:")
        print(classification_report(all_labels, all_predictions, 
                                  target_names=class_names))
        
        # Матрица ошибок
        cm = confusion_matrix(all_labels, all_predictions)
        plt.figure(figsize=(10, 8))
        sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
        plt.xlabel('Predicted')
        plt.ylabel('Actual')
        plt.title('Confusion Matrix')
        plt.show()
        
        return {
            'predictions': all_predictions,
            'labels': all_labels,
            'confusion_matrix': cm
        }
    
    def generate_text(self, prompt, max_length=100, temperature=0.7, top_p=0.9):
        """Генерация текста"""
        self.model.eval()
        
        input_ids = self.tokenizer.encode(prompt, return_tensors='pt').to(self.device)
        
        with torch.no_grad():
            output = self.model.generate(
                input_ids=input_ids,
                max_length=max_length,
                temperature=temperature,
                top_p=top_p,
                do_sample=True,
                pad_token_id=self.tokenizer.eos_token_id
            )
        
        generated_text = self.tokenizer.decode(output[0], skip_special_tokens=True)
        return generated_text[len(prompt):].strip()  # Удаление промпта
    
    def calculate_perplexity(self, dataloader):
        """Расчет перплексии"""
        self.model.eval()
        total_loss = 0
        total_tokens = 0
        
        with torch.no_grad():
            for batch in dataloader:
                input_ids = batch['input_ids'].to(self.device)
                attention_mask = batch['attention_mask'].to(self.device)
                labels = batch['labels'].to(self.device)
                
                outputs = self.model(
                    input_ids=input_ids,
                    attention_mask=attention_mask,
                    labels=labels
                )
                
                loss = outputs.loss
                mask = attention_mask.view(-1)
                loss = loss * mask
                total_loss += loss.sum().item()
                total_tokens += mask.sum().item()
        
        avg_loss = total_loss / total_tokens
        perplexity = torch.exp(torch.tensor(avg_loss))
        
        return perplexity.item()

5.2 Развертывание модели

import torch
import numpy as np
from fastapi import FastAPI
from pydantic import BaseModel
import uvicorn

class ModelInput(BaseModel):
    text: str
    max_length: int = 100
    temperature: float = 0.7
    top_p: float = 0.9

class LLMService:
    def __init__(self, model, tokenizer, device='cuda'):
        self.model = model.to(device)
        self.tokenizer = tokenizer
        self.device = device
        self.model.eval()
        
        # Оптимизация для инференса
        self.model.half() if device == 'cuda' else None
    
    def predict(self, text: str, max_length: int = 100, 
               temperature: float = 0.7, top_p: float = 0.9):
        """Предсказание"""
        input_ids = self.tokenizer.encode(text, return_tensors='pt').to(self.device)
        
        with torch.no_grad():
            output = self.model.generate(
                input_ids=input_ids,
                max_length=max_length,
                temperature=temperature,
                top_p=top_p,
                do_sample=True,
                pad_token_id=self.tokenizer.eos_token_id
            )
        
        generated_text = self.tokenizer.decode(output[0], skip_special_tokens=True)
        return generated_text[len(text):].strip()
    
    def batch_predict(self, texts: list, **kwargs):
        """Пакетное предсказание"""
        results = []
        for text in texts:
            result = self.predict(text, **kwargs)
            results.append(result)
        return results

# Пример FastAPI приложения
app = FastAPI()

# Инициализация сервиса
llm_service = LLMService(model, tokenizer, device='cuda')

@app.post("/predict")
async def predict(input_data: ModelInput):
    result = llm_service.predict(
        text=input_data.text,
        max_length=input_data.max_length,
        temperature=input_data.temperature,
        top_p=input_data.top_p
    )
    return {"generated_text": result}

@app.post("/batch_predict")
async def batch_predict(texts: list[str], max_length: int = 100, 
                       temperature: float = 0.7, top_p: float = 0.9):
    results = llm_service.batch_predict(
        texts=texts,
        max_length=max_length,
        temperature=temperature,
        top_p=top_p
    )
    return {"results": results}

@app.get("/health")
async def health_check():
    return {"status": "healthy", "device": llm_service.device}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

6. Лучшие практики и рекомендации

6.1 Организация кода

import os
import json
import logging
from datetime import datetime
from pathlib import Path

class LLMTrainingPipeline:
    def __init__(self, config_path="config.json"):
        self.config = self.load_config(config_path)
        self.setup_logging()
        self.setup_directories()
        
    def load_config(self, config_path):
        """Загрузка конфигурации"""
        with open(config_path, 'r') as f:
            config = json.load(f)
        return config
    
    def setup_logging(self):
        """Настройка логирования"""
        log_dir = Path(self.config['logging']['log_dir'])
        log_dir.mkdir(exist_ok=True)
        
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        log_file = log_dir / f"training_{timestamp}.log"
        
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler(log_file),
                logging.StreamHandler()
            ]
        )
        
        self.logger = logging.getLogger(__name__)
    
    def setup_directories(self):
        """Создание необходимых директорий"""
        dirs = ['checkpoints', 'logs', 'eval', 'data']
        for dir_name in dirs:
            Path(self.config['paths'][dir_name]).mkdir(exist_ok=True)
    
    def save_checkpoint(self, model, optimizer, epoch, loss, 
                        checkpoint_dir="checkpoints"):
        """Сохранение чекпоинта"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        checkpoint_path = Path(checkpoint_dir) / f"checkpoint_epoch_{epoch}_{timestamp}"
        checkpoint_path.mkdir(exist_ok=True)
        
        torch.save({
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'optimizer_state_dict': optimizer.state_dict(),
            'loss': loss,
        }, checkpoint_path / "model.pt")
        
        self.logger.info(f"Checkpoint saved to {checkpoint_path}")
    
    def load_checkpoint(self, checkpoint_path):
        """Загрузка чекпоинта"""
        checkpoint = torch.load(checkpoint_path)
        return checkpoint
    
    def create_experiment_config(self, experiment_name):
        """Создание конфигурации эксперимента"""
        experiment_config = {
            'experiment_name': experiment_name,
            'timestamp': datetime.now().isoformat(),
            'config': self.config,
            'git_commit': os.popen('git rev-parse HEAD').read().strip(),
            'system_info': check_system_resources()
        }
        
        config_path = Path('experiments') / experiment_name / 'config.json'
        config_path.parent.mkdir(parents=True, exist_ok=True)
        
        with open(config_path, 'w') as f:
            json.dump(experiment_config, f, indent=2)
        
        return config_path

6.2 Мониторинг и эксперименты

import wandb
import matplotlib.pyplot as plt
from typing import Dict, List

class ExperimentTracker:
    def __init__(self, project_name="llm-training", experiment_name=None):
        wandb.init(
            project=project_name,
            name=experiment_name,
            config=self.get_experiment_config()
        )
        
        self.metrics_history = {
            'train_loss': [],
            'val_loss': [],
            'learning_rate': [],
            'perplexity': []
        }
    
    def get_experiment_config(self):
        """Получение конфигурации эксперимента"""
        return {
            'model': 'transformer',
            'dataset': 'custom',
            'batch_size': 32,
            'learning_rate': 1e-4,
            'epochs': 10
        }
    
    def log_metrics(self, metrics: Dict[str, float], step: int):
        """Логирование метрик"""
        for key, value in metrics.items():
            self.metrics_history[key].append(value)
            
        wandb.log(metrics, step=step)
    
    def log_model_artifact(self, model_path, model_name):
        """Логирование артефакта модели"""
        artifact = wandb.Artifact(model_name, type='model')
        artifact.add_dir(model_path)
        wandb.log_artifact(artifact)
    
    def log_confusion_matrix(self, y_true, y_pred, class_names):
        """Логирование матрицы ошибок"""
        cm = confusion_matrix(y_true, y_pred)
        fig, ax = plt.subplots(figsize=(10, 8))
        sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
                   xticklabels=class_names, yticklabels=class_names)
        plt.xlabel('Predicted')
        plt.ylabel('Actual')
        plt.title('Confusion Matrix')
        
        wandb.log({"confusion_matrix": wandb.Image(fig)})
        plt.close(fig)
    
    def log_learning_curves(self):
        """Логирование кривых обучения"""
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
        
        # Loss curve
        ax1.plot(self.metrics_history['train_loss'], label='Train Loss')
        if self.metrics_history['val_loss']:
            ax1.plot(self.metrics_history['val_loss'], label='Val Loss')
        ax1.set_xlabel('Epoch')
        ax1.set_ylabel('Loss')
        ax1.set_title('Learning Curves - Loss')
        ax1.legend()
        ax1.grid(True)
        
        # Learning rate
        ax2.plot(self.metrics_history['learning_rate'])
        ax2.set_xlabel('Epoch')
        ax2.set_ylabel('Learning Rate')
        ax2.set_title('Learning Rate Schedule')
        ax2.grid(True)
        
        wandb.log({"learning_curves": wandb.Image(fig)})
        plt.close(fig)
    
    def finish_experiment(self):
        """Завершение эксперимента"""
        self.log_learning_curves()
        wandb.finish()

Заключение

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

  1. Подготовку данных — сбор, очистку и токенизацию огромных объемов текста
  2. Архитектуру модели — выбор и реализацию трансформерных архитектур
  3. Обучение — pre-training и fine-tuning с использованием современных оптимизаторов
  4. Оптимизацию — снижение вычислительных требований и памяти
  5. Оценку — валидация качества и развертывание модели

Ключевые факторы успеха:

  • Качество и разнообразие данных
  • Правильная архитектура и гиперпараметры
  • Эффективное использование вычислительных ресурсов
  • Системный мониторинг и эксперименты
  • Постоянное улучшение и обновление модели

Этот подход позволяет создавать мощные LLM, способные решать широкий спектр задач в области обработки естественного языка.