精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

從模型原理到代碼實踐,深入淺出上手 Transformer,叩開大模型世界的大門

人工智能 開發
Transformer 主要是設計用來做翻譯的,分兩大塊編碼器和解碼器,GPT基本可以認為就是Transformer的解碼器部分。

作者 | Plus

一、序言

作為非算法同學,最近被Cursor、DeepSeek搞的有點焦慮,同時也非常好奇這里的原理,所以花了大量業余時間自學了Transformer并做了完整的工程實踐。希望自己心得和理解可以幫到大家~

如有錯漏,歡迎指出~

本文都會以用Transformer做中英翻譯的具體實例進行闡述。 

二、從宏觀邏輯看Transformer

讓我們先從宏觀角度解釋一下這個架構。

首先 Transformer也是一個神經網絡,神經網絡的本質是模擬人腦神經元的思考過程,數學上是一種擬合,當然,人腦內部的信號處理是否連續或者可擬合我們不得而知,但Transformer在我的機器上實實在在地思考并輸出了正確的答案。

Transformer 主要是設計用來做翻譯的,分兩大塊,如上圖,左邊的編碼器和右邊的解碼器。

編碼器負責提取原文的特征, 解碼器負責提取當前已有譯文序列的特征,并結合原文特征(編碼器解碼器的連線部分),給出下一個詞的預測。

GPT基本可以認為就是Transformer的解碼器部分。

接下來我們分幾個部分逐步講解并附上代碼實踐,很長 得慢慢看....

三、輸入和輸出

我認為大部分文章都沒有把輸入和輸出講得很細,其實理解了輸入輸出,你就基本可以理解Transformer的大半了。

1. 模型的輸入

以中英翻譯為例,Transformer的輸入分兩部分:

  • 源文序列(中文) 即圖1左側部分,輸入到編碼器。 舉例: 我愛00700
  • 目標譯文(英文)即圖1右側部分,輸入到解碼器。 一開始只有一個,僅用于告訴模型開始翻譯

2. 模型的輸出

很顯然,對于上述輸入,我們期待的輸出是 I love 00700

的作用是表示翻譯結束,因為翻譯的英文不一定和輸入的中文等長,所以需要有一個結束符。

模型并不能一下子輸出完整的句子,他是一個詞一個詞( /token/ )吐出來的,并且每一個詞都需要作為下一詞的輸入,這也是為什么大模型都是打字機交互的原因。  具體例子:

第一個循環 
 編碼器輸入 我 愛 00700  
 解碼器輸入 <bos>
輸出 I

第二個循環 
 編碼器輸入 我 愛 00700 
 解碼器輸入 <bos> I
輸出 love

第三個循環 
 編碼器輸入 我 愛 00700 
 解碼器輸入 <bos> I love
輸出 00700
 
第四個循環 
 編碼器輸入 我 愛 00700 
 解碼器輸入 <bos> I love 00700
輸出 <eos>
// 輸出了結束符,翻譯完成

請注意上述是為了簡化理解的一個陳述,實際上模型真正的輸出并不是一個詞,而是整個 /詞表/ 內任意一個詞可能的概率,也就是圖2所示的probabilities。

具體說,就是一個數組[0.2, 0.7, 0.1],  序號為0的詞的概率是0.2,序號為1的詞的概率是0.7,序號為2的詞的概率是0.1。 假設這是第二輪循環,詞表是[I, love, 00700] 取最大概率0.7的詞,然后得到第二輪輸出是love, 和第一輪輸出拼起來就是 I love。

3. 詞表 & token

劃線的兩個詞  /token/   /詞表/  你可能仍有疑惑。這正是我們真正弄清楚整個輸入輸出的關鍵。

顯然,計算機只能理解二進制數據,我們說輸入"我愛00700"的時候,實際上輸入的是處理好的二進制數據。

如何把句子轉化成模型可以理解的二進制數據呢? 不妨先想想我們怎么學英語的, 沒錯,背單詞啊! 我們需要先認識詞,然后理解句子,模型也是一樣的。

我們背的英文單詞表,和這里的模型 /詞表/ 其實是一個意思,當然,形式略有不同。  詞表里的每一個詞,就是我們說的 /token/ ,請注意 token 不一定是一個英語單詞。上述例子的英文詞表可能是: [I, lo, ve, 00, 7] 。 顯然,token不是按照空格分的。 原因是算力難以覆蓋,英文單詞有幾十萬個(不權威),老黃聽了都搖頭??。 而更低維度的分詞,可以壓縮詞表的數量,[I, lo, ve, 00, 7]是我瞎寫的,理解意思就行,讓DS給我們舉一個更好的例子:

假設語料庫包含以下高頻單詞:

  • play (10次)
  • player (8次)
  • playing (6次)
  • plays (5次)
  • replay (7次)
  • replaying (4次)

直接按空格分詞會生成獨立詞表:

空格分詞詞表大小:6
[play, player, playing, plays, replay, replaying]
# bpe分詞算法最終詞表(目標大小=4)
# ?表示這個token只會出現在開始,想一想 還原句子的時候你需要知道兩個token是拼起來還是插入空格
[?play, re, ing, er]

對比空格分詞, /bpe分詞算法/ (想了解的自行ds,限于篇幅不贅述)可以有效壓縮詞表大小,在大量語料的情況下會更明顯,40GB數據的GPT2也僅5w詞表大小

4. 嵌入(embedding)

