天天看點

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

目錄

1. Attention Mechanism

1.1 Background

1.1.1 時代背景

1.1.2 模型背景

1.2 Attention Algorithm

1.3 Scaled Dot-Product Attention 

1.3.1 attention scale縮放:​編輯

1.4 Self-Attention 自注意力機制

1.4.1 Q、K、V的擷取

1.4.2 matmul for similarity

1.4.3 softmax + scale

1.4.4 matmul for new vector z1

1.4.5 self-attention summary

1.4.6 Attention和Self-Attention差別

1.4.7 Selft-attention和RNNs的差別

1.4.8 masked self-attention 實作-1

1.4.9 self-attention 實作-2

1.5 Masked Self-Attention

1.6 Multi-head Self-Attention

1.6.1 multi-head self-attention algorithm

1.6.2 multi-head self-attention意義

1.6.3 pytorch實作 multi-head attention

1.7 Positional Encoding

1.7.1 positional encoding algorithm

1.7.2 positional encoding 實作

2. Transformer(Encoder-Decoder Architecture)

2.1 Transformer 編碼器 Encoder 

2.1.1 Encoder 總結

2.2 Transformer 解碼器 Decoder

2.2.1 為什麼Decoder需要做mask

2.2.2 為什麼Encoder給予Decoders的是K、V矩陣 

2.3 Transformer 動态流程

2.4 Transformer疑問解答

2.4.1 為什麼用張量tensor?

2.4.2 詞向量input必須是one-hot嗎?換成word2vec可以提升模型performance嗎?

2.4.3 transformer強大之處

2.5 pytorch 實作Transformer

extension:  python面向對象--封裝(class類、__init__初始化函數)

2.5.1 殘差函數add、歸一化Norm

2.5.2 Feed Forward layer(FFN)

2.5.3 Linear層 + Softmax層

2.5.4 masked multi-head self-attention

2.5.5 建構編碼器 encoder

2.5.6 解碼器 decoder

2.5.7 整體Transformer

2.6 Huggingface transformers package

2.6.1 background

2.6.2 AutoClass介紹

2.6.3 分詞器對象 AutoTokenizer

2.6.4 配置檔案 AutoConfig

2.6.5 建立預訓練模型 AutoModel

2.7 “調包俠”實作transformer、bert

2.7.1 “調包俠”實作transformer

2.7.2 “調包俠”實作bert

2.8 bert project application

2.9 cuda GPU application

2.9.1 選擇GPU顯示卡

2.9.2 上傳模型model

2.9.3 訓練階段training--上傳資料data

2.9.4 驗證階段eval--上傳資料data

2.9.5  run model

參考

1. Attention Mechanism

1.1 Background

1.1.1 時代背景

理論基礎:類似于CNN中"感受野"receptive field的思想,attention mechanism從全部到隻關注重點。

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

 大資料背景,什麼資料都有,重要的,不重要的。但model很難自己區分重要的内容和不重要的内容,--> attention mechanism.

1.1.2 模型背景

problem:在機器翻譯時,RNN(極限50詞)、LSTM(極限200詞)存在長序列依賴問題long-term dependecies、無法做并行計算

solution:self-attention,突破RNNs等序列sequence模型的固有缺陷,捕獲長距離資訊依賴,生成具有句法特征和語義特征的新向量。

  • 句法特征。
  • 語義特征。

1.2 Attention Algorithm

本質:QKV權重求和。

權重矩陣Wq * () = Q ; Wk * () = K; Wv * () = V,這才是attention計算中的qkv。

q1通過與k1計算相似度s1,softmax後得到注意力機率a1,即k1代表的單詞(KV一般同源)在q1單詞新的向量表示中的占比,最後實作是通過權重求和,q1新的向量表示z1=a1*v1 + a2*v2。即長距離強相關的單詞資訊融入到q1新向量表示中,克服了RNN長距離相關單詞在q1新向量表示中資訊丢失的問題。

self-attention: QKV相乘(QKV同源),QK相乘得到相似度A,AV相乘得到注意力值Z。

10 Transformer 之 Self-Attention(自注意力機制)_哔哩哔哩_bilibili

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

,我們一般使用點乘的方式。

1. 通過點乘的方式計算Q和K裡的每一個元素的相似度,就可以拿到Q和k1的相似值s1,Q和k2的相似值s2,Q和kn的相似值sn。

2. 做一層softmax(s1, s2,...,sn),就可以得到機率(a1, a2,...,an)。進而就可以找出哪個對Q而言更重要

3. 還得進行一個彙總,即權重求和的過程。(a1,a2,...,an) * (v1,v2,...,vn) = (a1*v1 + a2*v2 + ...+ an*vn)  a1是一個數值0.3, 0.4等、v1是一個向量哈

這樣的話,就得到了一個新的矩陣V'。

一般K=V,在transformer裡,K可以!= V,但是K和V之間一定具有某種聯系,這樣QK的點乘才能指導V哪些重要,哪些不重要。

1.3 Scaled Dot-Product Attention 

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)
  1. QK相乘求相似度,做一個scale放縮(未來做softmax時機率分布避免出現極端情況)
  2. 然後,做softmax得到機率
  3. 新的向量表示了K和V(K==V),然後這種表示還暗含了Q的資訊(于Q而言,K裡面重要資訊)

1.3.1 attention scale縮放:
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

是K的次元

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

是為了防止因為資料太過離散而導緻softmax計算出來的機率不穩定

softmax:

51, 49 --> 0.51, 0.4

80, 20 --> 0.999, 0.0001

a1和a2之間的差額越大,這個機率就越離譜。因為softmax在梯度更新時的缺陷。

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)
:80/8, 20/8 --> 0.9, 0.1

1.4 Self-Attention 自注意力機制

self-attention的關鍵在于,不僅僅是K≈V≈Q來源于同一個X,這三者是同源的

