Transformer圖解

前言

transformer是目前NLP甚至是整個深度學習領域不能不提到的框架,同時大部分LLM也是使用其進行訓練生成模型,所以transformer幾乎是目前每一個機器人開發者或者人工智能開發者不能越過的一個框架。接下來本文將從頂層往下去一步步掀開transformer的面紗。

transformer概述

Transformer模型來自論文Attention Is All You Need。

在論文中最初是為了提高機器翻譯的效率,它使用了Self-Attention機制和Position Encoding去替代RNN。后來大家發現Self-Attention的效果很好,并且在其它的地方也可以使用Transformer模型。并引出后面的BERT和GPT系列。

大家一般看到的transformer框架如下圖所示:

transformer模型概覽

首先把模型看成一個黑盒,如下圖所示,對于機器翻譯來說,它的輸入是源語言(法語)的句子,輸出是目標語言(英語)的句子。

把黑盒子稍微打開一點,Transformer(或者任何的NMT系統)可以分成Encoder和Decoder兩個部分,如下圖所示。

再展開一點,Encoder由很多結構一樣的Encoder堆疊而成,Decoder也是一樣。如下圖所示。

每一個Encoder的輸入是下一層Encoder輸出,最底層Encoder的輸入是原始的輸入(法語句子);Decoder也是類似,但是最后一層Encoder的輸出會輸入給每一個Decoder層,這是Attention機制的要求。

每一層的Encoder都是相同的結構,它由一個Self-Attention層和一個前饋網絡(全連接網絡)組成,如下圖所示。

每一層的Decoder也是相同的結構,它除了Self-Attention層和全連接層之外還多了一個Attention層,這個Attention層使得Decoder在解碼時會考慮最后一層Encoder所有時刻的輸出。它的結構如下圖所示。

transformer流程串聯

transformer的串流需要tensor的加入,輸入的句子需要通過Embedding把它變成一個連續稠密的向量,如下圖所示。

Embedding之后的序列會輸入Encoder,首先經過Self-Attention層然后再經過全連接層

我們在計算𝑧𝑖時需要依賴所有時刻的輸入𝑥1,…,𝑥𝑛,這是可以用矩陣運算一下子把所有的𝑧𝑖計算出來的。而全連接網絡的計算則完全是獨立的,計算i時刻的輸出只需要輸入𝑧𝑖就足夠了,因此很容易并行計算。下圖更加明確的表達了這一點。圖中Self-Attention層是一個大的方框,表示它的輸入是所有的𝑥1,…,𝑥𝑛,輸出是𝑧1,…,𝑧𝑛。而全連接層每個時刻是一個方框(但不同時刻的參數是共享的),表示計算𝑟𝑖只需要𝑧𝑖。此外,前一層的輸出𝑟1,…,𝑟𝑛直接輸入到下一層。

Self-Attention介紹

比如我們要翻譯如下句子”The animal didn’t cross the street because it was too tired”(這個動物無法穿越馬路,因為它太累了)。這里的it到底指代什么呢,是animal還是street?要知道具體的指代,我們需要在理解it的時候同時關注所有的單詞,重點是animal、street和tired,然后根據知識(常識)我們知道只有animal才能tired,而street是不能tired的。Self-Attention用Encoder在編碼一個詞的時候會考慮句子中所有其它的詞,從而確定怎么編碼當前詞。如果把tired換成narrow,那么it就指代的是street了。

下圖是模型的最上一層Encoder的Attention可視化圖。這是tensor2tensor這個工具輸出的內容。我們可以看到,在編碼it的時候有一個Attention Head(后面會講到)注意到了Animal,因此編碼后的it有Animal的語義。

下面我們詳細的介紹Self-Attention是怎么計算的,首先介紹向量的形式逐個時刻計算,這便于理解,接下來我們把它寫出矩陣的形式一次計算所有時刻的結果。

