天天看點

如何使用PyTorch實作面向NLP的深度學習?

深度學習建構塊:映射,非線性和目标

深度學習包括以巧妙的方式組合具有非線性的線性。非線性的引入允許強大的模型。在本節中,我們将使用這些核心元件,組成一個目标函數,并檢視模型是如何訓練的。

映射

深度學習的一個工作核心就是映射,他是函數f(x),定義為:f(x) = Ax + b ,對于一個矩陣A和向量x及b。這裡要學習的參數是 A 和 b。通常 b 被稱為偏差項。

人話:就是高中數學裡面的函數映射,y = ax+b, 這裡就是學習參數a和b。

PyTorch和大多數其他深度學習架構的工作方式與傳統的線性代數有一點差別。它映射輸入的行而不是列。也就是說,下面輸出的第i行是A下輸入的第i行的映射,加上偏差項。請看下面的例子。

人話:這裡就是線性代數裡面的的外積或叉乘。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

torch.manual_seed(1)


lin = nn.Linear(5,3)   #将5列映射為3列,參數A,b
'''   
例子:
        0.21    0.21    0.81
        0.42    0.11    0.51
lin=    0.33    0.28    0.14
        0.74    0.41    0.28
        0.55    0.9    0.85
'''
#資料是2×5.
data = torch.randn(2,5)

'''
例子:
        0.21    0.61    0.91  0.75    0.23
data =
        0.31    0.81    0.41  0.65    0.42
'''

data =  lin(data)
'''
data

'''

print(data)
print(data.grad_fn)           
如何使用PyTorch實作面向NLP的深度學習?

非線性函數

首先,注意接下來的事實,這将解釋為什麼我們首先需要非線性。假如,我們有兩個映射f(x) = Ax+b 和 g(x)=Cx+d。f( g(x))是什麼呢?

f( g(x)) =A(C x+d)+b =ACx+(Ad+b)

AC是一個矩陣,Ad+b是一個向量,是以,我們看到構成映射的又給了一個映射。

從這,你可以看出,如果你想神經網絡組成一個長的映射鍊,這樣增對你的模型沒有任何作用,隻是做單個映射。

如果我們在映射層之間引入非線性函數,将不會是這樣,我們可以建構更加強大的模型。

這是少數幾個核心的非線性函數。tanh(x),σ(x),ReLU(x) 非常常用。你可能會想:“為什麼是這些函數?我可以想到一堆其他的非線性函數。”主要原因是他們有梯度同時容易計算,計算梯度是學習的本質。例如:

如何使用PyTorch實作面向NLP的深度學習?

(sigmoid函數的導數)。

快速說明:雖然您可能已經在AI的介紹課程中學習了一些神經網絡,其中σ(x)是預設的非線性,但在實踐中人們通常會回避它。這是因為随着參數的絕對值增加,梯度消失得非常快。小漸變意味着很難學習。大多數人預設為tanh或ReLU。

#在PyTorch中,大多數的非線性函數都在torch.functional(我們已經将它定義為F)
#注意非線性函數通常不像線性函數那樣擁有參數
#也就是說,他們沒有在訓練期間更新的權重。
data = torch.randn(2,2)
print(data)
print(F.relu(data))           
如何使用PyTorch實作面向NLP的深度學習?

Softmax和機率(probabilities)

Softmax(x) 函數也是一個非線性的,但是它特殊在于它通常是網絡的最後操作。這是因為它接收實數向量并傳回機率分布。它的定義如下。x__ 是實數向量(沒有限制正數還是負數)。那麼Softmax(x)__ 的第i個分量是

如何使用PyTorch實作面向NLP的深度學習?

這非常清楚輸出是機率分布:每個元素都是非負的,并且所有分量的總和是1。

您還可以将其視為僅将元素指數運算符應用于輸入,以使所有内容都為非負值,然後除以标準化常量。

data = torch.randn(5)

print(data)
print(F.softmax(data,dim=0))
print(F.softmax(data,dim=0).sum())
print(F.log_softmax(data,dim=0))           
如何使用PyTorch實作面向NLP的深度學習?

目标函數

目标函數是您的網絡正在被訓練以最小化的功能(在這種情況下,它通常被稱為損失函數或成本函數)。首先選擇一個訓練執行個體,通過神經網絡運作它,然後計算輸出的丢失。然後通過擷取損失函數的導數來更新模型的參數。直覺地說,如果你的模型對答案完全有信心,答案是錯誤的,你的損失就會很高。如果它對答案非常有信心,答案是正确的,那麼損失就會很低。

