深度学习——RNN案例_AI歌词生成器
大白话总结:
1.将歌词用jieba.lcut进行切词,切词去重后共计5703个词。
2.拿这5703个词转换成数字索引的形式,把这个索引丢给RNN神经网络进行训练,这个训练其实就是一个多分类器训练,输出的是索引;训练完成后保存模型。
3.加载训练好的模型后,进行模型测试;测试过程是把歌词转换为索引,然后进行预测,预测结果也是索引,然后将索引转换为原来的歌词,预测完毕。
导包
import torch
import re
import jieba
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import time
1. 获取数据,进行分词,获取词表
def build_vocab():
# 1. 定义变量,记录:去重后所有的词,每行文本分词结果
unique_words,all_words =[],[]
# 2. 遍历数据集,获取到每行文本
for line in open('./data/jaychou_lyrics.txt','r',encoding='utf-8'): # ./ 代表当前代码文件所在的文件夹,r代表只读
# 2.1 获取每行歌词,进行分词
words = jieba.lcut(line) # ['想要', '有', '直升机', '\n']
# print(f'每行数据:{words}')
# 2.2 所有分词结果记录到all_words中
all_words.append(words) # [['想要', '有', '直升机', '\n'],['想要', '和', '你', '飞到', '宇宙', '去', '\n']]
# 2.3 遍历分词结果,去重后,添加到unique_words中
for word in words:
if word not in unique_words:
unique_words.append(word)
# 3. 统计语料中(去重后)词的数量
word_count = len(unique_words) # 5703
# 4. 构建词表,字典形式,key是词,value是词的索引
word_to_index = {word:i for i,word in enumerate(unique_words)}
# print(f'word_to_index:{word_to_index}') # {'想要': 0, '有': 1, '直升机': 2,...,'要大卖': 5702}
# 5. 歌词文本用词表索引表示.
corpus_index = []
# 6. 遍历每一行分词结果
for words in all_words:
# 6.1 定义变量,记录:词索引列表.
tmp = []
# 6.2 获取每一行的词,并获取相应的索引
for word in words:
tmp.append(word_to_index[word])
# 6.3 在每行词之间,添加空格隔开
tmp.append(word_to_index[' '])
# 6.4 获取文档中每个词的索引,添加到corpus_idx中.
corpus_index.extend(tmp)
# print(f'corpus_index:{corpus_index}')
# 7. 返回结果:唯一词列表(5703个词),词表({'想要': 0, '有': 1, '直升机': 2,...,'要大卖': 5702}),(去重后)词的数量,歌词文本用词表索引表示
return unique_words,word_to_index,word_count,corpus_index
2. 数据预处理,构建数据集
# 定义数据集类,继承torch.utils.data.Dataset
class LyricsDataset(torch.utils.data.Dataset):
# 1. 初始化索引,词个数等
def __init__(self,corpus_index,num_chars):
# 1.1 文档中数据中词的索引
self.corpus_index = corpus_index
# 1.2 每个句子中词的个数
self.num_chars = num_chars
# 1.3 文档数据中词的数量,不去重
self.word_count = len(self.corpus_index)
# 1.4 句子数量.
self.number = self.word_count // self.num_chars
# 2. 当使用len(obj)时,会自动调用此方法
def __len__(self):
# 返回句子数量
return self.number
# 3. 当使用obj[index]时,自动调用此方法
def __getitem__(self,idx):
# idx:指的是词的索引,并将其修正索引值 到 文档的范围里边
# 3.1 确保索引start在合法范围内,避免越界;start:当前样本的起始索引。idx* num_chars:当前句子的起始索引.
start = min(max(idx,0),self.word_count - self.num_chars -1)
# 3.2 计算当前样本的结束索引
end = start + self.num_chars
# 3.3 输入值,从文档中取出start~end的索引的词 ——>作为 x
x = self.corpus_index[start:end]
# 3.4 输出值,网络预测结果,预测结果是下一个词的索引
y = self.corpus_index[start+1:end+1]
# 3.5 返回输入值和输出值 ——> 张量形式
return torch.tensor(x),torch.tensor(y)
3. 搭建RNN神经网络
class TextGenerator(nn.Module):
# 1. 初始化方法
def __init__(self,unique_word_count): # unique_word_count:去重的词的数量
# 1.1 初始化父类的成员
super().__init__()
# 1.2 初始化词嵌入层:语料中词的数量,词向量的维度
self.ebd = nn.Embedding(unique_word_count,128)
# 1.3 循环网络层:词向量维度,隐藏层维度:256,网络层数:1
self.rnn = nn.RNN(128,256,1)
# 1.4 输出层(全连接层):特征向量维度(和隐藏向量维度一致),词表中词的个数
self.out = nn.Linear(256,unique_word_count)
# 2. 前向传播方法
def forward(self,inputs,hidden):
# 2.1 初始化,词嵌入层处理。
# embd格式:(batch句子的数量,句子的长度,词向量维度)
embd = self.ebd(inputs) # 参1
# 2.2 rnn处理 (句子的长度,batch句子的数量,隐藏层维度)
output,hidden = self.rnn(embd.transpose(0,1),hidden)
# 2.3 全连接,输入内容必须是二维数据,即:词的数量 * 词的维度
# 输入维度:(seq_len句子数量*batch,词向量维度256)
# 输出维度:(seq_len句子数量*batch,词表中词的个数)
output = self.out(output.reshape(shape=(-1,output.shape[-1])))
# 2.4 返回结果,预测结果,隐藏层
return output,hidden
# 3. 隐藏层的初始化方法
def init_hidden(self,bs): # batch_size
# 隐藏层初始化:[网络层数,batch,隐藏层向量维度]
return torch.zeros(1,bs,256)
4. 训练模型
def train():
# 1. 构建词典
unique_words,word_to_index,unique_word_count,corpus_index = build_vocab()
# 2. 获取数据集
lyrics = LyricsDataset(corpus_index,32)
# 3. 初始化模型
model = TextGenerator(unique_word_count) # 需要预测5703个词,每个词的概率
# 4. 创造数据加载器对象
# 参1:数据集对象;参2:批次大小(每批五个句子,每个句子32个词);参3:是否打乱数据
lyrics_dataloader = DataLoader(lyrics,batch_size=5,shuffle=True)
# 5. 定义损失函数
criterion = nn.CrossEntropyLoss()
# 6. 定义优化器
optimizer = optim.Adam(model.parameters(),lr=0.001)
# 7. 模型训练
# 7.1 定义变量,定义训练轮数
epochs = 10
# 7.2 具体每轮训练动作
for epoch in range(epochs): #epoch:0,1,2,3,...,9 分别表示:第一轮,第二轮...第十轮
# 7.3 定义变量记录:本轮开始记录时间,迭代次数,训练总损失
start,iter_num,total_loss = time.time(),0,0.0
# 7.4 具体的本轮各批次训练动作
# 遍历数据集 后台会调用 LyricsDataset__getitem__()方法,获取到每个样本的数据和标签,训练
for x,y in lyrics_dataloader:
# 7.5 获取隐藏层初始值
# hidden = torch.zeros(1,5,256)
hidden = model.init_hidden(5)
# 7.6 模型计算
output,hidden = model(x,hidden)
# 7.7 计算损失
# y的形状:(batch,seq_len,词向量维度)-->转成一维向量-->每个词的下标索引
# output的形状:(seq_len,batch,词向量维度)
y = torch.transpose(y,0,1).reshape(shape=(-1,))
loss = criterion(output,y)
# 7.8 梯度清零+反向传播+更新参数
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 7.9 累积损失 和 迭代次数
total_loss += loss.item()
iter_num += 1
# 7.10 走到这里,说明本轮训练结束,打印本轮的训练数据
print(f'epoch:{epoch+1},time:{time.time() - start:2f}s,loss:{total_loss/iter_num:.4f}')
# 8. 走到这里,说明多轮训练结束(模型训练结束),保存即可
torch.save(model.state_dict(),'text_generator.pth')
5. 模型测试
def evaluate(start_word,sentence_length):
# 1. 构建词典
unique_words,word_to_index,unique_word_count,corpus_index = build_vocab()
# 2. 获取模型
model = TextGenerator(unique_word_count)
# 3. 加载模型参数
model.load_state_dict(torch.load('text_generator.pth'))
# 4. 获取隐藏层的初始值
hidden = model.init_hidden(1)
# 5. 将输入的 开始此 转换成 索引
word_idx = word_to_index[start_word]
# 6. 定义列表,存放:预测的句子
generator_sentence = [word_idx]
# 7. 遍历句子长度,获取到每一个词
for i in range(sentence_length):
# 7.1 模型预测
output,hidden = model(torch.tensor([[word_idx]]),hidden)
# 7.2 获取预测结果 argmax()从所有结果(5703个词的概率)中,找到最大值对应的索引
word_idx = torch.argmax(output)
# 7.3 把预测结果添加到列表中
generator_sentence.append(word_idx)
# 8. 将索引转成词并打印
for idx in generator_sentence:
print(unique_words[idx],end='')
6. 测试
if __name__ == '__main__':
# 1. 获取数据,进行分词,获取词表
unique_words,word_to_index,word_count,corpus_index = build_vocab()
# print(f'词的数量:{word_count}') # 去重后,5703个词
# print(f'去重后的词:{unique_words}') #['想要', '有', '直升机',...,'要大卖']
# print(f'每个词的索引:{word_to_index}') # 词表{'想要': 0, '有': 1,...,'要大卖': 5702}
# print(f'文档中每个词对应的索引:{corpus_index}') #[0, 1, 2, 3,..., 3, 40]
# 2. 构建数据集
dataset = LyricsDataset(corpus_index,5)
# print(f'句子数量{len(dataset)}')
# # 查看下 输入值 和 目标值
# x,y=dataset[1]
# print(x)
# print(y)
# 3. 创建模型对象
# model = TextGenerator(word_count)
# # 查看参数
# for name,parameter in model.named_parameters():
# print(f'name: {name}, parameter: {parameter.shape}')
# 4. 训练(并保存)模型
# train()
# 5. 模型测试
evaluate('星星',50)

浙公网安备 33010602011771号