通過X找到X裡面的關鍵點

1.4.1 Q、K、V的擷取

并不是K=V=Q=X,而是通過三個參數

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

V = {v1, v2,...vn} 

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

1.4.2 matmul for similarity

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

1.4.3 softmax + scale

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

1.4.4 matmul for new vector z1

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)
z1表示的就是thinking新的向量表示。
  • 對于thinking,初始詞向量為x1
  • 現在通過thinking machines這句話去查詢這句話裡每一個單詞和thinking之間的相似度。
新的z1依然是thinking的詞向量表示,隻不過這個詞向量的表示蘊含了thinking machines這句話對于thinking而言哪個更重要的資訊。

1.4.5 self-attention summary

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

1.4.6 Attention和Self-Attention差別

注意力機制attention是一個很寬泛的概念,QKV相乘就是注意力,但是它沒有規定QKV怎麼來的。

  • 通過一個查詢變量Q,去找到V裡面比較重要的東西。
  • 假設K==V,然後QK相乘求相似度A,然後AV相乘得到注意力值Z,這個Z就是V的另一種形式表示。
  • Q可以是任何一種東西,V也是任何一個東西,K往往和V同源。
  • 它沒有規定QKV怎麼來,隻規定QKV怎麼做

自注意力機制self-attention是注意力機制的一個子集

  • 本質上QKV可以看作是相等的,同源,來源于同一個X
  • 對于一個詞向量,做的是空間上的對應。乘上參數矩陣,做線性變換,依然代表X
  • 詞向量是不固定的,目前沒有任何模型可以說一定能生活準備代表某個單詞的詞向量。

擴充:交叉注意力機制

Q和K不同源,但是K和V同源,同源代表着一定意義上的相等

QKV不固定,你自己定義QKV,可以自己賦予新的名字(e.g. 小貓注意力),但attention計算的本質不變。

1.4.7 Selft-attention和RNNs的差別

RNN、LSTM存在長序列依賴問題,無法做并行

self-attention得到新的詞向量具有句法特征和語義特征(表征更完善) 

  • 句法特征
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

self-attention會和句子中的每一個單詞做注意力計算,解決了長序列依賴問題,無論句子多長,第一個單詞和最後一個單詞都能産生聯系。

  • 語義特征
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

不做注意力,its的詞向量就是單純的its,沒有任何附加資訊。

也就是說,its有law這層意思,而通過注意力機制得到的its詞向量,則會包含一定的laws資訊

self-attention計算量太大,計算複雜度

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

1.4.8 masked self-attention 實作-1

import torch.nn.functional as F

def self-attention(query, key, value, dropout=None, mask=None):
    d_k = query.size(-1)
    scores = torch.matmul(query, key.transpose(-1, -2)) / math.sqrt(d_k)
    # mask的操作在QK之後,softmax之前
    if mask is not None:
        mask.cuda()
        scores = scores.masked_fill(mask == 0, -1e9)
    self_attn = F.softmax(scores, dim=-1)
    if dropout is not None:
        self_attn = dropout(self_attn)
    return torch.matmul(self_attn, value), self_attn
           

1.4.9 self-attention 實作-2

import torch
import torch.nn as nn
 
class SelfAttention(nn.Module):
    def __init__(self, d_model, nhead):
        super(SelfAttention, self).__init__()
        self.d_model = d_model
        self.nhead = nhead
        self.query_linear = nn.Linear(d_model, d_model)
        self.key_linear = nn.Linear(d_model, d_model)
        self.value_linear = nn.Linear(d_model, d_model)
        self.softmax = nn.Softmax(dim=-1)
 
    def forward(self, input):
        # input: (batch_size, sequence_length, d_model)
        query = self.query_linear(input)
        key = self.key_linear(input)
        value = self.value_linear(input)
        
        # compute attention scores
        attention_scores = torch.matmul(query, key.transpose(-2, -1))
        attention_scores = attention_scores / torch.sqrt(torch.tensor(self.d_model, dtype=attention_scores.dtype))
        
        # apply softmax to get attention weights
        attention_weights = self.softmax(attention_scores)
        
        # compute the final attention output
        attention_output = torch.matmul(attention_weights, value)
        return attention_output
           

1.5 Masked Self-Attention

掩碼自注意力

why要在self-attention上做這個改進?

生成模型,在生成單詞時,是一個一個生成的。

當我們做生成任務時,我們也想對生成的這個單詞做注意力計算。但是,生成的句子是一個一個單詞生成的。很簡單,不能用未來的預期結果預測現在的

e.g. I have a dream

1. I  第一次注意力計算,隻有I

2. I have  第二次注意力計算,隻有I和have

3. I have a 

4. I have a dream

5. I have a dream <eos>

掩碼自注意力機制

自注意力機制明确知道這句話有多少個單詞,并且一次給足,就相當于預知未來指導現在穿越、降維打擊?就很扯。。而masked掩碼是分批次給,最後一次才給足。

  • 掩碼後1
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)
  •  掩碼後2
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

1.6 Multi-head Self-Attention

self-attention是attention的一個具體做法。給定一個x,通過self-attention model,得到一個z,這個z就是對x的新的表征(詞向量),z這個詞向量相比較x擁有了句法特征和語義特征。

z相比較x有了提升,通過multi-head self-attention得到的z',相比較z又有了進一步的提升!

multi-head,多頭,就是多次,一般用h=8表示。

Query,Key,Value首先經過一個線性變換,然後輸入到放縮點積attention,注意這裡要做h次,其實也就是所謂的多頭,每一次算一個頭。而且,每次線性變換的w是不一樣的。然後将h次的放縮點積attention結果進行拼接,再進行一次線性變換得到的值作為多頭attention的結果。

可以允許模型在不同的表示子空間裡學習到相關的資訊。

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

1.6.1 multi-head self-attention algorithm