對于輸入的每一個向量(第一層是詞的Embedding,其它層是前一層的輸出),我們首先需要生成3個新的向量Q、K和V,分別代表查詢(Query)向量、Key向量和Value向量。Q表示為了編碼當前詞,需要去注意(attend to)其它(其實也包括它自己)的詞,我們需要有一個查詢向量。而Key向量可以認為是這個詞的關鍵的用于被檢索的信息,而Value向量是真正的內容。

具體的計算過程如下圖所示。比如圖中的輸入是兩個詞”thinking”和”machines”,我們對它們進行Embedding(這是第一層,如果是后面的層,直接輸入就是向量了),得到向量𝑥1,𝑥2。接著我們用3個矩陣分別對它們進行變換,得到向量𝑞1,𝑘1,𝑣1和𝑞2,𝑘2,𝑣2。比如𝑞1=𝑥1𝑊𝑄,圖中𝑥1的shape是1x4,𝑊𝑄是4x3,得到的𝑞1是1x3。其它的計算也是類似的,為了能夠使得Key和Query可以內積,我們要求𝑊𝐾𝑊𝑄的shape是一樣的,但是并不要求𝑊𝑉和它們一定一樣(雖然實際論文實現是一樣的)。

每個時刻t都計算出𝑄𝑡,𝐾𝑡,𝑉𝑡之后,我們就可以來計算Self-Attention了。以第一個時刻為例,我們首先計算𝑞1和𝑘1,𝑘2的內積,得到score,過程如下圖所示。

接下來使用softmax把得分變成概率,注意這里把得分除以8(𝑑𝑘)之后再計算的softmax,根據論文的說法,這樣計算梯度時會更加穩定(stable)。計算過程如下圖所示。

接下來用softmax得到的概率對所有時刻的V求加權平均,這樣就可以認為得到的向量根據Self-Attention的概率綜合考慮了所有時刻的輸入信息,計算過程如下圖所示。

這里只是演示了計算第一個時刻的過程,計算其它時刻的過程是完全一樣的。

softmax示例代碼:

import numpy as npdef softmax(x):"""Compute softmax values for each sets of scores in x."""# e_x = np.exp(x)e_x = np.exp(x )return e_x / e_x.sum()if __name__ == '__main__':x = np.array([-3, 2, -1, 0])res = softmax(x )print(res)                        # [0.0056533  0.83902451 0.04177257 0.11354962]

特別注意,以上過程是可以并行計算的

Multi-Head Attention

論文還提出了Multi-Head Attention的概念。其實很簡單,前面定義的一組Q、K和V可以讓一個詞attend to相關的詞,我們可以定義多組Q、K和V,它們分別可以關注不同的上下文。計算Q、K和V的過程還是一樣,不過現在變換矩陣從一組(𝑊𝑄,𝑊𝐾,𝑊𝑉)變成了多組(𝑊𝑄0,𝑊𝐾0,𝑊𝑉0) ,(𝑊𝑄1,𝑊𝐾1,𝑊𝑉1)。如下圖所示。

對于輸入矩陣(time_step, num_input),每一組Q、K和V都可以得到一個輸出矩陣Z(time_step, num_features)。如下圖所示。

但是后面的全連接網絡需要的輸入是一個矩陣而不是多個矩陣,因此我們可以把多個head輸出的Z按照第二個維度拼接起來,但是這樣的特征有一些多,因此Transformer又用了一個線性變換(矩陣𝑊𝑂)對它進行了壓縮。這個過程如下圖所示。

上面的步驟涉及很多步驟和矩陣運算,我們用一張大圖把整個過程表示出來,如下圖所示。

我們已經學習了Transformer的Self-Attention機制,下面我們通過一個具體的例子來看看不同的Attention Head到底學習到了什么樣的語義。

從上面兩圖的對比也能看出使用多個Head的好處——每個Head(在數據的驅動下)學習到不同的語義。

位置編碼(Positional Encoding)

