学习笔记。仅供参考!
基于keras框架的深度学习实战,利用三国演义文本集,LSTM神经网络模型,训练文本数据,预测文本,自动生成文本。
本文使用Sequential 顺序模型,它可以由多个网络层线性堆叠。也可以使用 Keras 函数式 API,可以构建任意的神经网络图。
Sequential 模型如下所示:
from keras.models import Sequential
model =Sequential()
使用 .add() 来堆叠模型:
from keras.layers import Dense
model.add(Dense(units=64, activation=‘relu’, input_dim=100))
model.add(Dense(units=10, activation=‘softmax’))
在完成了模型的构建后, 使用.compile() 来配置学习过程:
model.compile(loss=‘categorical_crossentropy’, optimizer=‘sgd’,metrics=[‘accuracy’])
如果需要,进一步地配置你的优化器。Keras 的核心原则是使事情变得相当简单,同时又允许用户在需要的时候能够进行完全的控制(终极的控制是源代码的易扩展性。
model.compile(loss=keras.losses.categorical_crossentropy,optimizer=keras.optimizers.SGD(lr=0.01, momentum=0.9, nesterov=True))
现在,你可以批量地在训练数据上进行迭代了:
model.fit(x_train, y_train, epochs=5, batch_size=32)
或者,你可以手动地将批次的数据提供给模型
model.train_on_batch(x_batch, y_batch)
只需一行代码就能评估模型性能:
loss_and_metrics = model.evaluate(x_test, y_test, batch_size=128)
对新的数据生成预测:
classes = model.predict(x_test, batch_size=128)
三国演义文本数据自取:
链接:https://pan.baidu.com/s/19-MktXVj2SaKGmAMIgVnFg
提取码:ri9w
数据处理,思路:60000多个字符集,按照步长为3做平移,得到20000多个长度为20的样本,作为输入X,每20个字符来预测第21个字符Y,sentences = [ ] 是X的集合,next_chars = [ ]是Y的集合
加载数据集
f = open('三国演义.txt', encoding='utf-8')
text = f.read()
print(text[:300])
f.close()
print('语料总长度:', len(text))
sent_len = 20 # 样本长度,用前20个字来预测第21个字
step = 3 # 在总文本上面平移取样本,步长为3
sentences = [] # 样本集X,用来预测
next_chars = [] # 样本集Y,真实值
for i in range(0, len(text) - sent_len, step):
# 获取样本,20个字符为一个样本,步长为3
sentences.append(text[i:i + sent_len])
# 预测文本的真实值
next_chars.append(text[i + sent_len])
print('训练样本数目:', len(sentences))
创建字典,根据索引对应相应的字符,N表示字典个数
char_set = sorted(list(set(text))) # 创建字典,文本的字符集,sorted()对文本进行排序
N = len(char_set) # 字典的长度,字的个数
print('字典中字的个数:', N)
# 根据值来获取索引,存入字典
char_indices = dict((char, index) for index, char in enumerate(char_set))
print(char_indices)
'''
语料总长度: 603705 >>>>文本总字符
训练样本数目: 201229 >>>>>取步长为3,得到训练样本2w
字典中字的个数: 3843 >>>>>根据N中字符的index来做one-hot编码,取index为1,其余取0
'''
对于输如样本X,是一个三维张量,(样本数量,样本长度,词向量N),利用N来表示词向量,将N中的值置为0,再根据char_indices ,将对应的字符找到相应的索引值,置为1,进而将词向量转成one-hot编码
print('Vectorization...')
print(len(sentences), sent_len, len(char_set))
# len(sentences), sent_len, len(char_set): 201229 20 3843
# 将样本数据np.zeros()张量数据置0,这里的N就是每一个字的词向量
x = np.zeros((len(sentences), sent_len, N), dtype=bool) # X用来做预测
y = np.zeros((len(sentences), len(char_set)), dtype=bool) # Y 真实集,sent_len=1
# 对每一个字做one-hot编码,构成词向量
for i, sentence in enumerate(sentences):
for t, char in enumerate(sentence):
x[i, t, char_indices[char]] = 1 # 将x索引值的文本置1,形成one——hot编码
y[i, char_indices[next_chars[i]]] = 1 # 将Y索引值的文本置1,形成one——hot编码
print(len(x), len(y))
模型的构建与训练,使用Sequential容器转载模型,三层LSTM神经网络模型,最后全连接层做分类,分为N个类别:
# 构建模型
model = Sequential(name='text_generation')
# 将3843维降维到300,输入维度,样本:20个字,每个字3843维
model.add(LSTM(300, return_sequences=True, input_shape=(sent_len, N)))
model.add(Bidirectional(LSTM(64, return_sequences=True))) # LSTM双向传递
model.add(Bidirectional(LSTM(32, return_sequences=False)))
model.add(Dense(N, activation="softmax")) # 全连接层,有N个分类
print('网络结构:', end='')
model.summary() # 模型和参数框架
model.compile(loss='categorical_crossentropy', optimizer=Adam(learning_rate=0.01))
输出网络结构,通过model.summary函数实现:
网络结构:Model: "text_generation"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm (LSTM) (None, 20, 300) 4972800
bidirectional (Bidirectiona (None, 20, 128) 186880
l)
bidirectional_1 (Bidirectio (None, 64) 41216
nal)
dense (Dense) (None, 3843) 249795
=================================================================
Total params: 5,450,691
Trainable params: 5,450,691
Non-trainable params: 0
将数据传入神经网络模型,传入了6000份样本(数据太大,我的gpu会一直报错,所以截取了6000个样本)训练十轮,随机截取20长度的句子做200个字符的预测,逐字预测,最后生成文本:
# 模型训练
for epoch in range(1, 10):
print('epoch', epoch)
model.fit(x[0:6000], y[0:6000], batch_size=128, epochs=1)
model.save('text_generation.h5')
# 随机取文本做预测
start_index = random.randint(0, len(text) - sent_len - 1)
generated_text = text[start_index:start_index + sent_len]
print('随机文本:', generated_text)
for gamma in [0.2, 0.5, 1.0, 1.2, 1.5]:
print('Gamma:', gamma)
sys.stdout.write(generated_text) # 打印
for i in range(200): # 预测未来200个字符
sampled = np.zeros((1, sent_len, N)) # 第一个维度为1,取单个样本
for t, char in enumerate(generated_text):
sampled[0, t, char_indices[char]] = 1
preds = model.predict(sampled, verbose=0)[0] # 返回3843个概率值的向量
preds = preds.astype(float)
next_index = sample(preds, gamma)
next_char = char_set[next_index]
# 将预测文本从第一位截断,再做下一次预测
generated_text = (generated_text + next_char)[1:] # 向后滑动一次
sys.stdout.write(next_char)
sys.stdout.flush() # 设置缓冲区,逐次打印
print()
调用sample函数,preds参数返回了N个概率值的向量,利用gamma对N个概率值做数据处理,放大或者缩小概率值,来影响文本的生成结果,分别做了五次gamma值[0.2, 0.5, 1.0, 1.2, 1.5]的文本生成结果
# 对样本结果做处理
def sample(preds, gamma=1.0):
preds **= 1 / gamma
preds /= np.sum(preds)
# 从多项分布中抽取样本
probas = np.random.multinomial(1, preds, 1)
return np.argmax(probas) # 返回probas中的元素最大值的索引值
从文本生成结果来看,三层的LSTM神经网络模型做文本生成是有一定的效果的,利用sample函数中不同的gamma值可以很大程度上调整文本生成的结果,当Gamma值大于1时,本文的生成结果更加丰富
tensorflow.python.framework.errors_impl.InternalError: Failed copying
input tensor from /job:localhost/replica:0/task:0/device:CPU:0 to
/job:localhost/replica:0/task:0/device:GPU:0 in order to run
_EagerConst: Dst tensor is not initialized.
试了各种办法,后面将样本缩小到6000就没有错误了
全部代码如下:
# 文本预测和生成
import numpy as np
import random
import sys
from keras import Sequential
from keras.layers import LSTM, Dense, Bidirectional
from tensorflow.keras.optimizers import Adam, RMSprop, SGD
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
# 对样本结果做处理
def sample(preds, gamma=1.0):
preds **= 1 / gamma
preds /= np.sum(preds)
# 从多项分布中抽取样本
probas = np.random.multinomial(1, preds, 1)
return np.argmax(probas) # 返回probas中的元素最大值的索引值
if __name__ == '__main__':
# 加载数据集
f = open('三国演义.txt', encoding='utf-8')
text = f.read()
print(text[:300])
f.close()
print('语料总长度:', len(text))
sent_len = 20 # 样本长度,用前20个字来预测第21个字
step = 3 # 在总文本上面平移取样本,步长为3
sentences = [] # 样本集X,用来预测
next_chars = [] # 样本集Y,真实值
for i in range(0, len(text) - sent_len, step):
# 获取样本,20个字符为一个样本,步长为3
sentences.append(text[i:i + sent_len])
# 预测文本的真实值
next_chars.append(text[i + sent_len])
print('训练样本数目:', len(sentences))
# print(sentences)
# print(next_chars)
char_set = sorted(list(set(text))) # 创建字典,文本的字符集,sorted()对文本进行排序
N = len(char_set) # 字典的长度,字的个数
print('字典中字的个数:', N)
# 根据值来获取索引,存入字典
char_indices = dict((char, index) for index, char in enumerate(char_set))
print(char_indices)
'''
语料总长度: 603705 >>>>文本总字符
训练样本数目: 201229 >>>>>取步长为3,得到训练样本2w
字典中字的个数: 3843 >>>>>根据N中字符的index来做one-hot编码,取index为1,其余取0
'''
print('Vectorization...')
print(len(sentences), sent_len, len(char_set))
# len(sentences), sent_len, len(char_set): 201229 20 3843
# 将样本数据np.zeros()张量数据置0,这里的N就是每一个字的词向量
x = np.zeros((len(sentences), sent_len, N), dtype=bool) # X用来做预测
y = np.zeros((len(sentences), len(char_set)), dtype=bool) # Y 真实集,sent_len=1
# 对每一个字做one-hot编码,构成词向量
for i, sentence in enumerate(sentences):
for t, char in enumerate(sentence):
x[i, t, char_indices[char]] = 1 # 将x索引值的文本置1,形成one——hot编码
y[i, char_indices[next_chars[i]]] = 1 # 将Y索引值的文本置1,形成one——hot编码
print(len(x), len(y))
# 构建模型
model = Sequential(name='text_generation')
# 将3843维降维到300,输入维度,样本:20个字,每个字3843维
model.add(LSTM(300, return_sequences=True, input_shape=(sent_len, N)))
model.add(Bidirectional(LSTM(64, return_sequences=True))) # LSTM双向传递
model.add(Bidirectional(LSTM(32, return_sequences=False)))
model.add(Dense(N, activation="softmax")) # 全连接层,有N个分类
print('网络结构:', end='')
model.summary() # 模型和参数框架
model.compile(loss='categorical_crossentropy', optimizer=Adam(learning_rate=0.01))
# 模型训练
for epoch in range(1, 10):
print('epoch', epoch)
model.fit(x[0:6000], y[0:6000], batch_size=128, epochs=1)
model.save('text_generation.h5')
# 随机取文本做预测
start_index = random.randint(0, len(text) - sent_len - 1)
generated_text = text[start_index:start_index + sent_len]
print('随机文本:', generated_text)
for gamma in [0.2, 0.5, 1.0, 1.2, 1.5]:
print('Gamma:', gamma)
sys.stdout.write(generated_text) # 打印
for i in range(200): # 预测未来200个字符
sampled = np.zeros((1, sent_len, N)) # 第一个维度为1,取单个样本
for t, char in enumerate(generated_text):
sampled[0, t, char_indices[char]] = 1
preds = model.predict(sampled, verbose=0)[0] # 返回3843个概率值的向量
preds = preds.astype(float)
next_index = sample(preds, gamma)
next_char = char_set[next_index]
# 将预测文本从第一位截断,再做下一次预测
generated_text = (generated_text + next_char)[1:] # 向后滑动一次
sys.stdout.write(next_char)
sys.stdout.flush() # 设置缓冲区,逐次打印
print()