1. 對于X,我們不是直接拿X得到Z,而是把X分成了8塊(8頭),經過8次線性變換(8個不同的Wq,Wk,Wv),得到z0-z7。Note that 

embed_dim

 will be split across 

num_heads

 (i.e. each head will have dimension 

embed_dim // num_heads

).

8個不同的w會将(x0,x1,...,x7)映射到8個不同的子空間進行attention計算。

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

2. 然後把z0-z7拼接起來,再做一次線性變換(改變次元)得到z。

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

1.6.2 multi-head self-attention意義

機器學習本質:y=wx+b,線性變換。

深度學習:y=σ(wx+b),非線性。改變空間上的位置坐标,讓一個次元空間上不合理的點變得合理。

非線性變換的本質:改變空間上的位置坐标,任何一個點都可以在次元空間上找到,通過某個手段,讓一個不合理的點(位置不合理)變得合理。

multi-head self-attention把X切分成8塊,這樣一個原先在一個位置上的X,去了空間上的8個子空間subspaces,找到更合理的位置z'。

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

1.6.3 pytorch實作 multi-head attention

__init__()函數,聲明初始化變量、子產品modules和函數functions。

forward函數,放入輸入資料input vector,傳出輸出output。

X輸入的第一個次元是batch,因為訓練模型時一個批度一個批度的輸入資料。

這裡的全連接配接層進行線性變換,取代w矩陣的功能,原本應該是8組不同的w矩陣,這裡用一個大的全連接配接層進行變換,然後通過次元變換轉換成8個頭的形式,輸入到self-attention子產品裡。

比如,[batch, 32, 512]  # 每個batch裡有32個詞,每個詞有512維。

将其切分成[batch, 8, 32, 64]  # 将512維切分成8個頭、64維

import math

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

def self_attention(query, key, value, dropout=None, mask=None):
    d_k = query.size(-1)
    scores = torch.matmul(query, key.transpose(-1, -2)) / math.sqrt(d_k)
    # mask的操作在QK之後,softmax之前
    if mask is not None:
        mask.cuda()
        scores = scores.masked_fill(mask == 0, -1e9)
    self_attn = F.softmax(scores, dim=-1)
    if dropout is not None:
        self_attn = dropout(self_attn)
    return torch.matmul(self_attn, value), self_attn

class MultiHeadAttention(nn.Module):
    # 聲明初始化變量和函數functions
    def __init__(self, head, d_model, dropout=0.1):
        super(MultiHeadAttention, self).__init__()
        '''
        :param head: 頭數,預設8
        :param d_model: 輸入的次元
        :param query: Q
        :param key: K
        :param value: V
        '''
        assert (d_model % head ==0)  # 取餘
        self.d_k = d_model // head  # 将輸入X平均分成8個頭
        self.head = head
        self.d_model = d_model
        # 自注意力機制QKV同源,線性變換
        self.linear_query = nn.Linear(d_model, d_model)  # linear線性層(input, output),中間暗含着w矩陣
        self.linear_key = nn.Linear(d_model, d_model)
        self.linear_value = nn.Linear(d_model, d_model)

        self.linear_out = nn.Linear(d_model, d_model)
        self.dropout = nn.Dropout(p=dropout)
        self.attn = None  # 用于接收self-attention function 傳回的attn 機率

    # forward函數傳入input vector,傳出輸出
    def forward(self, query, key, value, mask=None):
        if mask is not None:
            # 多頭注意力機制的線性變換層是4維,是把query[batch_size, frame_num, d_model]變成[batch_size, -1, head, d_k]
            # 在1,2維交換變成[batch_size, head, -1, d_k],是以mask要在第一維添加一維,與後面的self attention計算次元一樣
            mask = mask.unsqueeze(1)
        # 輸入的size(0)是batch,[batch,]
        n_batch = query.size(0)
        # 多頭需要對輸入X切分成多頭, query==key==value. [batch, 32, 512] -> [batch, 8, 32, 64]
        query = self.linear_query(query).view(n_batch, -1, self.head, self.d_k).transpose(1, 2)  # [batch, 8, 32, 64]
        key = self.linear_key(key).view(n_batch, -1, self.head, self.d_k).transpose(1, 2)
        value = self.linear_value(value).view(n_batch, -1, self.head, self.d_k).transpose(1, 2)  # []

        x, self.attn = self_attention(query, key, value, dropout=self.dropout, mask=mask)
        # 變成三維,或者說是concat head. [batch, 8, 32, 64] -> [batch, 32, 512]
        x = x.transpose(1, 2).contiguous().view(n_batch, -1, self.head * self.d_k)

        return self.linear_out(x)
           

1.7 Positional Encoding

 Attention mechanism解決了長序列依賴問題、可以并行。

缺點:計算成本太大。

展現不出詞與詞之間存在的順序關系order,位置編碼的問題。

RNN、LSTM是包含詞順序關系的。self-attention對于每個詞都是無位置關系的,把每個詞的順序打亂,得到的注意力值依然不變。

加上一個positional encoding

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

通過t1告訴你,x1在前面,x2在x1後面 

1.7.1 positional encoding algorithm

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

pos代表input embedding在整體句子中的位置,i代表embedding次元裡元素的位置,即2i表示次元裡的偶數項,用了sin函數;2i+1表示次元裡的奇數項,用了cos函數

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)
sin和cos函數存在和差化積公式。
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

即sin(pos+k)=sin(pos)*cos(k) + cos(pos)*sin(k),cos(pos+k)=cos(pos)*cos(k) - sin(pos)*sin(k)

pos+k是pos位置與k位置的位置向量線性組合,2i--偶數次元用sin函數,2i+1--奇數次元用cos函數,這樣的線性組合意味着位置向量中蘊含了相對位置資訊。

e.g. 我愛你,現在做第三個詞“你”的位置編碼

pos + k = 3 = 1+2

3 = 1+2 --> 1*2+2*1

