基于seq2seq的机器翻译系统(法译英)

数据集来源于Tab-delimited Bilingual Sentence Pairs from the Tatoeba Project (Good for Anki and Similar Flashcard Applications)

代码一:datapre 实现了数据的预处理和数据集的准备

 

SOS_token = 0
EOS_token = 1

# 建一个辅助类Lang
# 包含 word → index ( word2index) 和 index → word ( index2word) 字典,以及每个单词的计数word2count,
class Lang:
    def __init__(self, name):
        self.name = name
        self.word2index = {}
        self.word2count = {}
        self.index2word = {0: "SOS", 1: "EOS"}
        self.n_words = 2  # Count SOS and EOS

    def addSentence(self, sentence):
        for word in sentence.split(' '):
            self.addWord(word)

    def addWord(self, word):
        if word not in self.word2index:
            self.word2index[word] = self.n_words
            self.word2count[word] = 1
            self.index2word[self.n_words] = word
            self.n_words += 1
        else:
            self.word2count[word] += 1

# 统一编码为ascii
def unicodeToAscii(s):
    return ''.join(
        c for c in unicodedata.normalize('NFD', s)
        if unicodedata.category(c) != 'Mn'
    )


# 统一小写、修剪和删除非字母字符
def normalizeString(s):
    s = unicodeToAscii(s.lower().strip())
    s = re.sub(r"([.!?])", r" \1", s)
    s = re.sub(r"[^a-zA-Z.!?]+", r" ", s)
    return s

'''
为了读取数据文件,我们将文件分割成行,然后将行分割成对。
数据集都是英语→其他语言,所以如果我们想从其他语言→英语翻译,添加reverse 标志来反转对。
'''
def readLangs(language1, language2, reverse=False):
    print("Reading lines...")

    # 分行
    lines = open('data/%s-%s.txt' % (language1, language2), encoding='utf-8'). \
        read().strip().split('\n')

    # 将每行分割成语言对,并正则化
    pairs = [[normalizeString(s) for s in l.split('\t')] for l in lines]

    # 反转语言对
    if reverse:
        pairs = [list(reversed(p)) for p in pairs]
        input_lang = Lang(language2)
        output_lang = Lang(language1)
    else:
        input_lang = Lang(language1)
        output_lang = Lang(language2)

    return input_lang, output_lang, pairs

'''
由于有很多例句,要想快速训练一些东西,可以把数据集修剪成只包含相对较短和简单的句子。
代码设定句子的最大长度为 10 个单词(包括结尾标点符号)。
'''
MAX_LENGTH = 10

def filterPair(p):
    return len(p[0].split(' ')) < MAX_LENGTH and len(p[1].split(' ')) < MAX_LENGTH

def filterPairs(pairs):
    return [pair for pair in pairs if filterPair(pair)]

def prepareData(lang1, lang2, reverse=False):
    input_lang, output_lang, pairs = readLangs(lang1, lang2, reverse)
    print("Read %s sentence pairs" % len(pairs))
    pairs = filterPairs(pairs)
    print("Trimmed to %s sentence pairs" % len(pairs))
    print("Counting words...")
    for pair in pairs:
        input_lang.addSentence(pair[0])
        output_lang.addSentence(pair[1])
    print("Counted words:")
    print(input_lang.name, input_lang.n_words)
    print(output_lang.name, output_lang.n_words)
    return input_lang, output_lang, pairs

代码二:evaluate 定义了一些评估方法,用于测试

代码三:seq2seq_model 搭建了encoder和attention decoder,均采用GRU实现

代码四:train 训练脚本

# 获取句子中每个单词的索引,返回的是索引序列
def indexesFromSentence(lang, sentence):
    return [lang.word2index[word] for word in sentence.split(' ')]

# 根据索引序列建立张量
def tensorFromSentence(lang, sentence):
    indexes = indexesFromSentence(lang, sentence)
    indexes.append(EOS_token)
    return torch.tensor(indexes, dtype=torch.long, device=device).view(-1, 1)

# 输入张量是输入句子中单词的索引,输出张量是目标句子中单词的索引
def tensorsFromPair(pair):
    input_tensor = tensorFromSentence(input_lang, pair[0])
    target_tensor = tensorFromSentence(output_lang, pair[1])
    return (input_tensor, target_tensor)

teacher_forcing_ratio = 0.5

