机器翻译——基于注意力机制的seq2seq结构(上)

本文涉及的产品
文本翻译,文本翻译 100万字符
函数计算FC,每月15万CU 3个月
文档翻译,文档翻译 1千页
简介: 机器翻译——基于注意力机制的seq2seq结构

前言


该项目是一个基于注意力机制的seq2seq结构的由英语和西班牙语互译的项目,一共有11万对句子,文章中训练使用到的数据对为3万,可以根据个人机器配置灵活调整。


0、seq2seq结构介绍以及机器翻译整体流程介绍


ddb90800ad394235a0551e9b3d1eddba.png



0-1、seq2seq结构介绍


seq2seq结构介绍:seq2seq模型是以编码(Encode)和解码(Decode)为代表的架构方式,seq2seq模型是根据输入序列X来生成输出序列Y,在翻译,文本自动摘要和机器人自动问答以及一些回归预测任务上有着广泛的运用。以encode和decode为代表的seq2seq模型,encode意思是将输入序列转化成一个固定长度的向量,decode意思是将输入的固定长度向量解码成输出序列。其中编码解码的方式可以是RNN,CNN、LSTM、GRU等。

缺点是:

1、定长编码是信息瓶颈

2、长度越长,前面输入RNN的信息就被越稀释。


0-2、机器翻译整体流程介绍

机器翻译整体流程介绍

一、准备数据、数据预处理


1、加载数据

2、分割数据:因为数据集里西班牙语和英语在同一行里,需要切割开,一共有11万条数据。

3、数据处理:全部转化为小写、去掉首尾多余的空格、删除特殊字符、标点符号前后都要添加空格、给每个句子添加一个开始标记和结束标记。

4、数据id化:单词级别的数据id化,把一条句子转化为数字序列,并且后续做一个padding处理。


二、模型构建:


1、encoder构建:通过学习输入,将其编码成一个固定大小的中间状态向量,再将中间状态向量传递给decoder。这个过程称其为编码。

2、decoder构建:decoder通过对中间状态向量的学习来进行输出,得到输出序列,这个过程称其为解码。需要注意的是:中间状态向量只是作为初始状态参与decoder的构建,后面的运算都和中间状态向量无关。

3、attention机制的构建:

3-1、如果不引入attention机制的话,Encoder-Decoder结构在编码和解码阶段始终由一个中间状态向量来联系,这就造成了在编码阶段由于信息压缩到中间状态向量中而导致的部分信息丢失,在解码阶段时,在后边的序列容易丢失细节信息。

3-2、在编码阶段加入Attention模型,它不再要求编码器将所有输入信息都编码进一个固定长度的向量之中。此时编码器对源数据序列进行数据加权变换编码成一个向量的序列,而在解码的时候,每一步都会选择性的从向量序列中挑选一个子集进行进一步处理。

3-3、Bahdanau注意力公式:

EO: (Encoder_Output) encoder各个位置的输出。

H: (Hidden_State) decoder某一步的隐含状态。

FC: 全连接层

X: decoder的一个输入

score = FC(tanh(FC(EO)+FC(H)))

4、总结:加入了Attention机制以后,encoder不仅仅是把encoder最后一个节点的hidden state提供给decoder,而是提供了更多的数据给到了decoder,它采取了一种选择机制,把最符合的hidden state选出来,这就是注意力公式要干的事情。


三、损失、优化器:
四、train
五、评估


一、导入所有需要的库


sys.version_info: 用于返回你使用的python版本号,例子中表示输出的python版本是3.9.12

%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd
import sklearn
import sys
import tensorflow as tf
import time
from tensorflow import keras
print(tf.__version__)
print(sys.version_info)
for module in mpl, np, pd, sklearn, tf, keras:
    print(module.__name__, module.__version__)

输出:


2.4.1

sys.version_info(major=3, minor=7, micro=10, releaselevel=‘final’, serial=0)

matplotlib 3.4.2

numpy 1.19.5

pandas 1.2.4

sklearn 0.23.2

tensorflow 2.4.1

tensorflow.keras 2.4.0


二、数据预处理


unicodedata.category(unichr): 以字符串形式返回分配给Unicode字符unichr的常规类别。‘Mn’代表的是语气词类别,与数据处理无关,所以去掉。

unicodedata.normalize: 返回Unicode字符串unistr的常规表单形式。表单的有效值为’NFC’,‘NFKC’,‘NFD’和’NFKD’, 理解为标准化。