當變成你愛我,依然做第三個詞“我”的位置編碼

pos + k = 3 = 1+2 -->1*2+2*1

雖然都是1*2+2*1,但前面包含的單詞位置資訊不一樣了,“我愛”、“你愛”,得到的位置編碼不一樣了。

實際應用中,transformer中positional encoding效果并不理想 --> bert中使用自學習的一種方式

1.7.2 positional encoding 實作

偶數項用sin函數,奇數項用cos函數,詞向量次元一定是偶數維

輸入X的embedding和positional encoding沒有關系,比如:“我愛你”、“你愛我”,無論第一個詞是啥,它的positional encoding都是一樣的。

class PositionalEncoding(nn.Module):
    def __init__(self, dim, dropout, max_len=5000):
        super(PositionalEncoding, self).__init__()
        # 偶數項用sin函數,奇數項用cos函數,位置一定是成雙成對的
        if dim % 2 != 0:
            raise ValueError("Cannot use sin/cos positional encoding with"
                             "odd dim (got dim={:d})".format(dim))
        """
        建構位置編碼pe
        pe公式為:PE(pos, 2i/2i+1) = sin/cos(pos/10000^{2i/d_{model})
        """
        # max_len是解碼器生成句子的最長的長度,即生成初始化一個為空的postional encoding vector
        pe = torch.zeros(max_len, dim)  
        position = torch.arange(0, max_len).unsqueeze(1)
        div_term = torch.exp((torch.arange(0, dim, 2, dtype=torch.float) *
                              -(math.log(10000.0) / dim)))
        pe[:, 0::2] = torch.sin(position.float() * div_term)  # pe第一維表示多少個詞
        pe[:, 1::2] = torch.cos(position.float() * div_term)
        pe = pe.unsqueeze(1)
        self.register_buffer('pe', pe)
        self.drop_out = nn.Dropout(p=dropout)
        self.dim = dim

    def forward(self, emb, step=None):

        emb = emb * math.sqrt(self.dim)
        if step is None:
            # X的embedding vector和positional vector沒有關系
            emb = emb + self.pe[:emb.size(0)]  # 表示第幾個詞的embedding
        else:
            emb = emb + self.pe[step]
        emb = self.drop_out(emb)
        return emb
           

2. Transformer(Encoder-Decoder Architecture)

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

transformer其實就是attention的一個堆疊,分成兩部分:Encoder和Decoder。

Nx(乘)的意思是,transformer編碼器裡面又有N個小編碼器(預設N=6)。通過6個編碼器,對詞向量一步又一步的強化(增強)詳見下節

seq2seq模型,序列(編碼器encoder)到序列(解碼器decoder),比如:一句話到一句話。

  • 編碼器encoder:把輸入變成一個詞向量(Self-Attention)
  • 解碼器decoder:得到編碼器輸出的詞向量後,生成翻譯結果。

機器翻譯流程(transformer)

給定一個輸入,給出一個輸出(輸出是輸入的翻譯結果)

“我是一個學生” --> (通過transformer) I am a student

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

了解transformer,就是了解transformer裡的小的編碼器(Encoder)和小的解碼器(Decoder)。

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

FFN(feed Forward Network):前饋神經網絡,本質就是w2(σ(w1x+b1)) + b2

編碼器包括兩個子層:self-attention、Feed Forward

每一個子層的傳輸過程都會有一個(殘差網絡 + 歸一化)

2.1 Transformer 編碼器 Encoder 

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

thinking --> 得到green x1(詞向量,可以通過one-hot、word2vec得到) + positional encoding,給x1賦予位置資訊 --> 黃色x1

-->輸入到Self-Attention子層中,做注意力機制(x1、x2拼接起來的一句話做),得到z1(x1與x1、x2拼接起來的句子做了自注意力機制的詞向量,表征的仍然是thinking),也就是說z1擁有了位置屬性、句法特征、語義特征。

--> 殘差網絡Add(避免梯度消失)

“梯度消失”,self-attention裡面有很多wx+b,比如w3(w2(w1+b1)+b2)+b3,如果w1, w2, w3特别小,0.00...1,三者聯乘,w結果基本為0,x就相當于沒了 <--三階函數的梯度是二階,二階函數的梯度是一階

殘差網絡:加上x,讓x梯度求導後不會沒了。w3(w2(w1+b1)+b2)+b3 + x

--> LayerNorm歸一化

做标準化,限制區間,避免梯度爆炸(如果w1,w2,w3較大)。在資料挖掘中,如果某一特征太過離散,數值分布在幾十到幾千之間,對參數w帶來挑戰,影響模型效果。

--> 得到深粉色z1

--> Feed Forward(前面每一步走在做線性變換wx+b,線性變化的疊加永遠都是線性變化(線性變換就是在空間中放縮平移),激活函數非線性的提出是劃時代的,通過feed forward中的Relu做多次非線性變換,這樣的空間變換可以無線拟合空間中任何一種狀态了),得到r1(是thinking新的表征)

2.1.1 Encoder 總結

Encoder就是在做詞向量,這不過這個詞向量更加優秀。因為它每一步很合理,很完美,一比之下,word2vec就是渣渣! 

讓這個詞向量能夠更加準确的表示這個單詞、這句話 

?未來有一個東西專門做詞向量,而不是像transformer一樣定位在機器翻譯。

Bert

2.2 Transformer 解碼器 Decoder

編碼器 Encoder:轉換成詞向量。Encoder就是讓計算機能夠合理地認識人類世界客觀存在的一些東西。(這個詞向量不可能是100%正确的,是不确定的,隻是盡可能精确的)

解碼器會接收解碼器生成的詞向量,然後通過這個詞向量去生成翻譯結果。

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

2.2.1 為什麼Decoder需要做mask

解碼器的self-attention編碼已經生成的單詞,即masked self-attention。這是為了解決訓練階段和測試階段不比對