最小化訓練示例中的損失函數背後的想法是,您的網絡将有希望地進行一般化,并且在您的開發集,測試集或生産中看不見的示例有很小的損失。一個示例損失函數是負對數似然丢失,這是多類分類的一個非常常見的目标。對于有監督的多類分類,這意味着訓練網絡以最小化正确輸出的負對數機率(或等效地,最大化正确輸出的對數機率)。

優化和訓練

那麼我們可以為執行個體計算損失函數呢?我們該怎麼做?我們之前看到,Tensors知道如何根據用于計算它的東西來計算漸變。好吧,因為我們的損失是Tensor,我們可以根據用于計算它的所有參數計算梯度!然後我們可以執行标準漸變更新。設 θ__為我們的參數, __L(θ)為損失函數, η 為正學習率。那麼:

如何使用PyTorch實作面向NLP的深度學習?

有許多算法和積極的研究,試圖做更多的東西,而不僅僅是這個香草梯度更新。許多人試圖根據訓練時間發生的事情來改變學習率。除非您真的感興趣,否則您無需擔心這些算法的具體用途。Torch在torch.optim包中提供了許多,它們都是完全透明的。很多複雜的算法就如同使用最簡單的梯度更新。嘗試不同的更新算法和更新算法的不同參數(如不同的初始學習率)對于優化網絡性能非常重要。通常,隻需用Adam或RMSProp等優化器替換SGD就可以顯着提升性能。

用PyTorch建立網絡

在我們把注意力放在NLP之前,讓我們使用仿射映射和非線性來做一個在PyTorch中建構網絡的注釋示例。我們還将看到如何使用PyTorch内置的負對數似然來計算損失函數,并通過反向傳播更新參數。

所有網絡元件都應該從nn.Module繼承并覆寫forward()方法。就樣闆而言,就此而言。從nn.Module繼承可為元件提供功能。例如,它使其跟蹤其可訓練參數,您可以使用 .to(device) 方法在CPU和GPU之間交換它,其中裝置可以是CPU裝置 torch.device("cpu")或CUDA裝置torch.device("cuda:0")。

讓我們編寫一個帶注釋的網絡示例,該網絡采用稀疏的詞袋表示,并在兩個标簽上輸出機率分布:“英語”和“西班牙語”。這個模型隻是邏輯回歸。

例子:邏輯回歸Bag-of-Words分類器

我們的模型将映射稀疏的BoW表示以記錄标簽上的機率。我們為詞彙中的每個單詞指定一個索引。例如,假設我們的整個詞彙是兩個單詞“hello”和“world”,分别為0和1。句子“hello hello hello hello”的BoW向量是

[4,0]

如"hello world world hello",就是

[2,2]

通常,他是 [Count(hello),Count(world)]

将此BOW向量表示為X。我們網絡的輸出是:

logSoftmax(AX + b)

也就是說,我們參入的輸入值經過映射然後做log softmax。

data = [('me gusta comer en la cafeteria'.split(),'SPANISH'),
       ('Give it to me'.split(),'ENGLISH'),
        ("No creo que sea una buena idea".split(), "SPANISH"),
        ("No it is not a good idea to get lost at sea".split(), "ENGLISH")
       ]

test_data = [("it is lost on me".split(), "ENGLISH"),
             ("Yo creo que si".split(), "SPANISH"),
             ("this is english".split(), "ENGLISH")
             ]

#word_to_ix将詞彙中的每個單詞映射到一個唯一的整數,
#這将是它的單詞向量包的索引

word_to_ix={}

for sent , _ in data + test_data:
    for word in sent:
        if word in sent:
            if word not in word_to_ix:
                word_to_ix[word] = len(word_to_ix)

print(word_to_ix)

VOCAB_SIZE = len(word_to_ix)
NUM_LABELS = 2


class BoWClassifier(nn.Module):
    
    def __init__(self,num_labels,vocab_size):
        #調用nn.Module初始化函數。不要被文法弄糊塗
        #總是在nn.Module中做
        super(BoWClassifier,self).__init__()
        
        
        #定義您需要的參數。在這種情況下,我們需要A和b,
        #映射函數的參數。
        #Torch定義nn.Linear(),它提供映射函數。
        #確定你了解為什麼輸入次元是vocab_size
        #并且輸出是num_labels!
        self.linear = nn.Linear(vocab_size,num_labels)
        #注意!非線性函數log softmax不需要參數!
        #是以這裡,我們不需要太在意它
    
    def forward(self,bow_vec):
        #将輸入傳遞給線性層
        #然後将輸入傳遞給log_softmax
        #很多非線性和其他函數都在torch.nn.functional包中
        return F.log_softmax(self.linear(bow_vec),dim=1)

      