現在,我們有了詞表,很容易就可以得到二進制的序列了

還是老例子:

我 愛 00700 被編碼為 [1, 2, 3]    (為了方便,這里假設分詞就這樣。)

我們可以直接輸入到網絡訓練了嗎? 答案是否定的,不過我們也終于來到了有意思的地方。 在在訓練之前需要做一個 /嵌入/

來一個youtobe的動圖看下 /嵌入/ 大概是啥。

再來一個DS的靈魂解釋:

嵌入(Embedding)的核心思想是 將復雜、高維的數據(如文字、圖像)轉化為低維、連續的數值向量,同時保留其內在的語義或關系 。這種轉化讓計算機能像人類一樣“理解”數據的含義,并用數學方式計算相似性、分類或生成。

額... 有點抽象,似乎講了些什么,又似乎什么都沒講......

沒關系,讓我來舉一個形象的例子 觀察下面的句子:

紅色
當紅明星
生意好紅火啊

注意"紅"這個字,發現了嗎,在不同語境(或者說維度)它有不同的意思,如果我們直接輸入token編碼,這些信息是缺失的,模型就不太可能理解句子的意思。

那么,一個字或者說一個token到底有多少維度的語義呢?不知道啊??,但是沒關系,猜一個, 512。 沒錯,就是這么樸實無華!

于是我們就有了嵌入矩陣  (self.weight)

: 詞表大小(num_embeddings,如32000)

: 嵌入維度(embedding_dim,如512)

即E是一個32000x512的二維矩陣 (代碼里就是數組)

E[1,1]  E[1,2] ...... E[1,512]   的值表示詞表中序號為1的詞在維度1、2、512的語義, 由此同樣一個“紅”的token,就可以有多種語義,這樣模型就可以理解詞和句子了。

當我們輸入 我 愛 00700 被編碼為 [1, 2, 3] , 實際上我們應該輸入:

請注意請注意,所謂一個字有不同維度的語義,是我現編的比喻,如果它幫到你理解這個東西那最最好,如果覺得比喻得不太對,大可一笑了之。

繼續

E[1,1]  E[1,2] ...... E[1,512] 我們可以把它看作是以原點為起點的 512維的向量 ,一般就叫做詞的嵌入向量,也就是平時可能聽到的向量化,沒什么高大上的對吧。 向量化之后可以做什么呢?看圖

(以三維舉例,512維可以發揮想象力自行腦補)

為了讓模型理解詞和詞的關系,我們用數學語言去描述這種關系,就是向量的內積:

  •  是向量a的長度(范數)
  • 是向量b的長度(范數)
  • 是兩個向量之間的夾角

就算忘記了向量內積怎么算也沒關系,我們只需要理解,夾角越小,向量內積越大, 兩個詞的關聯越緊密就行了。 實際上大部分復雜公式的計算都是封裝好了的。 這里的內積,在自注意力的時候我們也會用到。

到了這里就可以解釋一下 的元素值是怎么來的了,我們定義了每一個詞有512個維度,那么每一個維度的值是什么呢?

答案是模型自己學習出來的,E是模型的參數的一部分,一個詞每個維度的值,初始化是一個隨機值,模型訓練的時候會被更新。

怎么學出來的? 試想一下: 語料里面有無數的詞,但是他們是有一定的關聯的,比如 I love 這兩個詞經常同時出現,那么他們的內積就應該較大,反之也一樣。實際上有點像聚類,怎么理解都行,模型學得差不多的時候,token的分布或者說嵌入向量的關系,一定是有規律的。

讓我們來用數學語言總結一下嵌入:

就是我們的查表或者說嵌入操作:輸入張量 

  • B: 批次大小(batch_size)
  • L: 序列長度(sequence_length)
  • D: 嵌入維度

嵌入查找操作上述例子 就是  ,L是句子長度也就是3

為什么這里也會叫查表? 實際上對索引為1的token做嵌入,就是在嵌入矩陣里到找第一行然后拿出來,就是 E[1,1]  E[1,2] ...... E[1,512] 。這個過程不就是查表么。

5. Batch 處理

細心的你可能發現了問題,這里多了一個維度B,輸入的結果 ,是三維的。這里解釋一下,我們的例子只有一句話,但是在模型的訓練中,其實是一次性輸入多句話并行計算的,batch_size 的值就是你一次性輸入多少句子來訓練,一般來說,這取決于你的顯存大小,自行嘗試調整就可以了。

假設batch 為2,一次輸入兩句:

兩句話的embedding矩陣合并,得到一個 2x3x512的三維矩陣 就是我們給編碼器的輸入了。 此時 B=2  L=3  D=512

6. padding掩碼矩陣

還沒完,讓我們來一點刁鉆的問題。 上述例子L=3,兩個句子長度都一樣=3。 如果句子長度不一樣呢?batch矩陣豈不是拼不出來? 當然不會,實際寫代碼的時候,L一般都是取一個較大值,比如語料中最長句子的值。L=100。 不夠長的句子怎么辦呢? 補padding,注意不是0,因為神經網絡的某些中間計算如softmax輸入0也是有值的。 一般是用一個特殊的token作為padding。

在自注意力計算的時候(下文會講)會根據padding的位置,生成mask 矩陣,計算時候padding替換為一個極小值比如-1e9,就可以不影響計算了。

7. PositionalEncoding

