
Доброго времени суток, «Хабр»!
В предыдущей части мы рассмотрели историю языковых моделей от робких шагов Маркова до долгой краткосрочной памяти. Сегодня мы продолжим, пройдемся по ключевым архитектурам последних лет и разберём, как модели научились интерпретировать контекст, предсказывать и даже спорить логически.
Пристегните токены — вход в зону трансформаций!
История языковых моделей: ч. 1, от цепей Маркова до ELIZA
История языковых моделей: ч. 2, от ChatGPT до рассуждающего режима ← вы находитесь тут.
2000-е годы: статистические машинные переводчики
Важным событием в развитии языковых моделей в 2000-х годах стало появление статистических машинных переводчиков, таких как Google Translate. Они начали широко применять методы статистического анализа для автоматического преобразования текстов между различными языками. Данный технологический прорыв открыл новые возможности для общения и взаимодействия людей из разных стран и культур, тем самым подчеркнув значимость NLP-технологий в глобальном контексте.
Первые версии этих переводчиков анализировали миллионы параллельных текстов, чтобы находить статистические закономерности между языками.
2013 год: word2vec
Word2vec - общее название для совокупности моделей на основе искусственных нейронных сетей, предназначенных для получения векторных представлений слов на естественном языке. Модели используют анализ семантики (раздел лингвистики, изучающий смысловые значения единиц языка) естественных языков, основанный на дистрибутивной семантике, машинном обучении и векторном представлении слов.

Программное обеспечение под названием word2vec было разработано группой исследователей Google в 2013 году. Инструменты для векторно-семантических моделей были и ранее, однако word2vec стал первой популярной реализацией: в первую очередь из-за удобства использования, открытого исходного кода и скорости работы.
Алгоритм word2vec принимает большой текстовый корпус в качестве входных данных, строит из него словарь и далее из корпуса и словаря строит сопоставление. Каждому слову из словаря сопоставляется вектор чисел с плавающей точкой - векторное представление слов, на выход алгоритм предоставляет нужное сопоставление.
Ниже приведен пример реализации word2vec на Python:
import torch
import torch.nn as nn
import torch.nn.functional as F
class SkipGramWithNegSampling(nn.Module):
def __init__(self, vocab_size, embed_size, num_neg_samples=5):
super().__init__()
self.target_embeddings = nn.Embedding(vocab_size, embed_size) # Эмбеддинги для целевых слов
self.context_embeddings = nn.Embedding(vocab_size, embed_size) # Эмбеддинги для контекстных слов
# Инициализация весов (стандартная практика для Word2Vec)
nn.init.xavier_uniform_(self.target_embeddings.weight)
nn.init.xavier_uniform_(self.context_embeddings.weight)
self.num_neg_samples = num_neg_samples # Количество негативных примеров на 1 позитивный
def forward(self, target_words, context_words):
"""
target_words: индексы целевых слов (форма [batch_size])
context_words: индексы контекстных слов (форма [batch_size])
"""
# Получаем эмбеддинги
target_emb = self.target_embeddings(target_words) # [batch_size, embed_size]
context_emb = self.context_embeddings(context_words) # [batch_size, embed_size]
# Вычисляем score для позитивных пар
positive_scores = torch.sum(target_emb * context_emb, dim=1) # [batch_size]
# Генерируем негативные примеры (упрощённый вариант)
batch_size = target_words.size(0)
negative_words = torch.randint(0, len(self.target_embeddings.weight),
(batch_size, self.num_neg_samples)) # [batch_size, num_neg]
negative_emb = self.context_embeddings(negative_words) # [batch_size, num_neg, embed_size]
# Вычисляем score для негативных пар
negative_scores = torch.bmm(negative_emb, target_emb.unsqueeze(2)).squeeze() # [batch_size, num_neg]
return positive_scores, negative_scores
# Гиперпараметры (типичные значения для примера)
VOCAB_SIZE = 10000 # Размер словаря
EMBED_SIZE = 300 # Размерность эмбеддингов
BATCH_SIZE = 1024 # Размер батча
NUM_NEG = 5 # Негативных примеров на 1 позитивный
# Инициализация модели
model = SkipGramWithNegSampling(VOCAB_SIZE, EMBED_SIZE, NUM_NEG)
# Пример данных (обычно генерируются через скользящее окно по тексту)
# Допустим, у нас есть батч пар (целевое_слово, контекстное_слово)
target_indices = torch.randint(0, VOCAB_SIZE, (BATCH_SIZE,)) # Случайные индексы
context_indices = torch.randint(0, VOCAB_SIZE, (BATCH_SIZE,)) # В реальности - соседние слова
# Прямой проход
pos_scores, neg_scores = model(target_indices, context_indices)
# Вычисление потерь (Max-margin loss)
positive_loss = F.logsigmoid(pos_scores).mean()
negative_loss = F.logsigmoid(-neg_scores).mean()
loss = -(positive_loss + negative_loss) # Усреднение уже выполнено в negative_loss
# Обучение (один шаг)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
optimizer.zero_grad()
loss.backward()
optimizer.step()
2017 год: трансформер
Трансформер - архитектура глубоких нейронных сетей, представленная в 2017 году исследователями из Google Brain.

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

