import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import SimpleRNN, Dense
import matplotlib.pyplot as plt
# 加载训练数据
text = open('chinese_text.txt', encoding='utf-8').read()
print(f'训练数据字符数: {len(text)}')
# 创建索引词典
chars = sorted(list(set(text)))
char_to_index = {c: i for i, c in enumerate(chars)} # 字符为键,下标为值的词典
index_to_char = {i: c for i, c in enumerate(chars)} # 下标为键,字符为值的词典
chars = [char_to_index[char] for char in text]
# 处理训练数据
maxlen = 10 # 序列长度
step = 1 # 每步移动的字符数
sentences = []
next_chars = []
for i in range(0, len(text) - maxlen, step):
sentences.append(text[i: i + maxlen]) # 将文本分段,每段10个字符
next_chars.append(text[i + maxlen])
print(f'句段总数: {len(sentences)}')
# 向量化输入和目标
X = np.zeros((len(sentences), maxlen, len(chars)), dtype=bool) # 第一维为总段数,第二维为每段的字符数,第三维为词典的长度
y = np.zeros((len(sentences), len(chars)), dtype=bool)
for i, sentence in enumerate(sentences):
for t, char in enumerate(sentence):
X[i, t, char_to_index[char]] = True # 以每段的每个字符在词典中的值为下标,给其下一维对应的位置置真
y[i, char_to_index[next_chars[i]]] = True # 以其对应X中段的下一个字符在词典中的值为下标,给其下一维对应的位置置真
# 构建RNN模型
model = Sequential()
model.add(SimpleRNN(128)) # 增加简单RNN层
# model.add(SimpleRNN(128, input_shape=(maxlen, len(chars))))
model.add(Dense(len(chars), activation='softmax')) # 增加全连接层
model.compile(loss='categorical_crossentropy', optimizer='adam') # 指定损失函数及优化器
# 训练模型
history = model.fit(X, y, batch_size=128, epochs=100)
loss_values = history.history['loss'] # 获取损失函数
loss_values = list(loss_values)
plt.figure()
plt.plot(range(1, 100 + 1), loss_values, c = '#000000', label = 'train loss')
plt.title('Train Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.savefig('Train Loss.svg')
plt.show()
# 定义采样函数
def sample(preds, temperature): # 预测概率数组和温度参数
preds = np.asarray(preds).astype('float64') # 将preds转换为NumPy数组,并确保其数据类型为float64
preds = np.log(preds) / temperature # 当温度参数较低时,概率分布更确定,较高时,概率分布更随机
exp_preds = np.exp(preds)
preds = exp_preds / np.sum(exp_preds) # 归一化
probas = np.random.multinomial(1, preds, 1)
return np.argmax(probas)
# 测试
test_text = '人物风流往往非,空余' # 十个字符
print(f'测试输入为: {test_text}')
print(f'完整输出古诗为: {test_text}', end='')
for i in range(100):
test = np.zeros((1, maxlen, len(chars)), dtype=bool)
for i, char in enumerate(test_text):
test[0, i, char_to_index[char]] = True
preds = model.predict(test, verbose=0)[0] # 不输出任何日志
next_index = sample(preds, temperature=0.5)
next_char = index_to_char[next_index]
test_text += next_char
test_text = test_text[1:] # 将预测后的结果加入段,去除段首成员,将新的段作为新的输入
print(next_char, end='')
if(next_char=='\n'):break # 以换行符作为诗结束的标志