自学内容网 自学内容网

基于Python的自然语言处理系列(12):使用TorchText和LSTM进行序列到序列(seq2seq)翻译

        在前面的文章中,我们探索了如何使用卷积神经网络(CNN)进行文本分类。这次,我们将深入探讨一种经典的深度学习模型——序列到序列模型(seq2seq),并结合长短期记忆网络(LSTM)来处理序列生成任务。本篇将展示如何使用TorchText加载数据,并构建基于LSTM的seq2seq模型来进行德语到英语的翻译任务。

1. 序列到序列模型简介

        seq2seq模型最常用于解决将一个序列映射到另一个序列的问题,如机器翻译和文本摘要。它的核心思想是通过编码器-解码器的结构,将输入序列压缩为一个上下文向量,并依赖这个向量生成输出序列。这个模型的结构如下:

        在模型中,输入序列首先被嵌入为词向量,然后经过编码器(通常是RNN或者LSTM)来生成上下文向量,接着解码器根据上下文向量逐步生成目标序列。在训练中,解码器的输入可能会使用实际的目标序列(称为Teacher Forcing),也可能使用模型上一步的预测。

2. 数据加载与预处理

        首先,我们将使用TorchText加载德语到英语的翻译数据集,并使用spacy进行文本标记化和词汇构建。

from torchtext.datasets import Multi30k
from torchtext.data.utils import get_tokenizer

SRC_LANGUAGE = 'de'
TRG_LANGUAGE = 'en'

# 加载Multi30k数据集
train = Multi30k(split=('train'), language_pair=(SRC_LANGUAGE, TRG_LANGUAGE))

# 使用spacy进行标记化
token_transform = {}
token_transform[SRC_LANGUAGE] = get_tokenizer('spacy', language='de_core_news_sm')
token_transform[TRG_LANGUAGE] = get_tokenizer('spacy', language='en_core_web_sm')

# 词汇构建
from torchtext.vocab import build_vocab_from_iterator

def yield_tokens(data, language):
    for data_sample in data:
        yield token_transform[language](data_sample[language])

vocab_transform = {}
vocab_transform[SRC_LANGUAGE] = build_vocab_from_iterator(yield_tokens(train, SRC_LANGUAGE), min_freq=2)
vocab_transform[TRG_LANGUAGE] = build_vocab_from_iterator(yield_tokens(train, TRG_LANGUAGE), min_freq=2)

        我们定义了词汇表并准备了数据加载器,接下来我们将进行序列标记的数值化,并创建输入输出的张量。

3. 模型设计:基于LSTM的Encoder和Decoder

Encoder

        编码器负责将输入序列转换为上下文向量。我们将使用多层LSTM来实现,它能够捕捉输入序列中的长短期依赖关系。

import torch.nn as nn

class Encoder(nn.Module):
    def __init__(self, input_dim, emb_dim, hid_dim, n_layers, dropout):
        super().__init__()
        self.embedding = nn.Embedding(input_dim, emb_dim)
        self.lstm = nn.LSTM(emb_dim, hid_dim, n_layers, dropout=dropout)
        self.dropout = nn.Dropout(dropout)

    def forward(self, src):
        embedded = self.dropout(self.embedding(src))
        outputs, (hidden, cell) = self.lstm(embedded)
        return hidden, cell

Decoder

        解码器使用编码器生成的上下文向量,并逐步生成目标序列。解码器的输出是每一步生成的单词。

class Decoder(nn.Module):
    def __init__(self, output_dim, emb_dim, hid_dim, n_layers, dropout):
        super().__init__()
        self.embedding = nn.Embedding(output_dim, emb_dim)
        self.lstm = nn.LSTM(emb_dim, hid_dim, n_layers, dropout=dropout)
        self.fc_out = nn.Linear(hid_dim, output_dim)
        self.dropout = nn.Dropout(dropout)

    def forward(self, input, hidden, cell):
        input = input.unsqueeze(0)
        embedded = self.dropout(self.embedding(input))
        output, (hidden, cell) = self.lstm(embedded, (hidden, cell))
        prediction = self.fc_out(output.squeeze(0))
        return prediction, hidden, cell

Seq2Seq模型

        最后,我们将编码器和解码器整合在一个Seq2Seq模型中,控制输入和输出的流动。

class Seq2Seq(nn.Module):
    def __init__(self, encoder, decoder, device):
        super().__init__()
        self.encoder = encoder
        self.decoder = decoder
        self.device = device

    def forward(self, src, trg, teacher_forcing_ratio=0.5):
        trg_len = trg.shape[0]
        batch_size = trg.shape[1]
        trg_vocab_size = self.decoder.output_dim
        outputs = torch.zeros(trg_len, batch_size, trg_vocab_size).to(self.device)
        hidden, cell = self.encoder(src)
        input = trg[0, :]
        for t in range(1, trg_len):
            output, hidden, cell = self.decoder(input, hidden, cell)
            outputs[t] = output
            teacher_force = random.random() < teacher_forcing_ratio
            input = trg[t] if teacher_force else output.argmax(1)
        return outputs

4. 模型训练与评估

        我们使用交叉熵损失函数,并定义训练和评估的函数。

criterion = nn.CrossEntropyLoss(ignore_index=PAD_IDX)

def train(model, iterator, optimizer, criterion, clip):
    model.train()
    epoch_loss = 0
    for i, (src, trg) in enumerate(iterator):
        src = src.to(device)
        trg = trg.to(device)
        optimizer.zero_grad()
        output = model(src, trg)
        output_dim = output.shape[-1]
        output = output[1:].view(-1, output_dim)
        trg = trg[1:].view(-1)
        loss = criterion(output, trg)
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), clip)
        optimizer.step()
        epoch_loss += loss.item()
    return epoch_loss / len(iterator)

def evaluate(model, iterator, criterion):
    model.eval()
    epoch_loss = 0
    with torch.no_grad():
        for i, (src, trg) in enumerate(iterator):
            src = src.to(device)
            trg = trg.to(device)
            output = model(src, trg, 0)  # 关闭Teacher Forcing
            output_dim = output.shape[-1]
            output = output[1:].view(-1, output_dim)
            trg = trg[1:].view(-1)
            loss = criterion(output, trg)
            epoch_loss += loss.item()
    return epoch_loss / len(iterator)

5. 测试与预测

        训练完成后,我们可以使用该模型进行翻译任务。以下是如何测试模型的简单示例。

model.eval()
with torch.no_grad():
    output = model(src, trg, 0)  # 关闭Teacher Forcing
    predicted_tokens = output.argmax(2)
    for token in predicted_tokens:
        print(vocab_transform[TRG_LANGUAGE].get_itos()[token.item()])

结语

        在本文中,我们详细介绍了如何使用TorchText和LSTM构建序列到序列翻译模型。通过这种结构,我们能够将一个序列映射为另一个序列,这在机器翻译、文本生成等任务中非常常见。

        不过,seq2seq模型在处理长序列时可能会遇到一些问题,尤其是在生成的序列与输入序列相差较远时。为了解决这个问题,下一篇文章中我们将引入另一种循环神经网络结构——门控循环单元(GRU),并探讨如何重复使用上下文向量和更有效的Teacher Forcing策略,从而提升模型在长序列生成任务中的表现。

如果你觉得这篇博文对你有帮助,请点赞、收藏、关注我,并且可以打赏支持我!

欢迎关注我的后续博文,我将分享更多关于人工智能、自然语言处理和计算机视觉的精彩内容。

谢谢大家的支持!


原文地址:https://blog.csdn.net/ljd939952281/article/details/142355298

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!