零基礎(chǔ)入門深度學(xué)習(xí)(9) - Transformer (2/3)

時隔八年,再次提筆續(xù)寫這個系列。毫無疑問,智能時代已經(jīng)到來,AGI(通用人工智能)似乎也不再是遙不可及的夢想。大模型,作為目前最接近實現(xiàn)AGI的技術(shù),甚至已經(jīng)可以寫代碼了!這對于程序員真是數(shù)十年未有之大變局,因此學(xué)好、用好大模型,就將一步跨過JavaScript、Java、C++直接走到鄙視鏈的頂端,用自然語言編程,成為超級程序員。希望這個系列幫助你快速從零基礎(chǔ)達到真入門級水平,理解大模型才能用好大模型。零基礎(chǔ)意味著你不需要太多的數(shù)學(xué)知識,只要會寫程序就行了。雖然文中會有很多公式你也許看不懂,但同時也會有更多的代碼,程序員的你一定能看懂的(我周圍是一群狂熱的Clean Code程序員,所以我寫的代碼也不會很差)。

文章列表

零基礎(chǔ)入門深度學(xué)習(xí)(1) - 感知器
零基礎(chǔ)入門深度學(xué)習(xí)(2) - 線性單元和梯度下降
零基礎(chǔ)入門深度學(xué)習(xí)(3) - 神經(jīng)網(wǎng)絡(luò)和反向傳播算法
零基礎(chǔ)入門深度學(xué)習(xí)(4) - 卷積神經(jīng)網(wǎng)絡(luò)
零基礎(chǔ)入門深度學(xué)習(xí)(5) - 循環(huán)神經(jīng)網(wǎng)絡(luò)
零基礎(chǔ)入門深度學(xué)習(xí)(6) - 長短時記憶網(wǎng)絡(luò)(LSTM)
零基礎(chǔ)入門深度學(xué)習(xí)(7) - 遞歸神經(jīng)網(wǎng)絡(luò)
零基礎(chǔ)入門深度學(xué)習(xí)(8) - Transformer(1/3)
零基礎(chǔ)入門深度學(xué)習(xí)(9) - Transformer(2/3)
零基礎(chǔ)入門深度學(xué)習(xí)(10) - Transformer(3/3)

往期回顧

在前面的文章中,我們介紹了全連接神經(jīng)網(wǎng)絡(luò)、卷積神經(jīng)網(wǎng)絡(luò)、循環(huán)神經(jīng)網(wǎng)絡(luò)等,可以它們看做是不同結(jié)構(gòu)的神經(jīng)網(wǎng)絡(luò)。本文將繼續(xù)從結(jié)構(gòu)這個角度出發(fā),介紹一種新的、極其重要的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu):Transformer。從時間來看,它也不是那么新,畢竟是2017年出現(xiàn)的,到現(xiàn)在(2025年)也將近十年了。然而,沒有一個更新的網(wǎng)絡(luò)結(jié)構(gòu)比它還成功,也說明其結(jié)構(gòu)設(shè)計是極為優(yōu)秀的。Transformer出現(xiàn)后,用了5年左右的時間,逐漸取代卷積神經(jīng)網(wǎng)絡(luò)成為當(dāng)紅一哥,又用另外5年開辟了一個全新的時代:大模型時代??梢哉f,目前所有的大模型,都是以Transformer結(jié)構(gòu)為基礎(chǔ)的各種變體。如果您剛剛開始學(xué)習(xí)AI,可以嘗試快速跳過Transformer之前的部分,把主要的精力放在Transformer的學(xué)習(xí)上。

由于Transformer內(nèi)容較多,為了保持一個良好的節(jié)奏,我將整個內(nèi)容分成三個部分。第一部分比較簡單,是一些基礎(chǔ)知識的介紹;第二部分重點講述transformer的注意力機制,這也是它的核心部分;第三部分講述模型的整體結(jié)構(gòu),以及訓(xùn)練和推理。

自注意力機制

集中注意力是人類智能中一個非常重要的能力。人類可以從海量的輸入信息中,抓住重點的少量信息,忽略掉無用的大量冗余信息,高效完成信息處理,從而達到很高的智能。那么,模型是否可以建立和人類類似的注意力機制,并且從中獲益呢?答案是肯定的。

注意力機制的核心,就是提升關(guān)鍵信息的權(quán)重,降低非關(guān)鍵信息的權(quán)重,通過給每個信息賦予不同的注意力權(quán)重,達到聚焦關(guān)鍵信息,忽略非關(guān)鍵信息的效果。下圖展示了在視覺處理中,注意力機制的原理:

在圖像分類的任務(wù)中,重點是對動物進行分類的準確性,與之最相關(guān)的圖像部分被注意力機制賦予了更高的權(quán)重,對分類結(jié)果影響舉足輕重;圖像的背景部分則被注意力機制賦予很低的權(quán)重,以至于對最終的分類結(jié)果影響極小。這種注意力機制有效的提升了模型分類準確率。

自然語言理解任務(wù)則更為復(fù)雜一些,因為同樣的詞在不同的上下文中,其含義是不一樣的,例如下面兩句話:

The animal didn't cross the street because it was too tired.
The animal didn't cross the street because it was too wide.

顯然,兩句話中僅僅最后一個單詞不同,但導(dǎo)致了代詞it 指代的含義不同。在第一句話中,it 指代 animal,而第二句話中,it 指代 street。從注意力機制角度看,顯然,第一句話中的it應(yīng)該更加注意road,而第二句話中的it應(yīng)該更注意cat。我們希望模型的注意力機制能夠達到下面的效果:

image.png

那么,我們應(yīng)該設(shè)計什么樣的模型結(jié)構(gòu),使得每個token可以根據(jù)上下文的不同,為每個Token分配不同的注意力權(quán)重?這就是Transformer結(jié)構(gòu)核心:自注意力機制(Self-Attention)。其核心是一個序列中的任意兩個Token之間都要計算注意力權(quán)重,也就是每個Token都要注意自身以及序列中的其它Token,因而叫做自注意力機制。

具體該如何計算呢?為了不失普遍性,我們任意選取序列中的兩個Token: ij ,詳細介紹他們之間如何計算注意力權(quán)重。只要把這個說清楚了,整個序列的注意力計算也就容易理解了。

第一步,需要為每個Token \mathbf{x} 線性投影出三個向量,分別命名為 \mathbf{q}\mathbf{k}\mathbf{v} 。這三個向量的維度可以和詞向量的維度不同, \mathbf{v} 的維度可以和 \mathbf{q},\mathbf{k} 不同。例如,可以設(shè)置詞向量 \mathbf{x} 維度是 D_x=512,\mathbf{q}\mathbf{k} 的維度是 D_{qk}=128 , \mathbf{v} 的維度是 D_v=256 。當(dāng)然, \mathbf{x} , \mathbf{q} , \mathbf{k}\mathbf{v} 都設(shè)置成同樣的維度也沒問題,很多模型也都是這樣做的。計算 \mathbf{q} , \mathbf{k} , \mathbf{v} 很簡單,只要經(jīng)過一次線性映射:
\mathbf{q}=W_q\mathbf{x}\\ \mathbf{k}=W_k\mathbf{x}\\ \mathbf{v}=W_v\mathbf{x}
其中,W_q,W_k,W_v 分別是三個權(quán)重矩陣,是模型可學(xué)習(xí)參數(shù)的一部分,在模型訓(xùn)練過程中尋找這些參數(shù)的最優(yōu)值。顯然,W_k,W_v \in \mathbb{R}^{D_{kv}\times D_x},W_v \in \mathbb{R}^{D_v\times D_x}。注意,這里我們按照慣例,用 \mathbf{x} 表示列向量,用 \mathbf{x}^\mathsf{T} 表示行向量。

q, k, v分別是Query、Key、Value的縮寫,它們是對注意力機制計算過程的抽象表達,有助于我們理解和實現(xiàn)。

第二步,計算兩個Token之間的注意力權(quán)重。我們以 ij 為例,這里需要注意的是 ij 的注意力和 ji 注意力是不同的。其實這很容易理解,人與人之間的注意力不也是如此么,黎叔說的好:我本將心向明月,奈何明月照溝渠,大概就是這么一回事。計算方法很簡單:
a_{ij}=\mathbf{q}_i^T\mathbf{k}_j\\ a_{ji}=\mathbf{q}_j^T\mathbf{k}_i

當(dāng)qk兩個向量的維度較大時,點積的數(shù)值幅度會顯著增大,會導(dǎo)致訓(xùn)練時梯度消失。為緩解這一效應(yīng),需要對點積的結(jié)果進行縮放。Transformer采用的縮放因子是 \frac{1}{\sqrt{D_{qk}}} ,其中 D_{qk} 是qk向量的維度。
a_{ij}=\frac{\mathbf{q}_i^T\mathbf{k}_j}{\sqrt{D_{qk}}}\\ a_{ji}=\frac{\mathbf{q}_j^T\mathbf{k}_i}{\sqrt{D_{qk}}}