notice : 经过unicodedata.normalize返回的一个格式才可以被unicodedata.category所判断。

有关于正则表达式:正则表达式——re库的一些常用函数.

# 因为西班牙语有一些是特殊字符,所以我们需要unicode转ascii,
# unicode:万国码 
# 这样值变小了,因为unicode太大
def unicode_to_ascii(s):
    # NFD是转换方法,把每一个字节拆开,Mn是重音 所以去除
    # unicodedata.normalize: 把一串UNICODE字符串转换为普通格式的字符串,将可能的字符进行分解。
    # unicodedata.category: 把一个字符返回它在UNICODE里分类的类型  [Mn] Mark, Nonspacing 
    # 把字符类型是Mn的去掉,把剩下类型的统统输出。
    return ''.join(c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn')
#下面我们找个样本测试一下
en_sentence = u"May I borrow this book?"
sp_sentence = u"¿Puedo tomar prestado este libro?"
print(type(unicode_to_ascii(en_sentence)))
print(type(unicode_to_ascii(sp_sentence)))
print(unicode_to_ascii(en_sentence))
print(unicode_to_ascii(sp_sentence))
# 这里字符串连接起来的类型是str
print(type(unicode_to_ascii(en_sentence)))
print(type(unicode_to_ascii(sp_sentence)))
# 进行句子的预处理
def preprocess_sentence(w):
    # lower():将字符串所有大写转化为小写
    # strip():删除头尾指定的字符,如果没有指定,那就是删除空格。
    # 变为小写,去掉多余的空格
    w = unicode_to_ascii(w.lower().strip())
    # creating a space between a word and the punctuation following it
    # eg: "he is a boy." => "he is a boy ."
    # Reference:- https://stackoverflow.com/questions/3645931/python-padding-punctuation-with-white-spaces-keeping-punctuation
    # 在标点符号前再加一个空格。
    # sub():替换字符串中的匹配项。第一个参数是正则表达式,需要替换的符号,第二个参数是替换成什么符号
    # []代表匹配方括号中列举的字符。
    # re.sub('正则表达式', '要加入的数字', '被替换的字符串')
    # 替换完之后,标点符号的前后各有一个空格。
    w = re.sub(r"([?.!,¿])", r" \1 ", w)
    # 因为可能有多余空格,所以处理一下
    # 加号代表多个空格转换为1个空格
    w = re.sub(r'[" "]+', " ", w)
    # replacing everything with space except (a-z, A-Z, ".", "?", "!", ",")
    w = re.sub(r"[^a-zA-Z?.!,¿]+", " ", w)
    # rstrip(): 删除末尾指定的字符,默认是空格。
    w = w.rstrip().strip()
    # adding a start and an end token to the sentence
    # so that the model know when to start and stop predicting.
    # 首尾添加start和end标记。
    w = '<start> ' + w + ' <end>'
    return w
print(preprocess_sentence(en_sentence))
# 编码,方便后续
print(preprocess_sentence(en_sentence).encode('utf-8'))
print(preprocess_sentence(sp_sentence).encode('utf-8'))

输出:

<class ‘str’>

<class ‘str’>

May I borrow this book?

¿Puedo tomar prestado este libro?

<class ‘str’>

<class ‘str’>

may i borrow this book ?

b’ may i borrow this book ? ’

b’ \xc2\xbf puedo tomar prestado este libro ? ‘’


三、读取数据,创建Dataset。


数据集:数据集为英语转化为西班牙语,如下所示。(左边英语,右边西班牙语)

Grab Tom. Sujeta a Tom.

Grab him. Agárralo.

Have fun. Diviértanse.

Have fun. Pásala bien.

Have fun. Pásenla bien.

data_path = './data_spa_en/spa.txt'
# 1. Remove the accents  移除口音
# 2. Clean the sentences
# 3. Return word pairs in the format: [ENGLISH, SPANISH]--> 前面西班牙,后面英文
def create_dataset(path, num_examples):
    # 打开路径
    # read(): 返回从字符串中读取到的字节,
    # strip():去除掉前后空格。
    # split():以换行符来分割字符。
    lines = open(path, encoding='UTF-8').read().strip().split('\n')
    # preprocess_sentence(): 句子的预处理
    # 用制表符来分割开数据
    # 数据本来是以\t分割的。
    word_pairs = [[preprocess_sentence(w) for w in l.split('\t')]  for l in lines[:num_examples]]
    print(type(word_pairs))
    # word_pairs是二维列表,每一行是一个列表,一个列表里有两个元素
    print("word_pairs为:",word_pairs[0])
    # zip:返回一个zip对象,其内部元素为元组;
    # zip的作用是产生一个序列 *是解包的意思,意思是zip完返回的是列表,内部元素是元组
    # 而如果经过解包的话,直接返回的是内部的几个元组。
    return zip(*word_pairs)
# 打开文件路径、分割数据
en, sp = create_dataset(data_path, None)
# print(en)
print(type(en))
print(type(sp))
print(en[5])
print(sp[5])


输出:

<class 'list'>
word_pairs为: ['<start> go . <end>', '<start> ve . <end>']
<class 'tuple'>
<class 'tuple'>
<start> run ! <end>
<start> corre ! <end>

解包的一个小例子:

a=[ (1, 2),(3,4),(5,6) ]
c,d=zip(*a)
print(c,d)

输出:

(1, 3, 5) (2, 4, 6)


四、文本向量化


tf.keras.preprocessing.text.Tokenizer: 将文本向量化,或将文本转换为序列(即单个字词以及对应下标构成的列表,从1开始)的类。调用即实例化Tokenizer类。

fit_on_texts:使用分词器(Tokenizer类)来训练文本。训练之后就可以用分词器把对应文本转化为张量。

texts_to_sequences:返回文本对应的序列列表。将对应文本转化为张量。

tf.keras.preprocessing.sequence.pad_sequences:将序列转化为经过填充以后得到的一个长度相同新的序列。

参数:padding,为pre或post,确定当需要补0时,在序列的起始还是结尾补。

# 返回最长的
def max_length(tensor):
    return max(len(t) for t in tensor)
def tokenize(lang):
    # token理解为登录的一个凭据。
    # 理解为分词器。
    # 文本翻译成id id翻译成字符串。
    # Tokenizer帮我们把词语式的转换为id式的,filters是黑名单
    # 用于向量化文本,将文本转化为序列(即单词在字典中的下标构成的列表,从1算起)的类
    # lang_tokenizer现在是一个分词器
    # filters:黑名单,设置为空
    lang_tokenizer = tf.keras.preprocessing.text.Tokenizer(filters='')
    # 往分词器里喂入事先准备好的数据
    lang_tokenizer.fit_on_texts(lang)
    # 变为id
    # texts_to_sequences:将文本转化为序列列表
    tensor = lang_tokenizer.texts_to_sequences(lang)
    # 做padding,自动补0。必须是给予定长的数据。
    tensor = tf.keras.preprocessing.sequence.pad_sequences(tensor, padding='post')
    # 返回的是文本转化为的序列列表、已经训练好的分词器
    return tensor, lang_tokenizer
# 加载数据
def load_dataset(path, num_examples=None):
    # creating cleaned input, output pairs
    # target input
    # create_dataset:打开路径,分割数据,返回处理后的数据。
    # num_examples指的是读取的行数,这里一共表示读取三万行。
    # 前3万行数据还是比较短的,先用一部分数据来看模型是否适合。
    targ_lang, inp_lang = create_dataset(path, num_examples)
    # 用tokenize来分别处理分割开的数据
    # 得到分别对应的序列列表和分词器
    input_tensor, inp_lang_tokenizer = tokenize(inp_lang)
    target_tensor, targ_lang_tokenizer = tokenize(targ_lang)
    return input_tensor, target_tensor, inp_lang_tokenizer, targ_lang_tokenizer
# Try experimenting with the size of that dataset
num_examples = 30000
#inp_lang  targ_lang 是tokenizer
# 加载数据、读取数据、分割数据、处理数据
# 输入和目标值的序列列表,输入和目标的分词器。
# 输入西班牙语,转换为英语。
input_tensor, target_tensor, inp_lang, targ_lang = load_dataset(data_path, num_examples)
print('-'*50)
print(input_tensor.shape, target_tensor.shape, inp_lang, targ_lang)
# inp_lang.word_index:
print('-'*50)
print(input_tensor[0])
print(target_tensor[0])
print(input_tensor[29999])
print(target_tensor[29999])
# Calculate max_length of the target tensors,可以看下最长的样本
max_length_targ, max_length_inp = max_length(target_tensor), max_length(input_tensor)
print(max_length_targ, max_length_inp)

输出:

<class 'list'>word_pairs为: ['<start> go . <end>', '<start> ve . <end>']
--------------------------------------------------
(30000, 16) (30000, 11) <keras_preprocessing.text.Tokenizer object at 0x7ff6f88ba650> <keras_preprocessing.text.Tokenizer object at 0x7ff734b62550>
--------------------------------------------------
[  1 135   3   2   0   0   0   0   0   0   0   0   0   0   0   0]
[ 1 36  3  2  0  0  0  0  0  0  0]
[   1   23 2175   10   39   98   87  314    3    2    0    0    0    0
    0    0]
[ 1 16 38 72  6 55  3  2  0  0  0]
11 16


五、创建训练集和验证集

# Creating training and validation sets using an 80-20 split
# 分割数据,把数据分割为x_train,x_test,y_train,y_test。
input_tensor_train, input_tensor_val, target_tensor_train, target_tensor_val = train_test_split(input_tensor, target_tensor, test_size=0.2)
# Show length
# 按单词进行切分的
# 测试tokenizer是否可以正常工作。
def convert(lang, tensor):
    # 遍历序列列表(向量表示)
    for t in tensor:
        #不等于0,就打印转换
        if t != 0:
            # 分词器内部转换,把数字转换为对应的字符。
            print ("%d ----> %s" % (t, lang.index_word[t]))
print("Input Language; index to word mapping")
# 输入的分词器,训练集的输入序列列表。
convert(inp_lang, input_tensor_train[0])
print()
print("Target Language; index to word mapping")
# 输出的分词器, 
convert(targ_lang, target_tensor_train[0])
#可以发现tokenizer正常工作


输出:

Input Language; index to word mapping
1 ----> <start>
286 ----> queremos
122 ----> hablar
3 ----> .
2 ----> <end>
Target Language; index to word mapping
1 ----> <start>
16 ----> we
47 ----> want
15 ----> to
149 ----> talk
3 ----> .
2 ----> <end>


相关文章
|
3月前
|
机器学习/深度学习 自然语言处理 算法
Seq2Seq模型在机器翻译任务中如何优化以提高翻译质量?
Seq2Seq模型在机器翻译任务中如何优化以提高翻译质量?
|
6月前
|
机器学习/深度学习 自然语言处理 算法框架/工具
python用于NLP的seq2seq模型实例:用Keras实现神经网络机器翻译
python用于NLP的seq2seq模型实例:用Keras实现神经网络机器翻译
|
机器学习/深度学习 存储 JSON
在PyTorch中使用Seq2Seq构建的神经机器翻译模型
在PyTorch中使用Seq2Seq构建的神经机器翻译模型
319 1
在PyTorch中使用Seq2Seq构建的神经机器翻译模型
|
机器学习/深度学习 自然语言处理 索引
机器翻译——基于注意力机制的seq2seq结构(下)
机器翻译——基于注意力机制的seq2seq结构(下)
|
机器学习/深度学习 自然语言处理 PyTorch
在PyTorch中使用Seq2Seq构建的神经机器翻译模型(三)
在PyTorch中使用Seq2Seq构建的神经机器翻译模型
145 0
在PyTorch中使用Seq2Seq构建的神经机器翻译模型(三)
|
机器学习/深度学习 自然语言处理 数据可视化
在PyTorch中使用Seq2Seq构建的神经机器翻译模型(二)
在PyTorch中使用Seq2Seq构建的神经机器翻译模型
206 0
在PyTorch中使用Seq2Seq构建的神经机器翻译模型(二)
|
机器学习/深度学习 存储 人工智能
NLP教程(6) - 神经机器翻译、seq2seq与注意力机制
本文介绍了序列到序列模型(seq2seq)及其在翻译系统中的应用,以及注意力机制、序列解码器、神经翻译系统、基于字符级别的翻译模型等。
1161 1
NLP教程(6) - 神经机器翻译、seq2seq与注意力机制
|
机器学习/深度学习 自然语言处理 Go
6_机器翻译与Seq2Seq模型
6_机器翻译与Seq2Seq模型
240 0
6_机器翻译与Seq2Seq模型
|
算法 C语言
算法竞赛入门【码蹄集新手村600题】(MT1200-1220)C语言(三)
算法竞赛入门【码蹄集新手村600题】(MT1200-1220)C语言(三)
260 1
|
机器学习/深度学习 算法 C语言
算法竞赛入门【码蹄集新手村600题】(MT1200-1220)C语言(一)
算法竞赛入门【码蹄集新手村600题】(MT1200-1220)C语言
140 1