语言模型可根据序列中出现的特定单词来预测下一个单词。可以使用神经网络在字符级别上开发语言模型。基于字符的语言模型有一个最大的优点,就是在处理单词、标点符号和其他文档结构的时候,能保持较小的词汇量和较强的灵活性。但所付出的代价是模型较大、训练较慢。然而,在神经网络语言模型领域,基于字符的模型为语言建模提供了一种通用、灵活和强大的方法。

在本教程中,你将了解到如何开发基于字符的神经网络语言模型。

学习完本教程,你将学会:

  • 如何针对基于字符的语言建模准备文本。
  • 如何使用LSTM开发基于字符的语言模型。
  • 如何使用训练过的基于字符的语言模型来生成文本。

教程概述

本教程分为四个部分:

  1. Sing a Song of Sixpence(译者注:一首英文童谣)
  2. 数据准备
  3. 训练语言模型
  4. 生成文本

Sing a Song of Sixpence

童谣“Sing a Song of Sixpence”在西方人人都会唱。我们将用它来开发基于字符的语言模型。

这首童谣很短,所以模型的拟合会很快,但不能太短,那样我们就不会看到任何有意思的东西。下面是这首童谣完整歌词:

Sing a song of sixpence,
A pocket full of rye.
Four and twenty blackbirds,
Baked in a pie.

When the pie was opened
The birds began to sing;
Wasn’t that a dainty dish,
To set before the king.

The king was in his counting house,
Counting out his money;
The queen was in the parlour,
Eating bread and honey.

The maid was in the garden,
Hanging out the clothes,
When down came a blackbird
And pecked off her nose.

复制这段文本,并将其保存到当前工作目录中的一个新文件中,文件名为“rhyme.txt”。

数据准备

第一步是准备文本数据。我们将首先定义语言模型的类型。

语言模型设计

语言模型必须用文本进行训练,在基于字符的语言模型中,输入和输出序列必须是字符。用于输入的字符的个数决定了需要提供给模型以引出第一个预测字符的字符数。在第一个字符生成之后,可以将其添加到输入序列上,作为模型的输入以生成下一个字符。

序列越长,则为模型提供的上下文也越多,同时,模型将耗费更长的时间来进行训练。我们这个模型使用的字符的长度是10。

下面我们将把原始文本转换成模型可以学习的形式。

加载文本

童谣的歌词必须加载到内存之后才能使用。下面是一个名为load_doc()的函数,用于加载指定文件名的文本文件并返回加载的文本。

# load doc into memory
def load_doc(filename):# open the file as read onlyfile = open(filename, 'r')# read all texttext = file.read()# close the filefile.close()return text

可以使用这个函数来加载名为“rhyme.txt”的文件,并将内容放入内存中。然后将文件的内容打印到屏幕上进行完整性检查。

# load text
raw_text = load_doc('rhyme.txt')
print(raw_text)

净化文本

接下来,需要净化加载的文本。

这里我们不会做太多的事情,只是删除所有的换行符,转换成一段按空格进行分割的长字符序列。

# clean
tokens = raw_text.split()
raw_text = ' '.join(tokens)

你可能需要探索一下净化数据的其他一些方法,例如将文本转换为小写字母或删除标点符号,以减少最终的词汇量,这样可以开发出更小、更精简的模型。

创建序列

长字符列表有了,下面就可以创建用于训练模型的输入输出序列了。

每个输入序列包含十个字符和一个输出字符,因此,每个序列包含了11个字符。我们可以通过枚举文本中的字符来创建序列,从索引为10也就是第11个字符开始。

# organize into sequences of characters
length = 10
sequences = list()
for i in range(length, len(raw_text)):# select sequence of tokensseq = raw_text[i-length:i+1]# storesequences.append(seq)
print('Total Sequences: %d' % len(sequences))

运行这段代码,我们可以看到,用来训练语言模型的序列其实只有不到400个字符。

Total Sequences: 399

保存序列

最后,将准备好的数据保存到文件中,后面在开发模型的时候再加载。

下面是save_doc()函数,给定字符串列表和文件名,将字符串保存到文件中,每行一个字符串。

# save tokens to file, one dialog per line
def save_doc(lines, filename):data = '\n'.join(lines)file = open(filename, 'w')file.write(data)file.close()

调用这个函数,将准备好的序列保存到当前工作目录下的“char_sequences.txt”文件中。