我們的目的是用Self-Attention替代RNN,RNN能夠記住過去的信息,這可以通過Self-Attention“實時”的注意相關的任何詞來實現等價(甚至更好)的效果。RNN還有一個特定就是能考慮詞的順序(位置)關系,一個句子即使詞完全是相同的但是語義可能完全不同,比如”北京到上海的機票”與”上海到北京的機票”,它們的語義就有很大的差別。我們上面的介紹的Self-Attention是不考慮詞的順序的,如果模型參數固定了,上面兩個句子的北京都會被編碼成相同的向量。但是實際上我們可以期望這兩個北京編碼的結果不同,前者可能需要編碼出發城市的語義,而后者需要包含目的城市的語義。而RNN是可以(至少是可能)學到這一點的。當然RNN為了實現這一點的代價就是順序處理,很難并行。

為了解決這個問題,我們需要引入位置編碼,也就是t時刻的輸入,除了Embedding之外(這是與位置無關的),我們還引入一個向量,這個向量是與t有關的,我們把Embedding和位置編碼向量加起來作為模型的輸入。這樣的話如果兩個詞在不同的位置出現了,雖然它們的Embedding是相同的,但是由于位置編碼不同,最終得到的向量也是不同的。

位置編碼有很多方法,其中需要考慮的一個重要因素就是需要它編碼的是相對位置的關系。比如兩個句子:”北京到上海的機票”和”你好,我們要一張北京到上海的機票”。顯然加入位置編碼之后,兩個北京的向量是不同的了,兩個上海的向量也是不同的了,但是我們期望Query(北京1)Key(上海1)卻是等于Query(北京2)Key(上海2)的。具體的編碼算法我們在代碼部分再介紹。位置編碼加入后的模型如下圖所示。

一個具體的位置編碼的例子如下圖所示。

殘差和歸一化

每個Self-Attention層都會加一個殘差連接,然后是一個LayerNorm層,如下圖所示。

下圖展示了更多細節:輸入𝑥1,𝑥2經self-attention層之后變成𝑧1,𝑧2,然后和殘差連接的輸入𝑥1,𝑥2加起來,然后經過LayerNorm層輸出給全連接層。全連接層也是有一個殘差連接和一個LayerNorm層,最后再輸出給上一層。

Decoder和Encoder是類似的,如下圖所示,區別在于它多了一個Encoder-Decoder Attention層,這個層的輸入除了來自Self-Attention之外還有Encoder最后一層的所有時刻的輸出。Encoder-Decoder Attention層的Query來自前面一層,而Key和Value則來自Encoder的輸出。

此外在解碼器的編碼器-解碼器注意力層中,掩碼的使用非常關鍵,以確保解碼器在生成每個目標詞時只能使用到源語言句子的信息和它之前已經生成的目標詞的信息

pytorch實現transformer

import torch
import torch.nn as nn
import math# 位置編碼模塊
class PositionalEncoding(nn.Module):def __init__(self, d_model, max_len=5000):super(PositionalEncoding, self).__init__()pe = torch.zeros(max_len, d_model)position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))pe[:, 0::2] = torch.sin(position * div_term)pe[:, 1::2] = torch.cos(position * div_term)pe = pe.unsqueeze(0)self.register_buffer('pe', pe)def forward(self, x):x = x + self.pe[:x.size(0), :]return x# Transformer模型
class TransformerModel(nn.Module):def __init__(self, ntoken, d_model, nhead, d_hid, nlayers, dropout=0.5):super(TransformerModel, self).__init__()self.model_type = 'Transformer'self.pos_encoder = PositionalEncoding(d_model)self.encoder = nn.Embedding(ntoken, d_model)self.transformer = nn.Transformer(d_model, nhead, d_hid, nlayers, dropout)self.decoder = nn.Linear(d_model, ntoken)self.init_weights()self.dropout = nn.Dropout(dropout)def generate_square_subsequent_mask(self, sz):# 生成后續掩碼,用于防止位置信息泄露mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))return maskdef init_weights(self):# 初始化權重initrange = 0.1self.encoder.weight.data.uniform_(-initrange, initrange)self.decoder.bias.data.zero_()self.decoder.weight.data.uniform_(-initrange, initrange)def forward(self, src, src_mask):# 前向傳播src = self.encoder(src) * math.sqrt(self.d_model)src = self.pos_encoder(src)output = self.transformer(src, src, src_key_padding_mask=src_mask)output = self.decoder(output)return output# 示例使用
ntokens = 1000  # 詞匯表大小
d_model = 512  # 嵌入維度
nhead = 8  # 多頭注意力中的頭數
d_hid = 2048  # 前饋網絡模型的維度
nlayers = 6  # 層數
dropout = 0.2  # dropout比率model = TransformerModel(ntokens, d_model, nhead, d_hid, nlayers, dropout)# 示例輸入
src = torch.randint(0, ntokens, (10, 32))  # (序列長度, 批量大小)
src_mask = model.generate_square_subsequent_mask(10)  # 創建掩碼output = model(src, src_mask)
print(output)

