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

Что такое seq2seq архитектура?

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

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

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

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

Seq2Seq архитектура: от входа к выходу

Seq2Seq (Sequence-to-Sequence) — это архитектура нейронной сети, предназначенная для преобразования одной последовательности в другую. Она состоит из двух основных компонентов: энкодера и декодера, соединённых через вектор контекста.

Основные компоненты

1. Энкодер (Encoder)

Обрабатывает входную последовательность и преобразует её в вектор контекста (контекстный вектор):

import torch
import torch.nn as nn

# Энкодер на основе LSTM
class Encoder(nn.Module):
    def __init__(self, input_dim, embedding_dim, hidden_dim):
        super().__init__()
        self.embedding = nn.Embedding(input_dim, embedding_dim)
        self.lstm = nn.LSTM(
            embedding_dim, 
            hidden_dim, 
            num_layers=2,
            bidirectional=True,
            batch_first=True
        )
    
    def forward(self, src):
        # src shape: (batch_size, seq_len)
        embedded = self.embedding(src)  # (batch_size, seq_len, embedding_dim)
        outputs, (hidden, cell) = self.lstm(embedded)
        # hidden shape: (num_layers*2, batch_size, hidden_dim)
        return hidden, cell, outputs

Энкодер последовательно обрабатывает входные токены и кодирует информацию в скрытых состояниях.

2. Вектор контекста

Последнее скрытое состояние энкодера или его комбинация — это вектор контекста, содержащий всю информацию о входной последовательности:

# После прохода энкодера получаем контекстный вектор
# shape: (num_layers*2, batch_size, hidden_dim)
context = hidden[-2:] if bidirectional else hidden[-1:]

3. Декодер (Decoder)

Принимает вектор контекста и генерирует выходную последовательность токен за токеном:

class Decoder(nn.Module):
    def __init__(self, output_dim, embedding_dim, hidden_dim):
        super().__init__()
        self.embedding = nn.Embedding(output_dim, embedding_dim)
        self.lstm = nn.LSTM(
            embedding_dim, 
            hidden_dim,
            num_layers=2,
            batch_first=True
        )
        self.fc = nn.Linear(hidden_dim, output_dim)
    
    def forward(self, trg, hidden, cell):
        # trg shape: (batch_size, 1) - один токен
        embedded = self.embedding(trg)  # (batch_size, 1, embedding_dim)
        output, (hidden, cell) = self.lstm(embedded, (hidden, cell))
        # output shape: (batch_size, 1, hidden_dim)
        logits = self.fc(output.squeeze(1))  # (batch_size, output_dim)
        return logits, hidden, cell

Полная архитектура Seq2Seq

class Seq2Seq(nn.Module):
    def __init__(self, encoder, decoder):
        super().__init__()
        self.encoder = encoder
        self.decoder = decoder
    
    def forward(self, src, trg, teacher_forcing_ratio=0.5):
        # src shape: (batch_size, src_len)
        # trg shape: (batch_size, trg_len)
        
        batch_size = trg.shape[0]
        trg_len = trg.shape[1]
        
        # Энкодим входную последовательность
        hidden, cell, _ = self.encoder(src)
        # Убеждаемся, что hidden имеет правильный размер
        hidden = hidden[-2:] if self.encoder.lstm.bidirectional else hidden
        
        # Декодим токен за токеном
        decoder_input = trg[:, 0].unsqueeze(1)  # <START> токен
        outputs = []
        
        for t in range(1, trg_len):
            logits, hidden, cell = self.decoder(decoder_input, hidden, cell)
            outputs.append(logits)
            
            # Teacher forcing: используем настоящий токен с вероятностью
            if torch.rand(1).item() < teacher_forcing_ratio:
                decoder_input = trg[:, t].unsqueeze(1)
            else:
                decoder_input = logits.argmax(dim=1).unsqueeze(1)
        
        outputs = torch.stack(outputs, dim=1)
        return outputs

Процесс обучения vs тестирования

Обучение (Teacher Forcing):

  • Декодер получает настоящие токены из целевой последовательности
  • Ускоряет обучение, но создаёт несоответствие с тестированием

Тестирование (Inference):

  • Декодер использует свои собственные предсказания как входные данные
  • Может привести к накоплению ошибок

Применение

Seq2Seq использовается в:

  1. Машинный перевод (Machine Translation)

    # Переводит английское предложение в французское
    src = tokenize("The cat sits on the mat")
    trg = model(src)  # "Le chat est assis sur le tapis"
    
  2. Диалоговые системы (Chatbots)

    • Входная последовательность: вопрос пользователя
    • Выходная последовательность: ответ бота
  3. Summarization (Суммаризация)

    • Входная последовательность: длинный документ
    • Выходная последовательность: краткое резюме
  4. Question Answering

    • Входная последовательность: вопрос
    • Выходная последовательность: ответ
  5. Code Generation

    • Входная последовательность: описание функции
    • Выходная последовательность: код

Проблемы базовой архитектуры

  1. Узкое горлышко контекста — один вектор содержит всю информацию

    • Решение: Attention Mechanism
  2. Ванилирующие/взрывающиеся градиенты при обучении на длинных последовательностях

    • Решение: gradient clipping, GRU/LSTM вместо RNN
  3. Несоответствие обучения и тестирования (teacher forcing)

    • Решение: Scheduled Sampling

Современное развитие: Attention и Transformers

# Современная архитектура использует Attention вместо простого контекстного вектора
# Decoder может "смотреть" на разные части входной последовательности

class AttentionDecoder(nn.Module):
    def __init__(self, output_dim, embedding_dim, hidden_dim):
        super().__init__()
        self.embedding = nn.Embedding(output_dim, embedding_dim)
        self.lstm = nn.LSTM(embedding_dim, hidden_dim, batch_first=True)
        self.attention = nn.MultiheadAttention(
            hidden_dim, 
            num_heads=8,
            batch_first=True
        )
        self.fc = nn.Linear(hidden_dim, output_dim)
    
    def forward(self, trg, hidden, cell, encoder_outputs):
        embedded = self.embedding(trg)
        lstm_out, (hidden, cell) = self.lstm(embedded, (hidden, cell))
        
        # Attention над encoder outputs
        attn_out, _ = self.attention(
            lstm_out, encoder_outputs, encoder_outputs
        )
        
        logits = self.fc(attn_out)
        return logits, hidden, cell

Выводы

Seq2Seq революционизировала обработку естественного языка и стала основой для современных трансформеров. Хотя Pure Seq2Seq с LSTM сейчас заменяется Attention-based механизмами и Transformers, понимание её принципов критично для освоения современных NLP архитектур.