# save sequences to file
out_filename = 'char_sequences.txt'
save_doc(sequences, out_filename)

完整的例子

将上面那些代码片段组合到一起,组成下面这份完整的代码:

# load doc into memory
def load_doc(filename):# open the file as read onlyfile = open(filename, 'r')# read all texttext = file.read()# close the filefile.close()return text# save tokens to file, one dialog per line
def save_doc(lines, filename):data = '\n'.join(lines)file = open(filename, 'w')file.write(data)file.close()# load text
raw_text = load_doc('rhyme.txt')
print(raw_text)# clean
tokens = raw_text.split()
raw_text = ' '.join(tokens)# organize into sequences of characters
length = 10
sequences = list()
for i in range(length, len(raw_text)):# select sequence of tokensseq = raw_text[i-length:i+1]# storesequences.append(seq)
print('Total Sequences: %d' % len(sequences))# save sequences to file
out_filename = 'char_sequences.txt'
save_doc(sequences, out_filename)

运行该示例,将生成“char_seqiences.txt”文件,内容如下:

Sing a song
ing a song
ng a song o
g a song ofa song of
a song of ssong of si
song of six
ong of sixp
ng of sixpe
...

下面准备训练基于字符的神经语言模型。

训练语言模型

本章节将为上面准备好的序列数据开发一个神经语言模型。该模型将读取已经编码的字符,并预测出序列的下一个字符。

加载数据

第一步是从"char_sequences.txt"加载字符序列数据。

我们可以使用上一章节中开发的load_doc()函数。载入后,将文本按换行符进行分割以得到序列列表。

# load doc into memory
def load_doc(filename):# open the file as read onlyfile = open(filename, 'r')# read all texttext = file.read()# close the filefile.close()return text# load
in_filename = 'char_sequences.txt'
raw_text = load_doc(in_filename)
lines = raw_text.split('\n')

序列编码

字符序列必须编码为整数。也就是说每个字符都会被分配一个指定的整数值,每个字符序列都会被编码为一个整数序列。

我们可以根据原始输入数据来创建映射关系。该映射关系是字符值映射到整数值的字典。

chars = sorted(list(set(raw_text)))
mapping = dict((c, i) for i, c in enumerate(chars))

接下来,逐个处理每个字符序列,并使用字典映射来查找每个字符的整数值。

sequences = list()
for line in lines:# integer encode lineencoded_seq = [mapping[char] for char in line]# storesequences.append(encoded_seq)

运行的结果是整数序列列表。

字典映射表的大小即词汇表的大小。

# vocabulary size
vocab_size = len(mapping)
print('Vocabulary Size: %d' % vocab_size)

运行这段代码,我们可以看到输入数据中的字符剔重后有38个。

Vocabulary Size: 38

分割输入和输出

现在,序列已经被编码成整数了,下面可以将列分割成输入和输出字符序列。可以使用一个简单的数组切片来完成此操作。

sequences = array(sequences)
X, y = sequences[:,:-1], sequences[:,-1]

接下来,将对每个字符进行独热编码,也就是说,每个字符都会变成一个向量。这为神经网络提供了更精确的输入表示,还为网络预测提供了一个明确的目标。

我们可以使用Keras API中的to_categorical()函数来对输入和输出序列进行独热编码。

sequences = [to_categorical(x, num_classes=vocab_size) for x in X]
X = array(sequences)
y = to_categorical(y, num_classes=vocab_size)

现在,我们已经为模型的拟合做好准备了。

模型拟合

该模型使用了一个针对独热编码输入序列采用10个时间步长和38个特征的输入层进行定义。我们在X输入数据上使用第二和第三个维度,而不是指定这些数字。这是因为当序列的长度或词汇表的大小发生改变的实话,无需改变模型的定义。

该模型有一个包含75个存储器单元的LSTM隐藏层,通过一些试验和错误进行选择。

该模型有一个完全连接的输出层,输出一个词汇表中所有字符概率分布的向量。在输出层上使用softmax激活函数来确保输出具有概率分布的属性。

# define model
model = Sequential()
model.add(LSTM(75, input_shape=(X.shape[1], X.shape[2])))
model.add(Dense(vocab_size, activation='softmax'))
print(model.summary())

运行这段代码将打印出网络的概要信息以进行完整性检查。