假如目标詞“我是一個學生”,

訓練階段:人工标注機器翻譯語料--<I'm a student, 我是一個學生>是已知的,decoder self-attention需要對“我是一個學生”做masked計算。

  • 如果不做masked,每次訓練階段,decoder都會獲得全部的資訊。
  • 如果做masked,self-attention第一次對“我”做計算;第二次對“我是”做計算,...,masked是為了訓練階段與測試階段預測未知目标詞行為保持一緻。
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

測試階段:目标翻譯詞是未知的,無法直接拿未知的翻譯結果輸入decoder

        1. 目标詞未知,假如目标詞是“我是一個老師”,self-attention第一次對“我”做計算。

        2. 第二次對“我是”做計算

        3. .....

測試階段:encoder每生成一個詞,decoder就獲得多一點

2.2.2 為什麼Encoder給予Decoders的是K、V矩陣 

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

--> Encoder-Decoder Attention中QKV不同源,交叉注意力計算

        Q來自Decoder,K=V來自Encoder,Q通過對全資訊KV進行查詢,後面經過linear次元變換和softmax機率計算,得出下一個詞。

--> Linear層映射為詞表的次元。因為線性變化wx+b,神經元個數即embedding次元

--> softmax得到最大機率的單詞

Q是查詢變量,K=V是源語句,通過部分(生成的詞)去全部(源語句)的裡面挑重點。 如果反過來,Q是源語句,KV是已經生成的詞,那麼源語句去已經生成詞裡找下一個翻譯詞的資訊,這根本是找不到的! 

解決了以前seq2seq架構的問題(lstm做編碼器,lstm做解碼器,這種方法去生成詞,每一次生成詞,都是通過C的全部資訊去生成)

2.3 Transformer 動态流程

  • 生成一個詞
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)
  •  生成全部單詞
預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

2.4 Transformer疑問解答

2.4.1 為什麼用張量tensor?

因為向量是2維,張量tensor是3維以上

2.4.2 詞向量input必須是one-hot嗎?換成word2vec可以提升模型performance嗎?

詞向量不一定必須是one-hot,可以使word2vec、ELMo、Bert等其他詞向量,但這不會改變模型效果,因為BP階段會不斷改變向量對應權重w,最終達到optimal,唯一差別在于BP時更新幅度和收斂熟讀不一樣。

e.g. 初始權重w0=5,optimal wf=10。one-hot編碼不完善,需要更新的次數多一點,而bert經過預訓練,可能需要的epoches更少,直接從7開始BP。

是以,input embedding可以随機初始一個向量,transformer會反向更新,它一定能将幫你更新到一個确切的位置。-->讓AI領域發生翻天覆地的變化。

2.4.3 transformer強大之處

transformer的核心是self-attention、multi-head attention

生成式語言模型的核心是masked multi-head attention

lstm其實也有上述特性,但是它們模型不能做得很大、沒有用self-attention;複雜的資料、大型的資料、大量的資料、沒有标簽的資料,lstm model無能為力。

transformer、bert 有強大的特征提取能力,不論是文本還是視訊!!!

2.5 pytorch 實作Transformer

自己寫transformer沒有任何實際意義!!

這裡隻是為了熟悉transformer代碼結構

實際應用請直接做調包俠!! 

extension:  python面向對象--封裝(class類、__init__初始化函數)

面向對象,即java的程式設計思想。一個資料類型抽象化建立為對象(class),這個對象擁有name和property兩種屬性,面向對象思想程式設計有三大特點:

  • 資料封裝。在于複用
  • 繼承
  • 多态

python程式設計基礎(五): 面向對象--封裝、繼承_天狼嘯月1990的部落格-CSDN部落格_自行封裝ready.py

init函數聲明初始變量和函數,

forward函數使用init變量、輸入x和輸出y

目前可以直接在forward函數中聲明變量,但這就寫死了後面應用修改太麻煩!!!

e.g. 

l1 = LayerNorm()

....

l1(128)

l1(128)

l1(128)

------------------------------------------------

l1 = LayerNorm(128)

....

l1()

l1()

l1()

2.5.1 殘差函數add、歸一化Norm

預訓練機制(2)~Attention mechanism、Transformer1. Attention Mechanism2. Transformer(Encoder-Decoder Architecture)

首先有一個norm函數

class LayerNorm(nn.Module):

    def __init__(self, feature, eps=1e-6):
        """
        param feature: self-attention的x的大小
        param eps:做平滑
        """
        super(LayerNorm, self).__init__()
        self.a_2 = nn.Parameter(torch.ones(feature))
        self.b_2 = nn.Parameter(torch.zeros(feature))
        self.eps = eps

   def forward(self, x):
        mean = x.mean(-1, keepdim=True)
        std = x.std(-1, keepdim=True)
        return self.a_2 * (x-mean)/(std+self.eps) + self.b_2
           

 然後norm裡面做殘差,會輸入(x和淡粉色z1,殘內插補點),輸出一個紫粉色的z1 

class SublayerConnection(nn.Module):
    """
    這不僅僅做了殘差,這是把殘差和layernorm一起做了
    """
    def __init__(self, size, dropout=0.1):
        super(SublayerConnection, self).__init__()
        # 第一步做 layernorm
        self.layer_norm = LayerNorm(size)
        # 第二步做 dropout,随機置0,防止過拟合
        self.dropout = nn.Dropout(p=dropout)

    def forward(self, x, sublayer):
        """
        param x: self-attention input
        param sublayer: self-attention output
        """
        return self.dropout(self.layer_norm(x + sublayer(x)))
           

2.5.2 Feed Forward layer(FFN)

layer_norm: 對X做标準化,深度學習中的正常操作。 

w2(relu(w1x+b1))+b2。如果對線性變換或線性回歸比較熟悉的話,b1和b2有時會放到w1和w2裡面,預設X多增加了[1,1,1,...]這一維。

