文章目录

  • 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学习 (二十九):周杰伦歌词数据集测试循环神经网络相关推荐

  1. pytorch学习笔记(二十九):简洁实现循环神经网络

    本节将使用PyTorch来更简洁地实现基于循环神经网络的语言模型.首先,我们读取周杰伦专辑歌词数据集. import time import math import numpy as np impor ...

  2. 花书+吴恩达深度学习(十五)序列模型之循环神经网络 RNN

    目录 0. 前言 1. RNN 计算图 2. RNN 前向传播 3. RNN 反向传播 4. 导师驱动过程(teacher forcing) 5. 不同序列长度的 RNN 如果这篇文章对你有一点小小的 ...

  3. torch学习 (二十四):卷积神经网络之GoogleNet

    文章目录 引入 1 Inception块 2 GoogleNet模型 3 模型训练 完整代码 util.SimpleTool 引入   GoogleNet吸收了NIN网络串联网络的思想,并在此基础上做 ...

  4. Java多线程学习二十九:AtomicInteger(原子类) 和 synchronized 的异同点?

    原子类和 synchronized 关键字都可以用来保证线程安全,在本课时中,我们首先分别用原子类和 synchronized 关键字来解决一个经典的线程安全问题,给出具体的代码对比,然后再分析它们背 ...

  5. Golang学习(二十九)序列化和反序列化

    我们不同编程语言之间的数据是无法直接交互的,我们想要解决这个问题 就需要将不同语言之间传输的数据做一个统一规范,而json是目前最流行的数据格式 一.json是什么 json 是一种数据交换格式,主要 ...

  6. ballerina 学习二十九 数据库操作

    ballerina 数据操作也是比较方便的,官方也我们提供了数据操作的抽象,但是我们还是依赖数据库驱动的. 数据库驱动还是jdbc模式的 项目准备 项目结构 ├── mysql_demo │ ├── ...

  7. python学习 (二十九) range函数

    1:list函数可以将其他类型转成list. print(list(range(0, 10))) 2: list函数把元组转成list t = (1, 3, 3, 5) print(list(t)) ...

  8. OpenGL学习二十九:模板缓冲区与模板测试

    帧缓冲区有许多缓冲区构成,这些缓冲区大致分为: 颜色缓冲区:用于绘图的缓冲区,它包含了颜色索引或者RGBA颜色数据. 深度缓冲区:存储每个像素的深度值,当启动深度测试时,片段像素深度值和深度缓冲区深度 ...

  9. torch学习 (三十二):周杰伦歌词数据集与长短期记忆 (LSTM)

    文章目录 1 引入 2 长短期记忆 2.1 输入门.遗忘门和输出门 2.2 候选记忆细胞 2.3 记忆细胞 2.4 隐藏状态 3 代码 致谢 1 引入   本文介绍一种常用的门控循环神经网络:长短期记 ...

  10. 花书+吴恩达深度学习(十九)构建模型策略(训练模型顺序、偏差方差、数据集划分、数据不匹配)

    目录 0. 前言 1. 调试模型顺序 2. 偏差方差的解决方法 3. 数据集的选取划分 4. 数据不匹配问题 5. 评估指标的选取 6. 贝叶斯最佳误差 如果这篇文章对你有一点小小的帮助,请给个关注, ...

最新文章

  1. 计科1高雨妍作业(1)
  2. 事务的控制(保存点)
  3. 第四讲 Python3中的int型和浮点型
  4. android 定时器5秒执行一次,如何在android中每30秒执行一次查询?
  5. MyBatis相应API
  6. 使用脚本进行 SAP Spartacus 安装工作
  7. 滴滴试行网约车遗失物品处理规则:司机返还遗失物品将收费
  8. 使用 Anthem.NET 的常见回调(Callback)处理方式小结[转]
  9. Java线程基础回顾及内存模型,看你还记得多少?
  10. java list对象转json_java中List对象转换为JSON对象
  11. XXX客户2020年护网行动总结报告
  12. Android百度地图显示POI
  13. 找文心一言问了几个嵌入式软件开发的问题
  14. HH SaaS电商系统的各种编号(编码/代码/代号)设计
  15. Java踩坑记录-00001 BeanCreationException
  16. php 滑块 爬虫_phpspider爬虫框架如何爬取异步加载的数据?
  17. hdu4416[多串后缀自动机]
  18. 安全防御(二)--- 防火墙域间双向NAT、域内双向NAT、基于VRRP的双机热备
  19. whois命令_WHOIS使用Whois搜索
  20. 豆瓣电台WP7客户端 开发记录7

热门文章

  1. IDE、SATA、SCSI、SAS、iSCSI
  2. 阿里架构师经验分享!写给即将正在找工作的Java攻城狮,吊打面试官
  3. MySQL报错: Incorrect string value: '\xE5\x85\xA8\xE7\x90\x83...' for column 'cname' at row 1
  4. 刷脸支付的场景应用遍布大街小巷
  5. 2021年7月20日我国暴雨趋势遥感监测与评估
  6. iOS应用安全Part1:搭建移动渗透测试平台
  7. 深圳市专利代理机构名单(截至2016年3月)
  8. MySQL relay_log_recovery源码分析
  9. R语言:Newton法、似然函数
  10. Python内存优化,节省内存字典ConstDict