引入
该数据集收集了杰伦第一张专辑《Jay》到第十张专辑《跨时代》中的歌词。
下载地址:https://codechina.csdn.net/mirrors/shusentang/dive-into-dl-pytorch/-/blob/master/data/jaychou_lyrics.txt.zip
参考文献:
【1】李沐、Aston Zhang等老师,动手学深度学习
1 原始数据处理
函数参数包括数据集的路径及原始数据集的使用范围。处理方式包括以下:
1)建立单个字符的不重复列表;
2)将每个字符映射到字典中;
3)将每个字符转换为索引
函数返回值包括以上三个步骤的处理结果:
import torch
import zipfile
import numpy as np
def load_jaychou_lyrics(path="../Data/data_jaychou_lyrics.txt.zip", tr_range=None):
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(set(ori_data))
"""每个字符分配索引"""
char2idx = dict([(char, i) for i, char in enumerate(idx2char)])
"""获取字符对应的索引列表"""
data_idx = [char2idx[char] for char in ori_data]
return idx2char, char2idx, data_idx
2 时序数据的采样
时序数据的一个样本通常包含连续的字符。例如时间步数为
5
5
5时,样本序列相应为
5
5
5个字符。假设样本序列为“想”、“要”、“有”、“直”、“升”,则该样本的标签序列为这些字符分别在训练集中的下一个字符,例如“要”、“有”、“直”、“升”、“机”。
接下来使用两种方式对时序数据采样。
2.1 随机采样
在随机采样中,每个样本是原始序列上任意截取的一段序列。相邻的两个随机小批量在原始序列上的位置不一定相邻。因此无法用一个小批量最终时间步的隐藏状态来初始化下一个小批量的隐藏状态。
在模型训练时,每次随机采样都需要重新初始化隐藏状态:
def load_jaychou_lyrics_iter_random(path="../Data/data_jaychou_lyrics.txt.zip", tr_range=(0, 10000),
batch_size=100, n_steps=5,
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")):
idx2char, char2idx, data_idx = load_jaychou_lyrics(path, tr_range)
n_data = (len(data_idx) - 1) // n_steps
epoch_size = n_data // batch_size
idxes = list(range(n_data))
np.random.shuffle(idxes)
def _data(pos):
return data_idx[pos: pos + n_steps]
for i in range(epoch_size):
i = i * batch_size
batch_idx = idxes[i: i + batch_size]
x = [_data(j * n_steps) for j in batch_idx]
y = [_data(j * n_steps + 1) for j in batch_idx]
yield torch.tensor(x, dtype=torch.float32, device=device),\
torch.tensor(y, dtype=torch.float32, device=device)
if __name__ == '__main__':
for a, b in load_jaychou_lyrics_iter_random(batch_size=2):
print(a, b)
break
输出如下:
tensor([[ 58., 902., 27., 513., 947.],
[499., 642., 89., 484., 477.]]) tensor([[902., 27., 513., 947., 403.],
[642., 89., 484., 477., 8.]])
2.2 相邻采样
这里的相邻采样是指:两个随机小批量在原始序列上的位置相毗邻。这时,就可以用一个小批量最终时间步的隐藏状态来初始化下一个小批量的隐藏状态,从而使下一个小批量的输出也取决于当前小批量的输入:
def load_jaychou_lyrics_iter_consecutive(path="../Data/data_jaychou_lyrics.txt.zip", tr_range=(0, 10000),
batch_size=100, n_steps=5,
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")):
idx2char, char2idx, data_idx = load_jaychou_lyrics(path, tr_range)
data_idx = torch.tensor(data_idx, dtype=torch.float32, device=device)
n_data = len(data_idx)
n_batch = n_data // batch_size
idxes = data_idx[0: batch_size * n_batch].view(batch_size, n_batch)
n_epoch = (n_batch - 1) // n_steps
for i in range(n_epoch):
i = i * n_steps
x = idxes[:, i: i + n_steps]
y = idxes[:, i + 1: i + n_steps + 1]
yield x, y
if __name__ == '__main__':
for a, b in load_jaychou_lyrics_iter_consecutive(batch_size=2):
print(a, b)
break
输出如下:
tensor([[ 138., 132., 1012., 485., 737.],
[ 848., 27., 625., 147., 459.]]) tensor([[ 132., 1012., 485., 737., 1002.],
[ 27., 625., 147., 459., 570.]])