class PositionWiseFeedForward(nn.Module):

    '''
    layer_norm: 對X做标準化
    FFN: w2(relu(w1x+b1))+b2
    '''
    def __init__(self, d_model, d_ff, dropout=0.1):
        super(PositionWiseFeedForward, self).__init__()
        self.w_1 = nn.Linear(d_model, d_ff)
        self.w_2 = nn.Linear(d_ff, d_model)
        self.layer_norm = nn.LayerNorm(d_model, eps=1e-6)
        self.dropout_1 = nn.Dropout(dropout)
        self.relu = nn.ReLU()
        self.dropout_2 = nn.Dropout(dropout)

    def forward(self, x):
        inter = self.dropout_1(self.relu(self.w_1(self.layer_norm)))
        output = self.dropout_2(self.w_2(inter))
        return output
           

2.5.3 Linear層 + Softmax層

class Generator(nn.Module):

    def __init__(self, d_model, vocab_size):
        super(Generator, self).__init__()
        self.linear = nn.Linear(d_model, vocab_size)

    def forward(self, x):
        return F.log_softmax(self.linear(x), dim=-1)
           

2.5.4 masked multi-head self-attention

隻是對multi-head self-attention的輸入做了一個masking操作

# masking multi-head attention
def pad_mask(src, r2l_trg, trg):
    if trg is not None:  # trg 是标簽y
        # 把trg丢到subsequent_mask,對它進行掩碼處理,改變它的形狀,隻能看前幾個
        trg_mask = subsequent_mask(trg.size(1)).type_as(src_image_mask.data)

# 掩碼處理 masking, 即生成一個上三角矩陣,隻給你看到生成詞的資訊
def subsequent_mask(size):
    """
    mask out subsequent positions.
    """
    attn_shape = (1, size, size)
    mask = np.triu(np.ones(attn_shape), k=1).astype('uint8')
    return (torch.from_numpy(mask) == 0).cuda()
           

2.5.5 建構編碼器 encoder

# 這裡是一層編碼器
class EncoderLayer(nn.Module):
    def __init__(self, size, attn, feed_forward, dropout=0.1):  # 執行個體化出來的attn, feed_forward層
        super(EncoderLayer, self).__init__()
        self.attn = attn
        self.feed_forward = feed_forward
        # 克隆,module複制n次。transformer編碼器encoder裡有2個殘差連接配接
        self.sublayer_connection = clones(SublayerConnection(size, dropout), 2)

    def forward(self, x, mask):
        x = self.sublayer_connection[0](x, lambda x: self.attn(x, x, x, mask))  # forward前向傳播把資料流串起來
        return self.sublayer_connection[1](x, self.feed_forward)

# transformer encoder裡面有6個編碼器encoderlayer
class Encoder(nn.Module):
    # encoder_layer = EncoderLayer()
    def __init__(self, n, encoder_layer):
        super(Encoder, self).__init__()
        self.encoder_layer = clones(encoder_layer, n)  # 克隆n次,transformer裡面是6層

    def forward(self, x, src_mask):
        for layer in self.encoder_layer:  # 編碼器前向傳播,前向傳播一次得到新的x,再放入下一層編碼器,共有6層
            x = layer(x, src_mask)
        return x
           

2.5.6 解碼器 decoder

# 首先建構一層解碼器
class DecoderLayer(nn.Module):

    def __init__(self, size, attn, feed_forward, sublayer_num, dropout=0.1):
        super(DecoderLayer, self).__init__()
        self.attn = attn  # 這樣的寫好處是,沒有寫死。可以根據傳入參數,确定是multi-head self-attention還是 masked multi-head self-attention
        self.feed_forward = feed_forward
        self.sublayer_connection = clones(SublayerConnection(size, dropout), sublayer_num)

    def forward(self, x, memory, src_mask, trg_mask, r2l_memory=None, r2l_trg_mask=None):
        x = self.sublayer_connection[0](x, lambda x: self.attn(x, x, x, trg_mask))
        x = self.sublayer_connection[1](x, lambda x: self.attn(x, memory, memory, src_mask))  # memory是編碼器輸出
        # 雙向解碼器
        # if r2l_memory is not None:
        #     x = self.sublayer_connection[-2](x, lambda x: self.attn(x, r2l_memory, r2l_memory, r2l_trg_mask))

        return self.sublayer_connection[-1](x, self.feed_forward)

# 再建構6層解碼器
class R2L_Decoder(nn.Module):  #  雙向解碼器--正向解碼器

    def __init__(self, n ,decoder_layer):
        super(R2L_Decoder, self).__init__()
        self.decoder_layer = clones(decoder_layer, n)

    def forward(self, x, memory, src_mask, r2l_trg_mask):
        for layer in self.decoder_layer:
            x = layer(x, memory, src_mask, r2l_trg_mask)
        return x

# # 雙向解碼器--反向解碼器
# class L2R_Decoder(nn.Module):
#
#     def __init__(self, n, decoder_layer):
#         super(L2R_Decoder, self).__init__()
#         self.decoder_layer = clones(decoder_layer, n)
#
#     def forward(self, x, memory, src_mask, trg_mask, r2l_memory, r2l_trg_mask):
#         for layer in self.decoder_layer:
#             x = layer(x, memory, src_mask, trg_mask, r2l_memory, r2l_trg_mask)
#         return x
           

2.5.7 整體Transformer

__init__,初始化一些小的結構layer

