動手學習深度學習 | 語言模型和循環神經網絡筆記

0.文本處理整體概況

step1:對原始數據進行分詞
step2:對分詞後的數據進行去重編號,得到[詞語to序號]的列表,和[序號to詞語]的字典。將這兩部分用作後續訓練循環神經網絡的數據集。
step3:通過一些採樣方法對構建的數據集進行採樣,得到訓練的批次。常見的採樣方法有隨機採樣和相鄰採樣。
step4:利用語言模型對上述的數據集進行訓練,得到一個nlp模型。語言模型有n元語法模型,RNN模型,LSTM模型等。

1.使用spacy可以進行語言分詞

達到很好的直觀效果,相較於自己構建的邏輯,更加符合語言本身詞意的分詞操作,且可以將分詞對應的idx對應輸出。

import spacy
text = "Mr. Chen doesn't agree with my suggestion."
nlp = spacy.load('en_core_web_sm')
doc = nlp(text)
print([token.text for token in doc])
print([token.idx for token in doc])

#------------------
['Mr.', 'Chen', 'does', "n't", 'agree', 'with', 'my', 'suggestion', '.']
[0, 4, 9, 13, 17, 23, 28, 31, 41]
2.隨機採樣和相鄰採樣
2.1 隨機採樣

下面的代碼每次從數據裏隨機採樣一個小批量。其中批量大小batch_size是每個小批量的樣本數,num_steps是每個樣本所包含的時間步數。 在隨機採樣中,每個樣本是原始序列上任意截取的一段序列,相鄰的兩個隨機小批量在原始序列上的位置不一定相毗鄰。

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  # 下取整,得到不重疊情況下的樣本個數
    example_indices = [i * num_steps for i in range(num_examples)]  # 每個樣本的第一個字符在corpus_indices中的下標
    random.shuffle(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個隨機樣本
        batch_indices = example_indices[i: i + batch_size]  # 當前batch的各個樣本的首字符的下標
        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)
2.2 相鄰採樣


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

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
3.語言模型

語言模型的目標就是給定一個通過分詞得到的詞序列,並且評估該序列是否合理,即計算該序列的概率。語言模型從最初的n-gram方法受限於模型參數大,參數稀疏,計算效率低等不足,發展到現在的基於深度學習的RNN,LSTM等方法,大大的提高了語言模型的能力。

3.1 n-gram

如下描述所示,n-gram語言模型是一種基於統計的方法。該方法通過分詞在語料庫中的比重以及條件概率構建概率模型。由於條件概率的存在,該方法的最終計算需要基於n階馬爾科夫假設。且該方法由於基於分詞在語料庫中的比重,從而導致很多分詞的出現頻次很少甚至爲零,導致得到的值的稀疏性太強,計算效率低。

3.2 RNN遞歸神經網絡模型

如下描述所示,遞歸神經網絡RNN是一種基於參數學習的語言模型。該類模型通過中間狀態位的保留,隱式的實現了上述概率模型中的條件概率模型。而且此種方法學習友好,使用方便。且如下代碼所示,RNN模型的參數與輸入輸出的時間步數無關,因此模型的複用性很強。

vocab_size = 1027
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
# num_inputs: d
# num_hiddens: h, 隱藏單元的個數是超參數
# num_outputs: q

def get_params():
    def _one(shape):
        param = torch.zeros(shape, device=device, dtype=torch.float32)
        nn.init.normal_(param, 0, 0.01)
        return torch.nn.Parameter(param)

    # 隱藏層參數
    W_xh = _one((num_inputs, num_hiddens))
    print(W_xh.shape)
    W_hh = _one((num_hiddens, num_hiddens))
    print(W_hh.shape)
    b_h = torch.nn.Parameter(torch.zeros(num_hiddens, device=device))
    # 輸出層參數
    W_hq = _one((num_hiddens, num_outputs))
    print(W_hq.shape)
    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device))
    return (W_xh, W_hh, b_h, W_hq, b_q)
get_params()
print(num_inputs, num_hiddens, num_outputs)

#--------------------

torch.Size([1027, 256])
torch.Size([256, 256])
torch.Size([256, 1027])
1027 256 1027
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章