torch学习 (二十九):周杰伦歌词数据集测试循环神经网络
文章目录
- 1 项目组织结构
- 2 实现
- 2.1 数据导入
- 2.2 One-hot向量
- 2.3 梯度裁剪
- 2.4 模型构建
- 2.5 模型训练
- 2.5.1 网络随机初始化测试
- 2.5.2 模型训练及测试
- 3 相关函数
- 3.1 __init__.py
- 3.2 function.py
- 3.3 main.py
1 项目组织结构
项目组织结构如下:
└── Project
├── Data
└── jaychou_lyrics.txt.zip
├── Test
└── __init__.py
└── function.py
└── main.py
2 实现
2.1 数据导入
使用的数据集为周杰伦歌词数据集,函数的参数包括:
1)数据集选取范围,None默认选取全部;
2)数据集目录。
数据集已上传至:https://gitee.com/inkiinki/data20201205/blob/master/Data20201205/jaychou_lyrics.txt.zip
采样方式请参照:https://blog.csdn.net/weixin_44575152/article/details/112753980
def load_jaychou_lyrics(tr_range=None, path="../Data/jaychou_lyrics.txt.zip"):""":param tr_range: 数据集选取范围:param path: 数据集存储路径"""with zipfile.ZipFile(path) as zin:with zin.open('jaychou_lyrics.txt') as f:ori_data = f.read().decode("utf-8")ori_data = ori_data.replace("\n", " ").replace("\r", " ")"""设置原始数据集的选取范围并选取"""if tr_range is None:tr_range = (0, len(ori_data))ori_data = ori_data[tr_range[0]: tr_range[1]]# 不重复字符列表idx2char_list = list(set(ori_data))# 字符索引字典char2idx_dict = dict([(char, i) for i, char in enumerate(idx2char_list)])# 字典大小,即不重复字符的数量dict_size = len(char2idx_dict)# 字符索引列表char2idx_list = [char2idx_dict[char] for char in ori_data]return idx2char_list, char2idx_dict, dict_size, char2idx_list
2.2 One-hot向量
为了将词表示成向量输入,一个简单的方法是one-hot向量:
1)假设词典中不同字符的数量为NNN,即vocab_size;
2)每个字符同[0..(N−1)][0..(N - 1)][0..(N−1)]的整数索引相对应;
3)如果一个字符的索引是整数iii,那么创建一个全为000的NNN维向量,其中iii位置设置为111,其余为零。这个向量则是相应字符的one-hot向量:
def to_one_hot(X, n_class, device=torch.device('cuda' if torch.cuda.is_available() else "cpu")):""":param X: 数据:param n_class: 不同字符的数量:param device"""return [one_hot(X[:, i], n_class, device=device) for i in range(X.shape[1])]def one_hot(x, n_class, dtype=torch.float32, device=torch.device('cuda' if torch.cuda.is_available() else "cpu")):""":param x: x.shape --> (d), d是向量维度:param n_class --> 不同字符的数量:param dtype:param device:return: ret.shape --> (d, n_class)"""x = x.long()res = torch.zeros(x.shape[0], n_class, dtype=dtype, device=device)# scatter_(dim, index, src):将src中的数据按照索引index,在维度dim上进行填充到指定tensor,例如下例中的resres.scatter_(1, x.view(-1, 1), 1)return res
2.3 梯度裁剪
循环神经网络容易出现梯度衰减和梯度保证,因此需要对梯度进行裁剪。假设把所有模型参数梯度的元素拼接为一个向量g\boldsymbol{g}g,并设置裁剪阈值为θ\thetaθ,则裁剪后梯度为:
min(θ∥g∥,1)g.\min(\frac{\theta}{\|\boldsymbol{g}\|, 1})\boldsymbol{g}. min(∥g∥,1θ)g.
def grad_clipping(params, theta, device):norm = torch.tensor([0.], device=device)for param in params:norm += (param.grad.data ** 2).sum()norm = norm.sqrt().item()if norm > theta:for param in params:param.grad.data *= (theta / norm)
2.4 模型构建
class RNNModel(nn.Module):def __init__(self, run_layer, vocab_size):super(RNNModel, self).__init__()self.rnn = run_layerself.hidden_size = self.rnn.hidden_size * (2 if self.rnn.bidirectional else 1)self.vocab_size = vocab_sizeself.dense = nn.Linear(self.hidden_size, self.vocab_size)self.state = Nonedef forward(self, X, state):X = to_one_hot(X, self.vocab_size)Y, self.state = self.rnn(torch.stack(X), state)Y = self.dense(Y.view(-1, Y.shape[-1]))return Y, self.state
2.5 模型训练
2.5.1 网络随机初始化测试
def train(prefix, num_chars, model, idx2char, char2idx,device=torch.device("cuda" if torch.cuda.is_available() else "cpu")):state = Noneoutput = [char2idx[prefix[0]]]for t in range(num_chars + len(prefix) - 1):X = torch.tensor([output[-1]], device=device).view(1, 1)if state is not None:if isinstance(state, tuple): # LSTM, state:(h, c)state = (state[0].to(device), state[1].to(device))else:state = state.to(device)(Y, state) = model(X, state) # 前向计算不需要传入模型参数if t < len(prefix) - 1:output.append(char2idx[prefix[t + 1]])else:output.append(int(Y.argmax(dim=1).item()))return ''.join([idx2char[i] for i in output])def test():idx2char_list, char2idx_dict, dict_size, _ = load_jaychou_lyrics(tr_range=(0, 10000))hidden_size = 256device = torch.device("cuda" if torch.cuda.is_available() else "cpu")rnn_layer = get_rnn_layer(input_size=dict_size, hidden_size=hidden_size)model = RNNModel(rnn_layer, dict_size).to(device)print(train("分开", 10, model, idx2char_list, char2idx_dict, device=device))if __name__ == '__main__':test()
网络随机初始化一次之后,预测的示例如下:
分开乌羞直羞直极能极能物
2.5.2 模型训练及测试
def train_predict(model, data_idx, idx2char, char2idx, num_epoch, num_step,lr, clipping_theta, batch_size, pred_period, pred_len, prefixes,device=torch.device("cuda" if torch.cuda.is_available() else "cpu")):loss = nn.CrossEntropyLoss()optimizer = torch.optim.Adam(model.parameters(), lr=lr)model.to(device)state = Nonefor epoch in range(num_epoch):l_sum, n, start = 0.0, 1e-5, time.time()data_iter = load_jaychou_lyrics_iter_consecutive(data_idx, batch_size, num_step, device) # 相邻采样for X, Y in data_iter:if state is not None:# 使用detach函数从计算图分离隐藏状态, 这是为了# 使模型参数的梯度计算只依赖一次迭代读取的小批量序列(防止梯度计算开销太大)if isinstance(state, tuple): # LSTM, state:(h, c)state = (state[0].detach(), state[1].detach())else:state = state.detach()(output, state) = model(X, state) # output: 形状为(num_steps * batch_size, vocab_size)# Y的形状是(batch_size, num_steps),转置后再变成长度为# batch * num_steps 的向量,这样跟输出的行一一对应y = torch.transpose(Y, 0, 1).contiguous().view(-1)l = loss(output, y.long())optimizer.zero_grad()l.backward()# 梯度裁剪grad_clipping(model.parameters(), clipping_theta, device)optimizer.step()l_sum += l.item() * y.shape[0]n += y.shape[0]if (epoch + 1) % pred_period == 0:print('epoch %d, perplexity %f, time %.2f sec' % (epoch + 1, math.exp(l_sum / n), time.time() - start))for prefix in prefixes:print(' -', train(prefix, pred_len, model, idx2char, char2idx))def test1():num_epoch, batch_size, lr, clipping_theta, tr_range = 250, 32, 1e-3, 1e-2, (0, 10000)pred_period, pred_len, prefixes = 50, 50, ["分开", "不分开"]idx2char_list, char2idx_dict, dict_size, char2idx_list = load_jaychou_lyrics(tr_range=tr_range)hidden_size, num_step = 256, 25device = torch.device("cuda" if torch.cuda.is_available() else "cpu")rnn_layer = get_rnn_layer(input_size=dict_size, hidden_size=hidden_size)model = RNNModel(rnn_layer, dict_size).to(device)train_predict(model, char2idx_list, idx2char_list, char2idx_dict,num_epoch, num_step, lr, clipping_theta, batch_size,pred_period, pred_len, prefixes, device=device)if __name__ == '__main__':test1()
输出如下:
epoch 50, perplexity 3.530170, time 0.58 sec- 分开 我不能再想 我不能再想 我不 我不 我不能再想 我不能再想 我不 我不 我不能再想 我不能再想 我- 不分开 我有你这样 我不 这样 我不 我不 我不 我不 我不能再想 我不 我不 我不 我不 我不能再想 我
epoch 100, perplexity 1.103285, time 0.57 sec- 分开 我不多难熬 没有你在我有多难熬多烦恼 没有你烦 我有多烦恼 没有你烦我有多烦恼多难熬 穿过- 不分开 我有你这节奏 后 从不能活力 一颗风颗三颗四颗 连成线背著背默默许下心愿 看远方的星是否听的见 手
epoch 150, perplexity 1.039727, time 0.59 sec- 分开 我不 这爱的 爸一你 手对一阵莫名感动 我想带你 回我的外婆家 一起看着日落 一直到我们都睡着 我- 不分开不能不想 你的黑色幽默我想通 说穿了其实我的愿望就怎么小 就怎么每天祈祷我的心跳你知道 杵在伊斯坦
epoch 200, perplexity 1.024952, time 0.60 sec- 分开 我不 爱情走的太快就像龙卷风 不能承受我已无处可躲 我不要再想 我不要再想 我不 我不 我不要再- 不分开不能不能承受我已无处可躲 我不要再想 我不要再想 我不 我不 我不要再想你 爱情来的太快就像龙卷风
epoch 250, perplexity 1.018972, time 0.58 sec- 分开 我不 这可的我爱如果说散 想一定人演云多 对我用铅笔写一个人 什么都一轻人慢慢温习 我爱还是- 不分开不能不能承受我已无处可躲 我不要再想 我不要再想 我不 我不 我不要再想你 不知不觉 你已经离开我
3 相关函数
3.1 init.py
"""
@author: Inki
@email: inki.yinji@qq.com
@create: 2021 0602
@lost modify: 2021 0602
"""
import math
import numpy as np
import time
import torch
import torch.nn.functional as F
import zipfile
from torch import nn, optim
from .function import (load_jaychou_lyrics, load_jaychou_lyrics_iter_consecutive,load_jaychou_lyrics_iter_random,grad_clipping, get_rnn_layer,RNNModel)__all__ = ["math","np","time","torch","F","zipfile","nn","optim","load_jaychou_lyrics","load_jaychou_lyrics_iter_consecutive","load_jaychou_lyrics_iter_random","grad_clipping","get_rnn_layer","RNNModel",]
3.2 function.py
# coding: utf-8
"""
@author: Inki
@email: inki.yinji@qq.com
@create: 2021 0602
@lost modify: 2021 0602
"""
from Test import *def load_jaychou_lyrics(tr_range=None, path="../Data/jaychou_lyrics.txt.zip"):""":param tr_range: 数据集选取范围:param path: 数据集存储路径"""with zipfile.ZipFile(path) as zin:with zin.open('jaychou_lyrics.txt') as f:ori_data = f.read().decode("utf-8")ori_data = ori_data.replace("\n", " ").replace("\r", " ")"""设置原始数据集的选取范围并选取"""if tr_range is None:tr_range = (0, len(ori_data))ori_data = ori_data[tr_range[0]: tr_range[1]]# 不重复字符列表idx2char_list = list(set(ori_data))# 字符索引字典char2idx_dict = dict([(char, i) for i, char in enumerate(idx2char_list)])# 字典大小,即不重复字符的数量dict_size = len(char2idx_dict)# 字符索引列表char2idx_list = [char2idx_dict[char] for char in ori_data]return idx2char_list, char2idx_dict, dict_size, char2idx_listdef load_jaychou_lyrics_iter_random(data_idx, batch_size=2, num_step=5,device=torch.device("cuda" if torch.cuda.is_available() else "cpu")):""":param data_idx: 数据选取索引:param batch_size: 批次大小:param num_step: 每个样本的时间步数:param device: 设备"""# 减1是因为输出的索引x是相应输入的索引y+1num_data = (len(data_idx) - 1) // num_stepnum_epoch = num_data // batch_sizeidx = np.random.permutation(num_data)def _data(pos):return data_idx[pos: pos + num_step]for i in range(num_epoch):j = i * batch_sizebatch_idx = idx[j: j + batch_size]X = [_data(k * num_step) for k in batch_idx]Y = [_data(k * num_step + 1) for k in batch_idx]yield (torch.tensor(X, dtype=torch.float32, device=device),torch.tensor(Y, dtype=torch.float32, device=device))def load_jaychou_lyrics_iter_consecutive(data_idx, batch_size=2, num_step=5,device=torch.device("cuda" if torch.cuda.is_available() else "cpu")):""":param data_idx: 数据选取索引:param batch_size: 批次大小:param num_step: 每个样本的时间步数:param device: 设备"""data_idx = torch.tensor(data_idx, dtype=torch.float32, device=device)num_data = len(data_idx)num_batch = num_data // batch_sizeidx = data_idx[0: batch_size * num_batch].view(batch_size, num_batch)num_epoch = (num_batch - 1) // num_stepfor i in range(num_epoch):j = i * num_stepX = idx[:, j: j + num_step]Y = idx[:, j + 1: j + num_step + 1]yield X, Ydef to_one_hot(X, n_class, device=torch.device("cuda" if torch.cuda.is_available() else "cpu")):""":param X: 数据:param n_class: 不同字符的数量:param device"""return [one_hot(X[:, i], n_class, device=device) for i in range(X.shape[1])]def one_hot(x, n_class, dtype=torch.float32, device=torch.device("cuda" if torch.cuda.is_available() else "cpu")):""":param x: x.shape --> (d), d是向量维度:param n_class --> 不同字符的数量:param dtype:param device:return: ret.shape --> (d, n_class)"""x = x.long()res = torch.zeros(x.shape[0], n_class, dtype=dtype, device=device)# scatter_(dim, index, src):将src中的数据按照索引index,在维度dim上进行填充到指定tensor,例如下例中的resres.scatter_(1, x.view(-1, 1), 1)return resdef grad_clipping(params, theta, device):norm = torch.tensor([0.], device=device)for param in params:norm += (param.grad.data ** 2).sum()norm = norm.sqrt().item()if norm > theta:for param in params:param.grad.data *= (theta / norm)def get_rnn_layer(input_size, hidden_size=256):""":param input_size: 不同字符的数量:param hidden_size: 隐藏层结点数"""rnn_layer = nn.RNN(input_size=input_size, hidden_size=hidden_size)return rnn_layerclass RNNModel(nn.Module):def __init__(self, run_layer, vocab_size):super(RNNModel, self).__init__()self.rnn = run_layerself.hidden_size = self.rnn.hidden_size * (2 if self.rnn.bidirectional else 1)self.vocab_size = vocab_sizeself.dense = nn.Linear(self.hidden_size, self.vocab_size)self.state = Nonedef forward(self, X, state):X = to_one_hot(X, self.vocab_size)Y, self.state = self.rnn(torch.stack(X), state)Y = self.dense(Y.view(-1, Y.shape[-1]))return Y, self.stateif __name__ == '__main__':for (a, b) in load_jaychou_lyrics_iter_random(list(range(30))):print(a, "\n", b)
3.3 main.py
"""
@author: Inki
@email: inki.yinji@qq.com
@create: 2021 0602
@lost modify: 2021 0602
"""
from Test import *def train(prefix, num_chars, model, idx2char, char2idx,device=torch.device("cuda" if torch.cuda.is_available() else "cpu")):state = Noneoutput = [char2idx[prefix[0]]]for t in range(num_chars + len(prefix) - 1):X = torch.tensor([output[-1]], device=device).view(1, 1)if state is not None:if isinstance(state, tuple): # LSTM, state:(h, c)state = (state[0].to(device), state[1].to(device))else:state = state.to(device)(Y, state) = model(X, state) # 前向计算不需要传入模型参数if t < len(prefix) - 1:output.append(char2idx[prefix[t + 1]])else:output.append(int(Y.argmax(dim=1).item()))return ''.join([idx2char[i] for i in output])def test():idx2char_list, char2idx_dict, dict_size, _ = load_jaychou_lyrics(tr_range=(0, 10000))hidden_size = 256device = torch.device("cuda" if torch.cuda.is_available() else "cpu")rnn_layer = get_rnn_layer(input_size=dict_size, hidden_size=hidden_size)model = RNNModel(rnn_layer, dict_size).to(device)print(train("分开", 10, model, idx2char_list, char2idx_dict, device=device))def train_predict(model, data_idx, idx2char, char2idx, num_epoch, num_step,lr, clipping_theta, batch_size, pred_period, pred_len, prefixes,device=torch.device("cuda" if torch.cuda.is_available() else "cpu")):loss = nn.CrossEntropyLoss()optimizer = torch.optim.Adam(model.parameters(), lr=lr)model.to(device)state = Nonefor epoch in range(num_epoch):l_sum, n, start = 0.0, 1e-5, time.time()data_iter = load_jaychou_lyrics_iter_consecutive(data_idx, batch_size, num_step, device) # 相邻采样for X, Y in data_iter:if state is not None:# 使用detach函数从计算图分离隐藏状态, 这是为了# 使模型参数的梯度计算只依赖一次迭代读取的小批量序列(防止梯度计算开销太大)if isinstance(state, tuple): # LSTM, state:(h, c)state = (state[0].detach(), state[1].detach())else:state = state.detach()(output, state) = model(X, state) # output: 形状为(num_steps * batch_size, vocab_size)# Y的形状是(batch_size, num_steps),转置后再变成长度为# batch * num_steps 的向量,这样跟输出的行一一对应y = torch.transpose(Y, 0, 1).contiguous().view(-1)l = loss(output, y.long())optimizer.zero_grad()l.backward()# 梯度裁剪grad_clipping(model.parameters(), clipping_theta, device)optimizer.step()l_sum += l.item() * y.shape[0]n += y.shape[0]if (epoch + 1) % pred_period == 0:print('epoch %d, perplexity %f, time %.2f sec' % (epoch + 1, math.exp(l_sum / n), time.time() - start))for prefix in prefixes:print(' -', train(prefix, pred_len, model, idx2char, char2idx))def test1():num_epoch, batch_size, lr, clipping_theta, tr_range = 250, 32, 1e-3, 1e-2, (0, 10000)pred_period, pred_len, prefixes = 50, 50, ["分开", "不分开"]idx2char_list, char2idx_dict, dict_size, char2idx_list = load_jaychou_lyrics(tr_range=tr_range)hidden_size, num_step = 256, 25device = torch.device("cuda" if torch.cuda.is_available() else "cpu")rnn_layer = get_rnn_layer(input_size=dict_size, hidden_size=hidden_size)model = RNNModel(rnn_layer, dict_size).to(device)train_predict(model, char2idx_list, idx2char_list, char2idx_dict,num_epoch, num_step, lr, clipping_theta, batch_size,pred_period, pred_len, prefixes, device=device)if __name__ == '__main__':test()
torch学习 (二十九):周杰伦歌词数据集测试循环神经网络相关推荐
- pytorch学习笔记(二十九):简洁实现循环神经网络
本节将使用PyTorch来更简洁地实现基于循环神经网络的语言模型.首先,我们读取周杰伦专辑歌词数据集. import time import math import numpy as np impor ...
- 花书+吴恩达深度学习(十五)序列模型之循环神经网络 RNN
目录 0. 前言 1. RNN 计算图 2. RNN 前向传播 3. RNN 反向传播 4. 导师驱动过程(teacher forcing) 5. 不同序列长度的 RNN 如果这篇文章对你有一点小小的 ...
- torch学习 (二十四):卷积神经网络之GoogleNet
文章目录 引入 1 Inception块 2 GoogleNet模型 3 模型训练 完整代码 util.SimpleTool 引入 GoogleNet吸收了NIN网络串联网络的思想,并在此基础上做 ...
- Java多线程学习二十九:AtomicInteger(原子类) 和 synchronized 的异同点?
原子类和 synchronized 关键字都可以用来保证线程安全,在本课时中,我们首先分别用原子类和 synchronized 关键字来解决一个经典的线程安全问题,给出具体的代码对比,然后再分析它们背 ...
- Golang学习(二十九)序列化和反序列化
我们不同编程语言之间的数据是无法直接交互的,我们想要解决这个问题 就需要将不同语言之间传输的数据做一个统一规范,而json是目前最流行的数据格式 一.json是什么 json 是一种数据交换格式,主要 ...
- ballerina 学习二十九 数据库操作
ballerina 数据操作也是比较方便的,官方也我们提供了数据操作的抽象,但是我们还是依赖数据库驱动的. 数据库驱动还是jdbc模式的 项目准备 项目结构 ├── mysql_demo │ ├── ...
- python学习 (二十九) range函数
1:list函数可以将其他类型转成list. print(list(range(0, 10))) 2: list函数把元组转成list t = (1, 3, 3, 5) print(list(t)) ...
- OpenGL学习二十九:模板缓冲区与模板测试
帧缓冲区有许多缓冲区构成,这些缓冲区大致分为: 颜色缓冲区:用于绘图的缓冲区,它包含了颜色索引或者RGBA颜色数据. 深度缓冲区:存储每个像素的深度值,当启动深度测试时,片段像素深度值和深度缓冲区深度 ...
- torch学习 (三十二):周杰伦歌词数据集与长短期记忆 (LSTM)
文章目录 1 引入 2 长短期记忆 2.1 输入门.遗忘门和输出门 2.2 候选记忆细胞 2.3 记忆细胞 2.4 隐藏状态 3 代码 致谢 1 引入 本文介绍一种常用的门控循环神经网络:长短期记 ...
- 花书+吴恩达深度学习(十九)构建模型策略(训练模型顺序、偏差方差、数据集划分、数据不匹配)
目录 0. 前言 1. 调试模型顺序 2. 偏差方差的解决方法 3. 数据集的选取划分 4. 数据不匹配问题 5. 评估指标的选取 6. 贝叶斯最佳误差 如果这篇文章对你有一点小小的帮助,请给个关注, ...
最新文章
- 计科1高雨妍作业(1)
- 事务的控制(保存点)
- 第四讲 Python3中的int型和浮点型
- android 定时器5秒执行一次,如何在android中每30秒执行一次查询?
- MyBatis相应API
- 使用脚本进行 SAP Spartacus 安装工作
- 滴滴试行网约车遗失物品处理规则:司机返还遗失物品将收费
- 使用 Anthem.NET 的常见回调(Callback)处理方式小结[转]
- Java线程基础回顾及内存模型,看你还记得多少?
- java list对象转json_java中List对象转换为JSON对象
- XXX客户2020年护网行动总结报告
- Android百度地图显示POI
- 找文心一言问了几个嵌入式软件开发的问题
- HH SaaS电商系统的各种编号(编码/代码/代号)设计
- Java踩坑记录-00001 BeanCreationException
- php 滑块 爬虫_phpspider爬虫框架如何爬取异步加载的数据?
- hdu4416[多串后缀自动机]
- 安全防御(二)--- 防火墙域间双向NAT、域内双向NAT、基于VRRP的双机热备
- whois命令_WHOIS使用Whois搜索
- 豆瓣电台WP7客户端 开发记录7
热门文章
- IDE、SATA、SCSI、SAS、iSCSI
- 阿里架构师经验分享!写给即将正在找工作的Java攻城狮,吊打面试官
- MySQL报错: Incorrect string value: '\xE5\x85\xA8\xE7\x90\x83...' for column 'cname' at row 1
- 刷脸支付的场景应用遍布大街小巷
- 2021年7月20日我国暴雨趋势遥感监测与评估
- iOS应用安全Part1:搭建移动渗透测试平台
- 深圳市专利代理机构名单(截至2016年3月)
- MySQL relay_log_recovery源码分析
- R语言:Newton法、似然函数
- Python内存优化,节省内存字典ConstDict