class ABDTransformer(nn.Module):

    def __init__(self, vocab, d_feat, d_model, d_ff, n_heads, n_layers, dropout, feature_mode,
                 device='cuda', n_heads_big=128):
        super(ABDTransformer, self).__init__()
        self.vocab = vocab  # 詞表大小
        self.device = device  # 是否使用GPU
        self.feature_mode = feature_mode  # 多模态

        c = copy.deepcopy()

        # attn_no_heads = MultiHeadAttention(0, d_model, dropout)  # 無頭multi-head attention = 一層注意力

        attn = MultiHeadAttention(n_heads, d_model, dropout)

        # attn_big = MultiHeadAttention(n_heads_big, d_model, dropout)  # 大頭 multi-head attention

        feed_forward = PositionWiseFeedForward(d_model, d_ff)

        if feature_mode == 'one':
            self.src_embed = FeatEmbedding(d_feat, d_model, dropout)  # 視訊嵌入
        elif feature_mode == 'two':
            pass
        elif feature_mode == 'three':
            pass
        elif feature_mode == 'four':
            pass

        self.trg_embed = TextEmbedding(vocab.n_vocabs, d_model)
        self.pos_embed = PositionalEncoding(d_model, dropout)

        # 初始化編碼器
        self.encoder = Encoder(n_layers, EncoderLayer(d_model, c(attn), c(feed_forward), dropout))
        #  初始化一個正向解碼器
        self.l2r_decoder = L2R_Decoder(n_layers, DecoderLayer(d_model, c(attn), c(feed_forward),
                                                              sublayer_num=4, dropout=dropout))
        # 初始化一個反向解碼器
        # self.r2l_decoder = R2L_Decoder(n_layers, DecoderLayer(d_model, c(attn), c(feed_forward),
        #                                                       sublayer_num=4, dropout=dropout))
        # 生成器generator,就是linear + softmax layer
        self.generator = Generator(d_model, vocab.n_vocabs)

    # 單模态encode
    def encode(self, src, src_mask, feature_mode_two=False):
        x1 = self.image_src_embed(src[0])
        x1 = self.pos_embed(x1)
        x1 = self.encoder_big(x1, src_mask[0])  # 将 X embedding + positional encoding 輸入編碼器

    # 正向解碼器 decode
    def l2r_decode(self, trg, memory, src_mask, trg_mask, r2l_memory, r2l_trg_mask):
        x = self.trg_embed(x)  # 解碼器輸入
        x = self.pos_embed(x)
        return self.l2r_decoder(x, memory, src_mask, trg_mask, r2l_memory, r2l_trg_mask)

    # # 反向解碼器
    # def r2l_decode(self, trg, memory, src_mask, trg_mask, r2l_memory, r2l_trg_mask):
    #     pass

    # 建構forward函數
    def forward(self, src, r2l_trg, trg, mask):
        src_mask, r2l_pad_mask, r2l_trg_mask, trg_mask = mask

        if self.feature_mode == 'two' or 'three' or 'four':

            encoding_outputs = self.encode(src, enc_src_mask)

            l2r_outputs = self.l2r_encode(trg, encoding_outputs, dec_src_mask[1], trg_mask, r2l_outputs, r2l_pad_mask)
        else:
            raise "沒有輸出"

        # r2l_pred = self.generator(r2l_outputs)
        l2r_pred = self.generator(l2r_outputs)
           

2.6 Huggingface transformers package

2.6.1 background

Transformer,由于其優越的性能表現,在工業界使用的越來越廣泛,同時,配合遷移學習理論,越來越多的Transformer預訓練模型和源碼庫逐漸開源,Huggingface就是其中做的最為出色的一家機構。Huggingface是一家在NLP社群做出傑出貢獻的紐約創業公司,其所提供的大量預訓練模型和代碼等資源被廣泛的應用于學術研究當中。Huggingface所開源的Transformers package提供了數以千計針對于各種任務的預訓練模型模型,開發者可以根據自身的需要,選擇模型進行訓練或微調,也可閱讀api文檔和源碼, 快速開發新模型。

pip install transformers
           

2.6.2 AutoClass介紹

problem:transformers package中有上百個算法,有bert model對應的BertModel類,有bart model對應的BartModel,當我們使用對應的pre-trained model是,都必須先找到對應類名,然後進行執行個體化,非常麻煩!

transformers庫提供了AutoClass進階對象,隻需要知道預訓練模型的名稱name或所在目錄dir,就可以快速便捷create pre-trained model。

AutoClass類隻能通過from_pretrained()方法建立模型。比如:

from transformers import AutoModel

model = AutoModel.from_pretrained("./models/bert-base-chinese")
print(type(model))
           

2.6.3 分詞器對象 AutoTokenizer

Huggingface的transformers庫中提供了進階API對象--AutoTokenizer,用來加載預訓練模型的分詞器。

Note that: 預訓練模型分詞器和模型model是配套使用的!

比如:我們使用的預訓練模型時"bert-base-chinese",那麼,預先加載的分詞器也必須使用"bert-base-chinese"

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
sentence = "床前明月光"
tokenizer(sentence)
-----------------------------------------------------
{'input_ids': [101, 2414, 1184, 3209, 3299, 1045, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1]}
           

tokenizer内部還提供其他豐富的參數用于實作多種功能:

tokenizer(
    ["床前明月光", "床前明月光,疑是地上霜。"],
    padding=True,   # 長度不足max_length時是否進行填充
    truncation=True,  # 長度超過max_length時是否進行截斷
    max_length=10,
    return_tensors="pt",  #指定傳回資料類型,pt:pytorch的張量,tf:TensorFlow的張量
)      

2.6.4 配置檔案 AutoConfig

problem: 每一類算法模型的架構結構不一樣、超參數配置也不一樣。如果每次加載預訓練模型,都要使用者手動找到對應的配置項,就很麻煩!

是以AutoClass提供了專門的配置管理入口--AutoConfig。

from transformers import AutoConfig
config = AutoConfig.from_pretrained("./models/bert-base-chinese")
           

通過config執行個體,我們可以對配置項進行修改。

config.num_hidden_layers=5
print(config)
           

修改之後的參數,如果後續需要再次使用,可以儲存到本地。

config.save_pretrained("./models/bert-base-chinese")
           

2.6.5 建立預訓練模型 AutoModel

