【深度学习】NLP|用GRU模型给周董写首歌

1、数据准备

  最近经常看到网友对新闻的评论,好多评论都说编辑是越来越懒,文章都是让机器人写的,词不达意,语句不通都放上去,骂声一片。突发奇想,使用GRU模型训练一段文本,看能否预测出让大家看得懂的文本。
  本人对周董的歌比较感兴趣,就收集到周董的歌词,下边就开始实现文本生成任务,让机器给周董写一首歪果仁(中国人估计也不是很懂哦,哇咔咔)听不懂的歌。

1.1 导包

from __future__ import absolute, division, print_function, unicode_litersla
import tensorflow as tf
import numpy as np
import os

1.2 获取文件

取一首歌作为例子

一步两步三步四步望著天 看星星
一颗两颗三颗四颗 连成线一步两步三步四步望著天 看星星
一颗两颗三颗四颗 连成线乘著风 游荡在蓝天边
一片云掉落在我面前
捏成你的形状
随风跟著我
一口一口吃掉忧愁
载著你 彷彿载著阳光
不管到哪里都是晴天
蝴蝶自在飞
花也布满天
一朵一朵因你而香
试图让夕阳飞翔
带领你我环绕大自然
迎著风 开始共渡每一天
手牵手
一步两步三步四步望著天 看星星
一颗两颗三颗四颗 连成线背著背默默许下心愿
看远方的星是否听的见
手牵手
一步两步三步四步望著天 看星星
一颗两颗三颗四颗 连成线背著背默默许下心愿
看远方的星如果听的见
它一定实现它一定实现
载著你 彷彿载著阳光
不管到哪里都是晴天
蝴蝶自在飞
花也布满天
一朵一朵因你而香
试图让夕阳飞翔
带领你我环绕大自然
迎著风 开始共渡每一天
手牵手
一步两步三步四步望著天 看星星
一颗两颗三颗四颗 连成线背著背默默许下心愿
看远方的星是否听的见
手牵手
一步两步三步四步望著天 看星星
一颗两颗三颗四颗 连成线背著背默默许下心愿
看远方的星如果听的见
它一定实现
它一定实现
# 获取文件路径
file_path = './data/jay_chou.txt'
def prepareData(file_path):
    """文本转换为码表函数"""
    text = open(data_path, 'rb').read().decode(encoding="utf-8")
    vocab = sorted(set(text))
    # 对文本进行数值映射
    char2index = {u: i for i, u in enumerate(vocab)}
    index2char = np.array(vocab)
    text_as_int = np.array([char2index[c] for c in text])
    return char2index, index2char, text_as_int, text, vocab

char2index, index2char, text_as_int, text, vocab = prepareData(file_path)

print(char2index)
print(index2char)
print(text_as_int)
print(vocab)

输出结果:

>>> {'\n': 0, ' ': 1, '!': 2, '$': 3, '&': 4, "'": 5, ',': 6, '-': 7, '.': 8, '3': 9, ':': 10, ';': 11, '?': 12, 'A': 13, 'B': 14, 'C': 15, 'D': 16, 'E': 17, 'F': 18, 'G': 19, 'H': 20, 'I': 21, 'J': 22, 'K': 23, 'L': 24, 'M': 25, 'N': 26, 'O': 27, 'P': 28, 'Q': 29, 'R': 30, 'S': 31, 'T': 32, 'U': 33, 'V': 34, 'W': 35, 'X': 36, 'Y': 37, 'Z': 38, 'a': 39, 'b': 40, 'c': 41, 'd': 42, 'e': 43, 'f': 44, 'g': 45, 'h': 46, 'i': 47, 'j': 48, 'k': 49, 'l': 50, 'm': 51, 'n': 52, 'o': 53, 'p': 54, 'q': 55, 'r': 56, 's': 57, 't': 58, 'u': 59, 'v': 60, 'w': 61, 'x': 62, 'y': 63, 'z': 64}