現在這個帶padding的矩陣可以輸入模型開始訓練了嗎? 還是不行.....  我們還缺少一個重要的信息,位置。 舉一個例子:

[ [0.3, 0.5, 0.1, 0.4]
[貓,吃,魚]  => Transformer =>    [0.1, -0.6, -0.2, 0.3], 
                                 [0.3, 0.5, 0.3, -0.1] ]

                                 [ [0.3, 0.5, 0.1, 0.4]
[魚,吃,貓]  => Transformer =>     [0.1, -0.6, -0.2, 0.3], 
                                   [0.3, 0.5, 0.3, -0.1] ]

很明顯,雖然只是交換了一個字的順序,但其實是兩個完全不同意義的句子, 而 Transformer輸出的分布卻是不變的,說明網絡沒有辦法識別位置信息。

為了解決這個問題,Transformer的論文里的方案是添加位置編碼,也就是PositionalEncoding。

其實沒有那么神秘,我們把 Transformer 看成函數T , 現在的問題是:

我們可以加上一個位置編碼P去規避這個問題:

: 當前的位置序號

來一個簡單粗暴易理解的 P(i) = i

貓 0
 吃 1
 魚 2

然后呢?就是簡單的直接加上去

沒錯,這樣其實我們的輸入就包含了位置信息了,只要信息在那里,模型總能學明白

當然,這里只是為了方便說明位置編碼本身,所以用了最簡單的方案,你說它能不能跑,那肯定也是能跑的,就是會有不少問題,實際上論文里實現是正弦torch.sin(position * div_term)和余弦函數torch.cos(position * div_term)來生成位置編碼, 解釋起來就篇幅太長,大家可以自己探索。

位置編碼不改變矩陣的維度,只是改變了數值, 我們給編碼器的最終輸入總結如下:

我愛我愛分詞嵌入

但是對于解碼器的輸入,還需要額外處理,繼續往下。

8. 并行計算 & Teacher Forcing

回顧下一開始的例子

第一個循環 
 編碼器輸入 我 愛 00700  
 解碼器輸入 <bos>
輸出 I

第二個循環 
 編碼器輸入 我 愛 00700 
 解碼器輸入 <bos> I
輸出 love

第三個循環 
 編碼器輸入 我 愛 00700 
 解碼器輸入 <bos> I love
輸出 00700
 
第四個循環 
 編碼器輸入 我 愛 00700 
 解碼器輸入 <bos> I love 00700
輸出 <eos>
// 輸出了結束符,翻譯完成

我們給編碼器的輸入始終是整個句子序列的embedding。而給解碼器的輸入,則是當前已經預測出來的 n-1個詞的序列的embedding。

(1) 并行計算

當你真的上手去寫代碼的時候,你就會發現, 在訓練階段,代碼里我們給解碼器直接輸入完整的句子I love 00700 ,且模型輸出直接就是整個句子。 只有一個循環。你現在肯定滿腦子問號,直接輸入正確答案了,還學什么?

別急,實際上我們輸入不是全部的ground truth,是 n-1序列的ground truth。怎么理解呢?真正的ground truth 是  I love 00700 eos 。  我們的輸入是I love 00700。 當然這樣僅僅是少了最后一個token的答案而已。

實際上解碼的輸入(姑且叫trg_input)在做計算的時候還需要做一個causal mask,叫做因果掩碼,字面意思就是為了防止計算的時候知道未來的信息。

>>> trg_input # 假設我們的input長這樣   1234代表 <bos> I love 00700
tensor([[1, 2, 3, 4],
        [1, 2, 3, 4],
        [1, 2, 3, 4],
        [1, 2, 3, 4]])
# 因果掩碼矩陣是這樣的,對角線上移一位的上三角矩陣,上三角的元素是極小值
>>> mask = torch.triu(torch.ones(4, 4), diagonal=1)
>>> mask = mask.float().masked_fill(mask == 1, float(-1e9))
>>> mask
tensor([[0., -inf, -inf, -inf],
        [0., 0., -inf, -inf],
        [0., 0., 0., -inf],
        [0., 0., 0., 0.]])
>>> 

#執行 causal mask
>>> trg_input + mask
tensor([[1., -inf, -inf, -inf],
        [1., 2., -inf, -inf],
        [1., 2., 3., -inf],
        [1., 2., 3., 4.]])

看看causal mask結果的第一行,[1., -inf, -inf, -inf] 其實就是相當于第一輪循環的輸入 bos, 因為無限小在計算的時候可以忽略。 后續的2、3、4行剛好也就是 對應的2、3、4輪循環。 get到了嗎, 直接作為一個矩陣輸入,在模型里并行計算,4個循環優化成了一個!能夠并行計算,這正是transformer一個重要特性。

(2) Teacher Forcing

想一想為什么我們可以實現并行計算? 因為訓練的時候,我們提前知道了正確答案,在生產環境推理過程中,我們不可能知道第一個token應該輸出I,第二個token應該輸出love ...., 所以推理的時候,只能順序執行循環。

再多想一點,在訓練的時候,模型還并不成熟,第一個字符輸出的未必是I,假設輸出了 He 呢? 然后第二輪循環我們的輸入就是 bos He, 這樣繼續循環下去只會越來越錯,非常不利于學習的收斂。 所以,我們第二輪循環不輸入He, 而是用正確答案 I 來繼續下一個token預測,實驗表明這樣學習的速度會更快。  這種訓練方法,我們就稱之為Teacher Forcing。