_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
lstm_1 (LSTM)                (None, 75)                34200
_________________________________________________________________
dense_1 (Dense)              (None, 38)                2888
=================================================================
Total params: 37,088
Trainable params: 37,088
Non-trainable params: 0
_________________________________________________________________

该模型将执行100次训练迭代来进行拟合。

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

保存模型

模型拟合完成之后,将其保存到文件以备后面使用。Keras提供了save()函数,可以使用该函数将模型保存到单个文件中,包括权重和拓扑信息。

# save the model to file
model.save('model.h5')

另外还要保存从字符到整数的映射关系,因为在使用模型的时候,需要对任意的输入进行编码,并对模型的输出进行解码。

# save the mapping
dump(mapping, open('mapping.pkl', 'wb'))

完整的例子

将上面那些代码片段组合到一起,组成下面这份基于字符的神经网络语言模型的完整代码:

from numpy import array
from pickle import dump
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM# load doc into memory
def load_doc(filename):# open the file as read onlyfile = open(filename, 'r')# read all texttext = file.read()# close the filefile.close()return text# load
in_filename = 'char_sequences.txt'
raw_text = load_doc(in_filename)
lines = raw_text.split('\n')# integer encode sequences of characters
chars = sorted(list(set(raw_text)))
mapping = dict((c, i) for i, c in enumerate(chars))
sequences = list()
for line in lines:# integer encode lineencoded_seq = [mapping[char] for char in line]# storesequences.append(encoded_seq)# vocabulary size
vocab_size = len(mapping)
print('Vocabulary Size: %d' % vocab_size)# separate into input and output
sequences = array(sequences)
X, y = sequences[:,:-1], sequences[:,-1]
sequences = [to_categorical(x, num_classes=vocab_size) for x in X]
X = array(sequences)
y = to_categorical(y, num_classes=vocab_size)# define model
model = Sequential()
model.add(LSTM(75, input_shape=(X.shape[1], X.shape[2])))
model.add(Dense(vocab_size, activation='softmax'))
print(model.summary())
# compile model
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit model
model.fit(X, y, epochs=100, verbose=2)# save the model to file
model.save('model.h5')
# save the mapping
dump(mapping, open('mapping.pkl', 'wb'))

这个例程的运行时间可能需要一分钟。

...
Epoch 96/100
0s - loss: 0.2193 - acc: 0.9950
Epoch 97/100
0s - loss: 0.2124 - acc: 0.9950
Epoch 98/100
0s - loss: 0.2054 - acc: 0.9950
Epoch 99/100
0s - loss: 0.1982 - acc: 0.9950
Epoch 100/100
0s - loss: 0.1910 - acc: 0.9950

运行结束之后,会在当前目录生成两个文件,model.h5mapping.pkl

接下来,看一下如何使用这个学习过的模型。

生成文本

我们将使用这个学习过的语言模型来生成具有相同统计特性的新的文本序列。

加载模型

第一步是加载文件“model.h5”中的模型,可以使用Keras API中的load_model()函数进行加载。

# load the model
model = load_model('model.h5')

还需要加载文件“mapping.pkl”文件中的字典,用于将字符映射为整数。

# load the mapping
mapping = load(open('mapping.pkl', 'rb'))

下面可以使用这个模型了。

生成字符

为了启动生成过程,必须提供包含10个字符的序列作为模型的输入。

首先,字符序列必须使用加载进来的映射关系编码为整数值。

# encode the characters as integers
encoded = [mapping[char] for char in in_text]

接下来,使用Keras中的pad_sequences()函数对整数值进行独热编码,并将序列重塑为三个维度。因为我们只有一个序列,而且LSTM需要所有的输入都有三个维度(样本、时间步长、特征)。

# one hot encode
encoded = to_categorical(encoded, num_classes=len(mapping))
encoded = encoded.reshape(1, encoded.shape[0], encoded.shape[1])

下面,就可以使用模型来预测序列中的下一个字符了。

使用predict_classes()而不是predict()来直接选择具有最高概率的字符整数。

# predict character
yhat = model.predict_classes(encoded, verbose=0)

可以通过查找映射中的整数-字符关系来对整数进行解码。

out_char = ''
for char, index in mapping.items():if index == yhat:out_char = charbreak