>>>['\n' ' ' '!' '$' '&' "'" ',' '-' '.' '3' ':' ';' '?' 'A' 'B' 'C' 'D' 'E'
 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W'
 'X' 'Y' 'Z' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o'
 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z']

>>>[18 47 56 ... 45  8  0]

>>>['\n', ' ', '!', '$', '&', "'", ',', '-', '.', '3', ':', ';', '?', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

1.3 生成训练数据

# 设定输入序列长度
seq_length = 100
# 获取样本总数
examples_per_epoch = len(text) // seq_length
# 将数值映射后的文本转化为dataset对象
char_dataset = tf.data.Dataset.from_tensor_slices(text2int)
# 使用dataset的batch方法按照字符长度+1划分, drop_remainder=True表示删除掉最后一批可能小于批次数量的数据
sequences = char_dataset.batch(seq_length + 1, drop_remainder=True)

def split_input_target(chunk):
    """划分输入序列和目标序列函数"""
    # 前100个字符为输入序列,第二个字符开始到最后为目标序列
    input_text = chunk[:-1]
    target_text = chunk[1:]
    return input_text, target_text

# 使用map方法调用该函数对每条序列进行划分
dataset = sequences.map(split_input_target)
print(dataset)
# 定义批次大小
BATCH_SIZE = 64
# 设置缓存区大小
BUFFER_SIZE = 10000
# 打乱数据并分批次
# dataset.shuffle就是说维持一个buffer size大小的shuffle buffer,所需的每个样本从shuffle buffer中获取, 取得一个样本后,就从源数据集中加入一个样本到shuffle buffer中
dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
print(dataset)
>>> <MapDataset shapes: ((100,), (100,)), types: (tf.int64, tf.int64)>

>>> <BatchDataset shapes: ((64, 100), (64, 100)), types: (tf.int64, tf.int64)>

1.4 构建模型

# 获取词汇集大小
vocab_size = len(vocab)
# 定义词潜入维度
embedding_dim = 256
# 定义GRU的隐层节点数量
rnn_units = 1024

# 构建模型函数
def gru_model(vocab_size, embedding_dim, rnn_units, batch_size):
	# 使用tf.keras.Sequential定义模型
    # GRU层的参数return_sequences为True说明返回结果为每个时间步的输出,而不是最后时间步的输出
    # stateful参数为True,说明将保留每个batch数据的结果状态作为下一个batch的初始化数据
    # recurrent_initializer='glorot_uniform',说明GRU的循环核采用均匀分布的初始化方法
    # 模型最终通过全连接层返回一个所有可能字符的概率分布.
    model = tf.keras.Sequential([
        tf.keras.layers.Embedding(vocab_size, embedding_dim, batch_input_shape=[batch_size, None]),
        tf.keras.layers.GRU(rnn_units, return_sequences=True, stateful=True, recurrent_initializer='glorot_uniform'),
        tf.keras.layers.Dense(vocab_size)
    ])
    return model

# 实例化模型
model = gru_model(vocab_size, embedding_dim, rnn_units, BATCH_SIZE)

1.5 构建损失函数

  定义损失函数labels真实标签,logits预测标签,由于模型返回logits,因此需要设置from_logits标志。

def loss(labels, logits):
    return tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)

# 添加优化器
model.compile(optimizer='adam', loss=loss)

1.6 设置检查点

# 配置检查点
checkpoint_dir = './training_checkpoints'
# 设置检查点文件名
checkpoint_prefix = os.path.join(checkpoint_dir, 'ckpt_{epoch}')
# 创建检查点的保存回调对象
checkpoint_calback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_prefix, save_weights_only=True)
# 模型训练并打印日志
EPOCHS = 100
history = model.fit(dataset, epochs=EPOCHS, callbacks=[checkpoint_calback])
# 使用模型生成文本
model = gru_model(vocab_size, embedding_dim, rnn_units, batch_size=1)
model.load_weights(tf.train.latest_checkpoint(checkpoint_dir))

1.7 构建生成函数