關于并行計算和Teacher Forcing感覺大部分文章都是一筆帶過,而我感覺這里很重要,包括理解架構本身或者是去理解為什么transformer帶來了技術的爆發,并行計算是一個很重要的因素。

9. 輸入輸出的代碼實現

作為一個嚴謹的工程師,最后我貼一下我自己的實現,并做簡單講解:

# 關于語料 我的數據集來自 wmt17 v13 zh-en  大約100m 30+w行長句子
# 從csv讀出來, 第一列是中文 第0列是英文
train_data = read_tsv_file(opt.train_tsv, 1, 0, opt.delimiter) 
valid_data = read_tsv_file(opt.valid_tsv, 1, 0, opt.delimiter) if opt.valid_tsv else []
test_data = read_tsv_file(opt.test_tsv, 1, 0, opt.delimiter) if opt.test_tsv else []
 
# 分詞器初始化 我這里是bpe bytelevel  32000 詞表大小,具體實現太長就不貼了
src_tokenizer_path = os.path.join(opt.data_dir, "tokenizer_zh.json")
src_tokenizer = train_tokenizer([src for src, _ in train_data], opt.vocab_size, src_tokenizer_path)
    
trg_tokenizer_path = os.path.join(opt.data_dir, "tokenizer_en.json")
trg_tokenizer = train_tokenizer([trg for _, trg in train_data], opt.vocab_size, trg_tokenizer_path)
 
# 有了分詞器之后,對訓練數據句子分詞 打包成pkl格式,方便模型訓練的時候讀取
processed_train = process_data(train_data, src_tokenizer, trg_tokenizer, opt.max_len)
 
# train階段
# 從pkl加載數據 略

# 訓練輸入
for i, batch in enumerate(dataloader):
        # 準備數據 每次循環處理一個batch的數據
        src = batch['src'].to(device) # 取出語料原文。我 愛 00700 (舉例,實際上batch是多句的數組,但是矩陣運算的時候都是一起并行算的)
        trg = batch['trg'].to(device) # 取出語料目標譯文。bos I love 00700 eos
        trg_input = trg[:, :-1]  # 去掉最后一個token, bos I love 00700  n-1的Teacher Forcing序列
        trg_output = trg[:, 1:]  # 去掉第一個token, I love 00700 eos 真實的groud truth,用于計算損失
        
        # 前向傳播 清空梯度
        optimizer.zero_grad()

        # 輸入模型計算一次
        # teacher forcing: trg_input 實際上就是ground truth(正確的值)
        # train 模式下,output是整個句子的預測值
        output = model(src, trg_input)
        ......
 

# 嵌入
"""初始化嵌入矩陣"""
        # 使用正態分布(高斯分布)初始化張量
        # 均值為0,標準差為0.02
        # 在數學上,512維的隨機向量,可以認為是互相正交的(就是任意兩個token之間一點關系都沒有)
        nn.init.normal_(self.weight, mean=0, std=0.02)
 
     # 基本的嵌入查找 
     # weight 是模型參數,自動參與學習
        embeddings = self.weight[x]
        

# mask
 """創建源序列和目標序列的掩碼"""
        src_padding_mask = (src == self.pad_idx) #  src padding掩碼
        trg_padding_mask = (trg == self.pad_idx) #  trg padding掩碼
        
        # 因果掩碼
        seq_len = trg.size(1)
        trg_mask = torch.triu(
            torch.ones((seq_len, seq_len), device=src.device), diagonal=1
        ).bool()
        
        return src_padding_mask, trg_padding_mask, trg_mask
 
 # 應用mask   我這里的mask是bool矩陣,true表示需要mask
 # 如果mask是true,scores對應位置的元素就填充為極小值 -1e9
        if mask is not None:
            scores = scores.masked_fill(mask == True, -1e9)

輸入輸出終于寫完 看到這里就真的不容易 感謝~!

四、自注意力機制

到這里我們已經完成了輸入的所有前置處理,訓練數據開始進入到網絡訓練。 而理解Transformer網絡的關鍵就在于自注意力機制,如下圖所示的三個模塊,他們是整個網絡的核心。

1. 注意力機制

注意力其實非常好理解, 如下圖: 萬綠叢中一點紅,你首先看到了紅,這就是注意力。

在訓練網絡中,注意力的作用是什么呢?

老例子:“ 我 愛 00700 ”  翻譯為    “ I love () ”    () 為當前正在預測的詞。

如上圖, 前面我們介紹過Transformer預測下一個詞的時候,會結合整個上下文,即“我 愛 00700  I love”,假如沒有注意力機制,那么所有詞對預測結果的貢獻是一樣的!  假設語料的言情文偏多,那輸出很可能是 I love you, 很明顯不合理, 此時我們希望網絡把注意力集中在“00700”這個詞上,其他詞應該忽略掉。

那么在數學上注意力是什么呢? 非常簡單,就是權重系數寫出來就是:

  • x ∈ R^(seq_len, d_model)  seq_len是句子長度,d_model=512是embdedding的緯度,忘了的話往回翻復習下吧
  • W ∈ R^(512, 512) 是權重矩陣

???  解釋了這么久 就這? 沒錯是的,取個高大上的名字而已。

2. 自注意力機制

自注意力畢竟多了一個字,肯定還是有點不一樣的,不一樣在哪呢,他乘的不是權重矩陣,它乘它自己!

  • 即權重 
  • x ∈ R^(seq_len, d_model)  seq_len是句子長度,d_model=512是embdedding的緯度,忘了的話往回翻復習下吧

