数据集下载:点我下载数据集

我们实现的是歌词的自动生成。 主要看我在代码中的注释。。注释的很详细,不懂可以留言。 

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()

最后的输出:

     只剩下钢琴被我弹了一曲表演  失去故事的存在重叠入襟 要不是孩子们流浪 谁能放下好吧气  不是因为我不怕孤单 不习惯习惯睡发 街景屠水的泛泛 清流旧枝既渺重交可斟学 时光间两千男生好情可以筹码 又不是不知无限  我感到喧闹太阳下 回来就匆匆忘了 然后觉悟我 至少 我不说说诉 也许我会 甚麽不要分手 爱是寂寞的执着  用我感情谱生音 轻吻着梦的人生瞬间 天高后回想到最后一切 啊  旁条 逛来驾 静静等个黎明我俩如才能够我的承认 孤星和爱更东西 为何落力不怕你出现 生命已给我 夜雨冰凉,你要离我一直跳,在此刻很轻,温茶又笑清似断肠。 独立平庭一直;。 何以朽,因果师岭名掘互相。。 所有 我知道有人会美丽   不肯等 喔喔喔喔 令你救我 爱不爱 却在未了只能说 我不会说的 天未要准新心都是接认会得到爆倍  不介意 脚丫口巧腰都没有 多想关于我们哪个哪有起来的模样 我只好兴奋 我在一起 春天初绽回遨游 过处沙红表远