def generate_text(model, start_string):
    # 要生成的文本个数
    num_generate = 1000
    # 将起始额字符串转化为数字
    input_eval = [char2index[s] for s in start_string]
    # 扩展维度满足模型要求
    input_eval = tf.expand_dims(input_eval, 0)

    # 空列表用于存储结果
    text_generate = []
    # 设定温度参数
    temperature = 1.0
    # 初始化模型参数
    model.reset_states()
    # 开始循环
    for i in range(num_generate):
        predictions = model(input_eval)
        # 压缩批次维度
        predictions = tf.squeeze(predictions, 0)
        # 使用温度参数和tf.random.categorical方法生成最终的预测字符索引
        predictions = predictions / temperature
        predictions_id = tf.random.categorical(predictions, num_samples=1)[-1, 0].numpy()
        # 将预测的输出再扩展维度作为下一次的模型输入
        input_eval = tf.expand_dims([predictions_id], 0)
        # 将该次输出映射成字符存到列表
        text_generate.append(index2char[predictions_id])
    #  最后将初始字符串和生成的字符串进行拼接
    return (start_string + ''.join(text_generate))

1.7 保存生成的文本

with open('./datasets/generate_text.txt', 'w') as f:
    f.write(generate_text(model, start_string='美女'))

1.8 效果展示

美女明白
忘记你对我的期待
读完了依赖
我很快就   
我只能永远读着对白
读到我给你的伤害
我原谅不了我
就请你当作我已不在
我睁开双眼看着空白
忘记你对我的期待
读完了依赖
我很快就离开
那混乱的年代
朝廷太腐败
人祸惹天灾
东汉王朝在
一夕之间
你轻把以来
这街上太拥挤
太多人有秘密
玻璃上有雾气在被隐藏起过去
你脸上的情绪
在还原那场雨
这巷弄太过弯曲走不回故事里
这日子不再绿
又斑驳了几句
剩下搬空回忆的我在大房子里
电影院的座椅
隔遥远的距离
是我会感到奖没有知道
我深埋珊瑚海
飘移
教室的那么形状
幻幻想失去
去到哪有外婆家知道
看着的大提琴
安静的旧旧的
我想你已表现的非常明白
我懂我也知道不到
没有你烦 我有多难熬
 没有你在我们多难熬 多烦恼 乎伊操心
虽然不关我的代誌 谁叫他是我的兄弟 耶
拢这样的默契很重要听我听你那么会抱
用我心碎你受罪你的美
我不配
天气 冷的让人生闷气
火气 我沮丧的很生气
空气 太糟需要有氧气
太多假义气一想就气
敲门 敲敲门 
基本礼貌叩要先问
离开也不随手关灯 
生活习惯真有够混
敲门 敲敲门 
简单动作叩也不等
你看来虽不像坏蛋 
但做事做人却很蠢
你像一团沼气 
影响我的士气 
损我的英气又那么神气
说话的语气 
败坏了风气 
我不想为你为你白花了力气
那么会扯去扯铃 
扯多你就会上瘾
扯你最善变的表情 
我的解释请你务必要听
那么会扯去扯铃 
却扯不出个命运
扯你最善变的表情 
嘿你这样说我有一点伤心
那么会扯去扯铃 
扯多你就会上瘾
扯你最善变的表情 
我的解释请你务必要听
那么会扯去扯铃 
却扯不出个命运
扯你最善变的表情 
嘿你这样说我有一点伤心
我轻轻地尝一口 你说的爱我
还在回味你给过的温柔
我轻轻地尝一口 这香浓的诱惑
我喜欢的样子你都有
你爱过头 竟然答是继续前进
oh oh 我不可能再回头
啦~~~~~
oh oh 我只能一直往前走
我在回家路上
看到路标指着演艺圈
如果选择往前走
我就必须强壮
走着走着 莫名其妙
冲出来好 幸福了这个季节
而你的脸颊象田里熟透的蕃茄
你突然对我说爱七
谁 说我要的是陪伴
而不是六百块
比你给的还简单
外婆她的无奈
无法期待
只有爱才能够明白
走在淡水河衅
听着她的最爱
把温暖放回口袋
外婆她的期待
慢慢变成无奈
大人们始终不明白
她要的是陪伴
而不是六百块
比你

  感兴趣的小伙伴可以搜集不同的文本进行训练,比如全国高考满分作文,只要数据集足够多,让机器写一篇满分作文也不是不可能的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值