Декодер включает три основные компонента: механизм самовнимания, который обрабатывает выход предыдущего слоя; механизм внимания к выходу кодировщика, принимающий на вход как результат самовнимания, так и выход кодера; и полносвязную нейронную сеть, получающую на вход результат работы второго механизма.

В трансформерах используется многоголовое внимание (multi-head attention, MHA), где внимание рассчитывается параллельно несколько раз с разными параметрами. То есть, например, вместо того, чтобы один раз определить, какие слова важны для текущего предложения, модель делает это несколько раз параллельно, с разных точек зрения.
Трансформеры, как и другие архитектуры нейросетей, могут быть реализованы на Python. Ниже пример того, как это осуществить:
import torch
import torch.nn as nn
import torch.nn.functional as F
class TransformerEncoderLayer(nn.Module):
"""
Один слой энкодера трансформера (Post-LN архитектура из оригинальной статьи).
Аргументы:
d_model (int): Размерность эмбеддингов (например, 512)
nhead (int): Количество голов внимания (например, 8)
dim_feedforward (int): Внутренняя размерность FFN (обычно 4*d_model)
dropout (float): Вероятность дропаута (по умолчанию 0.1)
"""
def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1):
super().__init__()
# 1. Слой многоголового внимания
self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
# 2. Двухслойная FFN сеть
self.linear1 = nn.Linear(d_model, dim_feedforward)
self.linear2 = nn.Linear(dim_feedforward, d_model)
# 3. Нормализация и дропауты
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
# 4. Активация для FFN (в оригинале ReLU)
self.activation = F.relu
def forward(self, src, src_mask=None, src_key_padding_mask=None):
"""
Вход:
src: (S, B, D) - входная последовательность (длина, батч, размерность)
src_mask: (S, S) - маска внимания (опционально)
src_key_padding_mask: (B, S) - маска батча (опционально)
"""
# Шаг 1: Самовнимание с residual connection
attn_output, _ = self.self_attn(
src, src, src,
attn_mask=src_mask,
key_padding_mask=src_key_padding_mask
)
src = src + self.dropout1(attn_output)
src = self.norm1(src) # Post-LN: нормализация ПОСЛЕ residual
# Шаг 2: FFN с residual connection
ff_output = self.linear1(src)
ff_output = self.activation(ff_output)
ff_output = self.dropout(ff_output)
ff_output = self.linear2(ff_output)
src = src + self.dropout2(ff_output)
src = self.norm2(src)
return src
# Пример использования (без вывода данных)
if __name__ == "__main__":
# Параметры модели
d_model = 512 # Размерность эмбеддингов
nhead = 8 # 8 голов внимания
seq_len = 100 # Длина последовательности
batch_size = 32 # Размер батча
# Создаём слой энкодера
encoder_layer = TransformerEncoderLayer(d_model, nhead)
# Генерируем тестовые данные:
# (S, B, D) = (длина_последовательности, батч, размерность)
test_input = torch.rand(seq_len, batch_size, d_model)
# Вызов слоя (без масок)
output = encoder_layer(test_input)
# Типичный пайплайн:
# 1. Добавить позиционные энкодинги к test_input
# 2. Пропустить через несколько слоёв энкодера
# 3. Обработать выход модели
Токенизация
Токенизация - процесс разбиения текста на минимальные значимые единицы, например на слова, части слова или символы.