假設(shè)整個序列長度為 S,那么,所有Token之間兩兩計算注意力權(quán)重后,我們必然會得到一個注意力矩陣 A \in S \times S。即:
A=\frac{\mathbf{Q}^T\mathbf{K}}{\sqrt{D_{qk}}}

其中, Q 是序列中所有Token的 \mathbf{q} 向量組成的矩陣, K 是序列中所有Token的 \mathbf{k} 向量組成的矩陣。 Q \in \mathbb{R}^{D_{kv} \times S},Q \in \mathbb{R}^{D_{kv} \times S}A \in \mathbb{R}^{S \times S}。

第三步,利用Softmax對注意力權(quán)重歸一化。前面計算出來的注意力權(quán)重a是未歸一化的,而我們希望歸一化,即所有注意力權(quán)重都應(yīng)該是非負數(shù),且加在一起應(yīng)該等于1,這樣注意力權(quán)重就等同于概率。例如,對于長度為 S 序列,任意Token i會計算得 S 個注意力權(quán)重,即它對序列中 S 個Token(包含對其自身)的注意力。我們希望這 S 個注意力權(quán)重都是非負值,且加在一起的和正好是1。做法也很簡單,只要對權(quán)重矩陣 A 按行計算Softmax即可:
a_i=Softmax(A[i])
其中,a_i 表示權(quán)重矩陣A的第i行,A[i] 表示從矩陣 A 中取出第 i 行的分塊。

第四步
對序列中的每個Token,按照第三步計算的注意力權(quán)重,對序列中所有的v向量計算加權(quán)和。例如,對于Token i ,在上一步計算出 S 個注意力權(quán)重(保存在A矩陣的第 i 行),用它們來計算所有 S\mathbf{v} 向量的加權(quán)和。
o_i^\mathsf{T}=A[i]V^\mathsf{T}

其中V \in \mathbb{R}^{D_v\times S}是所有v向量組成的矩陣。

我們可以把整個四個步驟用矩陣計算來表示:
\begin{align} Attention(Q,K,V)&=Softmax(\frac{\mathbf{Q}^\mathsf{T}\mathbf{K}}{\sqrt{D_{qk}}})V\\ Q&=W_qX\\ K&=W_kX\\ V&=W_vX \end{align}

以上就是自注意力機制,雖然看著復(fù)雜,其實也不簡單,但相信大家都能理解。如果還不理解,可以再參考下面的示意圖。在示意圖中,我們設(shè)置序列長度 S=3,詞向量維度 D_x=4,qk 向量維度 D_{qk}=2,v 向量維度 D_v=3。

下面自注意力機制的示例代碼:

import numpy as np
import math

S=3     # 序列長度
Dq=2    # q向量維度
Dk=2    # k向量維度
Dv=3    # v向量維度
Dx=4    # 詞向量維度


def softmax(x, axis=None):
    """數(shù)值穩(wěn)定的 Softmax 實現(xiàn)"""
    e_x = np.exp(x - np.max(x, axis=axis, keepdims=True))  # 減去最大值避免溢出
    return e_x / e_x.sum(axis=axis, keepdims=True)


# 初始化輸入詞向量矩陣,以及Wq、Wk、Wv三個權(quán)重矩陣
X=np.array([[0.312, 0.395, -1.343, 2.102],
       [1.232, 2.567, -0.123, 0.838],
       [2.134, -3.123, 0.123, -0.271]])
Wq=np.random.rand(Dx, Dq)
Wk=np.random.rand(Dx, Dk)
Wv=np.random.rand(Dx, Dv)


def self_attention(X):
    # 計算序列X的self_attention
    # 第一步,計算Q、K、V
    Q=np.dot(X,Wq)
    K=np.dot(X,Wk)
    V=np.dot(X,Wv)
    
    # 第二步,計算注意力權(quán)重
    A=np.dot(Q,K.T)/math.sqrt(Dq)
    
    # 第三步,歸一化注意力權(quán)重
    AA=softmax(A, axis=1)
    
    # 第四步,計算v向量加權(quán)和
    O=np.dot(AA, V)
    
    return O

多頭注意力

