網站首頁 編程語言 正文
文章目錄
- 摘要
- 一、Transformer
- 1.1 為什么要使用attention
- 1.2 Transformer的優點
- 二、Transformer模型Encoder和Decoder原理講解與其Pytorch逐行實現
- 2.1 word embedding
- 2.2 單詞索引構成源句子和目標句子
- 2.3 構建position embedding
- 2.4 構造encoder的self-attention mask
- 2.5 構造intra-attention的mask
- 2.6 構成decoder self-attention的mask
- 2.7 構建scaled self-attention
- 總結
摘要
本周學習了Transformer模型,以及Encoder和Decoder原理講解與其Pytorch逐行實現,主要從6個細節難點去學習了Transformer的原理與實現,詳細地學習了Transformer的word embedding、position embedding和mask原理。
一、Transformer
文獻參考:https://arxiv.org/abs/1706.03762
文獻題目:Attention Is All You Need
1.1 為什么要使用attention
1)因為t時刻的計算依賴于t-1時刻的計算結果,這樣會導致模型的并行能力受到限制。
2)雖然在之前學習到的LSTM可以在一定程度上緩解長期依賴的問題,但是對于特別長期的依賴問題,LSTM是無法解決問題的,也就是說在encoder和decoder之間的語義向量無法保存過多的信息。
1.2 Transformer的優點
它采用了Attention機制,將序列中的任意兩個位置之間的距離是縮小為一個常量,并且它不類似于RNN的順序結構,因此具有較好的并行性,也符合現有的GPU框架。
在論文中,Transformer沒有使用到傳統的CNN和RNN,整個網絡由Attention機制組成。
二、Transformer模型Encoder和Decoder原理講解與其Pytorch逐行實現
如下圖所示,這6個方面是Transformer學習的難點。
2.1 word embedding
關于word embedding,以序列建模為例,考慮source sentence 和 target sentence。
構建序列,序列的字符以其在詞表中的索引的形式表示。
src_seq:輸入的句子,第一個長度為2,第二個長度為4。
tgt_seq:目標的句子,第一個長度為4,第二個長度為3。
import numpy as np
import torch
import numpy
import torch.nn as nn
import torch.nn.functional as F
batch_size = 2
# 單詞表大小
max_num_src_words = 8
max_num_tgt_words = 8
# 序列的最大長度
max_src_seq_len = 5
max_tgt_seq_len = 5
model_dim = 8
max_position_len = 5
src_len = torch.Tensor([2, 4]).to(torch.int32)
tgt_len = torch.Tensor([4, 3]).to(torch.int32)
print(src_len)
print(tgt_len)
2.2 單詞索引構成源句子和目標句子
Step 1:用隨機數生成句子單詞索引,對句子做了padding,也就是對空白位置補0,以保證所有句子的長度一致,最后用torch,cat將兩個句子張量拼接在一起。
src_seq = torch.cat([torch.unsqueeze(F.pad(torch.randint(1, max_num_src_words, (L,)), (0, max_src_seq_len - L)), 0)
for L in src_len])
tgt_seq = torch.cat([torch.unsqueeze(F.pad(torch.randint(1, max_num_tgt_words, (L,)), (0, max_tgt_seq_len - L)), 0)
for L in tgt_len])
print(src_seq)
print(tgt_seq)
輸出源句子和目標句子
tensor([[6, 7, 0, 0, 0],
[7, 1, 5, 7, 0]])
tensor([[1, 5, 6, 2, 0],
[4, 7, 5, 0, 0]])
構造embedding
Step 2:構建word embedding,我們需要得到向量化的句子,也就是先構建詞表embedding,再利用索引得到向量化的句子。
src_embedding_table = nn.Embedding(max_num_src_words + 1, model_dim)
tgt_embedding_table = nn.Embedding(max_num_tgt_words + 1, model_dim)
src_embedding = src_embedding_table(src_seq)
tgt_embedding = tgt_embedding_table(tgt_seq)
print(src_embedding)
print(tgt_embedding)
輸出src_embedding和tgt_embedding
tensor([[[ 4.0562e-01, -4.7774e-01, 1.1197e+00, 2.5271e-01, -2.8610e-01,
-1.7786e+00, -3.4801e-01, 5.9147e-02],
[ 4.0562e-01, -4.7774e-01, 1.1197e+00, 2.5271e-01, -2.8610e-01,
-1.7786e+00, -3.4801e-01, 5.9147e-02],
[-1.1680e-01, 2.6256e-01, 9.0318e-01, 2.5852e-01, -6.7806e-01,
-3.2029e-01, 5.9314e-01, 1.4338e-01],
[-1.1680e-01, 2.6256e-01, 9.0318e-01, 2.5852e-01, -6.7806e-01,
-3.2029e-01, 5.9314e-01, 1.4338e-01],
[-1.1680e-01, 2.6256e-01, 9.0318e-01, 2.5852e-01, -6.7806e-01,
-3.2029e-01, 5.9314e-01, 1.4338e-01]],
[[ 2.0992e+00, 4.3112e-01, 1.1805e-03, 1.9759e-01, -7.2922e-01,
-2.1830e+00, -1.0120e-02, -4.0442e-01],
[ 1.7823e-01, -4.7776e-01, -1.1815e+00, -1.3451e+00, -1.1886e+00,
1.7289e+00, -3.9730e-01, 6.2238e-01],
[-1.0617e+00, -2.1093e-01, -1.1825e+00, -6.9534e-01, -4.2309e-01,
3.7529e-01, -1.8998e-01, 1.1653e-02],
[-1.0617e+00, -2.1093e-01, -1.1825e+00, -6.9534e-01, -4.2309e-01,
3.7529e-01, -1.8998e-01, 1.1653e-02],
[-1.1680e-01, 2.6256e-01, 9.0318e-01, 2.5852e-01, -6.7806e-01,
-3.2029e-01, 5.9314e-01, 1.4338e-01]]],
grad_fn=<EmbeddingBackward0>)
tensor([[[-0.6926, 0.3298, 0.8671, -3.5071, 1.3463, -0.5505, -0.5561,
-0.7192],
[-1.7076, -0.5672, -0.1689, 0.8521, 1.2127, 0.6972, 0.6094,
0.5606],
[-0.6211, -0.0036, 1.6586, -0.0787, 0.2682, -1.5178, -1.5031,
-1.7362],
[-1.3177, 1.1583, 0.4895, 0.9380, 0.8419, -0.0501, -1.0925,
-0.7124],
[ 1.5036, -0.3206, -0.2090, -0.2941, -0.0675, -0.1443, -0.5557,
0.5938]],
[[-1.7076, -0.5672, -0.1689, 0.8521, 1.2127, 0.6972, 0.6094,
0.5606],
[-0.5977, -0.7804, -1.0782, 1.7742, 2.0498, 0.4659, -1.1477,
1.5279],
[-0.6211, -0.0036, 1.6586, -0.0787, 0.2682, -1.5178, -1.5031,
-1.7362],
[ 1.5036, -0.3206, -0.2090, -0.2941, -0.0675, -0.1443, -0.5557,
0.5938],
[ 1.5036, -0.3206, -0.2090, -0.2941, -0.0675, -0.1443, -0.5557,
0.5938]]], grad_fn=<EmbeddingBackward0>)
2.3 構建position embedding
如下圖所示,我們根據論文中的公式去構建position embedding。
Step 3:pe_embedding_table[:, 0::2]得到偶數列編碼,pe_embedding_table[:, 1::2]得到奇數列編碼。
構建位置索引,再通過nn.embedding就可以直接得到position embedding。
pos_mat = torch.arange(max_position_len).reshape((-1, 1))
i_mat = torch.pow(10000, torch.arange(0, 8, 2).reshape(1, -1) / model_dim)
pe_embedding_table = torch.zeros(max_position_len, model_dim)
# 偶數列
pe_embedding_table[:, 0::2] = torch.sin(pos_mat / i_mat)
# 奇數列
pe_embedding_table[:, 1::2] = torch.cos(pos_mat / i_mat)
# print(pos_mat)
# print(i_mat)
# print(pe_embedding_table)
pe_embedding = nn.Embedding(max_position_len, model_dim)
pe_embedding.weight = nn.Parameter(pe_embedding_table, requires_grad=False)
src_pos = torch.cat([torch.unsqueeze(torch.arange(max(src_len)), 0) for _ in src_len]).to(torch.int32)
tgt_pos = torch.cat([torch.unsqueeze(torch.arange(max(tgt_len)), 0) for _ in tgt_len]).to(torch.int32)
src_pe_embedding = pe_embedding(src_pos)
tgt_pe_embedding = pe_embedding(tgt_pos)
print(src_pe_embedding)
print(tgt_pe_embedding)
輸出src_pe_embedding和tgt_pe_embedding
tensor([[[ 0.0000e+00, 1.0000e+00, 0.0000e+00, 1.0000e+00, 0.0000e+00,
1.0000e+00, 0.0000e+00, 1.0000e+00],
[ 8.4147e-01, 5.4030e-01, 9.9833e-02, 9.9500e-01, 9.9998e-03,
9.9995e-01, 1.0000e-03, 1.0000e+00],
[ 9.0930e-01, -4.1615e-01, 1.9867e-01, 9.8007e-01, 1.9999e-02,
9.9980e-01, 2.0000e-03, 1.0000e+00],
[ 1.4112e-01, -9.8999e-01, 2.9552e-01, 9.5534e-01, 2.9995e-02,
9.9955e-01, 3.0000e-03, 1.0000e+00]],
[[ 0.0000e+00, 1.0000e+00, 0.0000e+00, 1.0000e+00, 0.0000e+00,
1.0000e+00, 0.0000e+00, 1.0000e+00],
[ 8.4147e-01, 5.4030e-01, 9.9833e-02, 9.9500e-01, 9.9998e-03,
9.9995e-01, 1.0000e-03, 1.0000e+00],
[ 9.0930e-01, -4.1615e-01, 1.9867e-01, 9.8007e-01, 1.9999e-02,
9.9980e-01, 2.0000e-03, 1.0000e+00],
[ 1.4112e-01, -9.8999e-01, 2.9552e-01, 9.5534e-01, 2.9995e-02,
9.9955e-01, 3.0000e-03, 1.0000e+00]]])
tensor([[[ 0.0000e+00, 1.0000e+00, 0.0000e+00, 1.0000e+00, 0.0000e+00,
1.0000e+00, 0.0000e+00, 1.0000e+00],
[ 8.4147e-01, 5.4030e-01, 9.9833e-02, 9.9500e-01, 9.9998e-03,
9.9995e-01, 1.0000e-03, 1.0000e+00],
[ 9.0930e-01, -4.1615e-01, 1.9867e-01, 9.8007e-01, 1.9999e-02,
9.9980e-01, 2.0000e-03, 1.0000e+00],
[ 1.4112e-01, -9.8999e-01, 2.9552e-01, 9.5534e-01, 2.9995e-02,
9.9955e-01, 3.0000e-03, 1.0000e+00]],
[[ 0.0000e+00, 1.0000e+00, 0.0000e+00, 1.0000e+00, 0.0000e+00,
1.0000e+00, 0.0000e+00, 1.0000e+00],
[ 8.4147e-01, 5.4030e-01, 9.9833e-02, 9.9500e-01, 9.9998e-03,
9.9995e-01, 1.0000e-03, 1.0000e+00],
[ 9.0930e-01, -4.1615e-01, 1.9867e-01, 9.8007e-01, 1.9999e-02,
9.9980e-01, 2.0000e-03, 1.0000e+00],
[ 1.4112e-01, -9.8999e-01, 2.9552e-01, 9.5534e-01, 2.9995e-02,
9.9955e-01, 3.0000e-03, 1.0000e+00]]])
2.4 構造encoder的self-attention mask
Step 4:因為句子的長度不一,于是我們會用padding去補0,之后便可得到有效位置矩陣。現在我們需要去得到有效矩陣,于是我們通過torch.bmm將有效位置矩陣與有效位置矩陣的轉置矩陣的1,2維進行相乘(0維是batch維,不參與運算),便可得到有效矩陣。
valid_encoder_pos = torch.unsqueeze(torch.cat([torch.unsqueeze(F.pad(torch.ones(L), (0, max(src_len) - L)), 0)
for L in src_len]), 2)
valid_encoder_pos_matrix = torch.bmm(valid_encoder_pos, valid_encoder_pos.transpose(1, 2))
print(valid_encoder_pos_matrix)
輸出有效矩陣
tensor([[[1., 1., 0., 0.],
[1., 1., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
我們用1減去有效矩陣,并將其賦成bool型便可得到無效矩陣。
invalid_encoder_pos_matrix = 1 - valid_encoder_pos_matrix
mask_encoder_self_attention = invalid_encoder_pos_matrix.to(torch.bool)
print(invalid_encoder_pos_matrix)
print(mask_encoder_self_attention)
輸出無效矩陣
tensor([[[False, False, True, True],
[False, False, True, True],
[ True, True, True, True],
[ True, True, True, True]],
[[False, False, False, False],
[False, False, False, False],
[False, False, False, False],
[False, False, False, False]]])
通過softmax得到mask矩陣。
score = torch.randn(batch_size, max(src_len), max(src_len))
masked_score = score.masked_fill(mask_encoder_self_attention, -1e9)
prob = F.softmax(masked_score, -1)
print(src_len)
print(score)
print(masked_score)
print(prob)
查看輸出結果
tensor([2, 4], dtype=torch.int32)
tensor([[[-0.2043, -0.3917, -1.0137, -1.1826],
[-0.0821, 0.7781, -1.1928, 0.2586],
[-0.2179, 0.8631, -0.9092, 0.0432],
[ 0.3567, 1.0154, -1.0005, 0.2797]],
[[-0.9328, -0.6068, 1.0793, 1.0886],
[-0.8354, 0.6814, 1.3121, 1.2134],
[ 2.0781, -0.4271, -2.4216, 0.0345],
[-0.9104, -1.5460, 0.7630, 0.5593]]])
tensor([[[-2.0433e-01, -3.9166e-01, -1.0000e+09, -1.0000e+09],
[-8.2061e-02, 7.7813e-01, -1.0000e+09, -1.0000e+09],
[-1.0000e+09, -1.0000e+09, -1.0000e+09, -1.0000e+09],
[-1.0000e+09, -1.0000e+09, -1.0000e+09, -1.0000e+09]],
[[-9.3281e-01, -6.0677e-01, 1.0793e+00, 1.0886e+00],
[-8.3541e-01, 6.8140e-01, 1.3121e+00, 1.2134e+00],
[ 2.0781e+00, -4.2715e-01, -2.4216e+00, 3.4491e-02],
[-9.1038e-01, -1.5460e+00, 7.6298e-01, 5.5926e-01]]])
tensor([[[0.5467, 0.4533, 0.0000, 0.0000],
[0.2973, 0.7027, 0.0000, 0.0000],
[0.2500, 0.2500, 0.2500, 0.2500],
[0.2500, 0.2500, 0.2500, 0.2500]],
[[0.0574, 0.0796, 0.4295, 0.4335],
[0.0457, 0.2083, 0.3914, 0.3546],
[0.8181, 0.0668, 0.0091, 0.1060],
[0.0892, 0.0473, 0.4756, 0.3879]]])
2.5 構造intra-attention的mask
這里的步驟和Step 4差不多,但是要注意Q @ K ^ T,即decoder Q與encoder K ^ T的相關有效性。
valid_decoder_pos = torch.unsqueeze(torch.cat([torch.unsqueeze(F.pad(torch.ones(L), (0, max(tgt_len) - L)), 0)
for L in tgt_len]), 2)
valid_cross_pos_matrix = torch.bmm(valid_decoder_pos, valid_encoder_pos.transpose(1, 2))
invalid_cross_pos_matrix = 1 - valid_cross_pos_matrix
mask_cross_attention = invalid_cross_pos_matrix.to(torch.bool)
print(valid_cross_pos_matrix)
print(invalid_cross_pos_matrix)
print(mask_cross_attention)
查看輸出結果
tensor([[[1., 1., 0., 0.],
[1., 1., 0., 0.],
[1., 1., 0., 0.],
[1., 1., 0., 0.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[0., 0., 0., 0.]]])
tensor([[[0., 0., 1., 1.],
[0., 0., 1., 1.],
[0., 0., 1., 1.],
[0., 0., 1., 1.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[1., 1., 1., 1.]]])
tensor([[[False, False, True, True],
[False, False, True, True],
[False, False, True, True],
[False, False, True, True]],
[[False, False, False, False],
[False, False, False, False],
[False, False, False, False],
[ True, True, True, True]]])
2.6 構成decoder self-attention的mask
這里的步驟和Step 4差不多,但是這里用到torch.tril生成三角矩陣,并且對行和列都進行了補0。
valid_decoder_tri_matrix = torch.cat([torch.unsqueeze(F.pad(torch.tril(torch.ones((L, L))),
(0, max(tgt_len) - L, 0, max(tgt_len) - L)), 0)
for L in tgt_len])
invalid_decoder_tri_matrix = 1 - valid_decoder_tri_matrix
invalid_decoder_tri_matrix = invalid_decoder_tri_matrix.to(torch.bool)
print(valid_decoder_tri_matrix)
print(invalid_decoder_tri_matrix)
查看三角矩陣
tensor([[[1., 0., 0., 0.],
[1., 1., 0., 0.],
[1., 1., 1., 0.],
[1., 1., 1., 1.]],
[[1., 0., 0., 0.],
[1., 1., 0., 0.],
[1., 1., 1., 0.],
[0., 0., 0., 0.]]])
tensor([[[False, True, True, True],
[False, False, True, True],
[False, False, False, True],
[False, False, False, False]],
[[False, True, True, True],
[False, False, True, True],
[False, False, False, True],
[ True, True, True, True]]])
通過無效矩陣做softmax,得到mask。
score = torch.randn(batch_size, max(tgt_len), max(tgt_len))
masked_score = score.masked_fill(invalid_decoder_tri_matrix, -1e9)
prob = F.softmax(masked_score, -1)
print(tgt_len)
print(prob)
輸出得到的mask
tensor([4, 3], dtype=torch.int32)
tensor([[[1.0000, 0.0000, 0.0000, 0.0000],
[0.3802, 0.6198, 0.0000, 0.0000],
[0.6869, 0.2416, 0.0716, 0.0000],
[0.1204, 0.1475, 0.1840, 0.5481]],
[[1.0000, 0.0000, 0.0000, 0.0000],
[0.5099, 0.4901, 0.0000, 0.0000],
[0.1156, 0.8410, 0.0434, 0.0000],
[0.2500, 0.2500, 0.2500, 0.2500]]])
最后一行是因為補了一行的0,然后概率相等,各自都是0.2500。
2.7 構建scaled self-attention
def scaled_dot_product_attention(Q, K, V, attn_mask):
# shape of Q,K,V : (batch_size*num_head. seq_len, model_dim/num_head)
score = torch.bmm(Q, K.transpose(-2, -1)/torch.sqrt(model_dim))
masked_score = score.masked_fill(attn_mask, -1e9)
prob = F.softmax(masked_score, -1)
context = torch.bmm(prob, V)
return context
總結
在本周的學習中,主要學習了Transformer模型,是一種避免循環的模型結構,完全依賴于注意力機制對輸入輸出的全局依賴關系進行建模。Transformer模型的優勢是突破了 RNN 模型不能并行計算的限制;相比CNN來說,計算兩個位置之間的關聯所需的操作次數不隨距離增長;自注意力可以產生更具可解釋性的模型。本周對論文只是進行簡單地閱讀,在下周會對論文進行詳細地閱讀并理解,并對Transformer模型進行詳細地解釋說明。
原文鏈接:https://blog.csdn.net/peaunt1/article/details/127040300
相關推薦
- 2022-12-29 python查看包版本、更新單個包、卸載單個包的操作方法_python
- 2022-12-22 postgresql13主從搭建Ubuntu_PostgreSQL
- 2022-08-30 如何解決React?useEffect鉤子帶來的無限循環問題_React
- 2023-12-21 npm install 報錯(npm ERR! errno: -4048, npm ERR! c
- 2022-05-25 Python學習之文件的創建與寫入詳解_python
- 2023-10-12 利用touch-action解決驗證碼滑塊滑動時,背景跟隨一起滑動的問題,以及詳解touch-act
- 2022-04-01 k8s Error: could not find tiller
- 2022-05-18 一起來了解React的Hook_React
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支