这个字符随后可以添加到输入序列中去。然后通过截断输入序列文本中的第一个字符来确保输入序列是10个字符的长度。可以使用Keras API中的pad_sequences()函数来执行截断操作。

把上面这些放在一起,定义一个名为generate_seq()的新函数来使用模型生成新的文本序列。

# generate a sequence of characters with a language model
def generate_seq(model, mapping, seq_length, seed_text, n_chars):in_text = seed_text# generate a fixed number of charactersfor _ in range(n_chars):# encode the characters as integersencoded = [mapping[char] for char in in_text]# truncate sequences to a fixed lengthencoded = pad_sequences([encoded], maxlen=seq_length, truncating='pre')# one hot encodeencoded = to_categorical(encoded, num_classes=len(mapping))encoded = encoded.reshape(1, encoded.shape[0], encoded.shape[1])# predict characteryhat = model.predict_classes(encoded, verbose=0)# reverse map integer to characterout_char = ''for char, index in mapping.items():if index == yhat:out_char = charbreak# append to inputin_text += charreturn in_text

完整的例子

将上面那些代码片段组合到一起,组成下面这份基于字符的神经网络语言模型的完整代码:

from pickle import load
from keras.models import load_model
from keras.utils import to_categorical
from keras.preprocessing.sequence import pad_sequences# generate a sequence of characters with a language model
def generate_seq(model, mapping, seq_length, seed_text, n_chars):in_text = seed_text# generate a fixed number of charactersfor _ in range(n_chars):# encode the characters as integersencoded = [mapping[char] for char in in_text]# truncate sequences to a fixed lengthencoded = pad_sequences([encoded], maxlen=seq_length, truncating='pre')# one hot encodeencoded = to_categorical(encoded, num_classes=len(mapping))encoded = encoded.reshape(1, encoded.shape[0], encoded.shape[1])# predict characteryhat = model.predict_classes(encoded, verbose=0)# reverse map integer to characterout_char = ''for char, index in mapping.items():if index == yhat:out_char = charbreak# append to inputin_text += charreturn in_text# load the model
model = load_model('model.h5')
# load the mapping
mapping = load(open('mapping.pkl', 'rb'))# test start of rhyme
print(generate_seq(model, mapping, 10, 'Sing a son', 20))
# test mid-line
print(generate_seq(model, mapping, 10, 'king was i', 20))
# test not in original
print(generate_seq(model, mapping, 10, 'hello worl', 20))

运行该示例将生成三个文本序列。

第一个是测试这个模型在从童谣的开头进行预测的话表现如何。第二个是测试从某一行的中间开始预测表现如何。最后是测试模型遇到从未见过的字符序列时表现如何。

Sing a song of sixpence, A poc
king was in his counting house
hello worls e pake wofey. The

我们可以看到,这个模型在前两个例子中的表现得还不错,符合预期。但对于新的文本来说,预测的结果就有点匪夷所思了。

总结

通过阅读本教程,你已经学会了如何开发基于字符的神经网络语言模型,包括:

  • 如何针对基于字符的语言建模准备文本。
  • 如何使用LSTM开发基于字符的语言模型。
  • 如何使用训练过的基于字符的语言模型来生成文本。

文章原标题《How to Develop a Character-Based Neural Language Model in Keras》,作者: Jason Brownlee,译者:夏天,审校:主题曲。

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

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

