---恢复内容开始---
用RNN做文本生成
举个小小的例子,来看看LSTM是怎么玩的
我们这里用温斯顿丘吉尔的人物传记作为我们的学习语料。
(各种中文语料可以自行网上查找, 英文的小说语料可以从古登堡计划网站下载txt平文本:https://www.gutenberg.org/wiki/Category:Bookshelf)
第一步,一样,先导入各种库
In [66]:
import numpy
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import LSTM
from keras.callbacks import ModelCheckpoint
from keras.utils import np_utils
接下来,我们把文本读入
In [67]:
raw_text = open('../input/Winston_Churchil.txt').read()
raw_text = raw_text.lower()
既然我们是以每个字母为层级,字母总共才26个,所以我们可以很方便的用One-Hot来编码出所有的字母(当然,可能还有些标点符号和其他noise)
In [68]:
chars = sorted(list(set(raw_text))) #set将raw——text中所有出现的元素组成一个集合(由于集合中元素没有重复),然后再将无顺序的集合转化为列表(有序)并排序
char_to_int = dict((c, i) for i, c in enumerate(chars)) #生成字符和数字编号的对照表
int_to_char = dict((i, c) for i, c in enumerate(chars))
我们看到,全部的chars:
In [69]:
chars
Out[69]:
一共有:
In [70]:
len(chars)
Out[70]:
同时,我们的原文本一共有:
In [71]:
len(raw_text)
Out[71]:
我们这里简单的文本预测就是,给了前置的字母以后,下一个字母是谁?
比如,Winsto, 给出 n Britai 给出 n;如果给Winston会给出空格。
构造训练测试集
我们需要把我们的raw text变成可以用来训练的x,y:
x 是前置字母们 y 是后一个字母
In [72]:
seq_length = 100 #规定好前后语境的长度(100步)
x = []
y = []
for i in range(0, len(raw_text) - seq_length):
given = raw_text[i:i + seq_length] # 待训练的已知量
predict = raw_text[i + seq_length] # 预测量
x.append([char_to_int[char] for char in given])
y.append(char_to_int[predict])
我们可以看看我们做好的数据集的长相:
In [73]:
print(x[:3]) #前三个数组,每个小数组都为100维的
print(y[:3])
此刻,楼上这些表达方式,类似就是一个词袋,或者说 index。
接下来我们做两件事:
-
我们已经有了一个input的数字表达(index),我们要把它变成LSTM需要的数组格式: [样本数,时间步伐,特征]
-
第二,对于output,我们在Word2Vec里学过,用one-hot做output的预测可以给我们更好的效果,相对于直接预测一个准确的y数值的话。
In [74]:
n_patterns = len(x)
n_vocab = len(chars)
# 把x变成LSTM需要的样子
x = numpy.reshape(x, (n_patterns, seq_length, 1)) #1表示将每个元素变成一维的特征向量。不能省去,否则x原来是100维的,没有办法放到LSTM中
# 简单normal到0-1之间
x = x / float(n_vocab)
# output变成one-hot
y = np_utils.to_categorical(y) #否则,y就是一个0-61之间的实数,如果要输出31,此时要求ML得到的预测值应该锁定,30.5-31.5之间,而这对于61长条形的线性预测而言,很难预测而且还不一定能学会
# one-hot表示的时候可以分别给出61个位置分别为1的概率,此时只需要将概率最大的one-hot向量对应的数值取出即可得到预测值,大大减少运算量,输出也更加简便
#注意:x没用one-hot的原因是我们会采用word2vec来实现同样的效果
print(x[11]) #每个元素都是100个的一维数组
print(y[11]) #61维的one-hot表达式
模型建造
LSTM模型构建
In [ ]:
model = Sequential() #前后关系用sequential(),还有一种是图片性质的graphic()
model.add(LSTM(128, input_shape=(x.shape[1], x.shape[2]))) #第一层为LTSM,放128个神经元,x.shape[1]表示时间步伐100,x.shape[2]表每个中的小特征向量为1
model.add(Dropout(0.2)) #防止数据集过拟合,每次随机取输出的80%的神经元来表示输出,这样让变化速率减缓,不会很快直接下降到极点
model.add(Dense(y.shape[1], activation='softmax')) #dense为普通神经网络,在LSTM之后加一个普通神经网络做输出结果的计算。softmax来得出概率
model.compile(loss='categorical_crossentropy', optimizer='adam') #多元制的交叉熵,adam的学习率可以按照差距来调整步伐,减少过拟合概率
跑模型
In [ ]:
model.fit(x, y, nb_epoch=10, batch_size=32) #nb_epoch=10表示跑多少次,每个batch中放多少集
我们来写个程序,看看我们训练出来的LSTM的效果:
In [ ]:
def predict_next(input_array): #将处理后变成数字的数组放入
# 预处理x
x = numpy.reshape(input_array, (1, seq_length, 1))
x = x / float(n_vocab)
y = model.predict(x)
return y
def string_to_index(raw_input):
res = []
for c in raw_input[(len(raw_input)-seq_length):]: #利用查找表将输入的字符转化为数字
res.append(char_to_int[c])
return res
def y_to_char(y):
largest_index = y.argmax() #取y中最大数值的序列号(就是对照表中的数字顺序),来检索出预测的字符
c = int_to_char[largest_index]
return c
好,写成一个大程序:
In [ ]:
def generate_article(init, rounds=500):
in_string = init.lower()
for i in range(rounds): #rounds需要往后生成多少个字符
n = y_to_char(predict_next(string_to_index(in_string)))
in_string += n #将生成的附在之前的后面,当成下一次的输入,继续下一次的预测生成
return in_string
In [ ]:
init = 'Professor Michael S. Hart is the originator of the Project'
article = generate_article(init)
print(article)