推理過程

在Transformer模型的機器翻譯任務中,解碼器生成第一個翻譯后的詞(通常稱為第一個目標詞)的過程如下:

  1. 起始符號:在解碼器的輸入序列的開始位置,通常會添加一個特殊的起始符號,如 <sos>(Start Of Sentence)。這個符號告訴模型翻譯過程的開始。

  2. 初始化隱藏狀態:解碼器的隱藏狀態通常初始化為零向量或從編碼器的最后一層的輸出中獲得。這個隱藏狀態在生成序列的每一步中都會更新。

  3. 第一次迭代:在第一次迭代中,解碼器的輸入只包含起始符號 <sos>。解碼器通過以下步驟生成第一個詞:

  • 將起始符號 <sos> 通過嵌入層轉換為嵌入向量。

  • 將這個嵌入向量與編碼器的輸出一起輸入到解碼器的第一個注意力層。

  • 在自注意力層中,使用因果掩碼(Look-ahead Mask)確保解碼器只能關注到當前位置和之前的詞(在這個例子中只有 <sos>)。

  • 在編碼器-解碼器注意力層中,解碼器可以查看整個編碼器的輸出,因為這是第一次迭代,解碼器需要獲取關于整個源語言句子的信息。

  • 經過解碼器的前饋網絡后,輸出層會生成一個概率分布,表示下一個可能的詞。

  • 選擇概率最高的詞作為第一個翻譯后的詞,或者使用貪婪策略、束搜索(Beam Search)等解碼策略來選擇詞。

  1. 后續迭代:一旦生成了第一個詞,它就會被添加到解碼器的輸入序列中,與 <sos> 一起作為下一步的輸入。在后續的迭代中,解碼器會繼續生成下一個詞,直到遇到結束符號 <eos> 或達到最大序列長度。

在訓練階段,目標序列的真實詞(包括 <sos> 和 <eos>)會用于計算損失函數,并通過反向傳播更新模型的權重。在推理階段,解碼器使用上述過程逐步生成翻譯,直到生成完整的句子。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/62507.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62507.shtml
英文地址,請注明出處:http://en.pswp.cn/web/62507.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網絡安全在數字時代保護庫存數據中的作用

如今&#xff0c;通過軟件管理庫存已成為一種標準做法。企業使用數字工具來跟蹤庫存水平、管理供應鏈和規劃財務。 然而&#xff0c;技術的便利性也帶來了網絡威脅的風險。黑客將庫存數據視為有價值的目標。保護這些數據不僅重要&#xff0c;而且必不可少。 了解網絡安全及其…

種子流和花粉流怎么理解它們之間的大小關系

種子流和花粉流是植物繁殖和遺傳多樣性研究中的兩個重要概念&#xff0c;它們分別描述了種子和花粉在空間上的傳播過程。理解它們之間的大小關系&#xff0c;即傳播距離和對遺傳結構的影響&#xff0c;對于生態學和保護生物學具有重要意義。 種子流&#xff08;Seed Dispersal&…

