NLP学习记录六:序列到序列学习

目录

一、机器翻译与数据集

1.1 背景介绍

1.2 代码实现

二、序列到序列学习

2.1 编码器-解码器架构

2.2 seq2seq实现思路 

2.3 评价指标BLEU 

2.4 代码实现


一、机器翻译与数据集

1.1 背景介绍

        机器翻译是序列到序列学习课题中的一个典型案例,所以接下来的学习会以机器翻译这一案例展开。基于神经网络的翻译方法通常被称为神经机器翻译(neural machine translation),后续编写代码时将使用nmt这一缩写来代替名词神经机器翻译。

        在学习序列到序列模型的过程中,将使用到一个新的机器翻译数据集,数据集下载链接如下:http://d2l-data.s3-accelerate.amazonaws.com/fra-eng.zip

1.2 代码实现

nmtDataLoader.py:

导入依赖包:

import os
import torch
from preprocessing import Vocab

用于读取翻译数据集的函数:

def read_data_nmt(path):
    with open(os.path.join(path), 'r', encoding='utf-8') as f:
        return f.read()

翻译文本预处理函数:

def preprocess_nmt(text):
    # 判断标点符号(,.!?)前面有没有空格,如果没有返回True
    def no_space(char, prev_char):
        return char in set(',.!?') and prev_char != ' '

    # \u202f 是一个Unicode字符,代表窄非断行空格(Narrow No-Break Space)
    # \xa0 是另一个表示空格的字符,在Unicode中代表不间断空格(Non-Breaking Space)
    text = text.replace('\u202f', ' ').replace('\xa0', ' ').lower()

    # 遍历text中的每一个字符,如果该字符属于标点符号(,.!?)且前一个字符不是空格,
    # 那么在这个标点前面添加一个空格,方便后续把标点符号分割成一个单独的token
    out = [' ' + char if i > 0 and no_space(char, text[i - 1]) else char for i, char in enumerate(text)]

    return ''.join(out)

从翻译数据集中提取num_examples个token:

def tokenize_nmt(text, num_examples=None):
    source, target = [], []

    # 遍历翻译数据集中的每一行
    for i, line in enumerate(text.split('\n')):

        # 判断已提取的token数量是否达到上限
        if num_examples and i > num_examples:
            break

        # 根据制表符分割出每一行的法语部分和英语部分
        parts = line.split('\t')

        if len(parts) == 2:
            source.append(parts[0].split(' '))
            target.append(parts[1].split(' '))

    return source, target

为了保证每个文本序列具有相同的长度,定义一个用于截断或填充文本序列的函数:

def truncate_pad(line, num_steps, padding_token):
    # 截断
    if len(line) > num_steps:
        return line[:num_steps]

    # 填充
    return line + [padding_token] * (num_steps - len(line))

将机器翻译的文本序列转换成小批量:

def build_array_nmt(lines, vocab, num_steps):
    # token to index,实际上是调用了Vocab的__getitem__成员函数
    # 如:[['go', '.'], ['go', '.'], ['go', '.']] -> [[47, 4], [47, 4], [47, 4]]
    lines = [vocab[l] for l in lines]

    # 将特定的“<eos>”词元添加到序列末尾
    # 如:[[47, 4], [47, 4], [47, 4]] -> [[47, 4, 3], [47, 4, 3], [47, 4, 3]]
    lines = [l + [vocab['<eos>']] for l in lines]

    array = torch.tensor([truncate_pad(l, num_steps, vocab['<pad>']) for l in lines])

    # 统计每个序列除去填充词元后的有效长度
    # array != vocab['<pad>']操作会生成一个新的张量,其形状与array相同,如果array的某个元素满足不等式条件,则新张量在该位置上的值为True,否则为False
    # .type(torch.int32)操作将上一步得到的布尔张量转换为torch.int32类型的张量,即False变为0,True变为1
    # .sum(1)操作沿着张量的第二个维度进行求和
    valid_len = (array != vocab['<pad>']).type(torch.int32).sum(1)

    return array, valid_len

 将上面实现的功能封装在一个函数中,方便外部模块调用。该函数的主要功能是返回翻译数据迭代器和词表:

def load_data_nmt(path, batch_size, num_steps, num_examples=600):
    text = preprocess_nmt(read_data_nmt(path))

    # source:[['go', '.'], ['hi', '.'], ['run', '!'], ...]
    # target:[['va', '!'], ['salut', '!'], ['cours', '!'], ...]
    source, target = tokenize_nmt(text, num_examples)

    # min_freq=2表示出现频率小于2的token会被忽略
    # <pad>:在小批量时用于将序列填充到相同长度;<bos>:begin of sentence,开始词元;<eos>:end of sentence,结束词元
    src_vocab = Vocab(source, min_freq=2, reserved_tokens=['<pad>', '<bos>', '<eos>'])
    tgt_vocab = Vocab(target, min_freq=2, reserved_tokens=['<pad>', '<bos>', '<eos>'])

    # 经过处理后,原序列和目标序列中的子序列的长度都为num_steps
    src_array, src_valid_len = build_array_nmt(source, src_vocab, num_steps)
    tgt_array, tgt_valid_len = build_array_nmt(target, tgt_vocab, num_steps)

    data_arrays = (src_array, src_valid_len, tgt_array, tgt_valid_len)

    # is_train=True表示打乱文本序列的次序,每次返回batch_size个子序列
    data_iter = load_array(data_arrays, batch_size, is_train=True)

    return data_iter, src_vocab, tgt_vocab

 主函数测试:

if __name__=='__main__':
    train_iter, src_vocab, tgt_vocab = load_data_nmt('fra-eng/fra.txt', batch_size=2, num_steps=8)
    for X, X_valid_len, Y, Y_valid_len in train_iter:
        print('X:', X.type(torch.int32))
        print('X的有效长度:', X_valid_len)
        pri
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值