Keras词级自然语言模型

简介: 语言模型是许多自然语言处理模型(如机器翻译和语音识别)中的关键元素,本文就两个小例子带你创建简单的语言模型,附加中有源码!

更多深度文章,请关注云计算频道:https://yq.aliyun.com/cloud


语言模型是许多自然语言处理模型(如机器翻译和语音识别)中的关键元素,它可以根据给出的单词序列预测到序列中的下一个单词。在选择语言模型的框架时需要注意与语言模型的目的匹配。


本教程分为5个部分; 他们是:

1.语言建模框架。

2.模型1:单字输入,单字输出序列。

3.模型2:逐行序列。

4.模型3:双字输入,单字输出序列。

1、语言建模框架

语言模型是挑战自然语言处理问题(如机器翻译和语音识别)较大模型中的关键组成部分。它们也可以作为独立模型开发,用于生成与源文本具有相同统计属性的新序列。

语言模型可以同时学习和预测一个单词。网络的训练包括提供一系列的单词作为输入,在这个过程中,每一个输入序列都可以进行预测和学习。

同样地,在进行预测时,可以用一个或几个单词来表示这个过程,然后将预测得到的单词进行收集,作为对后续预测输入的源文本。

因此,每个模型都涉及将源文本分解为输入和输出序列,使得模型可以学习如何预测单词。

在本教程中,我们将探索在深度学习库Keras中开发基于单词的语言模型的三种不同方式。

模型1:单字输入,单字输出序列

我们可以从一个非常简单的模型开始。

给定一个单词作为输入,模型将学习预测序列中的下一个单词。例如:

X, y
Jack, and
and, Jill
Jill, went

第一步是将文本编码化为整数。

源文本中的每个小写单词都被赋予一个唯一的整数,我们可以将单词序列转换为整数序列。

Keras提供可用于执行此编码的Tokenizer类。首先,Tokenizer适合源文本来开发从单词到唯一整数的映射。然后,通过调用texts_to_sequences()函数将文本序列转换为整数序列。

# integer encode text
tokenizer = Tokenizer()
tokenizer.fit_on_texts([data])
encoded = tokenizer.texts_to_sequences([data])[0]

我们需要通过访问word_index类,从经过训练的Tokenizer中检索词汇表的大小得出以后的词汇的大小,以便在模型中定义词的嵌入层,并且使用一个热编码来编码输出词。

# determine the vocabulary size
vocab_size = len(tokenizer.word_index) + 1
print('Vocabulary Size: %d' % vocab_size)

在这个例子中,我们可以看到词汇的大小是21个单词。

因为我们需要将最大的编码的整数指定为一个数组索引,例如编码1到21的数组编号为0到21或22的位置,所以我们需要再添加一个编码。

接下来,我们需要创建一个单词序列,以一个单词作为输入,一个单词作为输出来匹配模型。

# create word -> word sequences
sequences = list()
for i in range(1, len(encoded)):
sequence = encoded[i-1:i+1]
sequences.append(sequence)
print('Total Sequences: %d' % len(sequences))

运行这一块显示,我们总共有24个输入输出来训练网络。

Total Sequences: 24

然后,我们可以将这些序列分解为输入(X)和输出元素(y)。这很简单,因为我们只有两列数据。

# split into X and y elements
sequences = array(sequences)
X, y = sequences[:,0],sequences[:,1]

我们将使用我们的模型来预测词汇表中所有单词的概率分布。这意味着我们需要将输出元素从单个整数转换为一个热门编码,对于词汇表中的每个单词都有一个0,对于实际单词来说,该值为1。这为网络提供了一个基本事实,从中可以计算出错误的地方并更新模型。

Keras提供了to_categorical()函数,我们可以使用该函数将整数转换为一个热门编码,同时指定类的数量作为词汇量大小。

# one hot encode outputs
y = to_categorical(y, num_classes=vocab_size)

我们现在准备定义神经网络模型。

该模型使用了一个在输入层中的学习词。这对词汇表中的每个单词都有一个实值向量,其中每个单词向量都有一个指定的长度。在本例中,我们将使用10维投影。输入序列包含一个单词,因此input_length = 1。

该模型有一个隐藏的LSTM层,有50个单元。这远远超出了需要。在词汇表中,输出层由一个神经元组成,并使用一个softmax激活函数,以确保能够标准输出。