唇形同步視頻生成工具:Wav2Lip

一、模型介紹 今天介紹一個唇形同步的工具-Wav2Lip&#xff1b;Wav2Lip是一種用于生成唇形同步&#xff08;lip-sync&#xff09;視頻的深度學習算法&#xff0c;它能夠根據輸入的音頻流自動為給定的人臉視頻添加準確的口型動作。 &#xff08;Paper&#xff09; Wav2Lip模型…

C編程求助問題:實驗報告類型如何畫出流程圖并編寫程序?

求助問題&#xff1a;請問一下怎么做 是實驗報告類型的 畫出流程圖并編寫程序&#xff1a; (1) 從鍵盤上任意輸入5個字母&#xff0c;按ASCII從小到大的順序依次排列輸出。 (2) 輸入某個字母&#xff0c;查找題(1)數組中是否存在&#xff0c;若存在則輸出該字母在數組中的位置。…

C—指針初階(2)

如果看完閣下滿意的話&#xff0c;能否一鍵三連呢&#xff0c;我的動力就是大家的支持與肯定&#xff0c;沖&#xff01; 二級指針 我們先看概念以及作用&#xff1a;用來存放一級指針的地址的指針 先看例子&#xff0c;我們逐一分析 我們先分析上面那個“1” 標注那里&#x…

Gradle-學習

本來沒有想了解Gradle&#xff0c;但是在想看SpringBoot源碼的時候發現&#xff0c;在SpringBoot2.2.8版本之后&#xff0c;不再使用maven進行構建&#xff0c;而是使用Gradle。想著把SpringBoot源碼導入idea學習下源碼&#xff0c;但是來來回回折騰了好幾回&#xff0c;都是報…

PE文件結構:NT頭部

NT 頭部&#xff08;NT Header&#xff09;是 PE 文件格式的核心部分之一&#xff0c;它包含了有關程序如何加載、執行以及一些重要的文件屬性。NT 頭部常被認為是 PE 頭部 的核心或“真正的”PE 頭部&#xff0c;因為操作系統加載 PE 文件時&#xff0c;首先會查找 DOS 頭部的…

Oracle EBS FA 如何打開關閉的資產會計期間?