'''
为了训练,我们通过编码器运行输入句子,并跟踪每个输出和最新的隐藏状态。
然后解码器被赋予<SOS>令牌作为它的第一个输入,编码器的最后一个隐藏状态作为它的第一个隐藏状态。
'''
def train(input_tensor, target_tensor, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion,
          max_length=MAX_LENGTH):
    encoder_hidden = encoder.initHidden()

    encoder_optimizer.zero_grad()
    decoder_optimizer.zero_grad()

    input_length = input_tensor.size(0)
    target_length = target_tensor.size(0)

    encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)

    loss = 0

    # 获取编码器的每个输出和隐藏状态,用于计算注意力权重
    for ei in range(input_length):
        encoder_output, encoder_hidden = encoder(
            input_tensor[ei], encoder_hidden)
        encoder_outputs[ei] = encoder_output[0, 0]

    decoder_input = torch.tensor([[SOS_token]], device=device)

    # 解码器第一个隐藏状态是编码器输出的隐藏状态
    decoder_hidden = encoder_hidden

    # 训练可以使用“Teacher forcing”策略:使用真实目标输出作为下一个输入,而不是使用解码器的猜测作为下一个输入。
    # 使用Teacher forcing会使模型收敛更快,但使用训练得到的网络时,可能会表现出不稳定。
    use_teacher_forcing = True if random.random() < teacher_forcing_ratio else False

    if use_teacher_forcing:
        # 将目标单词作为下一个解码输入
        for di in range(target_length):
            decoder_output, decoder_hidden, decoder_attention = decoder(
                decoder_input, decoder_hidden, encoder_outputs)
            loss += criterion(decoder_output, target_tensor[di])
            decoder_input = target_tensor[di]  # Teacher forcing

    else:
        # 用预测结果作为下一个解码输入
        for di in range(target_length):
            decoder_output, decoder_hidden, decoder_attention = decoder(
                decoder_input, decoder_hidden, encoder_outputs)
            topv, topi = decoder_output.topk(1)
            decoder_input = topi.squeeze().detach()  # detach from history as input

            loss += criterion(decoder_output, target_tensor[di])
            # 遇到终止符号就退出解码
            if decoder_input.item() == EOS_token:
                break

    # 反向传播
    loss.backward()

    encoder_optimizer.step()
    decoder_optimizer.step()

    return loss.item() / target_length

'''
@函数名:迭代训练
@参数说明:
    encoder:编码器
    decoder:解码器
    n_iters:训练迭代次数
    print_every:多少代输出一次训练信息
    plot_every:多少代绘制一下图
    learning_rate:学习率
'''
def trainIters(encoder, decoder, n_iters, print_every=1000, plot_every=100, learning_rate=0.01):
    start = time.time()
    plot_losses = []
    print_loss_total = 0  # Reset every print_every
    plot_loss_total = 0  # Reset every plot_every

    # 优化器用SGD
    encoder_optimizer = optim.SGD(encoder.parameters(), lr=learning_rate)
    decoder_optimizer = optim.SGD(decoder.parameters(), lr=learning_rate)
    training_pairs = [tensorsFromPair(random.choice(pairs))
                      for i in range(n_iters)]
    # 因为模型的输出已经进行了log和softmax,因此这里损失韩式只用NLL,三者结合起来就算二元交叉熵损失
    criterion = nn.NLLLoss()

    for iter in range(1, n_iters + 1):
        training_pair = training_pairs[iter - 1]
        input_tensor = training_pair[0]
        target_tensor = training_pair[1]

        loss = train(input_tensor, target_tensor, encoder,
                     decoder, encoder_optimizer, decoder_optimizer, criterion)
        print_loss_total += loss
        plot_loss_total += loss

        if iter % print_every == 0:
            print_loss_avg = print_loss_total / print_every
            print_loss_total = 0
            print('epoch:%d  %s (%d%%) loss:%.4f' % (iter, timeSince(start, iter / n_iters),
                                          iter / n_iters * 100, print_loss_avg))

        if iter % plot_every == 0:
            plot_loss_avg = plot_loss_total / plot_every
            plot_losses.append(plot_loss_avg)
            plot_loss_total = 0

    showPlot(plot_losses)

 

代码五:test 加载训练好的模型,实现翻译

代码六:util 包含了一些用到的工具函数,如绘图等

完整的项目:

真个项目本来是在get-hub上的但是有些时候有些网络环境无法访问get-hub网站,所以放在阿里云盘上了

如果想实现别的语言之间的互相翻译,可以在上面的数据来源的网站里下载数据集训练模型

1.已运行测试过的项目文件夹,可以直接使用

        包含训练模型

        python版本3.10.8

        pycharm版本20231.2

分享地址:data https://www.aliyundrive.com/s/GHYhqmfv818 点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线查看,视频原画倍速播放。

因为文件数有点多无法分享了,上面的是data里的数据集

分享时间:2023.6.22

有效时间:30天,链接失效可以在评论区发信息或者私信我

2.还没运行的项目包,要自己调环境,安装包。

注:原来的项目是法语翻译成英语的,所以如果下了原项目的,把data里的数据集改成维吾尔语翻译成英语的数据集。记住不要更改数据集的文件名格式,如果数据来源网址的数据集运行不出来的话,用上面的分享地址里把data里的数据集。

如果用pycharm跑的话记得看看新的版本支不支持改项目的包。

分享地址:attention-seq2seq https://www.aliyundrive.com/s/CoJz37CgdPP 点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线查看,视频原画倍速播放。

分享时间:2023.6.22

有效时间:30天,链接失效可以在评论区发信息或者私信我

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.凯撒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值