与RNN神经元一样,LSTM神经元在其管道中可以保持记忆,以允许解决顺序和时间问题,而不会出现影响其性能的消失梯度问题。
使用古诗数据集,利用LSTM神经网络模型训练自动生成中文藏头诗。
import pandas as pd
import string
import numpy as np
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding,LSTM,Dense,Activation
from tensorflow.keras import Model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.models import load_model
import tensorflow
tensorflow.__version__
'2.7.0'
一、 数据读入
import pandas as pd
poems_text = pd.read_table('poems.txt', header=None)
poems_text.columns = ["text"]
poems_text.head()
| text |
---|
0 | 答李司户夔:远方来下客 輶轩摄使臣 弄琴宜在夜 倾酒贵逢春 驷马留孤馆 双鱼赠故人 明朝散云... |
---|
1 | 寄天台司马道士:卧来生白发 览镜忽成丝 远愧餐霞子 童颜且自持 旧游惜疏旷 微尚日磷缁 不寄... |
---|
2 | 使往天平军马约与陈子昂新乡为期,及还而不相遇:入卫期之子 吁嗟不少留 情人去何处 淇水日悠悠... |
---|
3 | 过函谷关:二百四十载 海内何纷纷 六国兵同合 七雄势未分 从成拒秦帝 策决问苏君 鸡鸣将狗盗... |
---|
4 | 送朔方何侍郎:闻道云中使 乘骢往复还 河兵守阳月 塞虏失阴山 拜职尝随骠 铭功不让班 旋闻受... |
---|
中文古诗数据集(poems.txt)中含有15374首常用古代诗词,首先逐行读取txt文本数据,去除标题并进行文本补齐操作,将诗句分割成单个汉字组成的字符串存储在列表中。
import string
import numpy as np
f = open('poems.txt',"r",encoding='utf-8')
poems = []
for line in f.readlines():
title,poem = line.split(':')
poem = poem.replace(' ','')
poem = poem.replace('\n','')
poems.append(list(poem))
print(poems[0][:])
以第一首诗句为例,输出处理后的第一条数据: [‘远’, ‘方’, ‘来’, ‘下’, ‘客’, ‘輶’, ‘轩’, ‘摄’, ‘使’, ‘臣’, ‘弄’, ‘琴’, ‘宜’, ‘在’, ‘夜’, ‘倾’, ‘酒’, ‘贵’, ‘逢’, ‘春’, ‘驷’, ‘马’, ‘留’, ‘孤’, ‘馆’, ‘双’, ‘鱼’, ‘赠’, ‘故’, ‘人’, ‘明’, ‘朝’, ‘散’, ‘云’, ‘雨’, ‘遥’, ‘仰’, ‘德’, ‘为’, ‘邻’]
二、数据预处理
1.对数据进行从字符到正整数的映射,并加以截断/补齐操作。
从字符到正整数的映射 from keras.preprocessing.sequence import pad_sequences命令因第三方库的版本问题易出现报错,可在keras前加上tensorflow修正。 Tokenizer类允许使用两种方法向量化一个文本语料库: 将每个文本转化为一个整数序列(每个整数都是词典中标记的索引); 或者将其转化为一个向量,其中每个标记的系数可以是二进制值、词频、TF-IDF权重等。
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
tokenizer = Tokenizer()
tokenizer.fit_on_texts(poems)
vocab_size=len(tokenizer.word_index)+1
poems_digit = tokenizer.texts_to_sequences(poems)
poems_digit = pad_sequences(poems_digit,maxlen=50,padding='post')
poems_digit[0]
texts_to_sequences(poems)方法可将诗句文本列表转为序列的列表,列表中每个序列对应于一段输入文本。 pad_sequences可将多个序列截断或补齐为相同长度。此处设置序列最大长度(maxlen)为50,在序列后端补齐(post)。 编码+补全后的结果:
array([ 46, 171, 12, 40, 29, 3342, 528, 3176, 322, 592, 774,
352, 608, 44, 23, 648, 73, 593, 158, 8, 2020, 92,
188, 149, 548, 268, 305, 740, 114, 2, 61, 93, 333,
9, 45, 201, 1352, 781, 43, 491, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0])
2.提取因变量与自变量
X=poems_digit[:,:-1]
Y=poems_digit[:,1:]
X示例 | Y示例 |
---|
46 | 171 | 171 | 12 | 12 | 40 | 40 | 29 | 29 | 3342 | 3342 | 528 | … | … |
3.one-hot编码
one-hot编码是将类别变量转换为深度学习算法易于利用的一种形式的过程。运用to_categorical函数将类别向量转换为二进制的矩阵类型表示。在得到相应的embedding之后,可以送入到深度学习模型中进行后续处理。
from tensorflow.keras.utils import to_categorical
Y = to_categorical(Y,num_classes=vocab_size)
print(Y.shape)
(15374, 49, 5040)
三、构建深度神经网络模型
keras.layers各种层介绍https://www.cnblogs.com/lhxsoft/p/13534667.html
Keras中主要的模型是Sequential模型,Sequential是一系列网络层按顺序构成的栈,将一些网络层通过.add()堆叠起来,就构成了一个模型。 ? 嵌入层Embedding定义一个词典为vocab_size=5040的嵌入层,一个hidden_size1=128维的向量空间, 输入序列长度为49。 ? LSTM层设置输出空间的维度为64 ? 全连接层Dense设置输出维度为5040 ? 激活层Activation对一个层的输出施加激活函数
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding,LSTM,Dense,Activation
from tensorflow.keras import Model
hidden_size1=128
hidden_size2=64
model = Sequential()
model.add(Embedding(input_dim=vocab_size,output_dim=hidden_size1,input_length=49,mask_zero=True))
model.add(LSTM(hidden_size2,return_sequences=True))
model.add(Dense(vocab_size))
model.add(Activation('softmax'))
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (None, 49, 128) 645120
lstm (LSTM) (None, 49, 64) 49408
dense (Dense) (None, 49, 5040) 327600
activation (Activation) (None, 49, 5040) 0
=================================================================
Total params: 1,022,128
Trainable params: 1,022,128
Non-trainable params: 0
_________________________________________________________________
四、模型的训练及测试
完成模型的搭建后,需要使用.compile()方法来编译模型,并按batch进行一定次数的迭代训练,以拟合网络。 编译模型时必须指明损失函数和优化器
from tensorflow.keras.optimizers import Adam
model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.01),metrics=['accuracy'])
model.fit(X,Y,epochs=10,batch_size=64,validation_split=0.2)
D:\Anaconda\Anaconda3\Lib\site-packages\keras\optimizer_v2\adam.py:105: UserWarning: The `lr` argument is deprecated, use `learning_rate` instead.
super(Adam, self).__init__(name, **kwargs)
Epoch 1/10
193/193 [==============================] - 60s 298ms/step - loss: 4.7847 - accuracy: 0.0301 - val_loss: 4.6860 - val_accuracy: 0.0337
Epoch 2/10
193/193 [==============================] - 56s 290ms/step - loss: 4.5226 - accuracy: 0.0480 - val_loss: 4.5059 - val_accuracy: 0.0552
Epoch 3/10
193/193 [==============================] - 59s 306ms/step - loss: 4.3119 - accuracy: 0.0724 - val_loss: 4.3876 - val_accuracy: 0.0719
Epoch 4/10
193/193 [==============================] - 60s 309ms/step - loss: 4.1503 - accuracy: 0.0889 - val_loss: 4.3279 - val_accuracy: 0.0798
Epoch 5/10
193/193 [==============================] - 59s 304ms/step - loss: 4.0391 - accuracy: 0.0986 - val_loss: 4.3107 - val_accuracy: 0.0838
Epoch 6/10
193/193 [==============================] - 58s 299ms/step - loss: 3.9588 - accuracy: 0.1041 - val_loss: 4.3057 - val_accuracy: 0.0860
Epoch 7/10
193/193 [==============================] - 56s 290ms/step - loss: 3.8951 - accuracy: 0.1090 - val_loss: 4.3062 - val_accuracy: 0.0872
Epoch 8/10
193/193 [==============================] - 56s 291ms/step - loss: 3.8426 - accuracy: 0.1130 - val_loss: 4.3138 - val_accuracy: 0.0879
Epoch 9/10
193/193 [==============================] - 56s 291ms/step - loss: 3.7964 - accuracy: 0.1173 - val_loss: 4.3226 - val_accuracy: 0.0881
Epoch 10/10
193/193 [==============================] - 56s 290ms/step - loss: 3.7565 - accuracy: 0.1208 - val_loss: 4.3346 - val_accuracy: 0.0887
<keras.callbacks.History at 0x19d0b5a6b80>
保存模型
model.save('Poetry_LSTM.h5')
加载模型
from tensorflow.keras.models import load_model
model = load_model('Poetry_LSTM.h5')
加载训练好的模型,输入“诗头”,写藏头诗。
poem_incomplete='雨****轩****可****爱****'
poem_index=[]
poem_text=''
for i in range(len(poem_incomplete)):
current_word=poem_incomplete[i]
if current_word !='*':
index=tokenizer.word_index[current_word]
else:
x=np.expand_dims(poem_index,axis=0)
x=pad_sequences(x,maxlen=49,padding='post')
y=model.predict(x)[0,i]
y[0]=0
index=y.argmax()
current_word=tokenizer.index_word[index]
poem_index.append(index)
poem_text=poem_text+current_word
poem_text=poem_text[0:]
print(poem_text[0:5])
print(poem_text[5:10])
print(poem_text[10:15])
print(poem_text[15:20])
雨滴清风吹
轩树风吹笛
可怜一曲不
爱此时人不
|