# define model
model = Sequential()
model.add(Embedding(vocab_size, 10, input_length=1))
model.add(LSTM(50))
model.add(Dense(vocab_size, activation='softmax'))
print(model.summary())

网络的结构可以概括如下:

_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
embedding_1 (Embedding)      (None, 1, 10)             220
_________________________________________________________________
lstm_1 (LSTM)                (None, 50)                12200
_________________________________________________________________
dense_1 (Dense)              (None, 22)                1122
=================================================================
Total params: 13,542
Trainable params: 13,542
Non-trainable params: 0
_________________________________________________________________

接下来,我们可以在编码的文本数据上编译和适配网络。从技术上讲,我们正在使用分类交叉熵损失函数构建一个解决多元分类问题(预测词汇中的单词)的模型。

由于网络配置的原因, 我们选择了超出规定的配置,以确保我们可以专注于语言模型的框架上。

# compile network
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit network
model.fit(X, y, epochs=500, verbose=2)

模型拟合后,我们通过从词汇中传递给定的单词来测试它,让模型预测下一个单词。在这里,我们通过对“ Jack ”进行编码并调用model.predict_classes()来获取预测单词的整数输出。然后在词汇映射中查找这个关联词。

# evaluate
in_text = 'Jack'
print(in_text)
encoded = tokenizer.texts_to_sequences([in_text])[0]
encoded = array(encoded)
yhat = model.predict_classes(encoded, verbose=0)
for word, index in tokenizer.word_index.items():
if index == yhat:
print(word)

这个过程可以重复几次,以建立一个生成的单词序列。

为了使这更容易,我们将这个行为包含在一个函数中,我们可以通过传入我们的模型和种子词来调用这个函数。

# generate a sequence from the model
def generate_seq(model, tokenizer, seed_text, n_words):
in_text, result = seed_text, seed_text
# generate a fixed number of words
for _ in range(n_words):
# encode the text as integer
encoded = tokenizer.texts_to_sequences([in_text])[0]
encoded = array(encoded)
# predict a word in the vocabulary
yhat = model.predict_classes(encoded, verbose=0)
# map predicted word index to word
out_word = ''
for word, index in tokenizer.word_index.items():
if index == yhat:
out_word = word
break
# append to input
in_text, result = out_word, result + ' ' + out_word
return result

运行该示例,打印出每个训练时期的损失和准确性。

Epoch 496/500
0s - loss: 0.2358 - acc: 0.8750
Epoch 497/500
0s - loss: 0.2355 - acc: 0.8750
Epoch 498/500
0s - loss: 0.2352 - acc: 0.8750
Epoch 499/500
0s - loss: 0.2349 - acc: 0.8750
Epoch 500/500
0s - loss: 0.2346 - acc: 0.8750
我们可以看到,模型并不记住源序列,可能因为在输入序列中有一些不明确的地方模型并没有记住源序列。例如:

jack => and
jack => fell

在运行结束时,“ Jack ”被输入并产生预测或新的序列。

我们基于一些元素的来源,会得到一个合理的序列作为输出。

Jack and jill came tumbling after down

这是一个很好的语言模型,但是没有充分利用LSTM处理输入序列的能力问题,但通过使用更广泛的上下文来消除一些不明确的成对序列。

模型2:逐行序列

另一种方法是逐行分解源文本,然后将每行分解为一系列构建的单词。

例如:

X, y
_, _, _, _, _, Jack, and
_, _, _, _, Jack, and Jill
_, _, _, Jack, and, Jill, went
_, _, Jack, and, Jill, went, up
_, Jack, and, Jill, went, up, the
Jack, and, Jill, went, up, the, hill

这种方法可以允许模型使用每一行的上下文来帮助模型,在这种情况下,一个简单的单词进出模型会造成模糊性。

在这种情况下,这是以跨行预测单词为代价的,如果我们只对建模和生成文本行感兴趣,现在可能会很好。

请注意,在这种表示中,我们将需要填充序列以确保它们符合固定长度的输入。这是使用Keras时的要求。

首先,我们可以使用已经适合源文本的Tokenizer逐行地创建整数序列。

# create line-based sequences
sequences = list()
for line in data.split('\n'):
encoded = tokenizer.texts_to_sequences([line])[0]
for i in range(1, len(encoded)):
sequence = encoded[:i+1]
sequences.append(sequence)
print('Total Sequences: %d' % len(sequences))

接下来,我们可以填充准备好的序列。我们可以使用Keras中提供的pad_sequences()函数来做到这一点。首先涉及找到最长的序列,然后用它作为填充所有其他序列的长度。