重點看一下這個  

如果你對矩陣乘法熟,你馬上就會意識到,這不就是句子的詞和詞之間的內積嗎?

回憶一下嵌入的內容,我們說過詞向量的內積反應的是詞的關系遠近。

本質是,Transformer通過句子本身的詞和詞之間的關系計算注意力權重! 所以我們叫它 - 自注意力 !還算自洽吧~

當然,直接相乘沒有參數可以訓練啊,來個線性變換吧 ,于是

不好記,得起個名字, 第一個式子是原始請求 就叫Query吧,簡寫為Q

感覺不用糾結這個命名,反正論文沒提命名的道理 自行想象吧!


第二個式子是被用來計算關系的,就叫Key吧,簡寫為K

于是:

按照論文所提,實驗發現,QK點積可能會過大,數據方差太大,導致梯度不太穩定,所以需要把分布均勻一下,于是:

我們需要的是權重,所以需要轉化為 0.0~1.0 這樣的值,這正是 /softmax/ 函數的能力 于是:

代入 , y改寫為Attention,更高大上

另外為了增加網路的復雜度,我們不直接乘x,老辦法,線性變換一下, 記為V

最終

最終我們得到了和論文一模一樣的公式~

3. 多頭注意力機制

先看一個圖,這是一個自注意力計算的示例:

問題是: 我 - 我  愛-愛  00700-00700  的分數最高, 自己最關注自己,聽起來是合理的,但論文作者應該是覺得這不利于收斂。 所以提出了多頭機制。

其實這里論文并沒有太多解釋,我理解是一種實驗性的經驗。

那么什么是多頭? 其實也很簡單,就是分塊計算注意力,好比原來是一個頭看整個矩陣, 現在是變身哪吒三頭六臂,一個頭看一小塊,然后信息合并。 畫圖說明:

這種分頭其實本質上計算量是一樣的,只是注意力分塊了,直接看公式就好了。

  • Q, K, V 是輸入矩陣,形狀為 (batch_size, seq_len, d_model)
  • W_i^Q ∈ R^(d_model × d_k)
  • W_i^K ∈ R^(d_model × d_k)
  • W_i^V ∈ R^(d_model × d_v)
  • head_i 的形狀為 (batch_size, seq_len, d_v)

論文中 i=8, 8個頭, dk =dv = d_model/8 = 64

因果掩碼-多頭自注意力機制:

其實輸入輸出的時候就講了,就是解碼器做多頭計算的時候 使用teacher forcing,需要加上因果掩碼mask。 下面代碼說明。

4. 交叉-多頭自注意力機制

解碼器如何結合編碼器的上下文就在這里了,如果是交叉-多頭自注意力機制,那么 Q = x, K=V=編碼器的輸出,直接看代碼最直觀。

# 1. 線性變換 這里的 query=key=value 就是x 
     # 如果是解碼器的 交叉-多頭自注意力機制,那么 query = x, key=value=編碼器的輸出
        Q = self.q_linear(query)  # 這里的linear就是線性變換 wx+b,下同
        K = self.k_linear(key)
        V = self.v_linear(value)
        
        # 2. 分割成多頭 [batch_size, seq_len, d_model] -> [batch_size, seq_len, num_heads, d_k]
     # 實際上 torch的張量(矩陣) 是用一緯數組存的,所謂分割多頭、轉置,最終就是改一下數組元素的順序
        Q = Q.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        K = K.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        V = V.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        
        # 3. 計算注意力
     # K.transpose(-2, -1) 就是 k的轉置
        scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k) 
        
        # 4. 應用mask  因果掩碼 或者 padding 掩碼
        if mask is not None:
            scores = scores.masked_fill(mask == True, -1e9)
        
        # 5. softmax獲取注意力權重
        attn = F.softmax(scores, dim=-1)
     # dropout是隨機丟棄一些值(寫0),為了增加隨機性,訓練時才會開啟,避免過擬合
        attn = self.dropout(attn)
        
        # 6. 注意力加權求和  就是乘v
        out = torch.matmul(attn, V)
        
        # 7. 重新拼起來多頭 把維度轉置回來
        out = out.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model)
        
        # 8. 最后又加了一個線性變換 wx+b, 不要為問我為什么  ...... 實驗性經驗
        out = self.out_linear(out)
        
        return out

五、前向傳播

到這里就很簡單了,核心的部分都講完了。 還剩下幾個小塊簡單講一下。

1. Add & Norm

Add 叫殘差,超級簡單,直接上代碼:

# self_attn 就是算注意力,tgt_mask是因果掩碼。  
 # 本來應該是  x = self.dropout1(self.self_attn(x2, x2, x2, tgt_mask)) 
 # 殘差就是多加了 x,    x = x + self.dropout1(self.self_attn(x2, x2, x2, tgt_mask)) 
 # 簡單解釋就是 加這個x,防止梯度下降太快到后面沒了。詳細就不展開了,可自行gpt
 x = x + self.dropout1(self.self_attn(x2, x2, x2, tgt_mask))

Norm 是歸一化,簡單解釋一下,歸一化是讓數據分布更均衡,一般是消除一些離譜值、不同數據量綱的影響。 比如分析年齡和資產對相親成功率的影響,年齡只有0-100, 資產的范圍就很大,計算的時候就不好弄,需要把資產也縮放到一個合理的數值范圍 比如 0-100 萬。

