数据集下载:点我下载数据集
我们实现的是歌词的自动生成。 主要看我在代码中的注释。。注释的很详细,不懂可以留言。
1:我们加载所需要的模块,这里的模块都是比较常用的模块
from keras.models import Sequential
from keras.layers import Dense, LSTM, Embedding
from keras.callbacks import LambdaCallback
import numpy as np
import random
import sys
import pickle
2:加载数据集,整理汉字和id之间的映射。从我们的txt文件中读取歌词,每一行是一首歌,因为我们的部分歌中含有应为,我们这里也做一个小小的处理,那就是将英文占比比较大的歌扔掉不用。 见下面代码,并给处理了详细的注释:
# 加载数据 整理字和id之间的映射
sentences = []
with open('lyrics.txt', 'r', encoding='utf8') as fr:
lines = fr.readlines()
for line in lines:
line = line.strip()
count = 0
# 取出英文过多的歌词
for c in line: # 统计一周歌中英文字母占得个数
if (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z'):
count += 1
if count / len(line) < 0.1: # 若占比小于0.1则 说明英文少,这首歌就加入到我们的列表中
sentences.append(line)
print('共%d首歌' % len(sentences))
输出是: 共36616首歌
3:整理汉字与需要的映射关系,并存到本地,因为我们把模型训练好了,拿到其他地方运行,这个映射是不能少的
# 整理汉字和数字的映射
chars = {}
for sentence in sentences:
for c in sentence:
chars[c] = chars.get(c, 0) + 1 # 这里是字典的一个基础知识,不懂百度
chars = sorted(chars.items(), key=lambda x: x[1], reverse=True) # 按词频进行排序
chars = [char[0] for char in chars]
vocab_size = len(chars) # 总共的汉字数
print('共%d个字' % vocab_size, chars[:20])
char2id = {c: i for i, c in enumerate(chars)}
id2char = {i: c for i, c in enumerate(chars)}
with open('dictionary.pkl', 'wb') as fw:
pickle.dump([char2id, id2char], fw) # 把这两个字典扔进pkl中去
输出: 共10131个字 [' ', '的', '我', '你', '不', '一', '是', '在', '有', '了', '人', '爱', '心', '天', ',', '想', '过', '着', '这', '。']
4: 接下来,是整理我们的输入数据以及对应的输出,我们这里是输入10个词,取预测第11个词。接着往后走三个,接着取十个词,预测第11个,这样就生成了一大批样本。。见下图:
我们就这样生成了好多样本,一个输入对应一个输出。。
maxlen = 10 # 每隔10取一个词
step = 3 # 取一段序列,然后往后面移动三格,再进行取
vocab_size = len(chars) # 总共含有的汉字个数
X_data = []
Y_data = []
for sentence in sentences:
for i in range(0, len(sentence) - maxlen, step):
# 取前10个字
X_data.append([char2id[c] for c in sentence[i: i + maxlen]])
# 预测第11个字 预测的词用one_hot编码来表示
y = np.zeros(vocab_size, dtype=np.bool)
# 这里就是去除第11个词 然后找出对应的id 在id那个位置标记为1,表示这个词的存在
y[char2id[sentence[i + maxlen]]] = 1 # 用one_hot编码表示一个词的存在
Y_data.append(y)
X_data = np.array(X_data) # X_data中每条数据的长度都是10
Y_data = np.array(Y_data) # Y_data中每条数据是一个稀疏向量。几万维,只是在对应位置取1
print(X_data[:2])
print(X_data.shape, Y_data.shape) # 输出数据的形状
5:接下来,我们定义模型,这里我们首先接一个Embedding,就是将输入的向量中每个词转为 128维的向量,具体原理,参考下这篇博客:https://blog.csdn.net/jiangpeng59/article/details/77533309
# 模型中所需要的一些参数
embed_size = 128
hidden_size = 128
batch_size = 64
epochs = 20
# 定义模型
# input_dim:大或等于0的整数,字典长度,即输入数据最大下标+1 是embedding层的一些参数 embed_size就是要将最后的一个汉字映射到多少维的向量
# output_dim:大于0的整数,代表全连接嵌入的维度
model = Sequential()
model.add(Embedding(input_dim=vocab_size, output_dim=embed_size, input_length=maxlen))
model.add(LSTM(hidden_size, input_shape=(maxlen, embed_size))) # 经过embedding 我们的输入数据变为(max_len, embed_size)
model.add(Dense(vocab_size, activation='softmax')) # 经过上面的LSTM 我们再连接个Dense 预测的是下一个是所有词的概率。
model.compile(loss='categorical_crossentropy', optimizer='adam')
6:我们模型最后输出的是所有词作为下一个词的概率,我们不能单纯的取概率最大,我们这里给一个权重,让其多样性
def sample(preds, diversity=1.0):
preds = np.asarray(preds).astype('float64') # 得到当前预测的概率,我们不能单纯认为下一个词是概率最大的那个,要加个多样性
preds = np.log(preds + 1e-10) / diversity # 怎样实现多样性,它就是把概率稍加变动,再加个diversity
exp_preds = np.exp(preds)
preds = exp_preds / np.sum(exp_preds) # 每一个单词期望的概率 / 总的期望概率 才代表当前词的概率
probas = np.random.multinomial(1, preds, 1) #
return np.argmax(probas) # 经过一系列处理后 找出概率大的
7: 定义一个回调函数,就是让模型每次训练完一轮后,试着去生成一段序列,看看是什么样子
# 定义每次训练结束后的回调函数,也就是根据当前训练 试着去生成一段文本
def on_epoch_end(epoch, logs):
print('-' * 30)
print('Epoch', epoch)
index = random.randint(0, len(sentences)) # 从我们的句子中随机选取一个字
for diversity in [0.2, 0.5, 1.0]: # 给几种不同的多样性权重。。越小可能对概率高的汉字越不利
print('----- diversity:', diversity)
sentence = sentences[index][:maxlen] # 选出某个句子的前十个字 也就是给了起始文本
print('----- Generating with seed: ' + sentence)
sys.stdout.write(sentence)
for i in range(400):
x_pred = np.zeros((1, maxlen))
for t, char in enumerate(sentence):
x_pred[0, t] = char2id[char] # 将我们刚才生成的种子转换为对应的id
preds = model.predict(x_pred, verbose=0)[0] # 预测概率 这里面还有损失,因为我们只需要概率,所以这里最后加了个[0]
next_index = sample(preds, diversity) # 讲概率传进我们的函数中,得到下一个汉字
next_char = id2char[next_index] # 将预测的id转为它对应的汉字
sentence = sentence[1:] + next_char # 构造下一个输入
sys.stdout.write(next_char) # 后面逐个词进行写入
sys.stdout.flush()
8:训练模型并保存
# 训练并保存模型
model.fit(X_data, Y_data, batch_size=batch_size, epochs=epochs, callbacks=[LambdaCallback(on_epoch_end=on_epoch_end)])
model.save('song_keras.h5')
9:模型训练好,我们试着调用一下以训练好的模型,让其生成文本
from keras.models import load_model
import numpy as np
import pickle
import sys
maxlen = 10
model = load_model('song_keras.h5')
with open('dictionary.pkl', 'rb') as fr:
[char2id, id2char] = pickle.load(fr)
def sample(preds, diversity=1.0):
preds = np.asarray(preds).astype('float64')
preds = np.log(preds + 1e-10) / diversity
exp_preds = np.exp(preds)
preds = exp_preds / np.sum(exp_preds)
probas = np.random.multinomial(1, preds, 1)
return np.argmax(probas)
sentence = '只剩下钢琴被我弹了一天'
sentence = sentence[:maxlen]
diversity = 1.0
print('----- Generating with seed: ' + sentence)
print('----- diversity:', diversity)
sys.stdout.write(sentence)
for i in range(400):
x_pred = np.zeros((1, maxlen))
for t, char in enumerate(sentence):
x_pred[0, t] = char2id[char]
preds = model.predict(x_pred, verbose=0)[0]
next_index = sample(preds, diversity)
next_char = id2char[next_index]
sentence = sentence[1:] + next_char
sys.stdout.write(next_char)
sys.stdout.flush()
最后的输出:
只剩下钢琴被我弹了一曲表演 失去故事的存在重叠入襟 要不是孩子们流浪 谁能放下好吧气 不是因为我不怕孤单 不习惯习惯睡发 街景屠水的泛泛 清流旧枝既渺重交可斟学 时光间两千男生好情可以筹码 又不是不知无限 我感到喧闹太阳下 回来就匆匆忘了 然后觉悟我 至少 我不说说诉 也许我会 甚麽不要分手 爱是寂寞的执着 用我感情谱生音 轻吻着梦的人生瞬间 天高后回想到最后一切 啊 旁条 逛来驾 静静等个黎明我俩如才能够我的承认 孤星和爱更东西 为何落力不怕你出现 生命已给我 夜雨冰凉,你要离我一直跳,在此刻很轻,温茶又笑清似断肠。 独立平庭一直;。 何以朽,因果师岭名掘互相。。 所有 我知道有人会美丽 不肯等 喔喔喔喔 令你救我 爱不爱 却在未了只能说 我不会说的 天未要准新心都是接认会得到爆倍 不介意 脚丫口巧腰都没有 多想关于我们哪个哪有起来的模样 我只好兴奋 我在一起 春天初绽回遨游 过处沙红表远