# pad input sequences
max_length = max([len(seq) for seq in sequences])
sequences = pad_sequences(sequences, maxlen=max_length, padding='pre')
print('Max Sequence Length: %d' % max_length)

接下来,我们可以将序列分成输入和输出元素,就像以前一样。

# split into input and output elements
sequences = array(sequences)
X, y = sequences[:,:-1],sequences[:,-1]
y = to_categorical(y, num_classes=vocab_size)

然后可以像以前那样定义模型,除了现在输入序列比单个单词长。具体来说,它们的长度是max_length-1,因为当我们计算序列的最大长度时,它们包括输入和输出元素。

# define model
model = Sequential()
model.add(Embedding(vocab_size, 10, input_length=max_length-1))
model.add(LSTM(50))
model.add(Dense(vocab_size, activation='softmax'))
print(model.summary())
# compile network
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit network
model.fit(X, y, epochs=500, verbose=2)

我们可以像以前一样使用该模型生成新的序列。将所述generate_seq()函数更新通过后加入预测,以输入字每次迭代列表来建立一个输入序列。

# generate a sequence from a language model
def generate_seq(model, tokenizer, max_length, seed_text, n_words):
in_text = seed_text
# generate a fixed number of words
for _ in range(n_words):
# encode the text as integer
encoded = tokenizer.texts_to_sequences([in_text])[0]
# pre-pad sequences to a fixed length
encoded = pad_sequences([encoded], maxlen=max_length, padding='pre')
# predict probabilities for each word
yhat = model.predict_classes(encoded, verbose=0)
# map predicted word index to word
out_word = ''
for word, index in tokenizer.word_index.items():
if index == yhat:
out_word = word
break
# append to input
in_text += ' ' + out_word
return in_text

附件中提供了完整的代码示例。运行该示例可以更好地适应源数据。增加的上下文使模型能够消除一些例子的歧义。仍然有两行文字以“ Jack ” 开头,可能仍然是网络的问题。

Epoch 496/500
0s - loss: 0.1039 - acc: 0.9524
Epoch 497/500
0s - loss: 0.1037 - acc: 0.9524
Epoch 498/500
0s - loss: 0.1035 - acc: 0.9524
Epoch 499/500
0s - loss: 0.1033 - acc: 0.9524
Epoch 500/500
0s - loss: 0.1032 - acc: 0.9524

在运行结束时,我们会生成两个不同种子词的序列:“ Jack ”和“ Jill ”。

第一个生成的行看起来不错,直接匹配源文本。第二个看着有点奇怪,那是因为网络只能在序列中看到“ Jill ”,为了最后一行押韵,它强制输出单词“ Jill ”。

Jack fell down and broke
Jill jill came tumbling after

这是例子恰当的表明了框架可能会生成更好的新行,但是它并不是一段好的输入行。

模型3:双字输入,单字输出序列

我们可以使用单词输入法和整个句子输入法之间的媒介,输入一个单词的子序列。

这将提供两个框架之间的权衡,从而允许生成新的线路。

我们将使用3个单词作为输入来预测一个字作为输出。序列的制备与第一个例子非常相似,除了源序列阵列中的偏移量不同之外,如下所示:

# encode 2 words -> 1 word
sequences = list()
for i in range(2, len(encoded)):
sequence = encoded[i-2:i+1]
sequences.append(sequence)

完整的例子在附件中

再次运行这个例子得到合适的源文本的准确率在95%左右。

...
Epoch 496/500
Epoch 500/500
0s - loss: 0.0684 - acc: 0.9565

我们看看4代的例子,两个开始的行的情形和两个开始的中间行。

Jack and jill went up the hill
And Jill went up the
fell down and broke his crown and
pail of water jack fell down and

开始生成的第一个行是正确的,但是第二个不是。第二种情况是第四行的一个例子,与第一行的内容不一致。

我们可以看到,如何选择语言模型的框架必须和如何使用模型的要求是匹配的。如何选择语言模型的框架,以及如何使用模型的要求必须是兼容的。通常使用语言模型时需要仔细的设计,也许随后需要通过序列生成的现场测试来确认模型要求是否得到满足。

作者信息


Dr. Jason Brownlee 是一名机器学习从业者,学术研究人员,致力于帮助开发人员从入门到精通机器学习。

本文由北邮@爱可可-爱生活老师推荐,阿里云云栖社区组织翻译。