歸一化有很多種,這里用的是  /Layernorm/ ,這個公式還挺復雜,有興趣可以自己研究,這里我是偷懶的:

# 直接使用 pytorch的自帶公式
 self.norm1 = nn.LayerNorm(d_model)
 # 調包就完事
 x2 = self.norm1(x)

2. FeedForward

前饋全連接層(feed-forward linear layer) ,好多文章試圖解釋這個意義,其實沒啥意義,就是普通的兩層網絡, 一般來說就是一層神經網絡就是 線性變換+激活函數。

線性變換,無非就是升維和降維。舉例:升維是類似于你看圖片的時候,雙指放大,然后滑來滑去找到你需要的,是特征放大。降維么,就是截圖保存放大部分,然后輸出,是特征提取。

一升一降就是FeedForward啦,直接看代碼:

class FeedForward(nn.Module):
    def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1):
        super().__init__()
     # 從dmodel升維到d_ff = 2048 論文的值  
        self.linear1 = nn.Linear(d_model, d_ff)
 
     # 降回來提取特征  
        self.linear2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)
        
    def forward(self, x: torch.Tensor) -> torch.Tensor:
     # 怎么找到關注特征的 那不就是激活函數 F.relu ,很明顯,神經元被激活的地方就是感興趣的特征啊。
        return self.linear2(self.dropout(F.relu(self.linear1(x))))

3. 解碼器

到這里就沒啥了,每個塊我們都理解了,后續就是實現邏輯了。 直接上代碼,太長了好像沒必要,文末尾放git地址把。

4. 編碼器

同上。

六、反向傳播

前向傳播計算出一次訓練的結果,反向傳播就是根據結果的好or壞,更新參數,循環往復,最終得到一個滿意的模型。

自己實現 實在是有點累,調包只需要一句:

# 前向傳播
        output = model(src, trg_input)
        
     ......

        # 計算損失
        loss = criterion(output_flat, trg_output_flat)
        
        # 反向傳播
        loss.backward()

pytorch框架會記錄你整個模型前向傳播的圖,然后根據損失,使用 /鏈式法則 / & /梯度下降算法/ 幫你回溯計算更新每一個參數,太舒服了~

責任編輯:趙寧寧 來源: 騰訊技術工程
相關推薦

2024-12-12 09:00:28

2025-09-08 07:14:25

2021-07-20 15:20:02

FlatBuffers阿里云Java

2024-03-27 10:14:48

2025-09-04 01:33:00

Flowable工作流引擎

2018-12-25 08:00:00

2022-02-25 08:54:50

setState異步React

2023-01-06 12:50:46

ChatGPT

2024-07-07 21:49:22

2020-07-29 10:10:37

HTTP緩存前端

2021-08-10 14:10:02

Nodejs后端開發

2020-11-06 09:24:09

node

2021-03-16 08:54:35

AQSAbstractQueJava

2011-07-04 10:39:57

Web

2023-05-18 08:54:22

OkHttp源碼解析

2025-05-09 01:30:00

JavaScript事件循環基石

2025-07-03 02:12:00

RAG檢索系統

2019-11-11 14:51:19

Java數據結構Properties

2009-11-30 16:46:29

學習Linux

2022-12-02 09:13:28

SeataAT模式
點贊
收藏

51CTO技術棧公眾號