В зависимости от размеров и характера языковой модели, в качестве токенов могут выступать: слова, подслова, знаки препинания, специальные отметки(начало и конец предложения, маркеры заполнения и маркеры для неизвестных слов).
Токенизация позволяет сократить объем данных и упростить их обработку.
Модели могут иметь словари токенов, в которых отдельные слова или сочетания букв соответствуют определенным числам. Одна и та же фраза, в зависимости от используемой модели, может занимать различное количество токенов — всё из-за отличий в способе токенизации.
2018 год: BERT
В октябре 2018 года исследователями Google была представлена языковая модель BERT - алгоритм машинного обучения, предназначенный для обработки естественного языка.

Задача BERT - помочь компьютерам понимать смысл выражений, используя контекст.
Одна из ключевых особенностей BERT - двунаправленность. Слова могут продолжать анализироваться всё так же в одном направлении, но при этом контекст теперь учитывается не только расположенный слева (от текущего слова слева, до начала текста), но и расположенный справа (от текущего слова до конца текста).
Ниже приведен пример, как реализовать архитектуру на Python:
import torch
import torch.nn as nn
class BERT(nn.Module):
"""
Упрощённая реализация архитектуры BERT для образовательных целей.
Основные характеристики:
- Трансформеры с преднормализацией (отличие от стандартного PyTorch Transformer)
- Совместные веса эмбеддингов и декодера
- Поддержка двух задач: Masked Language Model (MLM) и Next Sentence Prediction (NSP)
Args:
vocab_size (int): Размер словаря
d_model (int): Размерность скрытого слоя (по умолчанию 768)
nhead (int): Количество голов внимания (по умолчанию 12)
dim_feedforward (int): Размерность FFN слоя (по умолчанию 3072)
n_layers (int): Количество слоёв трансформера (по умолчанию 12)
"""
def __init__(self, vocab_size, d_model=768, nhead=12, dim_feedforward=3072, n_layers=12):
super().__init__()
# Эмбеддинги (токены + сегменты + позиционные)
self.embedding = BERTEmbedding(vocab_size, d_model)
# Слои трансформера с преднормализацией
self.encoder_layers = nn.ModuleList([
PreNormTransformerLayer(d_model, nhead, dim_feedforward)
for _ in range(n_layers)
])
# Классификатор для задачи NSP (Next Sentence Prediction)
self.nsp_classifier = nn.Sequential(
nn.Linear(d_model, d_model),
nn.Tanh(),
nn.Linear(d_model, 2)
)
# Декодер для задачи MLM с совместными весами
self.mlm_decoder = nn.Linear(d_model, vocab_size)
self.mlm_decoder.weight = self.embedding.token_embed.weight # Weight tying
self.mlm_bias = nn.Parameter(torch.zeros(vocab_size))
def forward(self, input_ids, segment_ids, attention_mask=None):
"""
Args:
input_ids (Tensor): Индексы токенов [batch_size, seq_len]
segment_ids (Tensor): Индексы сегментов [batch_size, seq_len]
attention_mask (Tensor): Маска внимания [batch_size, seq_len]
Returns:
tuple: (логиты MLM, логиты NSP) или только NSP логиты
"""
# Эмбеддинги и кодирование
x = self.embedding(input_ids, segment_ids)
for layer in self.encoder_layers:
x = layer(x, attention_mask)
# Вычисление логитов для обеих задач
nsp_logits = self.nsp_classifier(x[:, 0]) # Используем [CLS] токен
# Для MLM возвращаем логиты для всех позиций
mlm_logits = self.mlm_decoder(x) + self.mlm_bias
return mlm_logits, nsp_logits
class PreNormTransformerLayer(nn.Module):
"""Кастомный слой трансформера с преднормализацией (как в оригинальном BERT)"""
def __init__(self, d_model, nhead, dim_feedforward):
super().__init__()
self.norm1 = nn.LayerNorm(d_model)
self.self_attn = nn.MultiheadAttention(d_model, nhead)
self.norm2 = nn.LayerNorm(d_model)
self.ffn = nn.Sequential(
nn.Linear(d_model, dim_feedforward),
nn.GELU(),
nn.Linear(dim_feedforward, d_model)
)
def forward(self, src, mask=None):
# Self-attention с преднормализацией
src = self.norm1(src)
src = src + self.self_attn(src, src, src, key_padding_mask=mask)[0]
# FFN с преднормализацией
src = self.norm2(src)
src = src + self.ffn(src)
return src
# Пример вызова модели
if __name__ == "__main__":
# Параметры аналогичные BERT-base
bert = BERT(vocab_size=30000)
# Пример входных данных (batch_size=2, seq_len=512)
input_ids = torch.randint(0, 30000, (2, 512))
segment_ids = torch.cat([torch.zeros(2, 256), torch.ones(2, 256)], dim=1)
attention_mask = torch.ones(2, 512) # Реальная маска обычно содержит 0/1
# Прямой проход
mlm_logits, nsp_logits = bert(input_ids, segment_ids, attention_mask)
# На практике:
# - MLM логиты используются для предсказания маскированных токенов
# - NSP логиты для классификации отношений между предложениями
2018–2025 годы: ChatGPT
В 2018 году мир впервые познакомился с GPT-1 — скромной по современным меркам, но революционной для своего времени языковой моделью от OpenAI. Основанная на архитектуре трансформеров, она содержала 117 миллионов параметров и могла генерировать относительно связные тексты, хотя её однонаправленность (анализ только слева направо) и ограниченная память в 512 токенов существенно снижали качество понимания контекста. Модель требовала дополнительного обучения для каждой конкретной задачи и демонстрировала слабую гибкость в диалогах.
Всего год спустя OpenAI представила GPT-2, совершив качественный скачок в развитии технологии. Новая модель с 1,5 миллиарда параметров, впервые продемонстрировала способность к zero-shot learning — выполнению задач без предварительного обучения на примерах. Увеличенный до 1024 токенов контекст позволил GPT-2 создавать более длинные и связные тексты, включая новостные заметки и литературные произведения. Однако сохранились проблемы с фактической точностью и логической последовательностью.
Настоящий прорыв произошёл в 2020 году с появлением GPT-3. Её 175 миллиардов параметров открыли ранее недоступные возможности, такие как few-shot learning (обучение на 1–3 примерах) и генерация программного кода. Это привело к созданию ChatGPT — первого по-настоящему популярного диалогового ИИ. Несмотря на прогресс, модель продолжала галлюцинировать и испытывать трудности с построением сложных логических цепочек.
Современный этап развития представлен GPT-4 (2023 год), которая не только улучшила текстовую генерацию, но и научилась работать с визуальной информацией. Снижение количества ошибок на 40%, способность анализировать графики и схемы, более проработанные ответы — всё из перечисленного стало возможным благодаря усовершенствованному механизму внимания и использованию обучения с подкреплением.
Последним словом технологии стали рассуждающие модели (large reasoning models, LRM), начавшие активно появляться во второй половине 2024 года. Эти системы, среди которых — линейка o1–o4 от OpenAI, DeepSeek R1, Claude 3.7 Sonnet Thinking и Grok 3, демонстрируют уникальные способности к дедуктивным рассуждениям. Они используют внешнюю память и архитектурные решения, позволяющие строить длинные цепочки логических выводов — вплоть до сложных сценариев гипотетического мышления. Одним из характерных примеров является анализ условных утверждений с выявлением внутренних логических противоречий.
Ознакомиться с языковыми моделями можно на сайтах разработчиков или на агрегаторах нейросетей, например на BotHub. Регистрируйся по специальной ссылке чтобы получить 100 000 токенов для доступа к любым моделям (работает без ВПН).