用戶“運行折舊”,誤勾選為“關閉期間”,還有一部分資產還需要操作報廢和調整,希望后臺打開關閉的資產會計期 系統環境 RDBMS : 12.1.0.2.0 Oracle Applications : 12.2.9 解決方案 由官方提供SQL腳本代碼如下: /*rollback120.sql - for Release 12.X only(based on r…

算法基礎學習Day6(動態窗口)

文章目錄 1.題目2.題目解答1.最大連續1的個數題目及題目解析算法學習思路一:暴力解法思路二:滑動窗口 代碼提交 2.將x減到0的最小操作數題目及題目解析算法學習滑動窗口解決問題 代碼提交 1.題目 1004. 最大連續1的個數 III - 力扣&#xff08;LeetCode&#xff09;1658. 將 x…

基于springboot+vue的公交線路查詢系統(全套)

一、系統架構 前端&#xff1a;vue | element-ui | html 后端&#xff1a;springboot | mybatis-plus 環境&#xff1a;jdk1.8 | mysql | maven | nodejs 二、代碼及數據庫 三、功能介紹 01. web端-首頁1 02. web端-首頁2 03. web端-注冊 04. web端-登錄 …

ASP.NET Core8.0學習筆記(二十五)——EF Core Include導航數據加載之預加載與過濾

一、導航屬性數據加載 1.在EF Core中可以使用導航屬性來加載相關實體。 2.加載實體的三種方式&#xff1a; (1)預先加載&#xff1a;直接在查詢主體時就把對應的依賴實體查出來&#xff08;作為初始查詢的一部分&#xff09; (2)顯式加載&#xff1a;使用代碼指示稍后顯式的從…

Linux 基礎環境的開發工具以及使用(下)

1. make / Makefile 自動化構建的工具 1&#xff09;引入 在我們進行一些大型的工程的時候&#xff0c;代碼量是極其大&#xff0c;當我們代碼在進行一系列的編譯的時候&#xff0c;難免會出現一些錯誤&#xff0c;當我們對錯誤進行一系列的更改之后&#xff0c;難道我們需要…

沃豐科技智能客服在跨境電商獨立站中的核心角色

隨著全球化進程的加速和互聯網技術的不斷發展&#xff0c;跨境電商行業蓬勃興起&#xff0c;為消費者提供了更廣闊、更便捷的購物選擇。在這樣一個競爭激烈的市場環境中&#xff0c;優質的客戶服務成為了企業脫穎而出的關鍵。沃豐科技智能客服憑借其先進的技術和人性化的設計理…

uniapp 彈出軟鍵盤后打開二級頁面,解決其UI布局變動

軟鍵盤彈出&#xff0c;此時點擊某按鈕打開二級頁面&#xff0c;position:fixed 位于底部的按鈕不見了&#xff08;通過加高其區域&#xff0c;發現被下移動了&#xff09;&#xff0c;什么原因不清楚? 但是發現是軟鍵盤彈出導致&#xff0c;問題解決通過隱藏鍵盤再打開二級頁…

Centos7下搭建Prometheus+Grafana監控

Prometheus 監控 Prometheus 監控系統的架構包括以下組件&#xff1a; Prometheus Server&#xff1a; Prometheus 服務器是監控系統的核心組件&#xff0c;負責收集、存儲和處理指標數據。它定期從各種數據源&#xff08;如 Exporter、Agent 等&#xff09;拉取指標數據&…

MyBatis-Plus(為簡化開發而生)

一、MyBatis-Plus概述 官網&#xff1a; baomidou.com MyBatis-Plus&#xff08;簡稱 MP&#xff09; 在 MyBatis 的基礎上只做增強不做改變&#xff0c;為簡化開發、提高效率而生。 &#xff08;1&#xff09;單表操作 不需要編寫sql語句&#xff0c;封裝方法&#xff0c;…

深入解析 C++11 的 `std::atomic`:誤區、性能與實際應用

在現代 C 開發中&#xff0c;std::atomic 是處理多線程同步時的重要工具之一。它通過提供原子操作保證了線程安全&#xff0c;但在實際使用時卻隱藏著許多不為人知的陷阱和性能影響。本篇文章將帶你深入理解 std::atomic 的使用方式、潛在問題&#xff0c;以及如何正確應用于多…

芋道源碼,芋道sql,yudao,yudao-vue-pro拒絕割韭菜

芋道的開發指南實際上只需要小小的操作就可以觀看啦 為了避免被割韭菜 我們可以使用插件去進行解鎖文檔 項目地址 otomayss/free-yd (github.com)[這里是圖片002]https://github.com/otomayss/free-yd

Mac軟件推薦

Mac軟件推薦 截圖SnipasteXnipBob 快捷啟動Raycast 系統檢測Stats 解壓縮The UnarchiverKeka&#xff08;付費&#xff09; 視頻播放IINA 視頻下載Downie&#xff08;付費&#xff09; 屏幕劉海TopNotchMediaMate&#xff08;付費&#xff09;NotchDrop&#xff08;付費&#x…

【ETCD】【源碼閱讀】 深入解析 raftNode.start`函數:Raft 核心啟動邏輯剖析

raftNode.start方法 是 etcd 中 Raft 模塊的核心啟動點&#xff0c;其職責是管理 Raft 狀態機的狀態變遷、日志處理及集群通信等邏輯。通過對源碼的逐行分析&#xff0c;我們將全面揭示其運行機制&#xff0c;探討其設計背后的分布式系統理念。 函數核心結構 raftNode.start 方…