用Keras开发字符级神经网络语言模型相关推荐

  1. 手把手教 | 使用Keras构造日文的神经网络语言模型(内附源码)

    作者:GjZero 标签:Python, Keras, 语言模型, 日语 本文约2400字,建议阅读10分钟. 本文介绍了语言模型,并介绍如何用MeCab和Keras实现一个日文的神经网络语言模型.( ...

  2. 当莎士比亚遇见Google Flax:教你用​字符级语言模型和归递神经网络写“莎士比亚”式句子...

    作者 | Fabian Deuser 译者 | 天道酬勤 责编 | Carol 出品 | AI科技大本营(ID:rgznai100) 有些人生来伟大,有些人成就伟大,而另一些人则拥有伟大. -- 威廉 ...

  3. pytorch dropout_手把手带你使用字符级RNN生成名字 | PyTorch

    作者 | News 编辑 | 奇予纪 出品 | 磐创AI团队出品 [磐创AI 导读]:本篇文章讲解了PyTorch专栏的第五章中的使用字符级RNN生成名字.查看专栏历史文章,请点击下方蓝色字体进入相应 ...

  4. CS224n自然语言处理(三)——问答系统、字符级模型和自然语言生成

    文章目录 一.问答系统 1.Stanford Question Answering Dataset (SQuAD) 2.Stanford Attentive Reader Stanford Atten ...

  5. NLP基础:n-gram语言模型和神经网络语言模型

    文章目录 语言模型的计算 n-gram 语言模型 n-gram 平滑技术 神经网络语言模型(NNLM) 基本思想 神经网络语言模型小结 语言模型评价指标-困惑度 语言模型是自然语言处理中的重要技术,假 ...

  6. 字符级Seq2Seq-英语粤语翻译的简单实现

    个人博客:http://www.chenjianqu.com/ 原文链接:http://www.chenjianqu.com/show-40.html 前一篇文章中使用简单的seq2seq搭建了单词级 ...

  7. 【NLP】神经网络语言模型(NNLM)

    背景 语言模型也经常会在NLP中提出.在深度学习大行其道的今天基于神经网络的语言模型与传统定义的又有什么区别呢?语言模型在NLP中有什么意义呢?不妨沉下心,了解一下. 语言模型是一个单纯的.统一的.抽 ...

  8. keras构建卷积神经网络_在python中使用tensorflow s keras api构建卷积神经网络的初学者指南...

    keras构建卷积神经网络 初学者的深度学习 (DEEP LEARNING FOR BEGINNERS) Welcome to Part 2 of the Neural Network series! ...

  9. keras训练完以后怎么预测_使用Keras建立Wide Deep神经网络,通过描述预测葡萄酒价格...

    你能通过"优雅的单宁香"."成熟的黑醋栗香气"或"浓郁的酒香"这样的描述,预测葡萄酒的价格吗?事实证明,机器学习模型可以. 在这篇文章中,我 ...

  10. 用飞桨做自然语言处理:神经网络语言模型应用实例

    允中 发自 凹非寺  量子位 报道 | 公众号 QbitAI 编者按: 语言模型的身影遍布在NLP研究中的各个角落,想要了解NLP领域,就不能不知道语言模型. 想要让模型能落地奔跑,就需借助深度学习框 ...

最新文章

  1. 在RHEL4上安装subversion1.4.2
  2. Coding: 整数反转
  3. java面试换背景颜色_实习|渣二本Java菜鸡0 Offer的春招血泪史,一定要主动去找实习呀!
  4. 29岁当上北大博导,她成2019年最年轻杰青建议资助人之一
  5. 如何动态为 tabstrip 中的 tab 页签指定标题
  6. 使用Flash Builder 4.5进行多平台游戏开发
  7. 视频教程-网络规划设计师5天修炼-软考
  8. cd linux制作u盘启动盘,【cdlinux u盘启动】cdlinux制作U盘启动盘详细教程
  9. chromecast 协议_如何解决常见的Google Chromecast问题
  10. IEEE trans使用latex模板部分字体是黑色,部分变成了绿色
  11. windows11安装日语输入法(添加输入法)
  12. OpenHarmony恢复启动子系统init进程之服务管理与发布
  13. 词云图制作(wordcloud pyecharts)
  14. android安装自动打开网页,Android调用系统自带浏览器打开网页的实现方法
  15. 分享一个Python画樱花树的代码
  16. 关于socket中的send函数
  17. JavaWeb---HTTP与Request
  18. 基于机智云的嵌入式系统应用开发—空调远程智能控制系统
  19. 特斯拉充电电流设置多大_特斯拉充电时间
  20. 使用Docker制作镜像并推送到镜像仓库

热门文章

  1. 根据两点经纬度计算距离 whh博客转载
  2. 第一阶段——day06
  3. Vue3 开发实战分享——打印插件 Print.js 的使用(Vue3 + Nodejs + Print.js 实战)以及 el-table 与 el-pagination 的深入使用(下)
  4. 【毕业设计】基于微信小程序的水果店销售商城设计与实现
  5. 建造者模式、原型模式、单例模式
  6. java文件上传实例_java 8 上传文件小例子报错!
  7. Python学习5:计算弓形的面积
  8. git---如何解决The authenticity of host 'gitee.com (120.55.226.24)' can't be established
  9. R语言文本分析《三国演义》
  10. Redis 中地理位置功能 Geospatial 了解一下?