语言模型根据序列中前面出现的特定单词来预测序列中的下一个单词。
还可以使用神经网络在字符级别开发语言模型。基于字符的语言模型的优点是词汇量小,并且能够灵活处理任何单词、标点符号和其他文档结构。这需要以更大的模型为代价,训练速度较慢。
尽管如此,在神经网络语言模型领域,基于字符的模型为语言建模的通用、灵活和强大的方法提供了巨大的前景。
在本教程中,您将学习如何开发基于字符的神经网络语言模型。
完成本教程后,您将了解:
- 如何为基于字符的语言模型准备文本。
- 如何使用 LSTM 开发基于字符的语言模型。
- 如何使用训练好的基于字符的语言模型生成文本。
通过我的新书《自然语言处理深度学习》**启动您的项目**,包括*分步教程*和所有示例的*Python 源代码*文件。
让我们开始吧。
- 2018年2月更新:Keras 2.1.3 中 API 更改导致生成模块进行了小幅更新。
- 2021年2月更新:更新了最终代码示例以删除冗余行。

如何在 Keras 中开发基于字符的神经语言模型
图片由hedera.baltica提供,保留部分权利。
教程概述
本教程分为4个部分,它们是:
- 唱一首六便士的歌
- 数据准备
- 训练语言模型
- 生成文本
需要深度学习处理文本数据的帮助吗?
立即参加我的免费7天电子邮件速成课程(附代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
唱一首六便士的歌
童谣“唱一首六便士的歌”在西方广为人知。
第一节很常见,但我们还将使用一个四节版本来开发我们的基于字符的语言模型。
它很短,所以模型拟合会很快,但又不是短到我们看不到任何有趣的东西。
我们将用作源文本的完整四节版本如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
唱一首六便士的歌, 满满一口袋的黑麦。 二十四只乌鸦, 烤在一个派里。 当派被打开时 鸟儿们开始唱歌; 那不是一道美味的菜吗, 呈献给国王。 国王在他的账房里, 数着他的钱; 王后在客厅里, 吃着面包和蜂蜜。 女仆在花园里, 晾晒着衣服, 这时一只乌鸦飞了下来 啄掉了她的鼻子。 |
复制文本并将其保存在您当前工作目录中的一个新文件中,文件名为“rhyme.txt”。
数据准备
第一步是准备文本数据。
我们首先定义语言模型的类型。
语言模型设计
语言模型必须在文本上进行训练,对于基于字符的语言模型,输入和输出序列必须是字符。
用作输入字符的数量也将定义需要提供给模型的字符数量,以引出第一个预测字符。
生成第一个字符后,可以将其附加到输入序列中,并用作模型生成下一个字符的输入。
较长的序列为模型学习下一个输出字符提供了更多的上下文,但训练时间更长,并且在生成文本时对模型进行初始化的负担更大。
我们将为该模型使用任意长度的10个字符。
文本不多,10个字符是几个单词。
我们现在可以将原始文本转换为模型可以学习的形式;具体来说,是字符的输入和输出序列。
加载文本
我们必须将文本加载到内存中以便对其进行操作。
下面是一个名为 load_doc() 的函数,它将加载给定文件名的文本文件并返回加载的文本。
1 2 3 4 5 6 7 8 9 |
# 加载文档到内存 def load_doc(filename): # 以只读方式打开文件 file = open(filename, 'r') # 读取所有文本 text = file.read() # 关闭文件 file.close() 返回 文本 |
我们可以使用童谣的文件名“rhyme.txt”调用此函数,将文本加载到内存中。然后将文件内容打印到屏幕上作为健全性检查。
1 2 3 |
# 加载文本 raw_text = load_doc('rhyme.txt') print(raw_text) |
清理文本
接下来,我们需要清理加载的文本。
我们在这里不会做太多。具体来说,我们将去除所有换行符,这样我们就得到一个只用空格分隔的长字符序列。
1 2 3 |
# 清理 tokens = raw_text.split() raw_text = ' '.join(tokens) |
您可能希望探索其他数据清理方法,例如将大小写标准化为小写或删除标点符号,以期减小最终词汇量并开发更小更精简的模型。
创建序列
现在我们有了一个长字符列表,我们可以创建用于训练模型的输入-输出序列。
每个输入序列将是10个字符,带有一个输出字符,使每个序列长度为11个字符。
我们可以通过枚举文本中的字符来创建序列,从索引10的第11个字符开始。
1 2 3 4 5 6 7 8 9 |
# 组织成字符序列 长度 = 10 sequences = list() for i in range(length, len(raw_text)): # 选择令牌序列 seq = raw_text[i-length:i+1] # 存储 sequences.append(seq) print('总序列数: %d' % len(sequences)) |
运行此代码片段,我们可以看到我们最终得到了近400个字符序列来训练我们的语言模型。
1 |
总序列数:399 |
保存序列
最后,我们可以将准备好的数据保存到文件中,以便在开发模型时稍后加载。
下面是一个名为 save_doc() 的函数,给定一个字符串列表和一个文件名,它将字符串保存到文件中,每行一个。
1 2 3 4 5 6 |
# 将令牌保存到文件,每行一个对话 def save_doc(lines, filename): data = '\n'.join(lines) file = open(filename, 'w') file = file.write(data) file.close() |
我们可以调用此函数,并将准备好的序列保存到当前工作目录中的文件名“char_sequences.txt”。
1 2 3 |
# 将序列保存到文件 out_filename = 'char_sequences.txt' save_doc(sequences, out_filename) |
完整示例
将所有这些结合起来,完整的代码清单如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# 加载文档到内存 def load_doc(filename): # 以只读方式打开文件 file = open(filename, 'r') # 读取所有文本 text = file.read() # 关闭文件 file.close() return text # 将令牌保存到文件,每行一个对话 def save_doc(lines, filename): data = '\n'.join(lines) file = open(filename, 'w') file = file.write(data) file.close() # 加载文本 raw_text = load_doc('rhyme.txt') print(raw_text) # 清理 tokens = raw_text.split() raw_text = ' '.join(tokens) # 组织成字符序列 长度 = 10 sequences = list() for i in range(length, len(raw_text)): # 选择令牌序列 seq = raw_text[i-length:i+1] # 存储 sequences.append(seq) print('总序列数: %d' % len(sequences)) # 将序列保存到文件 out_filename = 'char_sequences.txt' save_doc(sequences, out_filename) |
运行示例以创建“char_seqiences.txt”文件。
看看里面,您应该会看到以下内容
1 2 3 4 5 6 7 8 9 10 11 |
唱一首歌 唱一首歌 唱一首歌 唱一首歌 一首歌的 一首歌的s 六便士的歌 六便士的歌 六便士的ong 六便士的ng ... |
我们现在准备训练我们的基于字符的神经网络语言模型。
训练语言模型
在本节中,我们将为准备好的序列数据开发一个神经网络语言模型。
该模型将读取编码字符并预测序列中的下一个字符。将使用长短期记忆循环神经网络隐藏层从输入序列中学习上下文以进行预测。
加载数据
第一步是从“char_sequences.txt”加载准备好的字符序列数据。
我们可以使用上一节中开发的相同 load_doc() 函数。加载后,我们按换行符分割文本,得到一个准备好进行编码的序列列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 加载文档到内存 def load_doc(filename): # 以只读方式打开文件 file = open(filename, 'r') # 读取所有文本 text = file.read() # 关闭文件 file.close() return text # 加载 in_filename = 'char_sequences.txt' raw_text = load_doc(in_filename) lines = raw_text.split('\n') |
编码序列
字符序列必须编码为整数。
这意味着每个唯一字符将被分配一个特定的整数值,并且每个字符序列将被编码为整数序列。
我们可以根据原始输入数据中唯一的排序字符集创建映射。映射是一个将字符值映射到整数值的字典。
1 2 |
chars = sorted(list(set(raw_text))) mapping = dict((c, i) for i, c in enumerate(chars)) |
接下来,我们可以一次处理一个字符序列,并使用字典映射查找每个字符的整数值。
1 2 3 4 5 6 |
sequences = list() for line in lines: # 整数编码行 encoded_seq = [mapping[char] for char in line] # 存储 sequences.append(encoded_seq) |
结果是一个整数列表的列表。
我们稍后需要知道词汇表的大小。我们可以将其作为字典映射的大小来检索。
1 2 3 |
# 词汇表大小 vocab_size = len(mapping) print('词汇表大小: %d' % vocab_size) |
运行此代码片段,我们可以看到输入序列数据中有38个唯一字符。
1 |
词汇量大小:38 |
拆分输入和输出
现在序列已经过整数编码,我们可以将列分离为字符的输入和输出序列。
我们可以使用简单的数组切片来完成此操作。
1 2 |
sequences = array(sequences) X, y = sequences[:,:-1], sequences[:,-1] |
接下来,我们需要对每个字符进行独热编码。也就是说,每个字符都变成一个与词汇表一样长的向量(38个元素),其中特定字符标记为1。这为网络提供了更精确的输入表示。它还为网络提供了明确的预测目标,模型可以输出字符的概率分布,并与实际下一个字符为1且所有其他值为0的理想情况进行比较。
我们可以使用 Keras API 中的 to_categorical() 函数对输入和输出序列进行独热编码。
1 2 3 |
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 激活函数,以确保输出具有概率分布的特性。
1 2 3 4 5 |
# 定义模型 model = Sequential() model.add(LSTM(75, input_shape=(X.shape[1], X.shape[2]))) model.add(Dense(vocab_size, activation='softmax')) print(model.summary()) |
运行此命令将打印定义网络的摘要,作为健全性检查。
1 2 3 4 5 6 7 8 9 10 11 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= lstm_1 (LSTM) (无, 75) 34200 _________________________________________________________________ dense_1 (Dense) (无, 38) 2888 ================================================================= 总参数:37,088 可训练参数:37,088 不可训练参数: 0 _________________________________________________________________ |
模型正在学习多类分类问题,因此我们使用适用于此类问题的分类交叉熵损失。梯度下降的有效 Adam 实现用于优化模型,并在每次批量更新结束时报告准确性。
模型训练了100个 epoch,这也是经过反复试验后确定的。
1 2 3 4 |
# 编译模型 model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) # 拟合模型 model.fit(X, y, epochs=100, verbose=2) |
保存模型
模型拟合完成后,我们将其保存到文件以备后用。
Keras 模型 API 提供了 save() 函数,我们可以使用它将模型保存到单个文件,包括权重和拓扑信息。
1 2 |
# 将模型保存到文件 model.save('model.h5') |
我们还保存了字符到整数的映射,在模型使用时编码任何输入以及解码模型的任何输出时,我们都需要它。
1 2 |
# 保存映射 dump(mapping, open('mapping.pkl', 'wb')) |
完整示例
将所有这些整合在一起,下面列出了用于拟合基于字符的神经网络语言模型的完整代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
from numpy import array from pickle import dump from keras.utils import to_categorical from keras.models import Sequential from keras.layers import Dense 来自 keras.层 导入 LSTM # 加载文档到内存 def load_doc(filename): # 以只读方式打开文件 file = open(filename, 'r') # 读取所有文本 text = file.read() # 关闭文件 file.close() return text # 加载 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: # 整数编码行 encoded_seq = [mapping[char] for char in line] # 存储 sequences.append(encoded_seq) # 词汇表大小 vocab_size = len(mapping) print('词汇表大小: %d' % vocab_size) # 分离输入和输出 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) # 定义模型 model = Sequential() model.add(LSTM(75, input_shape=(X.shape[1], X.shape[2]))) model.add(Dense(vocab_size, activation='softmax')) print(model.summary()) # 编译模型 model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) # 拟合模型 model.fit(X, y, epochs=100, verbose=2) # 将模型保存到文件 model.save('model.h5') # 保存映射 dump(mapping, open('mapping.pkl', 'wb')) |
运行示例可能需要一分钟。
注意:由于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
您会发现模型很好地学习了这个问题,可能太好了,以至于生成了令人惊讶的字符序列。
1 2 3 4 5 6 7 8 9 10 11 |
... 第96/100个周期 0秒 - 损失:0.2193 - 准确率:0.9950 第97/100个周期 0秒 - 损失:0.2124 - 准确率:0.9950 第98/100个周期 0秒 - 损失:0.2054 - 准确率:0.9950 Epoch 99/100 0秒 - 损失:0.1982 - 准确率:0.9950 Epoch 100/100 0秒 - 损失:0.1910 - 准确率:0.9950 |
运行结束时,您将有两个文件保存到当前工作目录,分别是 model.h5 和 mapping.pkl。
接下来,我们可以看看如何使用学习到的模型。
生成文本
我们将使用学习到的语言模型生成具有相同统计属性的新文本序列。
加载模型
第一步是加载保存到文件“model.h5”的模型。
我们可以使用 Keras API 中的 load_model() 函数。
1 2 |
# 加载模型 model = load_model('model.h5') |
我们还需要从文件“mapping.pkl”加载用于将字符映射到整数的 pickle 字典。我们将使用 Pickle API 加载该对象。
1 2 |
# 加载映射 mapping = load(open('mapping.pkl', 'rb')) |
我们现在准备使用加载的模型了。
生成字符
我们必须向模型提供10个字符的序列作为输入,才能开始生成过程。我们将手动选择这些序列。
给定的输入序列需要以与为模型准备训练数据相同的方式进行准备。
首先,字符序列必须使用加载的映射进行整数编码。
1 2 |
# 将字符编码为整数 encoded = [mapping[char] for char in in_text] |
接下来,需要使用 to_categorical() Keras 函数对序列进行独热编码。
1 2 |
# One-Hot 编码 encoded = to_categorical(encoded, num_classes=len(mapping)) |
然后我们可以使用模型来预测序列中的下一个字符。
我们使用 predict_classes() 而不是 predict() 直接选择具有最高概率的字符的整数,而不是获取整个字符集的完整概率分布。
1 2 |
# 预测字符 yhat = model.predict_classes(encoded, verbose=0) |
然后我们可以通过查找映射来解码这个整数,以查看它映射到的字符。
1 2 3 4 5 |
out_char = '' for char, index in mapping.items(): if index == yhat: out_char = char break |
然后可以将此字符添加到输入序列中。然后,我们需要通过截断输入序列文本中的第一个字符来确保输入序列为10个字符。
我们可以使用 Keras API 中的 pad_sequences() 函数来执行此截断操作。
将所有这些组合在一起,我们可以定义一个名为 generate_seq() 的新函数,用于使用加载的模型生成新的文本序列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# 使用语言模型生成字符序列 def generate_seq(model, mapping, seq_length, seed_text, n_chars): in_text = seed_text # 生成固定数量的字符 for _ in range(n_chars): # 将字符编码为整数 encoded = [mapping[char] for char in in_text] # 将序列截断为固定长度 encoded = pad_sequences([encoded], maxlen=seq_length, truncating='pre') # 独热编码 encoded = to_categorical(encoded, num_classes=len(mapping)) # 预测字符 yhat = model.predict_classes(encoded, verbose=0) # 反向映射整数到字符 out_char = '' for char, index in mapping.items(): if index == yhat: out_char = char break # 附加到输入 in_text += char return in_text |
完整示例
将所有这些整合在一起,下面列出了使用拟合的神经网络语言模型生成文本的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
from pickle import load from keras.models import load_model from keras.utils import to_categorical from keras.preprocessing.sequence import pad_sequences # 使用语言模型生成字符序列 def generate_seq(model, mapping, seq_length, seed_text, n_chars): in_text = seed_text # 生成固定数量的字符 for _ in range(n_chars): # 将字符编码为整数 encoded = [mapping[char] for char in in_text] # 将序列截断为固定长度 encoded = pad_sequences([encoded], maxlen=seq_length, truncating='pre') # 独热编码 encoded = to_categorical(encoded, num_classes=len(mapping)) # 预测字符 yhat = model.predict_classes(encoded, verbose=0) # 反向映射整数到字符 out_char = '' for char, index in mapping.items(): if index == yhat: out_char = char break # 附加到输入 in_text += char return in_text # 加载模型 model = load_model('model.h5') # 加载映射 mapping = load(open('mapping.pkl', 'rb')) # 测试韵文的开头 print(generate_seq(model, mapping, 10, 'Sing a son', 20)) # 测试行中间 print(generate_seq(model, mapping, 10, 'king was i', 20)) # 测试原始文本中没有的内容 print(generate_seq(model, mapping, 10, 'hello worl', 20)) |
运行示例会生成三个文本序列。
注意:由于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
第一个测试模型从韵文开头开始的表现。第二个测试模型从一行中间开始的表现。最后一个示例测试模型处理从未见过的字符序列的表现。
1 2 3 |
唱一首六便士的歌,一个口袋 国王在他的账房里 你好,世界,一个派。这 |
我们可以看到,模型在前面两个例子中表现非常好,正如我们所预料的。我们还可以看到,模型仍然为新文本生成了一些东西,但那是胡说八道。
扩展
本节列出了一些您可能希望探索的扩展本教程的想法。
- 填充。更新示例,只按行提供序列,并使用填充将每个序列填充到最大行长。
- 序列长度。尝试不同的序列长度,看看它们如何影响模型的行为。
- 调整模型。尝试不同的模型配置,例如内存单元的数量和时期,并尝试开发一个用更少资源实现更好性能的模型。
进一步阅读
如果您想深入了解此主题,本节提供了更多资源。
总结
在本教程中,您学习了如何开发基于字符的神经网络语言模型。
具体来说,你学到了:
- 如何为基于字符的语言模型准备文本。
- 如何使用 LSTM 开发基于字符的语言模型。
- 如何使用训练好的基于字符的语言模型生成文本。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
嗨,Jason - 感谢分享这篇文章。我正在学习阶段,当我尝试运行您的程序(定义 load_doc 函数)时,我收到了错误。在运行代码之前,我需要安装任何包吗?
运行第一组将文档加载到内存中的行时,我收到以下错误
> return text
错误:在“return text”中出现意外符号
这看起来像一个复制粘贴错误,请确保您保持 Python 代码的缩进。
非常感谢杰森。一个一般性问题。在您的博客上,我读了很多关于独热编码的内容。从 mnist 数据集中我了解到,比较概率很容易(例如,如果数字是2,我希望我的网络在第三行输出1)。但是当涉及到语言时,庞大的词汇量是不是独热编码完全低效?我的意思是,例如,100万的词汇量,每个单词一个向量,一个1和999.999个零?我真的不明白。
是的,通常我们尝试减小词汇量的大小,以确保模型在合理的时间内进行训练。
但替代方案是什么?也许是更不清晰的单词预测和更差的技能?
对于Klass提到的“按词分词”问题(因为按词分词产生的类别比按字符分词多得多,导致X数据非常宽),是否可以通过简单地将嵌入序列而不是独热编码序列(其中只有一行包含每个词/词元的嵌入)输入模型来解决?
您必须更改示例中的输入层,但这种方法是否有效并能缓解高维度带来的问题?
是的,你对输入进行序数编码,将整数输入到嵌入层,然后将嵌入向量序列输入到 LSTM 或你喜欢的任何模型中。
我在博客上有很多关于这方面的例子,例如
https://machinelearning.org.cn/use-word-embedding-layers-deep-learning-keras/
又一篇出色的教程,Jason 博士。
谢谢 Ravi!
你好,
我们可以使用 nltk 作为 Keras 的辅助库来实现文本转换吗?
斯里哈里
是的,这里有一个例子
https://machinelearning.org.cn/clean-text-machine-learning-python/
很棒的文章。非常感谢逐行的详细程度。
我认为文章的这一部分有一些错误
“接下来,整数需要使用 pad_sequences() Keras 函数进行独热编码。”
我想你的意思是截断而不是独热编码?另外,它缺少配套的代码片段。
谢谢,已修复。我指的是用于独热编码的 to_categorical() 函数。
酷,谢谢分享!
不客气。
快速提问,如果可以的话……如果我们想用一些额外的输入特征来描述序列,我们该如何准备数据呢?举个例子,继续上面的例子,我可能想将每个序列与编写序列的人的名字关联起来。这个特征可能会稍微改变下一个字符的预测。我曾想过一个选项是为每个人创建和训练不同的模型,但我认为这会非常次优,因为学习到的大部分规则对于每个人都是通用的,并且每个模型/人的数据集都会减少。另一个选项是将个人信息编码到输入中,在每个时间步中。但在这种情况下,会有一点冗余,因为个人输入特征在所有时间步中都是相同的。那么,有没有一种方法可以将一个在所有时间步中独立且不变的特征作为输入提供给模型,但又能表征整个输入序列呢?非常感谢
好问题,也许是多输入模型
https://machinelearning.org.cn/keras-functional-api-deep-learning/
嗨,Jason,感谢您的精彩文章!
我有一个关于训练阶段的问题。我正在考虑使用字符嵌入,例如在字符上拟合 word2vec 模型,然后我将用它来训练 LSTM,而不是使用独热编码字符。您认为这会带来任何性能提升吗?我打算自己测试这个想法,但我很好奇您是否首先尝试过,或者您认为这是一种值得尝试的方法。
我没有尝试过字符嵌入,抱歉。
嗨 Ethan B,你有没有在字符上拟合过 word2vec 模型?
这样做没有意义。字符没有语义。
嗨,这真是一个很棒的教程!!
我有一个问题。当我尝试使用 generate_seq 进行预测时,出现此错误 ValueError: Error when checking : expected lstm_1_input to have shape (408, 37) but got array with shape (10, 37)
为什么会发生这种情况?谢谢!!!
你好,我遇到了一个类似的错误:ValueError: 无法将大小为380的数组重塑为形状(1,1,10)
当我注释掉 generate_seq() 中的行:encoded = encoded.reshape(1, encoded.shape[0], encoded.shape[1]) 时,代码对我来说就工作了
@Riiya,感谢指出这一点。
@Jason,AI 和 Riya 说得对。代码无法运行(您的博客或书)。我们需要
#encoded = encoded.reshape(1, encoded.shape[0], encoded.shape[1])
才能使预测工作。
您能确认您已安装了最新版本的 Keras 和其他库吗?
现在更新,但问题仍然存在。
不用替我回答——我很开心它能运行。
重塑那一行绝对有问题
ValueError: 无法将大小为 780 的数组重塑为形状 (1,1,10)
检查了已安装库的版本。据我所知,这里需要
keras
、numpy
和tensorflow
。numpy==1.14.5
Keras==2.2.0
Keras-Applications==1.0.2
Keras-Preprocessing==1.0.1
tensorflow==1.8.0
注释掉重塑代码确实有助于成功运行代码,但从输出看,它似乎搞砸了实际的预测……有什么想法吗?
我想知道这是否是数据文件的问题,您能确认文本的原始数据与帖子匹配吗?
确实,有一个错误……请看下面
嗨,Jason,
您能帮我解决一个错误吗?我是 Python 新手,所以很多时候我不知道如何解决错误。
错误 1
mapping = load(open(‘mapping.pkl’, ‘rb’))
回溯(最近一次调用)
File “”, line 1, in
mapping = load(open(‘mapping.pkl’, ‘rb’))
NameError: 名称“load”未定义
错误 2
from pickle import load
mapping = load(open(‘mapping.pkl’, ‘rb’))
回溯(最近一次调用)
File “”, line 1, in
mapping = load(open(‘mapping.pkl’, ‘rb’))
EOFError: 输入耗尽
谢谢,
内拉吉
也许仔细检查一下你是否复制了示例中的所有代码?
嗨 Jason,这非常有用且细节详尽。感谢您的分享。
我正在解决一个问题,我有大约32000行乱七八糟的字符“wewlsfnskfddsl…eredsda”,每行长度为406。这些字符可能被哈希了。我需要预测它们属于哪个类别?这里类别是1-12本图书的名称。
关于如何修改您上面的代码,您有什么建议吗?我的问题仍然需要文本生成吗?因为这是一个多类别分类问题。
非常感谢。期待您的建议。
这听起来像是分类问题。语言模型/文本生成不会有帮助。
我建议测试 MLP、CNN 和 LSTM 来解决这个问题。还可以查阅博客上关于情感分类的一些教程,它们将提供一个模板。也不需要词嵌入。
我认为多对一的 RNN 模型应该能解决您的问题
嗨 Jason,感谢您上面的建议。我正在尝试实现一个 LSTM 模型,并按照这篇文章来设置。我有一个关于 LSTM 和全连接层的 input_shape 的问题。
我的 xtrain 是一个数字序列,作为 NumPy 数组。给您一个背景:我有32514行混乱的字符“wewlsfnskfddsl...”,我将其重塑为:X = X.reshape((1,32514,1)),用于 LSTM 的 input_shape,灵感来自您的文章“https://machinelearning.org.cn/reshape-input-data-long-short-term-memory-networks-keras/”。
然而,这里我的 y 是12个类别(0到11)。这是一个多类分类问题。
y = to_categorical(y, num_classes=12)
我应该如何定义我的全连接层?根据 Keras 文档,全连接层和 LSTM 层的 input_shape 如下
全连接层:(batch_size, input_dim)
LSTM:(batch_size, timesteps, input_dim)
# 定义模型
model = Sequential()
model.add(LSTM(75, input_shape=(32514,1)))
model.add(Dense(input(1), activation=’softmax’))
打印(model.summary())
# 编译模型
model.compile(loss=’categorical_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
# 拟合模型
model.fit(X, y, epochs=100, verbose=2)
# 将模型保存到文件
model.save(‘model.h5’)
# 保存映射
dump(mapping, open(‘mapping.pkl’, ‘wb’))
当我运行上面的单元格时,程序似乎要求另一个输入。
这正确吗?非常感谢。我喜欢您的博客。
您可以在此处了解有关 LSTMS 数据形状的更多信息
https://machinelearning.org.cn/faq/single-faq/how-do-i-prepare-my-data-for-an-lstm
非常感谢 Jason 的主题和详细解释,看起来真的很棒。
我有一个问题:为什么你将LSTM隐藏层分配给75个记忆单元?
该模型是通过反复试验配置的。
嗨,Jason,
你知道我是否可以使用这种技术(字符)来聚类稀有词,比如DriverId与Driver,Name等?或者Vehicle与vehicleId,location,latitude,longitude?
谢谢。
也许你可以为这些特征使用嵌入?
非常感谢您的这个教程。我的LM在训练时出现此错误,我无法弄清楚如何克服此问题。
IndexError: 数组索引过多
它发生在第39行:
X, y = sequences[:,:-1], sequences[:,-1]
据我所知,它不想从一维向量创建二维向量。我尝试使用numpy重塑,但我一直收到类似性质的错误。你有什么办法解决这个问题吗?
也许这篇关于numpy数组的文章可以帮助你检查你的代码并找出问题。
https://machinelearning.org.cn/index-slice-reshape-numpy-arrays-machine-learning-python/
检查你的char_sequences.txt
可能它在最后一行有一个空行
所以当代码尝试索引最后一行(空行)时,它会给出太多索引错误(因为它只有一个索引)
你好!
感谢这个极其易于遵循的教程。
我想知道有没有比使用to_categorical更节省内存的独热编码方法?
我正在处理的序列文本文件接近1GB,我的词汇量相当大,因此在以下位置出现了MemoryErrors
sequences = [to_categorical(x, num_classes=vocab_size) for x in X]
任何帮助都将不胜感激!
嗯。一些想法
不要使用独热编码,使用整数编码和词嵌入,并通过渐进加载以块为单位处理文件。
放弃了独热编码,改用fastText词向量,从此一切顺利。谢谢!
干得好!
你好 Jason..我想查看每个字符的嵌入表示(使用LSTM嵌入)..你能否提供所需的代码行?
抱歉,我没有基于字符的嵌入示例——如果存在这种东西的话。
最后完整的例子中有一处错误。在第17行
“encoded = encoded.reshape(1, encoded.shape[0], encoded.shape[1])”
应该是
“encoded = encoded.reshape(-1, encoded.shape[1], encoded.shape[2])”
请您再检查一下?
这究竟是为什么?
我不确定为什么,但这个改动使代码对我有效。我一直遇到和其他人一样的错误。
有意思。
所有库都更新了吗?Python 3?Keras?TensorFlow?
我正在使用字符级语言模型来预测给定前一个字符序列(例如20个字符)的下一个字符。我的训练序列几乎有1088637个。我正在使用以下模型进行训练
model = Sequential()
model.add(LSTM(1000, input_shape=(X.shape[1], X.shape[2])))
model.add(Dropout(0.2))
model.add(Dense(vocab_size, activation=’softmax’, kernel_initializer=’normal’))
但我只得到了50%的正确文本。它总是从文本的中间开始出错。有什么建议吗?感谢您的帮助。
也许模型配置或学习配置需要调整,一个好的起点在这里
https://machinelearning.org.cn/start-here/#better
请问有没有字符级机器翻译的教程?
我好像没有,抱歉。
嗨,Jason,
在你的基于词的语言模型示例中,你向我们展示了
1. 单词输入 -> 单词输出的框架
2. N个词输入 -> 单词输出的框架
3. 逐行框架
同样,对于你在这里使用的基于字符的语言模型,你使用
1. N个字符输入 -> 单个字符输出
2. 也建议将逐行作为实验使用
然而,你也可以使用一种不同的方法来学习语言模型,即
序列到序列 (Sequence2Sequence)
输入和输出的例子
Hello Jason!-E-
-S-Hello Jason!
这可以使用一个带有`return_sequences=True`的LSTM来完成,其中输入序列长度等于输出序列长度。
输入可以编码为词嵌入,输出可以编码为独热编码。
对吗?
是的,但是seq2seq需要一个编码器-解码器模型,而不仅仅是LSTM返回的序列。
一个非常基本的疑问,抱歉,因为我是这方面的初学者……你根据什么将X和Y声明为sequences[,:-1]和[:,-1]?
你是什么意思?也许你可以详细说明你的疑问?
嗨!
谢谢!我正在尝试运行这个例子,但代码中似乎有一行错误
X, y = sequences[:,:-1], sequences[:,-1]
结果是,`sequences`首先是一个值列表,然后才转换为数组。这个数组不是多维的,因此上述引用的行中出现了错误。我尝试通过将该行替换为
X, y = sequences[:-1], sequences[-1]
来修复这个问题,这对于一维数组来说是合理的,但现在在下一行又抛出了另一个错误
model.add(LSTM(75, input_shape=(X.shape[1], X.shape[2])))
我理解X的形状必须不同,这就是为什么在最后引用的这一行中,它旨在提供两个维度中的两个整数。
有什么办法可以解决这个问题吗?
谢谢!
谢谢。你确定吗?我相信这个例子在不改变的情况下可以按预期运行。
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
嗨,谢谢你这么快回复!
你说得对,我没有正确复制代码。
尽管如此,我还是复制了,并收到了这个错误
回溯(最近一次调用)
文件“generate.py”,第37行,在
print(generate_seq(model, mapping, 10, ‘Sing a son’, 20))
文件“generate.py”,第18行,在generate_seq中
encoded = encoded.reshape(1, encoded.shape[0], encoded.shape[1])
ValueError: 无法将大小为380的数组重塑为形状(1,1,10)
在生成文本时。注释掉第18行可以使其工作,但我不确定它是否按预期工作。您能确认一下吗?
您能确认您的Keras和TensorFlow版本是否最新吗?
嗨,谢谢你的回复
我可以确认我的系统是最新版本。这意味着tensorflow包版本为2.2.0,它也提供了Keras。这样可以吗?
您的版本看起来很好。我将进行调查。
更新:该示例在TensorFlow 2.2和Keras 2.3下可以正常运行。
请确保您复制了完整的代码示例。
Jason,您好,
首先,感谢您为此付出的所有努力。
我只是再次将三个完整的代码复制到空文件中,但仍然收到上面发布的相同错误
回溯(最近一次调用)
文件“generate_.py”,第36行,在
print(generate_seq(model, mapping, 10, ‘Sing a son’, 20))
文件“generate_.py”,第17行,在generate_seq中
encoded = encoded.reshape(1, encoded.shape[0], encoded.shape[1])
ValueError: 无法将大小为380的数组重塑为形状(1,1,10)
我只需注释掉那行代码即可解决问题。
我的python版本是3.8.3,tensorflow是2.2.0,keras是2.3.0-tf。我所有的一切都是从官方Archlinux仓库安装的(packages {,python}tensorflow-opt)。
也许有些与操作系统相关的东西搞砸了。说实话,我不认为这值得费太多麻烦。我能够通过修改那行代码来创建序列、训练模型和生成文本。我们的讨论在这里,所以任何人都可以查看这些评论并在需要时找到解决方案。
再次感谢您,
巴勃罗。
也许可以。
坚持住。
如果我的特征(字符)非常大,例如扩展到88个,那么是否需要对特征进行独热编码?因为那会严重影响我的训练时间和内存……
也许可以尝试一下,并与序数编码和嵌入等其他编码进行比较。
是的,非常感谢!我也正在尝试将其转换为字节“utf-8”以简化嵌入查找,然后反转以在预测后获得可读文本。
告诉我进展如何。
你好,Jason。
我想问一下这段代码中的指标。
model.compile(loss=’categorical_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
据我所知,这些指标是基于序列比较的。
你好,Jason。
我想问一下这段代码中的指标。
model.compile(loss=’categorical_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
据我所知,这些指标是基于序列比较的。
model.fit(X, y, epochs=30, verbose=2, callbacks=[csv_logger])
结果,最终数据集被分为2个X和Y。在这种情况下,它是无监督学习吗?而“y”只用于计算指标吗?
如果我理解有误,请解释为什么将数据分成“X”和“y”
这种训练最终是什么类型的,我不太明白?
提前感谢。
PS. 抱歉我的英语不好
在上面,它作用于每个字符输出,而不是序列。
如果您对序列感兴趣,那么seq2seq模型可能更合适
https://machinelearning.org.cn/develop-neural-machine-translation-system-keras/
交叉熵被最小化,因此模型预测所需的输出。
准确率用于评估输出的分类变量,例如预期的字符。在这种情况下,它可能不是最合适的指标。例如,如果达到100%的准确率,模型可能已经记住了输入,这可能不是我们期望的。
还有一个问题。我不太明白为什么会有两个输入数组“X”和“y”?
你能详细解释一下吗?
谢谢。
好问题,请看这个
https://machinelearning.org.cn/faq/single-faq/what-are-x-and-y-in-machine-learning
对于大小为1.59 MB的生物序列文件,100个epoch中的一个epoch大约需要180秒,损失为1.3362,准确率为0.3520。
如何减少它?
这里有一些建议。
https://machinelearning.org.cn/faq/single-faq/how-do-i-speed-up-the-training-of-my-model
嗨,Jason,
执行代码时出现此错误
ValueError: 无法将大小为380的数组重塑为形状(1,1,10)
请您提供建议。谢谢。
或许这些提示会有帮助
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
你好,感谢你的好信息。如果你能看一下,我将非常高兴。我完全按照代码运行它。然而,我在model.fit部分收到了以下错误。
ValueError: 层 sequential_15 的输入0与该层不兼容:预期 ndim=3,但找到 ndim=4。接收到的完整形状:[None, 398, 11, 38]
我该如何解决这个问题?
谢谢您。
不客气。
很抱歉听到这个消息,这些技巧会有帮助
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
再次感谢,我修好了。我还有另一个问题。您有什么推荐的资源可以解释如何用代码进行简单的可控文本生成吗?或者您会考虑就这个主题制作一个教程吗?
干得好!
是的,你可以在博客上找到许多语言模型的例子。
https://machinelearning.org.cn/?s=language+models&post_type=post&submit=Search