研究發(fā)現(xiàn),如果僅僅為一個Token線性投影出一份 D_{qk}\mathbf{q}、\mathbf{k}、\mathbf{v} 向量,那么模型很難關(guān)注來自不同語義子空間的信息,因為多個語義子空間的信息被平均化了。解決方法就是多頭注意力機制,即為每個Token投影出 h 份不同 \mathbf{q}、\mathbf{k}、\mathbf{v} 向量,每份 \mathbf{q}\mathbf{k}、\mathbf{v} 向量都執(zhí)行上一章講述的自注意力函數(shù),并將最終的 h 份輸出拼接在一起,再次進行投影,從而得到最終的值,如下圖所示。

image.png

為了不增加計算量和模型參數(shù)量,在多頭注意力機制中,通常設(shè)置每個注意力頭的維度是原先的 1/h,即 D_{hqk}=D_{qk}/h,D_{hv}=D_{v}/h

計算公式為:
\begin{align} MultiHead(Q,K,V)&=Concat(head_1,...,head_h)W_o\\ head_i&=Attention(Q_i,K_i,V_i)\\ Q_i&=XW_q^i\\ K_i&=XW_k^i\\ V_i&=XW_v^i \end{align}

其中 W_q^i \in \mathbb{R}^{D_{hqk}\times D_x},W_k^i \in \mathbb{R}^{D_{hqk}\times D_x}W_v^i \in \mathbb{R}^{D_{hv}\times D_x},W_o \in \mathbb{R}^{D_v\times D_x}。注意,這里設(shè)置最終的輸出維度為 D_x ,和詞向量的維度一致。

因果注意力

文本處理任務(wù)通??梢苑殖?strong>文本理解和文本生成兩大類,為了便于解釋,讀者可以簡單將其類比為人類的,它們對注意力機制的要求是不同的。對于文本理解任務(wù),可以從前到后、從后到前兩個方向?qū)ξ谋具M行全面理解,相當(dāng)于我們在一篇文章時,可以翻來覆去的正著讀、倒著讀,這樣可以加深對文章的理解(可能您閱讀本文時就需要這樣)。對于文本生成任務(wù),文本是一個詞接著一個詞按順序生成,在生成當(dāng)前這個詞的時候,只能注意到它前面已經(jīng)生成的詞,因為后面的字還沒有被生成。相當(dāng)于我們在寫一篇文章時,當(dāng)前落筆更多的是參考之前寫下的內(nèi)容,因為后面容還沒有被寫出來,對當(dāng)前要寫的具體內(nèi)容影響極?。赡苤挥幸粋€大概的思路)。這樣,我們就需要兩種不同的注意力機制來對應(yīng)上述兩個不同的過程。對于文本理解,注意力機制設(shè)計為雙向的,即一個Token既可以注意序列中之前的Token,也可以注意后續(xù)的Token。對于文本生成,注意力機制設(shè)計為單向的,即一個Token既只能注意序列中之前的Token,不能注意到后續(xù)的Token。這種單向的注意力機制,也叫做因果注意力機制。取這個名字,可能是提醒我們不能倒果為因吧。

上一節(jié)我們實現(xiàn)的是雙向注意力,這一節(jié)我們介紹單向注意力的實現(xiàn)方法。單向注意力機制可以用過掩碼實現(xiàn),設(shè)輸入序列長度為 S,掩碼矩陣 M \in \mathbb{R}^{S \times S} 的元素定義為:
M_{ij} = \begin{cases} 0, & \text{if } i \geq j \quad (\text{允許當(dāng)前和歷史位置交互}) \\ -\infty, & \text{if } i < j \quad (\text{禁止未來位置交互}) \end{cases}
其中:

  • i 表示 \mathbf{q} 向量的位置。
  • j 表示 \mathbf{k} 向量的位置。

掩碼矩陣 M 實際上是一個上三角矩陣,其具體形式為:
M = \begin{bmatrix} 0 & -\infty & -\infty & -\infty \\ 0 & 0 & -\infty & -\infty \\ 0 & 0 & 0 & -\infty \\ 0 & 0 & 0 & 0 \end{bmatrix}

在計算注意力權(quán)重時,掩碼通過加法融入注意力矩陣:
\text{Masked}(A) = A + M

經(jīng)過掩碼后,Softmax僅對有效位置( i\geq j )歸一化。因果注意力計算公式為:
\text{CausalAttention}(Q, K, V) = \text{Softmax}\left( \frac{QK^T}{\sqrt{d_k}} + M \right) V