Приведу пример того, как реализовать архитектуру GPT на Python:
import torch
import torch.nn as nn
class GPTBlock(nn.Module):
"""Один трансформер-блок GPT (декодер) с механизмом самовнимания"""
def __init__(self, d_model, nhead, dim_feedforward):
super().__init__()
# Нормализация перед вниманием (Pre-LN) улучшает стабильность обучения
self.ln1 = nn.LayerNorm(d_model)
# Механизм многоголового внимания
self.attn = nn.MultiheadAttention(d_model, nhead, batch_first=True)
self.ln2 = nn.LayerNorm(d_model)
# MLP с расширением размерности (типично для трансформеров)
self.mlp = nn.Sequential(
nn.Linear(d_model, dim_feedforward),
nn.GELU(), # Активно используется в GPT вместо ReLU
nn.Linear(dim_feedforward, d_model)
)
def forward(self, x, mask=None):
# Остаточное соединение + самовнимание
attn_output, _ = self.attn(
self.ln1(x), # Запрос
self.ln1(x), # Ключ
self.ln1(x), # Значение
attn_mask=mask # Маска будущих токенов
)
x = x + attn_output
# Остаточное соединение + MLP
x = x + self.mlp(self.ln2(x))
return x
class GPT(nn.Module):
"""Упрощённая реализация GPT-like-архитектуры"""
def __init__(self, vocab_size, d_model=768, nhead=12,
dim_feedforward=3072, n_layers=12):
super().__init__()
# Эмбеддинги токенов и позиций
self.token_embed = nn.Embedding(vocab_size, d_model)
self.pos_embed = nn.Parameter(torch.zeros(1, 1024, d_model)) # Обучаемые позиционные эмбеддинги
# Стек трансформер-блоков
self.blocks = nn.ModuleList([
GPTBlock(d_model, nhead, dim_feedforward)
for _ in range(n_layers)
])
# Финальные слои
self.ln_f = nn.LayerNorm(d_model)
self.head = nn.Linear(d_model, vocab_size) # Логиты для предсказания токена
def forward(self, x):
# x: (batch_size, sequence_length)
batch_size, seq_len = x.size()
# Собираем эмбеддинги
tok_emb = self.token_embed(x) # (B, T, D)
pos_emb = self.pos_embed[:, :seq_len, :] # Обрезаем позиционные эмбеддинги по длине последовательности
x = tok_emb + pos_emb
# Маска верхнего треугольника (предотвращает looking into the future)
mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1).bool().to(x.device)
# Проход через все блоки
for block in self.blocks:
x = block(x, mask)
# Финальное преобразование
x = self.ln_f(x)
return self.head(x) # (B, T, vocab_size)
# Пример использования
if __name__ == "__main__":
# Параметры как в GPT-2 Small (для примера)
vocab_size = 50257 # Размер словаря GPT-2
model = GPT(
vocab_size=vocab_size,
d_model=768,
nhead=12,
dim_feedforward=3072,
n_layers=12
)
# Пример входных данных: батч из 2 примеров по 16 токенов
input_ids = torch.randint(0, vocab_size, (2, 16))
# Прямой проход (типичное использование)
logits = model(input_ids) # Получаем логиты для следующего токена
# В реальности здесь бы добавили:
# 1) Выборку следующего токена (например, top-k sampling)
# 2) Итеративную генерацию (autoregressive loop)
# 3) Применение softmax к логитам
2020 год: Т5
T5 (Text-to-Text Transfer Transformer) - языковая модель, разработанная исследователями Google в 2020 году. T5 является дальнейшим развитием модели BERT и предназначена для решения широкого спектра задач обработки естественного языка.

