使用RNN文本预测-依据莎士比亚文本
使用RNN预测文本,将会使用莎士比亚的文字来训练。将会分成两份,一份是九成的文字作为训练,还有一成作为验证。训练集是用来训练模型的参数,验证集是用来算出训练出来的模型的准确率,用最优的模型作为最后的模型。这次没有测试数据集。网络结构使用老师提供的LSTM。
首先下载莎士比亚文本
import tensorflow as tf
import tensorflow_datasets as tfds
import numpy as np
text_url = 'https://homl.info/shakespeare'
filepath = tf.keras.utils.get_file('shakespeare.txt', origin=text_url)
with open(filepath) as f:
input_text = f.read()
input_text
出来的文字有点乱码,比如换行符号。还有大小写不统一。需要修正才方便训练
对input_text进行统一,比如大小写统一
tokenizer = tf.keras.preprocessing.text.Tokenizer(lower=True, char_level=True)
#lower=True 统一为小写字母
#char_level: 如果为 True,则每个字符都将被视为标记
tokenizer.fit_on_texts(input_text)
#train set for 90% and test set for 10%
total_count = tokenizer.document_count
train_size = total_count * 90 // 100
valid_size = total_count-train_size
print(f'train_size is:{train_size}, and valid size is {valid_size}')
train_size is:1003854, and valid size is 111540
开始把字符变成数字 即word2int
int2word = {
}
word2int = {
}
for word, index in tokenizer.word_index.items():
int2word[index-1] = word
word2int[word] = index-1
int2word是跟每个单独不重复的字符都有相对应的数字
把莎士比亚所有字符转变成数字
[encoded] = np.array(tokenizer.texts_to_sequences([input_text])) - 1
#把所有的字符变成数字
然后把分成训练集和验证集
x_train = tf.data.Dataset.from_tensor_slices(encoded[:train_size])
x_valid = tf.data.Dataset.from_tensor_slices(encoded[train_size:])
print(f'len of x_train is {len(x_train)}, and the len of x_valid is {len(x_valid)}')
len of x_train is 1003854, and the len of x_valid is 111540
开始训练
首先 使用window函数来实现单移动窗口,首先理解window函数先
window(size, shift=None, stride=1, drop_remainder=False)
size:表示拆分后每个窗口包含多少个采样点,即窗口宽度。
shift:表示滑动窗口中输入元素的跨度,即滑动步长。
stride:表示采样点之间的跨度;可选参数,默认为 None
drop_remainder=True 表示丢弃不足窗口宽度的数据
使用window函数来做训练
train_set = x_train.window(size=100, shift=1, drop_remainder=True)
即 是取100个数字作为一个区间,然后移动一个数字,再取100个数字,持续下去。
显示结果
train_set = x_train.window(size=100, shift=1, drop_remainder=True)
for window in train_set:
print(list(window.as_numpy_iterator()))
接下来是合成批次
其中
batch可以将数据集的连续元素合成批次。
函数形式:batch(batch_size,drop_remainder=False)
参数batch_size:表示要在单个批次中合并的此数据集的连续元素个数。
train_set = x_train.window(size=100, shift=1, drop_remainder=True)
train_set = train_set.flat_map(lambda element: element.batch(100))
#valid
valid_set = x_valid.window(size=100, shift=1, drop_remainder=True)
valid_set = valid_set.flat_map(lambda element: element.batch(100))
其中flat_map和Batch处理的结果是
list(valid_set.as_numpy_iterator())
为后续的训练做好数据处理。变成了array格式,然后再变成one hot code格式
先写一个函数来定义 one hot coding
#变成one hot 模式
def preprocess(element):
x, y = element[:-1], element[1:]
return tf.one_hot(x, depth=39),y
再打乱数据防止过度拟合
train_ds = train_set.map(preprocess).shuffle(1000, seed=123).repeat().batch(128).prefetch(1)
valid_ds = valid_set.map(preprocess).batch(128).prefetch(1)
现开始创建模型
model = tf.keras.Sequential([
tf.keras.layers.LSTM(256, return_sequences=True, input_shape=[None,39]),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.LSTM(256, return_sequences=True),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.LSTM(256, return_sequences=True),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(39, activation='softmax')
])
model.summary()
开始选优化器 Adam
opt = tf.keras.optimizers.Adam(0.001)
model.compile(optimizer=opt, loss='sparse_categorical_crossentropy')
model.fit(train_ds, epochs=10,validation_data= valid_ds)
训练完毕
使用trained model 来预测
def one_hot(x):
return tf.one_hot(x, depth=39)
写预测的函数
def pred(model,start_string):
#预测的数量,这里选择预测200个文字
num_generate = 200
id_list = []
text_generated= []
#这里录用 开始的几个数字
[input_eval] = np.array(tokenizer.texts_to_sequences([start_string])) - 1
for i in input_eval:
id_list.append(i)
#increase one dim
input_eval = tf.expand_dims(input_eval, 0)
input_t = tf.data.Dataset.from_tensor_slices(input_eval)
input_t = input_t.map(one_hot).batch(128)
for i in range(num_generate):
predictions = model.predict(input_t)
index = np.argmax(predictions,axis = 2)[0][-1] #take the last one as predictor character
id_list.append(index)
text_generated.append(int2word[index])
input_t = np.array(id_list)
input_t = tf.expand_dims(input_t, 0)
input_t = tf.data.Dataset.from_tensor_slices(input_t)
input_t = input_t.map(one_hot).batch(128)
return (start_string + ''.join(text_generated))
预测
start_string='Queen:'
print(pred(model,start_string))
start_string='ROMEO:'
print(pred(model,start_string))