以下是實例代碼:

import numpy as np

def causal_attention(Q, K, V):
    """
    實現(xiàn)Transformer Decoder的因果注意力機制
    
    參數(shù):
    query (np.ndarray): 查詢矩陣,形狀為 [S, dq]
    key (np.ndarray): 鍵矩陣,形狀為 [S, dk]
    value (np.ndarray): 值矩陣,形狀為 [S, dv]
    
    返回:
    np.ndarray: 注意力輸出,形狀為 [S, dv]
    """
    # 獲取維度信息
    d_k = query.shape[1]
    seq_len = query.shape[0]
    
    # 生成因果掩碼矩陣 M
    mask = np.triu(np.ones((seq_len, seq_len)), k=1)
    mask = np.where(mask == 0, 0.0, -1e9)  # 無效位置設(shè)為極大負數(shù)
    
    # 計算原始注意力矩陣
    attn = np.dot(query, key.T) / np.sqrt(d_k)
    
    # 應(yīng)用掩碼
    masked_attn = attn + mask
    
    # Softmax歸一化
    attention_weights = np.exp(masked_attn) / np.sum(np.exp(masked_attn), axis=1, keepdims=True)
    
    # 計算加權(quán)和
    output = np.dot(attention_weights, value)
    
    return output

按位置前饋網(wǎng)絡(luò)

自注意力機制可以看做Token與Token之間的信息混合,那么,Token內(nèi)部各個維度(通道)也可以做信息混合,類似卷積神經(jīng)網(wǎng)絡(luò)中卷積核的大小為1時起到的效果。

該部分由兩個全連接層組成,中間采用ReLU激活函數(shù)鏈接,如下圖所示:
![][5]

計算公式為:

\text{FFN}(x) = \max(0, xW_1+b_1)W_2+b_2

其中,W_1、b_1、W_2、b_2 都是模型的可學(xué)習(xí)參數(shù)。令 W_i 表示中間維度,則W_1 \in \mathbb{R}^{D_x \times D_i}b_1 \in \mathbb{R}^{D_i},W_2 \in \mathbb{R}^{D_i \times D_x}b_2 \in \mathbb{R}^{D_x}。中間維度通常設(shè)置為輸入維度的4倍,即 D_i = 4D_x。

以下是示例代碼:

import numpy as np

# 維度定義
S = 3          # 序列長度
Dx = 4         # 輸入維度
Di = 4 * Dx    # 中間維度

# 初始化權(quán)重參數(shù)
W1 = np.random.randn(Dx, Di) * 0.01  # 第一隱藏層權(quán)重
b1 = np.zeros(Di)                          # 第一隱藏層偏置
W2 = np.random.randn(Di, Dx) * 0.01   # 輸出層權(quán)重
b2 = np.zeros(Dx)                       # 輸出層偏置


def FFN(x):
    """
    前向傳播實現(xiàn)位置前饋網(wǎng)絡(luò)
    輸入形狀:(S, Dx)
    輸出形狀:(S, Dx)
    """
    # 將二維輸入轉(zhuǎn)換為一維 (S, Dx)
    x_flat = x.reshape(-1, x.shape[-1])
    
    # 計算第一個線性變換 + ReLU激活
    h = np.maximum(0, np.dot(x_flat, W1) + b1)
    
    # 計算第二個線性變換
    output_flat = np.dot(h, W2) + b2
    
    # 恢復(fù)原始三維形狀
    output = output_flat.reshape(S, -1)
    
    return output

小節(jié)

本文介紹了Transformer的核心:注意力機制。Transformer通過注意力機制實現(xiàn)了Token間信息混合,又通過按位置前饋網(wǎng)絡(luò)實現(xiàn)了Token內(nèi)不同通道信息的融合,從而為模型提供了豐富的表達能力。至此,Transformer模型結(jié)構(gòu)的關(guān)鍵元素都已經(jīng)介紹完了。下一篇文章,我們將介紹Transformer模型及其訓(xùn)練和推理方法,看看是如何將這些要素整合為一個強大的模型。

參考資料

  1. Attenion Is All You Need

相關(guān)文章

零基礎(chǔ)入門深度學(xué)習(xí)(8) - Transformer (1/3)
零基礎(chǔ)入門深度學(xué)習(xí)(9) - Transformer (2/3)
零基礎(chǔ)入門深度學(xué)習(xí)(10) - Transformer (3/3)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容