# 6.3 語言模型數據集(周杰倫專輯歌詞)
本節將介紹如何預處理一個語言模型數據集,并將其轉換成字符級循環神經網絡所需要的輸入格式。為此,我們收集了周杰倫從第一張專輯《Jay》到第十張專輯《跨時代》中的歌詞,并在后面幾節里應用循環神經網絡來訓練一個語言模型。當模型訓練好后,我們就可以用這個模型來創作歌詞。
## 6.3.1 讀取數據集
首先讀取這個數據集,看看前40個字符是什么樣的。
``` python
import torch
import random
import zipfile
with zipfile.ZipFile('../../data/jaychou_lyrics.txt.zip') as zin:
with zin.open('jaychou_lyrics.txt') as f:
corpus_chars = f.read().decode('utf-8')
corpus_chars[:40]
```
輸出:
```
'想要有直升機\n想要和你飛到宇宙去\n想要和你融化在一起\n融化在宇宙里\n我每天每天每'
```
這個數據集有6萬多個字符。為了打印方便,我們把換行符替換成空格,然后僅使用前1萬個字符來訓練模型。
``` python
corpus_chars = corpus_chars.replace('\n', ' ').replace('\r', ' ')
corpus_chars = corpus_chars[0:10000]
```
## 6.3.2 建立字符索引
我們將每個字符映射成一個從0開始的連續整數,又稱索引,來方便之后的數據處理。為了得到索引,我們將數據集里所有不同字符取出來,然后將其逐一映射到索引來構造詞典。接著,打印`vocab_size`,即詞典中不同字符的個數,又稱詞典大小。
``` python
idx_to_char = list(set(corpus_chars))
char_to_idx = dict([(char, i) for i, char in enumerate(idx_to_char)])
vocab_size = len(char_to_idx)
vocab_size # 1027
```
之后,將訓練數據集中每個字符轉化為索引,并打印前20個字符及其對應的索引。
``` python
corpus_indices = [char_to_idx[char] for char in corpus_chars]
sample = corpus_indices[:20]
print('chars:', ''.join([idx_to_char[idx] for idx in sample]))
print('indices:', sample)
```
輸出:
```
chars: 想要有直升機 想要和你飛到宇宙去 想要和
indices: [250, 164, 576, 421, 674, 653, 357, 250, 164, 850, 217, 910, 1012, 261, 275, 366, 357, 250, 164, 850]
```
我們將以上代碼封裝在`d2lzh_pytorch`包里的`load_data_jay_lyrics`函數中,以方便后面章節調用。調用該函數后會依次得到`corpus_indices`、`char_to_idx`、`idx_to_char`和`vocab_size`這4個變量。
## 6.3.3 時序數據的采樣
在訓練中我們需要每次隨機讀取小批量樣本和標簽。與之前章節的實驗數據不同的是,時序數據的一個樣本通常包含連續的字符。假設時間步數為5,樣本序列為5個字符,即“想”“要”“有”“直”“升”。該樣本的標簽序列為這些字符分別在訓練集中的下一個字符,即“要”“有”“直”“升”“機”。我們有兩種方式對時序數據進行采樣,分別是隨機采樣和相鄰采樣。
### 6.3.3.1 隨機采樣
下面的代碼每次從數據里隨機采樣一個小批量。其中批量大小`batch_size`指每個小批量的樣本數,`num_steps`為每個樣本所包含的時間步數。
在隨機采樣中,每個樣本是原始序列上任意截取的一段序列。相鄰的兩個隨機小批量在原始序列上的位置不一定相毗鄰。因此,我們無法用一個小批量最終時間步的隱藏狀態來初始化下一個小批量的隱藏狀態。在訓練模型時,每次隨機采樣前都需要重新初始化隱藏狀態。
``` python
# 本函數已保存在d2lzh_pytorch包中方便以后使用
def data_iter_random(corpus_indices, batch_size, num_steps, device=None):
# 減1是因為輸出的索引x是相應輸入的索引y加1
num_examples = (len(corpus_indices) - 1) // num_steps
epoch_size = num_examples // batch_size
example_indices = list(range(num_examples))
random.shuffle(example_indices)
# 返回從pos開始的長為num_steps的序列
def _data(pos):
return corpus_indices[pos: pos + num_steps]
if device is None:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
for i in range(epoch_size):
# 每次讀取batch_size個隨機樣本
i = i * batch_size
batch_indices = example_indices[i: i + batch_size]
X = [_data(j * num_steps) for j in batch_indices]
Y = [_data(j * num_steps + 1) for j in batch_indices]
yield torch.tensor(X, dtype=torch.float32, device=device), torch.tensor(Y, dtype=torch.float32, device=device)
```
讓我們輸入一個從0到29的連續整數的人工序列。設批量大小和時間步數分別為2和6。打印隨機采樣每次讀取的小批量樣本的輸入`X`和標簽`Y`。可見,相鄰的兩個隨機小批量在原始序列上的位置不一定相毗鄰。
``` python
my_seq = list(range(30))
for X, Y in data_iter_random(my_seq, batch_size=2, num_steps=6):
print('X: ', X, '\nY:', Y, '\n')
```
輸出:
```
X: tensor([[18., 19., 20., 21., 22., 23.],
[12., 13., 14., 15., 16., 17.]])
Y: tensor([[19., 20., 21., 22., 23., 24.],
[13., 14., 15., 16., 17., 18.]])
X: tensor([[ 0., 1., 2., 3., 4., 5.],
[ 6., 7., 8., 9., 10., 11.]])
Y: tensor([[ 1., 2., 3., 4., 5., 6.],
[ 7., 8., 9., 10., 11., 12.]])
```
### 6.3.3.2 相鄰采樣
除對原始序列做隨機采樣之外,我們還可以令相鄰的兩個隨機小批量在原始序列上的位置相毗鄰。這時候,我們就可以用一個小批量最終時間步的隱藏狀態來初始化下一個小批量的隱藏狀態,從而使下一個小批量的輸出也取決于當前小批量的輸入,并如此循環下去。這對實現循環神經網絡造成了兩方面影響:一方面,
在訓練模型時,我們只需在每一個迭代周期開始時初始化隱藏狀態;另一方面,當多個相鄰小批量通過傳遞隱藏狀態串聯起來時,模型參數的梯度計算將依賴所有串聯起來的小批量序列。同一迭代周期中,隨著迭代次數的增加,梯度的計算開銷會越來越大。
為了使模型參數的梯度計算只依賴一次迭代讀取的小批量序列,我們可以在每次讀取小批量前將隱藏狀態從計算圖中分離出來。我們將在下一節(循環神經網絡的從零開始實現)的實現中了解這種處理方式。
``` python
# 本函數已保存在d2lzh_pytorch包中方便以后使用
def data_iter_consecutive(corpus_indices, batch_size, num_steps, device=None):
if device is None:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
corpus_indices = torch.tensor(corpus_indices, dtype=torch.float32, device=device)
data_len = len(corpus_indices)
batch_len = data_len // batch_size
indices = corpus_indices[0: batch_size*batch_len].view(batch_size, batch_len)
epoch_size = (batch_len - 1) // num_steps
for i in range(epoch_size):
i = i * num_steps
X = indices[:, i: i + num_steps]
Y = indices[:, i + 1: i + num_steps + 1]
yield X, Y
```
同樣的設置下,打印相鄰采樣每次讀取的小批量樣本的輸入`X`和標簽`Y`。相鄰的兩個隨機小批量在原始序列上的位置相毗鄰。
``` python
for X, Y in data_iter_consecutive(my_seq, batch_size=2, num_steps=6):
print('X: ', X, '\nY:', Y, '\n')
```
輸出:
```
X: tensor([[ 0., 1., 2., 3., 4., 5.],
[15., 16., 17., 18., 19., 20.]])
Y: tensor([[ 1., 2., 3., 4., 5., 6.],
[16., 17., 18., 19., 20., 21.]])
X: tensor([[ 6., 7., 8., 9., 10., 11.],
[21., 22., 23., 24., 25., 26.]])
Y: tensor([[ 7., 8., 9., 10., 11., 12.],
[22., 23., 24., 25., 26., 27.]])
```
## 小結
* 時序數據采樣方式包括隨機采樣和相鄰采樣。使用這兩種方式的循環神經網絡訓練在實現上略有不同。
-----------
> 注:除代碼外本節與原書此節基本相同,[原書傳送門](https://zh.d2l.ai/chapter_recurrent-neural-networks/lang-model-dataset.html)
- Home
- Introduce
- 1.深度學習簡介
- 深度學習簡介
- 2.預備知識
- 2.1環境配置
- 2.2數據操作
- 2.3自動求梯度
- 3.深度學習基礎
- 3.1 線性回歸
- 3.2 線性回歸的從零開始實現
- 3.3 線性回歸的簡潔實現
- 3.4 softmax回歸
- 3.5 圖像分類數據集(Fashion-MINST)
- 3.6 softmax回歸的從零開始實現
- 3.7 softmax回歸的簡潔實現
- 3.8 多層感知機
- 3.9 多層感知機的從零開始實現
- 3.10 多層感知機的簡潔實現
- 3.11 模型選擇、反向傳播和計算圖
- 3.12 權重衰減
- 3.13 丟棄法
- 3.14 正向傳播、反向傳播和計算圖
- 3.15 數值穩定性和模型初始化
- 3.16 實戰kaggle比賽:房價預測
- 4 深度學習計算
- 4.1 模型構造
- 4.2 模型參數的訪問、初始化和共享
- 4.3 模型參數的延后初始化
- 4.4 自定義層
- 4.5 讀取和存儲
- 4.6 GPU計算
- 5 卷積神經網絡
- 5.1 二維卷積層
- 5.2 填充和步幅
- 5.3 多輸入通道和多輸出通道
- 5.4 池化層
- 5.5 卷積神經網絡(LeNet)
- 5.6 深度卷積神經網絡(AlexNet)
- 5.7 使用重復元素的網絡(VGG)
- 5.8 網絡中的網絡(NiN)
- 5.9 含并行連結的網絡(GoogLeNet)
- 5.10 批量歸一化
- 5.11 殘差網絡(ResNet)
- 5.12 稠密連接網絡(DenseNet)
- 6 循環神經網絡
- 6.1 語言模型
- 6.2 循環神經網絡
- 6.3 語言模型數據集(周杰倫專輯歌詞)
- 6.4 循環神經網絡的從零開始實現
- 6.5 循環神經網絡的簡單實現
- 6.6 通過時間反向傳播
- 6.7 門控循環單元(GRU)
- 6.8 長短期記憶(LSTM)
- 6.9 深度循環神經網絡
- 6.10 雙向循環神經網絡
- 7 優化算法
- 7.1 優化與深度學習
- 7.2 梯度下降和隨機梯度下降
- 7.3 小批量隨機梯度下降
- 7.4 動量法
- 7.5 AdaGrad算法
- 7.6 RMSProp算法
- 7.7 AdaDelta
- 7.8 Adam算法
- 8 計算性能
- 8.1 命令式和符號式混合編程
- 8.2 異步計算
- 8.3 自動并行計算
- 8.4 多GPU計算
- 9 計算機視覺
- 9.1 圖像增廣
- 9.2 微調
- 9.3 目標檢測和邊界框
- 9.4 錨框
- 10 自然語言處理
- 10.1 詞嵌入(word2vec)
- 10.2 近似訓練
- 10.3 word2vec實現
- 10.4 子詞嵌入(fastText)
- 10.5 全局向量的詞嵌入(Glove)
- 10.6 求近義詞和類比詞
- 10.7 文本情感分類:使用循環神經網絡
- 10.8 文本情感分類:使用卷積網絡
- 10.9 編碼器--解碼器(seq2seq)
- 10.10 束搜索
- 10.11 注意力機制
- 10.12 機器翻譯