通過AutoModel類,建立pre-trained model最簡單的方法就是直接傳入模型名稱或本地路徑。

from transformers import AutoModel
model = AutoModel.from_pretrained("./models/bert-base-chinese")

# 也可以在加載模型時,指定配置項執行個體
model = AutoModel.from_pretrained("./models/bert-base-chinese", config=config)
           

2.7 “調包俠”實作transformer、bert

2.7.1 “調包俠”實作transformer

2.7.2 “調包俠”實作bert

# -----------------------------------------Bert embeddings-----------------
from transformers import AutoTokenizer, AutoModel
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
bert_model = AutoModel.from_pretrained('bert-base-uncased')

df['bert_embeddings'] = df.text.apply(lambda x: bert_model(**tokenizer(x, return_tensors='pt'))[0][0][0][:128])

df_bert = df[['event_id','bert_embeddings']]
           

2.8 bert project application

https://www.cnblogs.com/chenhuabin/p/16997607.html

  • 自定義資料集
  • 自定義網絡
  • 訓練 training
  • run model

2.9 cuda GPU application

CUDA,Compute Unified Device Architecture 

本質:CUDA是用于深度學習模型,在cpu和GPU之間交換資料的并行計算架構。

在pytorch中,即使是有GPU的機器,它也不會自動使用GPU,而是需要在程式中顯示指定。調用model.cuda(),可以将模型加載到GPU上去。這種方法不被提倡,而建議使用model.to(device)的方式,這樣可以顯示指定需要使用的計算資源,特别是有多個GPU的情況下。

2.9.1 選擇GPU顯示卡

device = set_device(cuda_index=1)  # 選擇GPU
           

2.9.2 上傳模型model

class CustomBERTModel(nn.Module):
    def __init__(self, n_classes):
        super(CustomBERTModel, self).__init__()
        self.bert = AutoModel.from_pretrained("./models/bert-base-chinese")
        self.drop = nn.Dropout(p=0.3)
        self.out = nn.Linear(self.bert.config.hidden_size, n_classes)
    def forward(self, input_ids, attention_mask):
        _, pooled_output = self.bert(
            input_ids=input_ids,
            attention_mask=attention_mask,
            return_dict = False
        )
        output = self.drop(pooled_output) # dropout
        return self.out(output)

n_classes = len(label2id)
model = CustomBERTModel(n_classes)
model = model.to(device)
           

2.9.3 訓練階段training--上傳資料data

  • input_ids、attention_mask、targets上傳到GPU
  • model已經上傳過去了,直接在GPU上運作得到outputs
def train_epoch(model, data_loader,loss_fn,optimizer,device,scheduler, n_examples):
    model.train()
    losses = []
    correct_predictions = 0
    
    for i, d in bar(data_loader):
        input_ids = d["input_ids"].to(device)
        attention_mask = d["attention_mask"].to(device)
        targets = d["labels"].to(device)
        outputs = model(
            input_ids=input_ids,
            attention_mask=attention_mask
        )
        _, preds = torch.max(outputs, dim=1)
        loss = loss_fn(outputs, targets)
        correct_predictions += torch.sum(preds == targets)
        losses.append(loss.item())
        loss.backward()
        nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        optimizer.step()
        scheduler.step()
        optimizer.zero_grad()
    return correct_predictions.double() / n_examples, np.mean(losses)
           

2.9.4 驗證階段eval--上傳資料data

def eval_model(model, data_loader, loss_fn, device, n_examples):
    model.eval() # 驗證預測模式
    losses = []
    correct_predictions = 0

    with torch.no_grad():
        for d in data_loader:
            input_ids = d["input_ids"].to(device)
            attention_mask = d["attention_mask"].to(device)
            targets = d["labels"].to(device)

            outputs = model(
                input_ids=input_ids,
                attention_mask=attention_mask
            )
            _, preds = torch.max(outputs, dim=1)

            loss = loss_fn(outputs, targets)

            correct_predictions += torch.sum(preds == targets)
            losses.append(loss.item())

    return correct_predictions.double() / n_examples, np.mean(losses)
           

2.9.5  run model

  • 定義scheduler

        scheduler.step()是pytorch用來更新優化器學習率的,一般按照epoch為機關進行更換。

  • 定義損失函數,上傳GPU
EPOCHS = 5 # 訓練輪數

optimizer = AdamW(model.parameters(), lr=2e-5)
total_steps = len(train_dataloader) * EPOCHS

scheduler = get_linear_schedule_with_warmup(
  optimizer,
  num_warmup_steps=0,
  num_training_steps=total_steps
)

loss_fn = nn.CrossEntropyLoss().to(device)
           

參考

推薦:  

  • 理論篇:10 Transformer 之 Self-Attention(自注意力機制)_哔哩哔哩_bilibili

小白友好,簡單直白,邏輯性強,但有好好幾個點講錯了,-_-||,比如多頭和解碼器QKV部分

  • 實戰篇:03 Transformer 中的多頭注意力(Multi-Head Attention)Pytorch代碼實作_哔哩哔哩_bilibili
  • 英文教程:Tutorial 6: Transformers and Multi-Head Attention — UvA DL Notebooks v1.2 documentation
  • Huggingface:https://www.cnblogs.com/chenhuabin/p/16997607.html

66 使用注意力機制的seq2seq【動手學深度學習v2】_哔哩哔哩_bilibili

(我不推薦李沐大神的attention課程,仁者見仁智者見智。專家是指能把複雜數學概念直白講解到老農民都能聽懂的程度,雖然李沐已經簡化了數學公式,還是對小白不友好;認真用心程度低,雖然沐神是卡耐基的博士,但cs高度不代表課件用心投入程度高,即便是沐神,認真投入度低,事情做出來也不好;依托RNN講解self-attention,不了解機器翻譯seq-2-seq,聽attention雲裡霧裡的。)

繼續閱讀