Python 人工智能实战:机器翻译
1.背景介绍
作为重要的交际工具之一
2.核心概念与联系
机器翻译是实现不同语言环境下自然语言转换的技术过程。人工智能领域的研究始于20世纪70年代,在经过几十年的发展与演进,在这一领域内已实现了显著的进步。特别是在英语作为源语种的机器翻译任务中表现出了卓越的能力。过去两年多的时间里,继而有多个教育机构如国际认知计算联盟(ICC)、台湾国立大学语言学系以及加州大学圣巴巴拉分校依次开设了相关的机器翻译课程。这些课程主要采用当前最前沿的先进技术和方法来实现教学内容。本节将简要介绍相关的核心概念,并阐述它们之间的联系。
2.1 MT基础概念
机器翻译技术(MT)是一种由计算机自动处理语言转换过程。它的工作原理是根据人类的语言语法、词汇习惯以及对话语境特征,在计算机程序的作用下将输入语言中的句子转化为目标语言。作为人工智能领域的重要研究方向之一,在这一领域中主要关注两个关键问题:一是如何利用计算机识别并解析输入语句的内容,并生成相应的自然语言输出;二是如何准确地将源语言的句子转换为目标语言的表达形式。为了实现上述目标需求,在这一过程中需要解决两个核心问题:词法分析和句法分析。其中词法分析是指将输入语句分解成词素(wordpiece)或单词,并确定每个词素之间的上下文关系;而句法分析则是依据这些关系生成正确的翻译结果。
词法分析(Lexical Analysis)
词法分析是通过自然语言处理技术将输入语句分解为一系列由特定元素或符号构成的词素的过程。例如,在英语中对语句"The quick brown fox jumps over the lazy dog"进行词法分析的结果可能表现为:["the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]。在中文环境下,我们可以利用分词工具来完成这一过程;此外还有一种基于规则的方法可以使用:即把连续的中文字符视为一个独立的词素,并把数字和标点符号单独作为独立的词素处理。
句法分析(Syntactic Analysis)
通过识别每个词干及其周围的语境关联来完成对语言结构的理解过程。通常情况下, 采用基于语法规范和意义关联的技术体系来实现精准的翻译目标, 其中语法规范决定了整个句子的基本框架安排, 而意义关联则明确了各子词之间的相互作用机制. 在这一过程中, 分析系统所输出的结果往往呈现为一棵语法树, 即用于表示对输入句子进行语法解析的过程.
2.2 模型概览
图1:MT系统的模型概览
所述的多语言(MT)系统主要组成部分是三个关键模块:基础架构设计与算法研究(Language Model)、机器翻译模型构建与优化(Translation Model)以及智能优化技术应用(Optimizer)。其中基础架构设计与算法研究部分旨在实现输入序列的有效表示;机器翻译模型构建与优化部分则专注于生成对应的目标语言序列;智能优化技术应用部分则通过优化机制负责调整模型参数以持续提升翻译效果。该系统涵盖数据准备、训练过程、推理阶段以及评估体系等多个环节,并将详细介绍各个组件的设计理念及其具体功能。
2.2.1 语言模型(Language Model)
在机器翻译领域中,语言模型被视为一个基础模块,在此框架下用以表示输入语句的概率分布。一般而言,在自然语言处理领域中这一概念通过一系列词嵌入向量和概率分布函数得以实现。其中词嵌入向量可被视为一个向量空间,在此空间内对输入语句进行编码工作;而概率分布函数则用于预测下一个词的概率。考虑到上下文信息对生成当前单词的影响程度不同,在实际应用中通常会假设邻近单词对生成当前单词的影响较大,并以此为基础来提高生成质量。通过这样的机制设计可以使语言模型更加准确地捕捉到语义信息并有效预测后续词语序列的发生可能性;此外这种机制设计还可以帮助优化整个系统的性能指标并提升最终输出的质量水平
- 对齐句子:语言模型能够识别输入语句中的歧义词并修正错误的翻译输出。
- 概括生成:语言模型可辅助生成文章内容以维持一致性,并避免让生成内容过于冗长。
- 改善生成:语言模型通过积累参考资料来提升生成译文的质量。
2.2.2 翻译模型(Translation Model)
该翻译模型旨在完成源语言至目标语言句子的转换过程。传统的基础翻译模型主要依赖于词级别的双向长短期记忆网络结构。通过将输入句子中的每个词转化为嵌入形式后输入LSTM网络,在此过程中系统能够提取出每个词周围的语义信息,并逐步生成完整的译文序列。然而由于其局限性,在实际应用中往往难以达到理想的译文质量。为了提升译文质量与准确性,在本文中我们采用了基于注意力机制的新一代翻译架构。具体而言可划分为两种层次结构:一种是基于单个词位置的信息处理机制;另一种则是关注整个句子语义的整体关系。在关注整体语义时能够捕捉到不同位置上的上下文关联
2.2.3 优化器(Optimizer)
优化器负责更新模型参数的过程,并使翻译质量持续提升。传统的机器翻译方法主要包括基于最大似然估计、梯度上升以及非监督学习的三类方法。本文采用了条件随机场方法,在MT模型的输入层、输出层和隐藏层之间添加条件随机场结构,并建立输入输出关系模型。同时采用了反向传播算法来进行参数更新工作。
2.3 数据集简介
在机器翻译(MT)领域中,数据集合被视为一个关键资源,在训练、验证和模型评估中发挥着核心作用。当前公共数据集通常涵盖以下几个主要类别:文本对齐任务、多语言对照语料库以及大规模预训练语言模型的数据支持等具体应用场景。在实际应用中需要考虑的因素包括但不限于以下几点:
Parallel Resource Bank: 在机器翻译领域中, Parallel Resource Bank被视为一种核心资源库,它包含了成套的源语言文本段落与相应的目标语言翻译版本,这些材料不仅可作为训练集合,也可用于开发阶段的数据构建,以及在必要时用于测试阶段的数据验证.
有监督训练数据集基于机器翻译专业人员标注的源文本和目标文本,在人工筛选过程中生成。
- 机器翻译语料数据库:由机器翻译系统生成的机器翻译输出也可用作训练数据集。
测试数据集合:该集合涵盖了来自多个领域的真实对话样本,并可作为模型泛化能力的有效评估指标。
2.4 Tensorflow框架
作为首个开源深度学习框架,在自然语言处理、计算机视觉等多个领域均有广泛应用
3.核心算法原理和具体操作步骤
3.1 数据准备
首先收集并整理好用于机器翻译的训练集合开发集合测试集合本文采用OpenSubtitles数据集作为案例该数据集合自人工标注的儿童英语对配图构成具体而言我们下载了该数据集中英德法西四种语言版本共计约41万条对照配图接着将这些图片逐一标注并按照开发与测试的比例划分出训练集合验证集合鉴于OpenSubtitles规模较小无需额外补充
3.2 文件格式转换
下一步骤包括将OpenSubtitles数据集中的文件格式从原始形式转换为标准文本文件(.txt)的形式。这些图像最初是以图片格式存在的。我们需要先将其转换成txt格式。我们首先需要将这些图像转换成文本文件的形式。通过对每张图像进行像素矩阵提取并转化为字符串的形式进行存储。通过这一过程处理后,我们能够得到一批大小可控制的标准文本文件(.txt),从而满足后续的数据处理需求。
3.3 数据清洗
第三步骤是对手段数据进行清洗处理。考虑到原始样本中英语、德语、法语及西班牙语版本图片数量存在显著差异这一事实,在完成初步的数据整理后,我们选取这些语言版本图片数量的平均值作为各语言训练集的基础。随后去除多余标点符号及空格位置,并将全部文本内容转换为小写格式以便后续的数据分析工作。
3.4 数据集拆分
第四步是对数据集进行划分。在机器翻译过程中,我们常常需要将数据集合分为训练集、验证集与测试集三个部分。在本研究中,则采用了70%用于训练数据、10%作为验证数据以及20%用于测试的数据比例来进行建模。
3.5 词表建立
在第五阶段中构建词汇表,在机器翻译过程中,词汇表被定义为一种稀疏矩阵,用于编码输入语句和输出序列。通过调用Python编程语言中的collections模块中的Counter()函数来统计每句话中各个单词出现的频率。然后筛选出出现频率最高的前n个单词来构建词汇表。其中参数n设定为30,000字左右。
3.6 数据加载
第六步是数据分析与准备阶段。在这一阶段中,我们需要利用Python中的TensorFlow的数据API模块来完成对训练和验证所需的数据导入工作,并将其组织成适合模型训练的数据格式。该库提供的输入接口支持多种多样的数据集类型,例如包括如tf.data.Dataset对象等,这些功能使得我们能够高效地完成训练过程中的各项任务,例如快速读取大量样本以及灵活地设置批次大小等参数设置,从而提高整体训练效率
3.7 数据处理
第七步是数据处理。在机器翻译任务中,数据预处理主要包括:
数据增强是指利用随机变换的方式对原始图像进行处理,并基于此生成新的样本以提升数据集容量。主要采用的方法包括插值、换行、删减等。
-
数据归一化:将数据的特征缩放到相同的范围,减少特征的大小差异。
-
数据切分:将数据集拆分成训练集、验证集和测试集,以防止过拟合。
3.8 源语言数据处理
在处理流程中,源语言的数据预处理阶段的主要任务包括构建词表体系、实现字符到数字的映射关系以及对数据集进行科学的划分等核心环节。
3.9 目标语言数据处理
第九步是目标语言的数据预处理流程。该流程主要涉及词汇库构建、字符编码映射、标签对应关系建立以及数据集划分等多个具体步骤。
3.10 数据批处理
第十个步骤是数据批处理。具体指的是将整个数据集划分为固定长度的小批次,在训练过程中从每个小批次中随机选取样本进行训练。
3.11 训练集处理
第十一步是训练集处理。训练集处理主要包括:
-
文本编码:将文本转换为词索引序列。
-
句子排序:按照源句子长度进行排序。
-
生成数据迭代器:用于从数据集中批量获取训练数据
3.12 模型构建
在第十二步中进行模型构建。其主要内容是构建机器翻译系统。该系统主要包括编码器、解码器以及优化器等组件。
3.13 损失函数
在第十三步骤中涉及的是损失函数。它起着评估模型预测结果与实际值之间差距的作用。特别是在机器翻译任务中被采用一种基于注意力机制的设计,在这种架构下模型能够有效地捕捉到各个位置上的依赖关系,并准确识别出关键点之间的联系
3.14 优化器设置
在第十四步中进行优化器设置,在机器学习模型训练过程中进行参数更新和目标函数最小化操作的具体步骤即为优化器设置,在深度学习框架中常用的几种常见优化算法包括Adam、Adagrad以及随机梯度下降法SGD等
3.15 训练模型
在本步骤中将实施模型的训练过程。在进行该阶段的机器学习任务时,在线性回归算法中不断优化系数矩阵,并使损失函数达到最低值。整个学习过程被划分为多个周期?每个周期中的样本数量保持一致?完成一个周期后?若发现当前阶段的性能不达标,则对优化参数进行微调,并重新启动整个过程。
3.16 模型评估
第十六步属于model evaluation阶段。model evaluation主要通过分析model performance ontraining dataset和testing dataset来衡量model效果。通常会直接影响whether model deployment into production environment.通过testing dataset数据可以更加准确地反映model performance.
4.具体代码实例和详细解释说明
4.1 TensorFlow安装
在安装TensorFlow时,我们依赖于Anaconda这个官方发行版的Python工具套装。作为一个数据科学领域的核心工具之一,Anaconda旨在提供一个统一的环境来管理各种数据科学软件包,并支持轻松安装如TensorFlow等关键库。
conda create -n tf_env python=3.7 tensorflow numpy scipy scikit-learn matplotlib ipykernel notebook
activate tf_env # activate conda environment
代码解读
4.2 数据准备
import os
代码解读
def download_dataset(url): filename = url.split("/")[-1]; if not os.path.exists(filename): !wget $url
download_dataset("http://www.manythings.org/anki/deu-eng.zip")
!unzip deu-eng.zip && rm deu-eng.zip os.rename('deu', 'opensubtitles')
## 4.3 文件格式转换
```python
from PIL import Image
import numpy as np
images = []
for file in os.listdir("./opensubtitles"):
with open('./opensubtitles/' + file, encoding='utf-8') as f:
text = ''
for line in f:
text += line.strip('\n').lower().replace('-',' ')
w, h = img.size
pixels = list(img.getdata())
pixel_matrix = [pixels[i*w:(i+1)*w] for i in range(h)]
images.append((text,np.array(pixel_matrix)))
with open('train.txt', 'w', encoding='utf-8') as fw:
for image in images:
fw.write(' '.join([str(x) for x in image[1].flatten()])+'\t'+image[0]+'\n')
代码解读
4.4 数据清洗
import string
all_lines = []
with open('train.txt', encoding='utf-8') as fr:
lines = fr.readlines()[::3]
for line in lines:
all_lines.extend([line[:64], line[64:]])
new_lines = []
for line in all_lines:
new_line = ''.join([char for char in line if char not in string.punctuation and
char!='']).encode('ascii', errors='ignore').decode('ascii').lower()
if len(new_line)>0:
new_lines.append(new_line)
unique_words = set(['<unk>'] + sorted(list(set(' '.join(new_lines).split()))))
vocab_size = min(len(unique_words), 30000)
word2idx = {'<pad>':0}
idx2word = {0:'<pad>'}
for word in unique_words:
idx = len(idx2word)
if vocab_size==None or idx < vocab_size:
word2idx[word] = idx
idx2word[idx] = word
with open('vocab.txt', 'w', encoding='utf-8') as fw:
fw.write('<pad>\t0\n<unk>\t1\n'+'\n'.join([str(k)+ '\t'+ str(v) for k, v in word2idx.items()]))
代码解读
4.5 数据集拆分
import random
random.seed(0)
train_lines = random.sample(range(len(new_lines)), int(len(new_lines)*0.7))
dev_lines = random.sample(list(set(range(len(new_lines)))-set(train_lines)),int(len(new_lines)*0.1))
test_lines = list(set(range(len(new_lines)))-set(train_lines)-set(dev_lines))
print(len(train_lines))
print(len(dev_lines))
print(len(test_lines))
train_file = './train.txt'
dev_file = './dev.txt'
test_file = './test.txt'
with open(train_file, 'w', encoding='utf-8') as fw:
for idx in train_lines:
fw.write(new_lines[idx]+'\t'+new_lines[idx+1][:-1]+'\n')
with open(dev_file, 'w', encoding='utf-8') as fw:
for idx in dev_lines:
fw.write(new_lines[idx]+'\t'+new_lines[idx+1][:-1]+'\n')
with open(test_file, 'w', encoding='utf-8') as fw:
for idx in test_lines:
fw.write(new_lines[idx]+'\t'+new_lines[idx+1][:-1]+'\n')
代码解读
4.6 数据加载
import tensorflow as tf
class TextDataGenerator(object):
def __init__(self, data_path, batch_size):
self._batch_size = batch_size
input_files = ['./train.txt']
target_files = ['./dev.txt', './test.txt']
dataset = tf.data.Dataset.from_tensor_slices((input_files,target_files))
dataset = dataset.repeat().shuffle(buffer_size=len(input_files)).interleave(lambda x: tf.data.TextLineDataset(x), cycle_length=len(input_files), block_length=1)
# preprocess function
def _preprocess(src, trg):
src = tf.strings.split(src).to_tensor()
src = tf.keras.preprocessing.sequence.pad_sequences(src, maxlen=64, padding='post')
src = tf.one_hot(src, depth=len(word2idx), dtype=tf.float32)[..., :-1]
trg = tf.strings.split(trg).to_tensor()
trg = tf.keras.preprocessing.sequence.pad_sequences(trg, maxlen=64, padding='post')
trg = tf.concat(([word2idx['<start>']] * (trg.shape[0]-1)), axis=-1)
trg = tf.concat(([[word2idx.get(token.numpy(), word2idx['<unk>'])] for token in trg[:-1]], [[word2idx['<end>']]]), axis=-1)
return {"inputs": src}, {"outputs": trg}
# apply preprocessing to dataset
dataset = dataset.map(_preprocess)
dataset = dataset.batch(batch_size, drop_remainder=True)
# prefetch data into buffer
dataset = dataset.prefetch(buffer_size=tf.data.AUTOTUNE)
self._dataset = dataset
@property
def steps_per_epoch(self):
num_samples = sum([sum(1 for _ in open(f,'r'))//2 for f in ['./train.txt']])
return num_samples // self._batch_size
@property
def total_steps(self):
num_samples = sum([sum(1 for _ in open(f,'r'))//2 for f in ['./train.txt', './dev.txt', './test.txt']])
return num_samples // self._batch_size
def get_generator(self):
return iter(self._dataset)
代码解读
4.7 数据处理
import tensorflow as tf
import numpy as np
max_len = 64
num_examples = None
def preprocess_sentence(sent):
"""
sent : sentence containing space separated words
"""
sent = '<start>'+sent+'<end>'
return sent
def load_dataset(path, num_examples=None):
"""Load sentences and labels from path."""
sentences = []
labels = []
lang_labels=[]
with open(path, encoding="utf-8") as f:
lines = f.read().strip().split("\n")
count = 0
for line in lines:
source, target = line.split("\t")
source = preprocess_sentence(source)
target = preprocess_sentence(target)
if len(source.split()) > max_len or len(target.split()) > max_len:
continue
sentences.append(source)
labels.append(target)
lang_labels.append(count % 4)
if num_examples is not None:
count += 1
if count >= num_examples:
break
print(sentences[0])
print(labels[0])
return sentences, labels,lang_labels
sentences_train, labels_train,lang_labels_train=load_dataset('./train.txt', num_examples)
sentences_val, labels_val,lang_labels_val=load_dataset('./dev.txt', num_examples)
sentences_test, labels_test,lang_labels_test=load_dataset('./test.txt', num_examples)
print(len(sentences_train))
print(len(sentences_val))
print(len(sentences_test))
代码解读
4.8 源语言数据处理
from collections import Counter
import re
vocab_size = 30000
tokenizer = Tokenizer(num_words=vocab_size, oov_token="<unk>", lower=False)
tokenizer.fit_on_texts(sentences_train)
train_seq = tokenizer.texts_to_sequences(sentences_train)
train_seq = pad_sequences(train_seq, maxlen=max_len)
train_labels = tokenizer.texts_to_sequences(labels_train)
train_labels = pad_sequences(train_labels, maxlen=max_len)
word_index = tokenizer.word_index
embedding_dim = 256
"""Creating an embedding matrix"""
num_tokens = len(word_index) + 1
hits = 0
misses = 0
embedding_matrix = np.zeros((num_tokens, embedding_dim))
for word, i in word_index.items():
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
# Words not found in embedding index will be all-zeros.
# This includes the representation for "padding" and "OOV"
embedding_matrix[i] = embedding_vector
hits += 1
else:
misses += 1
print("Converted {} words ({:.2f}% of tokens)".format(hits, 100.0 * hits / num_tokens))
print("Missed {}".format(misses))
del embeddings_index
代码解读
4.9 目标语言数据处理
from keras_preprocessing.text import Tokenizer
from keras_preprocessing.sequence import pad_sequences
tokenizer = Tokenizer(oov_token='<unk>', filters='')
tokenizer.fit_on_texts(labels_train)
label_word_index = tokenizer.word_index
train_label_seq = tokenizer.texts_to_sequences(labels_train)
train_label_seq = pad_sequences(train_label_seq, padding='post', maxlen=max_len)
reverse_word_index = dict([(value, key) for (key, value) in label_word_index.items()])
def decode_review(text):
return''.join([reverse_word_index.get(i, '?') for i in text])
print(decode_review(train_label_seq[0]))
代码解读
4.10 数据批处理
BATCH_SIZE = 32
BUFFER_SIZE = 10000
train_dataset = tf.data.Dataset.from_tensor_slices((train_seq, train_label_seq))
train_dataset = train_dataset.cache()
train_dataset = train_dataset.shuffle(BUFFER_SIZE)
train_dataset = train_dataset.padded_batch(BATCH_SIZE, padded_shapes=([-1],[max_len]))
train_dataset = train_dataset.prefetch(tf.data.experimental.AUTOTUNE)
代码解读
4.11 模型构建
from tensorflow.keras.layers import Input, Embedding, LSTM, Dense, Concatenate
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
from sklearn.model_selection import KFold
num_layers = 2
units = 256
def build_model():
inputs = Input(shape=(None,))
embedding_layer = Embedding(vocab_size, embedding_dim, mask_zero=True)(inputs)
encoder = LSTM(units, return_state=True, name='encoder')
enc_output, enc_hidden, c_state = encoder(embedding_layer)
enc_states = [enc_hidden, c_state]
decoder_inputs = Input(shape=(None,), name='decoder_inputs')
dec_embedding = Embedding(vocab_size, embedding_dim, mask_zero=True)(decoder_inputs)
decoder_lstm = LSTM(units, return_sequences=True, return_state=True, name='decoder_lstm')
dec_outputs, _, _ = decoder_lstm(dec_embedding, initial_state=enc_states)
outputs = Dense(vocab_size, activation='softmax')(dec_outputs)
model = Model([inputs, decoder_inputs], outputs)
optimizer = Adam(lr=0.001)
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
model.compile(optimizer=optimizer, loss=[loss])
return model
model = build_model()
print(model.summary())
代码解读
4.12 损失函数
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
def loss_function(real, pred):
mask = tf.math.logical_not(tf.math.equal(real, 0))
loss_ = loss_object(real, pred)
mask = tf.cast(mask, dtype=loss_.dtype)
loss_ *= mask
return tf.reduce_mean(loss_)
代码解读
4.13 优化器设置
optimizer = tf.keras.optimizers.Adam()
代码解读
4.14 训练模型
history = model.fit(train_dataset, epochs=NUM_EPOCHS, validation_data=val_dataset, verbose=1)
代码解读
4.15 模型评估
loss = history.history['loss']
val_loss = history.history['val_loss']
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
代码解读
4.16 模型预测
def predict_sentence(sentence):
sentence = preprocess_sentence(sentence)
sequence = tokenizer.texts_to_sequences([sentence])[0][:max_len]
sequence = pad_sequences([sequence], maxlen=max_len)
translation = translate_model.predict([sequence])
predicted_sentence = tokenizer.sequences_to_texts(translation)[0]
final_sentence = re.sub("<.*?>","",predicted_sentence)
return final_sentence
translate_sentence = predict_sentence(sentences[0])
print(translate_sentence)
代码解读