Подобно GPT, архитектура T5 предлагает единый подход к решению разнообразных задач обработки естественного языка, сводя их к универсальному формату «текст на входе — текст на выходе». Такой метод позволяет модели одинаково эффективно справляться с переводом, суммаризацией, генерацией ответов на вопросы и другими NLP-задачами в рамках одного общего фреймворка.
Т5 способна работать с текстом в обоих направлениях, а также использует механизм самовнимания, который позволяет ей фокусироваться на наиболее важных частях текста и игнорировать менее важные.
Рассмотрим пример реализации T5:
import torch
import torch.nn as nn
class PositionalEncoding(nn.Module):
"""Упрощенная версия позиционных кодирований (в оригинале T5 использует относительные позиции)"""
def __init__(self, d_model, max_len=512):
super().__init__()
self.pe = nn.Embedding(max_len, d_model)
def forward(self, x):
positions = torch.arange(x.size(1), device=x.device).unsqueeze(0)
return x + self.pe(positions)
class T5EncoderBlock(nn.Module):
"""Блок энкодера T5 (отличается от декодера отсутствием cross-attention)"""
def __init__(self, d_model, nhead, dim_feedforward):
super().__init__()
self.self_attn = nn.MultiheadAttention(d_model, nhead)
self.ffn = nn.Sequential(
nn.Linear(d_model, dim_feedforward),
nn.ReLU(),
nn.Linear(dim_feedforward, d_model)
)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
def forward(self, x, mask=None):
# Self-attention с residual connection
attn_output = self.self_attn(x, x, x, attn_mask=mask)[0]
x = self.norm1(x + attn_output)
# Feed Forward Network
ffn_output = self.ffn(x)
return self.norm2(x + ffn_output)
class T5DecoderBlock(nn.Module):
"""Блок декодера T5 с self-attention, cross-attention и FFN"""
def __init__(self, d_model, nhead, dim_feedforward):
super().__init__()
self.self_attn = nn.MultiheadAttention(d_model, nhead)
self.cross_attn = nn.MultiheadAttention(d_model, nhead)
self.ffn = nn.Sequential(
nn.Linear(d_model, dim_feedforward),
nn.ReLU(),
nn.Linear(dim_feedforward, d_model)
)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
def forward(self, x, encoder_output, src_mask=None, tgt_mask=None):
# Self-attention (маска tgt_mask предотвращает утечку будущего)
attn_output = self.self_attn(x, x, x, attn_mask=tgt_mask)[0]
x = self.norm1(x + attn_output)
# Cross-attention с выходом энкодера
cross_output = self.cross_attn(x, encoder_output, encoder_output, attn_mask=src_mask)[0]
x = self.norm2(x + cross_output)
# Feed Forward Network
ffn_output = self.ffn(x)
return self.norm3(x + ffn_output)
class T5(nn.Module):
"""Упрощенная реализация T5 с ключевыми компонентами:
- Общие веса эмбеддингов и выходного слоя
- Отдельные блоки энкодера и декодера
- Позиционные кодирования
"""
def __init__(self, vocab_size, d_model=512, nhead=8, dim_feedforward=2048,
num_encoder_layers=6, num_decoder_layers=6):
super().__init__()
# Общие эмбеддинги для энкодера/декодера
self.token_embed = nn.Embedding(vocab_size, d_model)
self.pos_encoder = PositionalEncoding(d_model)
# Слои энкодера и декодера
self.encoder_layers = nn.ModuleList([
T5EncoderBlock(d_model, nhead, dim_feedforward)
for _ in range(num_encoder_layers)
])
self.decoder_layers = nn.ModuleList([
T5DecoderBlock(d_model, nhead, dim_feedforward)
for _ in range(num_decoder_layers)
])
# Выходной слой с общими весами
self.generator = nn.Linear(d_model, vocab_size)
self.generator.weight = self.token_embed.weight # Weight tying
def encode(self, src, src_mask=None):
x = self.token_embed(src)
x = self.pos_encoder(x)
for layer in self.encoder_layers:
x = layer(x, src_mask)
return x
def decode(self, tgt, memory, tgt_mask=None, src_mask=None):
x = self.token_embed(tgt)
x = self.pos_encoder(x)
for layer in self.decoder_layers:
x = layer(x, memory, src_mask, tgt_mask)
return x
def forward(self, src, tgt, src_mask=None, tgt_mask=None):
memory = self.encode(src, src_mask)
output = self.decode(tgt, memory, tgt_mask, src_mask)
return self.generator(output)
# Пример вызова модели
if __name__ == "__main__":
# Параметры для демонстрации
vocab_size = 32000 # Типичный размер словаря T5
batch_size = 2
seq_len = 32
# Инициализация модели
model = T5(
vocab_size=vocab_size,
d_model=256, # Уменьшено для демонстрации
nhead=4,
dim_feedforward=512,
num_encoder_layers=3,
num_decoder_layers=3
)
# Пример входных данных
src = torch.randint(0, vocab_size, (batch_size, seq_len)) # Входные токены
tgt = torch.randint(0, vocab_size, (batch_size, seq_len)) # Целевые токены
# Прямой проход
outputs = model(src, tgt)
# outputs.shape = [batch_size, seq_len, vocab_size]
Современные методы работы с языковыми моделями
Современные языковые модели достигли невероятных возможностей благодаря арсеналу продвинутых техник обучения и оптимизации.
Одной из ключевых концепций стало предобучение (pre-training), процесс, при котором модель сначала обучается на огромных массивах разнородных текстовых данных, осваивая базовые языковые паттерны и расширяя знания о мире. Этап требует колоссальных вычислительных ресурсов, но создает универсальную основу для последующей адаптации.
После предобучения обычно следует этап дообучения (fine-tuning), где модель настраивается для конкретных задач. Например, медицинский чат-бот сначала получает общие языковые навыки при предобучении, а затем дообучается на специализированных медицинских текстах и диалогах. Особенно эффективны методы обучения с подкреплением (reinforcement learning from human feedback, RLHF), когда модель корректирует свои ответы на основе человеческих предпочтений, — подобное решение и помогло стать ChatGPT столь убедительным в диалогах.
Для практического применения крупных моделей критически важны методы оптимизации. Квантизация позволяет уменьшить размер модели за счет сокращения точности числовых параметров (например, с 32-битных чисел до 8-битных), что значительно ускоряет работу при сохранении приемлемого качества. Дистилляция знаний (knowledge distillation) — еще один изящный подход, когда компактная «студенческая» модель обучается повторять поведение большой «учительской» модели, сохраняя основные знания при гораздо меньших вычислительных затратах.
Отдельный интерес представляют концепции mixture of experts (MoE) и mixture of agents (MoA). В MoE несколько специализированных моделей-экспертов работают параллельно, и специальный механизм решает, какой эксперт лучше справится с конкретной задачей: например, один эксперт может обрабатывать текст, а другой — изображения. В MoA различные агенты взаимодействуют в единой системе, каждый из которых отвечает за свою область, например один агент специализируется на математике, другой на литературном анализе.
Именно благодаря таким подходам современные языковые модели вроде ChatGPT o3, Claude 3.7 Sonnet Thinking и DeepSeek R1 не просто показывают выдающиеся результаты, но и становятся реально применимыми в жизни — от перевода текста и создания контента до автоматизации бизнес-процессов и поддержки клиентов.
Вот и подошла к концу наша история языковых моделей — хотя правильнее будет сказать, что это только начало. С каждым годом технологии развиваются экспоненциально, открывая новые горизонты, которые нам только предстоит увидеть.
Спасибо за прочтение. Надеюсь, вам было интересно.