語言模型的隨機採樣和相鄰採樣

自然語言數據可以看成一種特殊的時間序列數據。對於這種時序數據的採樣主要有有隨機採樣和相鄰採樣兩種方式。兩者都需要確定一個batch的樣本數量batch_size和每個樣本的時間步長num_steps。

隨機採樣

步驟:語料庫corpus_indices的長度爲nn,首先按照時間步長確定可能的樣本的起始索引,可能的樣本起始索引最後被隨機打散。這裏注意:採樣的單個樣本的最後一個單詞不可能是序列的最後一個字(否則,就沒有輸入子序列所對應的輸出子序列)

num_examples = (len(corpus_indices) - 1) // num_steps  
#下取整,得到不重疊情況下的樣本個數
print("num_examples:"+str(num_examples))
example_indices = [i * num_steps for i in range(num_examples)]  
#每個樣本的第一個字符在corpus_indices中的下標
random.shuffle(example_indices)

如圖所示:對於一個長度爲32序列0,1,2,…,31,時間步長取4,batch大小取3時,有效長度只有28。
在這裏插入圖片描述
這裏的隨機化是指單個batch內部的樣本是隨機抽取的,在一個batch內部如果起始索引相鄰則樣本可能相鄰。值得注意的是,相鄰的兩個batch在原始序列上不一定相鄰。取決於起始索引被隨機化的情況

import torch
import random
def data_iter_random(corpus_indices, batch_size, num_steps, device=None):
    # 減1是因爲對於長度爲n的序列,X最多隻有包含其中的前n - 1個字符
    num_examples = (len(corpus_indices) - 1) // num_steps  # 下取整,得到不重疊情況下的樣本個數
    print("num_examples:"+str(num_examples))
    example_indices = [i * num_steps for i in range(num_examples)]  # 每個樣本的第一個字符在corpus_indices中的下標
    random.shuffle(example_indices)
    print("example_indices"+str(example_indices))
    def _data(i):
        # 返回從i開始的長爲num_steps的序列
        return corpus_indices[i: i + num_steps]
    if device is None:
        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    
    for i in range(0, num_examples, batch_size):  #迭代使得采樣不重不漏
        # 每次選出batch_size個隨機樣本
        print(i)
        batch_indices = example_indices[i: i + batch_size]  # 當前batch的各個樣本的首字符的下標
        print("batch_indices: "+str(batch_indices))
        X = [_data(j) for j in batch_indices]
        Y = [_data(j + 1) for j in batch_indices]
        yield torch.tensor(X, device=device), torch.tensor(Y, device=device)

相鄰採樣

在相鄰採樣中,相鄰的兩個隨機小批量在原始序列上的位置相毗鄰。

先確定待採樣的有效序列的長度,在按照batch_size對該有效序列進行均分,然後按照時間步長num_step對堆疊的子序列進行逐個採樣。

corpus_len = len(corpus_indices) // batch_size * batch_size
corpus_indices = corpus_indices[: corpus_len]  # 僅保留前corpus_len個字符

如圖所示,一個框裏的就是一個batch裏的多個樣本:

在這裏插入圖片描述

完整代碼如下:

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_len = len(corpus_indices) // batch_size * batch_size  # 保留下來的序列的長度
    corpus_indices = corpus_indices[: corpus_len]  # 僅保留前corpus_len個字符
    indices = torch.tensor(corpus_indices, device=device)
    indices = indices.view(batch_size, -1)  # resize成(batch_size, )
    batch_num = (indices.shape[1] - 1) // num_steps
    for i in range(batch_num):
        i = i * num_steps
        X = indices[:, i: i + num_steps]
        Y = indices[:, i + 1: i + num_steps + 1]
        yield X, Y

容易發現,一個batch內部樣本是不鄰接的,但是相鄰batch裏對應位置的樣本一定是鄰接的

相鄰採樣的樣本一個batch一個batch的輸入是符合語言的前後依賴這一事實的。

發佈了305 篇原創文章 · 獲贊 35 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章