def make_bow_vector(sentence,word_to_ix):
    #建構詞袋向量。
    #首先建構一個元素數量與word_to_ix的長度相同的,值為0的向量。
    vec = torch.zeros(len(word_to_ix))
    for word in sentence:
        #将句子的出現的詞,進行計數。
        #并更新到vec向量中以wor_to_ix中的索引值的位置。
        vec[word_to_ix[word]] += 1
        
    #将vec展開   
    return vec.view(1,-1)
  
def make_target(label, label_to_ix):
    
    return torch.LongTensor([label_to_ix[label]])

  
#初始化模型
model = BoWClassifier(NUM_LABELS, VOCAB_SIZE)

for param in model.parameters():
    print(param)


    
with torch.no_grad():
    sample = data[0]
    print(sample)
    bow_vector = make_bow_vector(sample[0], word_to_ix)
    log_probs = model(bow_vector)
    print(log_probs)
    
    kind = torch.max(log_probs,1)
    print(kind[1].item())           
如何使用PyTorch實作面向NLP的深度學習?

以上哪個值對應于ENGLISH的對數機率,哪個對應SPANISH?我們從來沒有定義它,但是如果我們想訓練這個東西我們需要。

label_to_ix = {"SPANISH": 0, "ENGLISH": 1}           

是以讓我們訓練!為此,我們傳遞執行個體以擷取log機率,計算損失函數,計算損失函數的梯度,然後使用漸變步驟更新參數。損耗函數由Torch在nn包中提供。nn.NLLLoss()是我們想要的負對數似然丢失。它還定義了torch.optim中的優化函數。在這裡,我們将使用SGD。

請注意,NLLLoss的輸入是日志機率向量和目标标簽。它不計算我們的日志機率。這就是我們網絡的最後一層是log softmax的原因。損失函數nn.CrossEntropyLoss()與NLLLoss()相同,除了它為您執行log softmax。

# 在我們訓練之前,先在測試資料上運作,知識看看前後變化
with torch.no_grad():
    for instance, label in test_data:
        bow_vec = make_bow_vector(instance, word_to_ix)
        log_probs = model(bow_vec)
        print(log_probs)

# 列印與“creo”對應的矩陣列
print(next(model.parameters())[:, word_to_ix["creo"]])

loss_function = nn.NLLLoss()

#交叉熵損失函數,多分類損失函數
#loss_function = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)


#通常,您希望多次傳遞訓練資料。
#100比實際資料集大得多,但真正的資料集有超過
#2個執行個體。通常,在5到30個時期之間是合理的。
for epoch in range(100):
    for instance ,label in data:
        #第一步:請記住,PyTorch會累加梯度。
        #在運作每個執行個體前我們需要清空它們
        model.zero_grad()
        
        #第二步:制作我們的BOW向量,并且我們必須将目标作為整數包裝在
        #Tensor中。例如,如果目标是SPANISH,那麼
        #我們包裝整數0.然後,loss函數知道對數機率的第0個
        #元素是對應于SPANISH的對數機率 
        bow_vec = make_bow_vector(instance,word_to_ix)
        target = make_target(label,label_to_ix)
        
        #第三步:前向傳播
        log_probs = model(bow_vec)
        
        #第四步:計算損失,梯度并且通過調用optimizer.step()更新參數
        loss = loss_function(log_probs,target)
        loss.backward()
        optimizer.step()

with torch.no_grad():
    for instance, label in test_data:
        bow_vec = make_bow_vector(instance, word_to_ix)
        log_probs = model(bow_vec)
        #print(log_probs)
        result = torch.max(log_probs,1)
        print(result[1].item())
        


print(next(model.parameters())[:, word_to_ix["creo"]])           
如何使用PyTorch實作面向NLP的深度學習?

我們得到了正确的答案!您可以看到,在第一個示例中,西班牙語的對數機率要高得多,而對于測試資料,第二個例子中英語的對數機率要高得多,應該如此。

現在,您将了解如何建立PyTorch元件,通過它傳遞一些資料并執行漸變更新。我們已經準備好深入挖掘NLP的深層内容。

總結

本節,作者主要介紹了一下幾點内容:

1.PyTorch中線性和非線性函數的特點和功能。

2.着重介紹了Softmax函數,Softmax函數輸出的是機率分布。

3.簡單介紹了目标函數(一般稱為損失函數),通過優化器不斷優化參數,以達到逼近目标的目的。

4.介紹了優化器和訓練。,通過不斷更新參數,降低損失。

如何使用PyTorch實作面向NLP的深度學習?

5.建構一個詞袋的分類器。首先建構一個分類模型,模型需要繼承nn.Module,并重寫 forward()函數;其次定義損失函數和優化器;然後訓練。第一步,模型梯度清零;第二步:制作我們的BOW向量,第三步:訓練模型;第四步:計算損失、梯度并使用optimizer.step()函數更新參數。

繼續閱讀