文章原标题《How to Develop Word-Based Neural Language Models in Python with Keras》

作者:Dr.Jason Brownlee译者:乌拉乌拉,审阅:袁虎

文章为简译,更为详细的内容,请查看原文














相关文章
|
人工智能 自然语言处理
Promptriever:信息检索模型,支持自然语言提示响应用户搜索需求
Promptriever 是一种新型信息检索模型,由约翰斯·霍普金斯大学和 Samaya AI 联合推出。该模型能够接受自然语言提示,并以直观的方式响应用户的搜索需求。通过在 MS MARCO 数据集上的训练,Promptriever 在标准检索任务上表现出色,能够更有效地遵循详细指令,提高查询的鲁棒性和检索性能。
460 6
Promptriever:信息检索模型,支持自然语言提示响应用户搜索需求
|
自然语言处理 PyTorch 算法框架/工具
掌握从零到一的进阶攻略:让你轻松成为BERT微调高手——详解模型微调全流程,含实战代码与最佳实践秘籍,助你应对各类NLP挑战!
【10月更文挑战第1天】随着深度学习技术的进步,预训练模型已成为自然语言处理(NLP)领域的常见实践。这些模型通过大规模数据集训练获得通用语言表示,但需进一步微调以适应特定任务。本文通过简化流程和示例代码,介绍了如何选择预训练模型(如BERT),并利用Python库(如Transformers和PyTorch)进行微调。文章详细说明了数据准备、模型初始化、损失函数定义及训练循环等关键步骤,并提供了评估模型性能的方法。希望本文能帮助读者更好地理解和实现模型微调。
1741 2
掌握从零到一的进阶攻略:让你轻松成为BERT微调高手——详解模型微调全流程,含实战代码与最佳实践秘籍,助你应对各类NLP挑战!
|
机器学习/深度学习 自然语言处理 语音技术
探索深度学习中的Transformer模型及其在自然语言处理中的应用
探索深度学习中的Transformer模型及其在自然语言处理中的应用
936 5
|
机器学习/深度学习 人工智能 自然语言处理
探索深度学习与自然语言处理的前沿技术:Transformer模型的深度解析
探索深度学习与自然语言处理的前沿技术:Transformer模型的深度解析
906 1
|
机器学习/深度学习 自然语言处理 PyTorch
从零开始构建nlp情感分析模型!
本教程介绍了如何使用PyTorch和Hugging Face的Transformers库构建一个情感分析模型。主要内容包括导入所需库、读取训练数据集、加载预训练的BERT模型和分词器、定义情感数据集类、划分训练集和验证集、创建数据加载器、设置训练参数、训练模型、评估模型性能以及定义和测试预测函数。通过这些步骤,可以实现一个简单而有效的情感分析模型。
1560 2
|
自然语言处理
【NLP】from glove import Glove的使用、模型保存和加载
使用 from glove import Glove 进行词向量训练、保存和加载的基本示例。
390 2
【NLP】from glove import Glove的使用、模型保存和加载
|
机器学习/深度学习 人工智能 自然语言处理
【深度学习】探讨最新的深度学习算法、模型创新以及在图像识别、自然语言处理等领域的应用进展
深度学习作为人工智能领域的重要分支,近年来在算法、模型以及应用领域都取得了显著的进展。以下将探讨最新的深度学习算法与模型创新,以及它们在图像识别、自然语言处理(NLP)等领域的应用进展。
1037 6
|
自然语言处理 监控 自动驾驶
大模型在自然语言处理(NLP)、计算机视觉(CV)和多模态模型等领域应用最广
【7月更文挑战第26天】大模型在自然语言处理(NLP)、计算机视觉(CV)和多模态模型等领域应用最广
1800 11
|
机器学习/深度学习 自然语言处理 PyTorch
PyTorch与Hugging Face Transformers:快速构建先进的NLP模型
【8月更文第27天】随着自然语言处理(NLP)技术的快速发展,深度学习模型已经成为了构建高质量NLP应用程序的关键。PyTorch 作为一种强大的深度学习框架,提供了灵活的 API 和高效的性能,非常适合于构建复杂的 NLP 模型。Hugging Face Transformers 库则是目前最流行的预训练模型库之一,它为 PyTorch 提供了大量的预训练模型和工具,极大地简化了模型训练和部署的过程。
1322 2
|
机器学习/深度学习 自然语言处理 数据处理