五、文本生成、机器翻译和其他重复性语言建模任务
在第四章,我向你介绍了一些更先进的深度学习和 NLP 技术,我讨论了如何在一些基本问题中实现这些模型,比如映射词向量。在我们结束这本书之前,我将讨论一些其他的 NLP 任务,这些任务更特定于领域,但是仍然很有用。
至此,您应该对预处理各种格式的文本数据比较熟悉了,并且应该理解一些 NLP 任务,比如文档分类,并足以执行它们。因此,这一章通过解决几个问题,将重点放在结合我们所学的许多技能上。本章提供的所有解决方案都是可行的。我们非常欢迎您提出或完成超越他们的新解决方案。
使用 LSTMs 生成文本
文本生成在基于人工智能的工具中越来越重要。特别是在处理大量数据时,系统能够与用户进行通信以提供更加身临其境和信息丰富的体验是非常有用的。对于文本生成,主要目标是创建一个生成模型,提供某种关于数据的洞察力。您应该知道,文本生成不一定要创建文档的摘要,而是生成描述输入文本的输出。让我们从检查问题开始。
最初,对于这样的任务,我们需要一个数据源。由此,我们的数据源改变了结果。对于这个任务,我们从《哈利·波特与魔法石》开始。我选择了这本书,因为上下文应该提供一些关于包含在生成的文本中的主题的相当显著的结果。
让我们来回顾一下我们已经习惯的步骤。我们将利用我们在word_embeddings.py
中使用的load_data()
预处理函数;然而,我们唯一要做的改变是加载harry_potter.pdf
而不是economics_textbook.pdf
。
也就是说,只要目录和其他参数发生变化,这个函数就可以让您轻松地将预处理函数用于任何目的。因为这是一个文本生成示例,所以除了删除非 ASCII 字符之外,我们不应该清理数据。
以下是数据显示方式的示例:
“Harry Potter Sorcerer's Stone CHAPTER ONE THE BOY WHO LIVED Mr. Mrs. Dursley, number four, Privet Drive, proud say perfectly normal, thank much. They last people 'd expect involved anything strange mysterious, n't hold nonsense. Mr. Dursley director firm called Grunnings, made drills. He big, beefy man hardly neck, although large mustache. Mrs. Dursley thin blonde nearly twice usual amount neck, came useful spent much time craning garden fences, spying neighbors. The Dursleys small son called Dudley opinion finer boy anywhere. The Dursleys everything wanted, also secret, greatest fear somebody would discover. They think could bear anyone found Potters. Mrs. Potter Mrs. Dursley's sister, n't met several years; fact, Mrs. Dursley pretended n't sister, sister good-for-nothing husband unDursleyish possible. The Dursleys shuddered think neighbors would say Potters arrived street. The Dursleys knew Potters small son,, never even seen. This boy another good reason keeping Potters away; n't want Dudley mixing child like. When Mr. Mrs. Dursley woke dull, gray Tuesday story starts, nothing cloudy sky outside suggest strange mysterious things would soon happening country. Mr. Dursley hummed picked boring tie work, Mrs. Dursley gossiped away happily wrestled screaming Dudley high chair. None noticed large, tawny owl flutter past window. At half past eight, Mr. Dursley picked briefcase, pecked Mrs. Dursley cheek, tried kiss Dudley good-bye missed, 1 Dudley tantrum throwing cereal walls. `` Little tyke, "chortled Mr. Dursley left house. He got car backed number four's drive. It corner street noticed first sign something peculiar -- cat reading map. For second, Mr. Dursley n't realize seen -- jerked head around look. There tabby cat standing corner Privet Drive, n't map sight. What could thinking ? It must trick light. Mr. Dursley blinked stared cat. It stared back. As Mr. Dursley drove around corner road, watched cat mirror. It reading sign said Privet Drive --, looking sign; cats...”
让我们检查一下我们的预处理函数。
def preprocess_data(sequence_length=sequence_length, max_pages=max_pages, pdf_file=pdf_file):
text_data = load_data(max_pages=max_pages, pdf_file=pdf_file)
characters = list(set(text_data.lower()))
character_dict = dict((character, i) for i, character in enumerate(characters))
int_dictionary = dict((i, character) for i, character in enumerate(characters))
num_chars, vocab_size = len(text_data), len(characters)
x, y = [], []
for i in range(0, num_chars - sequence_length, 1):
input_sequence = text_data[i: i+sequence_length]
output_sequence = text_data[i+sequence_length]
x.append([character_dict[character.lower()] for character in input_sequence])
y.append(character_dict[output_sequence.lower()])
for k in range(0, len(x)): x[i] = [_x for _x in x[i]]
x = np.reshape(x, (len(x), sequence_length, 1))
x, y = x/float(vocab_size), np_utils.to_categorical(y)
return x, y, num_chars, vocab_size, character_dict, int_dictionary
在检查函数时,我们使用了类似于 Skip-Gram 模型的玩具示例中的tf_preprocess_data()
函数的方法。我们的输入和输出序列是固定长度的,我们将把 y 变量转换成一个一次性编码的向量,向量中的每个条目代表一个可能的字符。我们将字符序列表示为一个矩阵,其中每行代表整个观察值,每列代表一个字符。
让我们看看书中使用的 Keras 代码的第一个例子。
def create_rnn(num_units=num_units, activation=activation):
model = Sequential()
model.add(LSTM(num_units, activation=activation, input_shape=(None, x.shape[1])))
model.add(Dense(y.shape[1], activation="softmax"))
model.compile(loss='categorical_crossentropy', optimizer="adam")
model.summary()
return model
Keras 不像 TensorFlow 那样冗长。因此,这使得改变模型的架构变得相对容易。我们通过给一个变量赋值来实例化一个模型,然后简单地用Sequential().add()
函数添加层类型。
在用 200 个历元运行网络之后,我们得到以下结果:
driv, proud say perfecdly normal, thanp much. they last people 'd expect involved anytsing strange mysterious, s't hold donsense. mr. dursley director firm called grunnings, made drills. he big, berfy man, ardly neck, althougl larte mustache. mrs. dursley thic -londe. early twece uiual amount necd, came ueeful spent much time craning geddon fences, spying neighbors. the dursleys small son called dudley opinion finer boy anyw rd. the dursleys everything wanted, slso secret, greatest fear somebody would discover. they thinn could bear antone found potters. mrs. potterimrs. dursley's sister, n't met several years; fact, mrs. dursley pretended n't sister, sister good-sur-notding husband undursleyir pousible. the dursleys suuddered think auigybors would say potters arrived strett. the dursleys knew potters small. on, ever even seen. thit boy another good reason keeping potters away; n'e want dudley mixing child like. wten mr. mrs. dursley woke dull, gray tuesday story startss, nothing cloudy skycoutside suggest strange mytter ous taings would soon darpening codntry. mr. dursley tummed picked boring tie work, mrs. dursley gosudaed away happily wrestled screaming dudley aigh cuair. noneoloticed large, tawny owl flutter past wincow. at ialf past, ight, mr. dursley picked briefcase, pecked mrs. dursley cheek, tried kiss dudley good-bye missed, 1 dudley tantrum,hrowigg cereal walls. `` lwttle tykp, "chortled mr. dursley left house. he got car backel number four's drive. it corner street noticed fir t sign somathing pcculilr -- cat feading,ap. for sicond, mr. dursley r't realize scen -- jerked head around look. thereytab y cat standing corneraprivet drive, n'tamap sight. what sould thinking ? it muse trick light. mr. dursley blinked stared cat. it stayed back. as mr. dursley drove around corner road, watched catcmirror. it reading sign saidsprivet druve --, lookingtsign; cats could n't read maps signs. mr. durs
注意
有些文本是可以理解的,但显然不是所有的都尽如人意。在这种情况下,我建议您允许神经网络训练更长时间,并添加更多数据。还要考虑使用不同的模型和模型架构。除了这个例子之外,提出一个对语音建模也有用的 LSTM 的更高级版本将是有用的。
双向 RNNs (BRNN)
BRNNs 是由 Mike Schuster 和 Kukdip Paliwal 在 1997 年创建的,他们将这项技术介绍给了一家信号处理学术期刊。该模型的目的是利用在“正向和负向时间方向”上移动的信息具体来说,他们希望利用向预测方向移动的信息,以及向相反方向移动的相同输入流。图 5-1 展示了 BRNN 的架构。
图 5-1
双向 RNN
让我们想象一下,我们有一个单词序列,如下所示:这个人走在木板路上。
在常规的 RNN 中,假设我们想要预测单词 boardwalk ,输入数据将是的、的、的步行、向下、的。如果我们输入二元模型,它将是*、人、人、行走等等。我们继续遍历输入数据,预测在每个时间步长下最有可能出现的单词,最终得到我们的最终目标标签,这是一个概率,对应于在给定输入数据的情况下最有可能出现的独热编码向量。BRNN 的唯一区别是,当我们从左到右预测序列时,我们也从右到左预测序列。*
*BRNNs 对于 NLP 任务特别有用。以下是构建 BRNN 的代码:
def create_lstm(input_shape=(1, x.shape[1])):
model = Sequential()
model.add(Bidirectional(LSTM(unites=n_units,
activation=activation),
input_shape=input_shape))
model.add(Dense(train_y.shape[1]), activation=out_act)
model.compile(loss='categorical_crossentropy', metrics=['accuracy'])
return model
双向 RNN 的结构几乎是相同的,我们只是在我们的层上添加了一个Bidirectional()
角色。这通常会增加训练神经网络所需的时间,但总的来说,它在许多任务中优于传统的 RNN 体系结构。记住这一点,让我们应用我们的模型。
创建名称实体识别标记器
使用 NLTK 或类似软件包的人可能会遇到名称实体识别 (NER)标记者。NER 标签通常输出在较大类别(个人、组织、位置等)中识别实体的标签。).创建一个 NER 标记者需要大量带注释的数据。
对于这个任务,我们将使用来自 Kaggle 的数据集。当我们解压缩数据时,我们看到它采用以下格式:
played on Monday ( home team in CAPS ) :
VBD IN NNP ( NN NN IN NNP ) :
O O O O O O O O 0 O
American League
NNP NNP
B-MISC I-MISC
Cleveland 2 DETROIT 1
NNP CD NNP CD
B-ORG O B-ORG O
BALTIMORE 12 Oakland 11 ( 10 innings )
VB CD NNP CD ( CD NN )
B-ORG O B-ORG O O O O O
TORONTO 5 Minnesota 3
TO CD NNP CD
B-ORG O B-ORG O
Milwaukee 3 CHICAGO 2
NNP CD NNP CD
B-ORG O B-ORG O
Boston 4 CALIFORNIA 1
数据是制表符分隔的,但也是.txt
格式。在我们开始训练 BRNN 之前,这需要一些数据争论。
让我们从将文本数据转换成可解释的格式开始,如下所示:
def load_data():
text_data = open('/Users/tawehbeysolow/Downloads/train.txt', 'rb').readlines()
text_data = [text_data[k].replace('\t', ' ').split() for k in range(0, len(text_data))]
index = range(0, len(text_data), 3)
#Transforming data to matrix format for neural network
input_data = list()
for i in range(1, len(index)-1):
rows = text_data[index[i-1]:index[i]]
sentence_no = np.array([i for i in np.repeat(i, len(rows[0]))], dtype=str)
rows.append(np.array(sentence_no))
rows = np.array(rows).T
input_data.append(rows)
我们必须首先遍历.txt
文件的每一行。请注意,数据被组织成三个一组。典型的分组如下所示:
text_data[0]
['played', 'on', 'Monday', '(', 'home', 'team', 'in', 'CAPS', ')', ':']
text_data[1]
['VBD', 'IN', 'NNP', '(', 'NN', 'NN', 'IN', 'NNP', ')', ':']
text_data[2]
['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O']
第一组观察包含文本本身,第二组观察包含名称实体标记,最后一组包含特定标记。回到预处理函数,我们对句子进行分组,并添加一个包含句子编号标签的数组,稍后我将讨论它的重要性。
当查看input_data
变量的快照时,我们会看到以下内容:
input_data[0:1]
[array([['played', 'VBD', 'O', '1'],
['on', 'IN', 'O', '1'],
['Monday', 'NNP', 'O', '1'],
['(', '(', 'O', '1'],
['home', 'NN', 'O', '1'],
['team', 'NN', 'O', '1'],
['in', 'IN', 'O', '1'],
['CAPS', 'NNP', 'O', '1'],
[')', ')', 'O', '1'],
[':', ':', 'O', '1']], dtype='|S6')]
我们需要删除句子标签,同时以这样一种方式观察数据,即神经网络隐含地理解这些句子是如何分组的。我们想要去除这个标签的原因是神经网络以这样一种方式读取分类标签(句子编号是其模拟),即编号较高的句子明显比编号较低的句子具有更大的重要性。对于这项任务,我想你们大多数人都明白,我们而不是想把它融入培训过程。因此,我们转到以下代码体:
input_data = pan.DataFrame(np.concatenate([input_data[j] for j in range(0,len(input_data))]),
columns=['word', 'pos', 'tag', 'sent_no'])
labels, vocabulary = list(set(input_data['tag'].values)), list(set(input_data['word'].values))
vocabulary.append('endpad'); vocab_size = len(vocabulary); label_size = len(labels)
aggregate_function = lambda input: [(word, pos, label) for word, pos, label in zip(input['word'].values.tolist(),
input['pos'].values.tolist(),
input['tag'].values.tolist())]
我们将input_data
组织到一个数据帧中,然后创建几个其他变量,我们将在后面的函数中使用,以及train_brnn_keras()
函数。其中一些变量与前一章脚本中的其他变量很相似(例如,vocab_size
代表词汇表中的单词数)。但重要的部分主要是后两个变量,这才是你解决这个问题要重点关注的。
lambda 函数aggregate_function
将一个数据帧作为输入,然后为一个分组中的每个观察值返回一个三元组。这正是我们将如何把所有的观察组合在一句话里。转换后的数据快照如下:
sentences[0]
[('played', 'VBD', 'O'), ('on', 'IN', 'O'), ('Monday', 'NNP', 'O'), ('(', '(', 'O'), ('home', 'NN', 'O'), ('team', 'NN', 'O'), ('in', 'IN', 'O'), ('CAPS', 'NNP', 'O'), (')', ')', 'O'), (':', ':', 'O')]
我们几乎已经完成了所有必要的预处理;然而,有一个关键步骤你应该知道。
x = [[word_dictionary[word[0]] for word in sent] for sent in sentences]
x = pad_sequences(maxlen=input_shape, sequences=x, padding="post", value=0)
y = [[label_dictionary[word[2]] for word in sent] for sent in sentences]
y = pad_sequences(maxlen=input_shape, sequences=y, padding="post", value=0)
= [np_utils.to_categorical(label, num_classes=label_size) for label in y]
在前面的代码行中,我们像在许多其他示例中一样,将单词转换为它们的整数标签,并创建一个独热编码矩阵。这与上一章类似,但是,我们应该明确地不使用pad_sequences()
函数。
当处理句子数据时,我们并不总是得到等长的句子;然而,神经网络的输入矩阵必须在所有观测值中具有相同数量的特征。零填充用于添加额外的特征,使所有观察值的大小标准化。
完成这一步后,我们现在准备开始训练我们的神经网络。我们的模型如下:
def create_brnn():
model = Sequential()
model.add(Embedding(input_dim=vocab_size+1, output_dim=output_dim,
input_length=input_shape, mask_zero=True))
model.add(Bidirectional(LSTM(units=n_units, activation=activation,
return_sequences=True)))
model.add(TimeDistributed(Dense(label_size, activation=out_act)))
model.compile(optimizer='adam', loss="categorical_crossentropy", metrics=['accuracy'])
model.summary()
return model
我们的大部分模型类似于本章中建立的先前的 Keras 模型;然而,我们有一个嵌入层(类似于单词嵌入)堆叠在双向 LSTM 的顶部,该层随后堆叠在完全连接的输出层的顶部。
我们用大约 90%的数据训练我们的网络,然后评估结果。我们发现,我们在训练数据上的标记器产生了 90%甚至更高的准确率,这取决于我们训练它的时期数。
既然我们已经处理了这个分类任务,并且充分地使用了 BRNNs,那么让我们转到另一个神经网络模型,并且讨论它如何能够有效地应用于另一个 NLP 任务。
序列间模型(Seq2Seq)
序列到序列模型(seq2seq)值得注意,因为它们接受输入序列并返回输出序列,两者的长度都是可变的。这使得该模型特别强大,并且它倾向于在语言建模任务中表现良好。Sutskever 等人在一篇论文中对我们将利用的特定模型进行了最佳总结。图 5-2 展示了该模型。
图 5-2
编码器-解码器模型
该模型通常由两部分组成:编码器和解码器。编码器和解码器都是 rnn。编码器读取输入序列,并从 LSTM 单元输出除隐藏和单元状态之外的固定长度向量。随后,除了输出隐藏和单元状态之外,解码器还获取这个固定长度的向量,并将它们用作其第一个 LSTM 单元的输入。解码器输出一个固定长度的向量,我们将其作为目标标签。我们将一次一个字符地执行预测,这使我们能够很容易地评估从一个观察到下一个观察的不同长度的序列。接下来,您将看到这个模型的运行。
神经网络模型问答
深度学习在 NLP 中的一个流行应用是聊天机器人。许多公司使用聊天机器人来处理一般的客户服务请求,这需要他们灵活地将问题转化为答案。虽然我们看到的测试案例是问题和答案的缩影,但它是我们如何训练神经网络正确回答问题的一个例子。我们将使用斯坦福问答数据集。虽然它更能代表一般的知识,但你最好能认识到这些问题的结构方式。
让我们从研究如何利用以下函数预处理数据开始:
dataset = json.load(open('/Users/tawehbeysolow/Downloads/qadataset.json', 'rb'))['data']
questions, answers = [], []
for j in range(0, len(dataset)):
for k in range(0, len(dataset[j])):
for i in range(0, len(dataset[j]['paragraphs'][k]['qas'])):
questions.append(remove_non_ascii(dataset[j]['paragraphs'][k]['qas'][i]['question'])) answers.append(remove_non_ascii(dataset[j]['paragraphs'][k]['qas'][i]['answers'][0]['text']))
当我们查看数据快照时,我们观察到以下结构:
[{u'paragraphs': [{u'qas': [{u'question': u'To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?', u'id': u'5733be284776f41900661182', u'answers': [{u'text': u'Saint Bernadette Soubirous', u'answer_start': 515}]}, {u'question': u'What is in front of the Notre Dame Main Building?', u'id': u'5733be284776f4190066117f', u'answers': [{u'text': u'a copper statue of Christ', u'answer_start': 188}]}, {u'question': u'The Basilica of the Sacred heart at Notre Dame is beside to which structure?', u'id': u'5733be284776f41900661180', u'answers': [{u'text': u'the Main Building', u'answer_start': 279}]}, {u'question': u'What is the Grotto at Notre Dame?', u'id': u'5733be284776f41900661181', u'answers': [{u'text': u'a Marian place of prayer and reflection', u'answer_start': 381}]}, {u'question': u'What sits on top of the Main Building at Notre Dame?', u'id': u'5733be284776f4190066117e', u'answers': [{u'text': u'a golden statue of the Virgin Mary', u'answer_start': 92}]}], u'context': u'Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend "Venite Ad Me Omnes". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858\. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.'}, {u'qas': [{u'question': u'When did the Scholastic Magazine of Notre dame begin publishing?', u'id': u'5733bf84d058e614000b61be', u'answers'
我们有一个 JSON 文件,里面有问题和答案。与名称实体识别任务类似,我们需要将数据预处理成矩阵格式,以便输入到神经网络中。我们必须首先收集与正确答案相对应的问题。然后我们遍历 JSON 文件,将每个问题和答案附加到相应的数组中。
现在让我们讨论一下,我们实际上是如何为神经网络构建问题的。我们不是让神经网络预测每个单词,而是让神经网络预测给定字符输入序列的每个字符。由于这是一个多标签分类问题,我们将为输出向量的每个元素输出一个 softmax 概率,然后选择概率最高的向量。这表示在给定先前输入序列的情况下最有可能继续的字符。
在我们对整个输出序列做了这些之后,我们将连接这个输出字符的数组,这样我们就得到一个人类可读的消息。因此,我们转到代码的以下部分:
input_chars, output_chars = set(), set()
for i in range(0, len(questions)):
for char in questions[i]:
if char not in input_chars: input_chars.add(char.lower())
for i in range(0, len(answers)):
for char in answers[i]:
if char not in output_chars: output_chars.add(char.lower())
input_chars, output_chars = sorted(list(input_chars)), sorted(list(output_chars))
n_encoder_tokens, n_decoder_tokens = len(input_chars), len(output_chars)
我们遍历了每个问题和答案,并收集了输出和输入序列中所有独特的单个字符。这产生了下面的集合,它们分别代表输入和输出字符。
input_chars; output_chars
[u' ', u'"', u'#', u'%', u'&', u"'", u'(', u')', u',', u'-', u'.', u'/', u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9', u':', u';', u'>', u'?', u'_', u'a', u'b', u'c', u'd', u'e', u'f', u'g', u'h', u'i', u'j', u'k', u'l', u'm', u'n', u'o', u'p', u'q', u'r', u's', u't', u'u', u'v', u'w', u'x', u'y', u'z']
[u' ', u'!', u'"', u'$', u'%', u'&', u"'", u'(', u')', u'+', u',', u'-', u'.', u'/', u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9', u':', u';', u'?', u'[', u']', u'a', u'b', u'c', u'd', u'e', u'f', u'g', u'h', u'i', u'j', u'k', u'l', u'm', u'n', u'o', u'p', u'q', u'r', u's', u't', u'u', u'v', u'w', u'x', u'y', u'z']
这两个列表分别包含 53 和 55 个字符;然而,它们实际上是同质的,包含字母表中的所有字母,加上一些语法和数字字符。
我们转到预处理的最重要部分,其中我们将输入序列转换为神经网络可以解释的独热编码向量。
(code redacted, please see github)
x_encoder = np.zeros((len(questions), max_encoder_len, n_encoder_tokens))
x_decoder = np.zeros((len(questions), max_decoder_len, n_decoder_tokens))
y_decoder = np.zeros((len(questions), max_decoder_len, n_decoder_tokens))
for i, (input, output) in enumerate(zip(questions, answers)):
for _character, character in enumerate(input):
x_encoder[i, _character, input_dictionary[character.lower()]] = 1.
for _character, character in enumerate(output):
x_decoder[i, _character, output_dictionary[character.lower()]] = 1.
if i > 0: y_decoder[i, _character, output_dictionary[character.lower()]] = 1.
我们首先实例化两个输入向量和一个输出向量,用x_encoder
、x_decoder
和y_encoder
表示。接下来,这表示数据通过神经网络并对照目标标签进行验证的顺序。虽然我们在这里选择创建的一次性编码是相似的,但是我们通过创建一个三维数组来评估每个问题和答案,做了一点小小的改变。每行代表一个问题,每个时间步长代表一个字符,每列代表我们的字符集中的字符类型。我们对每个问答对重复这个过程,直到我们有一个包含整个数据集的数组,它产生 4980 个数据观察值。
最后一步定义模型,由encoder_decoder()
函数给出。
def encoder_decoder(n_encoder_tokens, n_decoder_tokens):
encoder_input = Input(shape=(None, n_encoder_tokens))
encoder = LSTM(n_units, return_state=True)
encoder_output, hidden_state, cell_state = encoder(encoder_input)
encoder_states = [hidden_state, cell_state]
decoder_input = Input(shape=(None, n_decoder_tokens))
decoder = LSTM(n_units, return_state=True, return_sequences=True)
decoder_output, _, _ = decoder(decoder_input, initial_state=encoder_states)
decoder = Dense(n_decoder_tokens, activation="softmax")(decoder_output)
model = Model([encoder_input, decoder_input], decoder)
model.compile(optimizer='adam', loss="categorical_crossentropy", metrics=['accuracy'])
model.summary()
return model
我们实例化的模型与其他 Keras 模型略有不同。这种创建模型的方法是通过使用函数式 API 来完成的,而不是像我们经常做的那样依赖于顺序模型。具体来说,这种方法在创建更复杂的模型时非常有用,比如 seq2seq 模型,而且一旦您学会了如何使用顺序模型,这种方法就相对简单了。我们没有向顺序模型添加层,而是将不同的层实例化为变量,然后通过调用我们创建的张量来传递数据。当我们通过调用 encoder(encoder_input)实例化变量encoder_output
时,我们看到了这一点。我们在编码器-解码器阶段一直这样做,直到我们得到一个输出向量,我们将其定义为具有 softmax 激活功能的密集/全连接层。
最后,我们转向培训,观察以下结果:
Model Prediction: saint bernadette soubiroust
Actual Output: saint bernadette soubirous
Model Prediction: a copper statue of christ
Actual Output: a copper statue of christ
Model Prediction: the main building
Actual Output: the main building
Model Prediction: a marian place of prayer and reflection
Actual Output: a marian place of prayer and reflection
Model Prediction: a golden statue of the virgin mary
Actual Output: a golden statue of the virgin mary
Model Prediction: september 18760
Actual Output: september 1876
Model Prediction: twice
Actual Output: twice
Model Prediction: the observer
Actual Output: the observer
Model Prediction: three
Actual Output: three
Model Prediction: 19877
Actual Output: 1987
如您所见,这个模型表现相当好,只有三个纪元。尽管由于添加了额外的字符,在拼写上有一些问题,但在大多数情况下,消息本身是正确的。您可以继续尝试这个问题,特别是通过改变模型架构来看看是否有一个能产生更好的准确性。
摘要
随着本章接近尾声,我们应该回顾一下在帮助我们成功训练算法方面最重要的概念。首先,您应该注意适用于不同问题的模型类型。编码器-解码器模型体系结构引入了“多对多”输入-输出方案,并显示了它适用的场合。
其次,您应该注意预处理技术在哪里可以应用于看似不同但相关的问题。从一种语言到另一种语言的数据翻译使用与创建基于不同响应回答问题的神经网络相同的预处理步骤。注意这些建模步骤以及它们如何与数据的底层结构相关联,可以在看似无关紧要的任务上节省时间。
结论和最后陈述
我们已经读完了这本书。我们解决了各种复杂程度和领域的 NLP 问题。有许多概念在所有问题类型中都是不变的,尤其是数据预处理。让机器学习变得困难的绝大多数是预处理数据。您看到了相似的问题类型共享预处理步骤,因为当我们转向更高级的问题时,我们经常重用部分解决方案。
从现在开始,有一些最后的原则值得记住。深度学习的 NLP 可能需要大量的文本数据。认真负责地收集数据,并在处理大型数据集时考虑选择优化运行时的语言(C/C++还是 Python 等)。).
总的来说,神经网络是相当简单的工作模型。困难在于找到具有预测能力的好数据,此外还要以一种我们的神经网络可以找到模式来利用的方式来构建数据。
仔细研究文档分类的预处理步骤,例如,创建一个单词嵌入,或者创建一个 NER 标签。其中的每一个都代表了可以应用于不同问题的特征提取方案,并为您的研究指明了前进的道路。
虽然在机器学习社区中经常谈到数据的智能预处理,但这对于深度学习和数据科学的 NLP 范式来说尤其如此。我们训练的模型为您提供了如何在专业或学术环境中处理类似数据集的路线图。然而,这并不意味着我们已经部署的模型可以在生产中使用并且工作良好。
有相当多的变量我没有讨论,因为它们是维护生产系统的问题,而不是模型背后的理论。例子包括词汇表中随着时间的推移出现的未知单词,何时重新训练模型,如何同时评估多个模型的输出,等等。
根据我的经验,通过收集大量实时性能数据,可以很好地解决何时重新训练模型的问题。观察信号何时不被接受,如果它们确实不被接受的话,并跟踪再训练的效果,以及模型再训练的持久性。即使你的模型是准确的,也不意味着它在实践中会很容易使用。
仔细考虑如何处理错误分类,特别是如果错误分类的惩罚可能导致金钱和/或其他资源的损失。不要害怕对多种问题类型使用多种模型。实验时,从简单开始,根据需要逐渐增加复杂性。这比一开始试图设计非常复杂的东西,然后试图调试一个你不理解的系统要容易得多。
除了利用我的 GitHub 页面上的代码以自己独特的方式解决问题之外,我们鼓励您在闲暇时重读这本书,并作为参考。虽然阅读这本书提供了一个开始,但精通数据科学的唯一方法是自己实践这些问题。
我希望你喜欢学习自然语言处理和深度学习,就像我喜欢解释它一样。*