97久久亚洲| 俺来也官网欧美久久精品| 国内精品在线播放| 欧美激情视频网址| 中文字幕高清视频| 24小时成人在线视频| 亚洲成人你懂的| 亚洲成人a**址| www.我爱av| 久久精品主播| 久久久久久久久久久亚洲| 加勒比一区二区| 久久9999免费视频| 色综合天天性综合| 久久久久久av无码免费网站下载| 青青草视频在线免费观看| 韩国av一区二区三区在线观看| 欧美亚洲日本网站| 清纯粉嫩极品夜夜嗨av| 欧美影院三区| 亚洲精品国产精品久久清纯直播| 激情黄色小视频| 欲香欲色天天天综合和网| 亚洲欧美日韩在线播放| 久久久久久久久久码影片| 国产wwwxxx| 蜜臀a∨国产成人精品| 91国语精品自产拍在线观看性色| 国产精品夜夜夜爽阿娇| 网曝91综合精品门事件在线| 欧美一级黄色大片| 性猛交ⅹ×××乱大交| 亚洲插插视频| 亚洲成av人影院| 欧美极品少妇无套实战| 免费黄色在线网站| 中文字幕 久热精品 视频在线| 精品综合在线| 蜜桃视频污在线观看| 国产美女精品人人做人人爽 | 肥熟一91porny丨九色丨| 中文字幕码精品视频网站| 亚洲免费影院| 97免费中文视频在线观看| 久久激情免费视频| 久久久久久美女精品 | 成人在线播放免费观看| 国产精品毛片久久久久久久| 日本日本精品二区免费| 免费福利在线观看| 26uuu亚洲| 久久久久国产精品视频| 日本又骚又刺激的视频在线观看| 91丝袜高跟美女视频| 国产乱码精品一区二区三区日韩精品 | 成人小视频在线| 91沈先生播放一区二区| 国产熟女一区二区三区四区| 精品在线一区二区三区| 成人国产精品一区| 国产一区二区三区中文字幕| 久久99这里只有精品| 国产一区二区在线免费| 国产孕妇孕交大片孕| 国产乱子轮精品视频| 亚洲a成v人在线观看| av中文字幕免费| 成人精品一区二区三区中文字幕| 国产精品对白一区二区三区| 国精品人妻无码一区二区三区喝尿 | 男人的天堂99| 午夜无码国产理论在线| 欧美三级资源在线| 中文字幕色网站| 一区二区网站| 亚洲精品97久久| 白白色免费视频| 久久免费精品视频在这里| 久热精品视频在线观看| 五月天婷婷网站| 久久久久99| 91欧美精品午夜性色福利在线| 国产成人久久精品77777综合| 成人av免费网站| 日韩欧美精品久久| 香蕉久久aⅴ一区二区三区| 偷窥国产亚洲免费视频| 午夜视频你懂的| 日韩成人在线观看视频| 日韩国产精品视频| 日本黄色激情视频| 韩日精品视频| 国产成人综合久久| 午夜精品久久久久久久第一页按摩 | 日本人成精品视频在线| 亚洲一级在线播放| 成人黄色在线看| 亚洲 国产 欧美一区| 影音先锋在线播放| 色播五月激情综合网| 日本黄色www| 国产91一区| 欧美成人精品h版在线观看| 天天综合网入口| 久久成人免费网| 精品一区二区三区日本| h片在线免费观看| 91成人免费在线视频| 日本成人在线免费观看| 经典一区二区| 久久久亚洲影院你懂的| 一级特黄aaaaaa大片| 99国产精品99久久久久久| 一区二区三区四区欧美| 在线观看的黄色| 日韩欧美久久一区| 亚洲精品成人av久久| 亚洲伦理一区| 99国产在线视频| 中文字幕在线播放| 一本大道久久精品懂色aⅴ| 亚洲国产日韩在线一区| 欧美亚洲国产精品久久| 1769国内精品视频在线播放| 国产精品久久久久久久免费| 国产日韩欧美综合一区| 3d动漫一区二区三区| 日韩精品久久久久久久软件91| 国产一区二区黄| 日韩精品在线观看免费| jvid福利写真一区二区三区| 久久综合亚洲精品| 97色婷婷成人综合在线观看| 伊人精品在线观看| 天天综合天天干| 99久久婷婷国产| 青青青在线视频播放| 日韩欧美久久| 欧美精品在线观看| 国产99久久九九精品无码免费| 国产精品每日更新| 亚洲精品自拍网| 狠狠做深爱婷婷综合一区| 欧美一级视频免费在线观看| 婷婷色在线观看| 亚洲国产中文字幕| 岛国av免费观看| 黑人一区二区三区四区五区| 99久久久精品免费观看国产| 亚洲91av| 精品国产网站在线观看| 久久亚洲精品大全| 成人精品国产一区二区4080 | 亚洲精品欧美| 久99久视频| 亚洲成a人片| 国产亚洲欧美视频| 中文字幕在线2019| 国产精品高潮呻吟| 天天av天天操| 欧美午夜一区二区福利视频| 国产欧美一区二区三区另类精品 | 亚洲成年人影院| 精品国产乱码久久久久夜深人妻| 韩日成人av| 久久www免费人成精品| 欧美性xxx| 日韩在线精品视频| 国产黄色美女视频| 午夜成人在线视频| 人人妻人人澡人人爽| 麻豆精品新av中文字幕| 欧美三级午夜理伦三级老人| 午夜电影一区| 欧洲成人午夜免费大片| av一本在线| 欧美一卡2卡3卡4卡| 国产精品.www| 国产亚洲精品超碰| 午夜大片在线观看| 一区二区三区福利| 亚洲高清视频在线观看| 一区三区自拍| 国产精品r级在线| 好吊日视频在线观看| 亚洲第一中文字幕在线观看| 日韩熟女一区二区| 亚洲欧美日韩国产综合在线| 亚洲香蕉中文网| 日韩专区中文字幕一区二区| youjizz.com亚洲| 另类在线视频| 成人激情视频网| 欧洲一区精品| 不卡av在线网站| 伦理片一区二区三区| 欧美一区二区三区日韩视频| 国产黄色免费观看| 亚洲人成精品久久久久久| 精品无码在线视频| 经典一区二区三区| 欧美黄网站在线观看| 欧美不卡高清| 日韩精品在在线一区二区中文| 日韩中文字幕无砖| 国产精品日韩欧美综合| 美女精品导航| 精品国产美女在线| 极品白浆推特女神在线观看| 日韩免费视频一区| 亚洲特级黄色片| 欧美小视频在线| 国产亚洲精品av| 亚洲欧洲www| 中文字幕第20页| 91在线视频观看| 无码人妻久久一区二区三区蜜桃| 日韩av电影免费观看高清完整版| 精品无码一区二区三区爱欲| 婷婷伊人综合| 亚洲伊人婷婷| 少妇精品久久久| 精品久久久久久一区| 日韩精品视频在线看| 国产精品自产拍高潮在线观看| 无遮挡爽大片在线观看视频| 欧美国产在线视频| а√天堂在线官网| 色爱精品视频一区| 国产51人人成人人人人爽色哟哟| 日韩精品极品毛片系列视频| 蜜桃视频在线观看www| 欧美成人精品二区三区99精品| 国产欧美综合视频| 7777女厕盗摄久久久| 亚洲天堂aaa| 欧美剧情片在线观看| 中文字幕在线观看免费| 欧美亚洲一区二区在线观看| 婷婷激情五月综合| 91九色最新地址| 综合网在线观看| 欧美日韩亚洲系列| 国产又黄又猛又粗又爽| 欧美性猛交xxxx乱大交极品| 麻豆久久久久久久久久| 欧美午夜视频一区二区| caoporn国产| 在线观看日韩电影| 中文字幕av影视| 欧美日韩mp4| aaa一区二区| 精品国产乱码久久久久久久久| 亚洲国产精品二区| 精品国产伦一区二区三区观看体验 | 99久久一区三区四区免费| 激情视频亚洲| 国产精品18毛片一区二区| 动漫3d精品一区二区三区乱码| 国产欧美日韩亚洲| 最新国产一区| 亚洲欧洲精品一区二区三区波多野1战4| 欧美一区二区麻豆红桃视频| 中文字幕欧美日韩一区二区| 一区二区三区网站| 日本熟妇人妻xxxx| 亚洲综合不卡| 国产wwwxx| 国产精品综合在线视频| 无码国产精品一区二区免费式直播 | 欧洲亚洲妇女av| 日韩一区二区三区免费| 国产精品亚洲网站| 日韩免费成人| 精品一区在线播放| 日韩情爱电影在线观看| 免费看黄色a级片| 亚洲免费大片| 国产精品无码一本二本三本色| 久久电影网电视剧免费观看| 老司机av网站| 国产人成一区二区三区影院| 免费成人深夜夜行网站| 亚洲成人自拍一区| www.久久网| 精品奇米国产一区二区三区| 丝袜+亚洲+另类+欧美+变态| 一区二区三区亚洲| 3d玉蒲团在线观看| 日本一本a高清免费不卡| 亚洲欧美在线人成swag| 国产精选在线观看91| 日韩黄色大片| 精品少妇人妻av免费久久洗澡| 裸体素人女欧美日韩| 中文字幕日韩综合| 91视频国产观看| 久久人妻无码aⅴ毛片a片app | 欧美午夜网站| 欧美久久久久久久| 欧美网站在线| 杨幂毛片午夜性生毛片| 成人av在线影院| 色哟哟一一国产精品| 日韩欧美亚洲综合| 国产丰满美女做爰| 国产亚洲免费的视频看| 羞羞的视频在线观看| 国产精品久久久久一区二区| 欧美18免费视频| 路边理发店露脸熟妇泻火| 日韩专区欧美专区| 中文字幕在线播放视频| 亚洲精品成人a在线观看| 欧美性受xxx黑人xyx性爽| 亚洲激情在线观看视频免费| 亚洲妇熟xxxx妇色黄| 国产日韩欧美夫妻视频在线观看 | 亚洲一区二区三区色| 国产欧美日韩一级| 欧美图片自拍偷拍| 亚洲天堂成人在线观看| 真实的国产乱xxxx在线91| 日韩电影中文字幕一区| 欧美6一10sex性hd| 亚洲尤物视频网| 欧美国产一区二区三区激情无套| aaa毛片在线观看| 91在线免费视频观看| 国产真人真事毛片| 精品国产三级电影在线观看| 在线观看h网| 91黄色精品| 最新国产精品| 国产高清av片| 亚洲日韩欧美一区二区在线| 一二三区中文字幕| 色噜噜国产精品视频一区二区| 成人看片网站| 日韩av一区二区三区美女毛片| 免费日韩av片| 亚洲一区二区三区蜜桃| 色综合久久久网| 国产一级片在线| 国产精品精品视频| 人人狠狠综合久久亚洲婷| 国产性生交xxxxx免费| 国产欧美一区二区精品性色超碰| 亚洲s码欧洲m码国产av| 精品偷拍一区二区三区在线看| 精精国产xxxx视频在线播放| 黄色小网站91| 美女精品在线观看| 精品亚洲aⅴ无码一区二区三区| 91成人在线观看喷潮| 国产精品麻豆一区二区三区| 国产精品精品久久久| 99成人在线视频| 国产chinesehd精品露脸| 亚洲第一久久影院| 日韩一区av| 国产精品久久久久77777| 久久综合国产| 人妻少妇偷人精品久久久任期| 亚洲一区二区高清| 亚洲av成人精品一区二区三区在线播放 | 欧美午夜激情小视频| 九色视频网站在线观看| 国产精品亚洲美女av网站| 欧美激情在线| 黄色片视频免费观看| 欧美又粗又大又爽| av网站在线免费看推荐| 国内精品一区二区| 日韩成人午夜电影| 校园春色 亚洲| 亚洲精品视频久久| 欧美xxxx性| 国产3p露脸普通话对白| 久久久久久久综合狠狠综合| 一卡二卡在线观看| 97国产在线观看| 日本大胆欧美| 欧美激情 亚洲| 欧美色国产精品| 狠狠躁少妇一区二区三区| 一区二区三区|亚洲午夜| 成人精品一区二区三区中文字幕| 亚洲 日本 欧美 中文幕| 免费99精品国产自在在线| 亚洲性视频大全| 亚洲精品乱码久久久久久动漫| 精品久久久久人成| 国产写真视频在线观看| 六十路精品视频| 粉嫩嫩av羞羞动漫久久久| 成人黄色三级视频| 97色在线播放视频| 性欧美欧美巨大69| 成人性生交大免费看|