成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

入門 Transformer:概念、代碼與流程詳解

人工智能 開發
本文介紹了非常基礎的Transformer代碼實現。內容涵蓋了從embedding、位置編碼、多頭注意力到前饋網絡的所有內容,并解釋了它們如何最終結合在一起形成完整的架構。

引言

論文《Attention is All You Need》(Vaswani等,2017)提出了Transformer架構,這一模型通過完全摒棄標準的循環神經網絡(RNN)組件,徹底改變了自然語言處理(NLP)領域。相反,它利用了一種稱為“注意力”的機制,讓模型在生成輸出時決定如何關注輸入的特定部分(如句子中的單詞)。在Transformer之前,基于RNN的模型(如LSTM)主導了NLP領域。這些模型一次處理一個詞元,難以有效捕捉長距離依賴關系。而Transformer則通過并行化數據流,并依賴注意力機制來識別詞元之間的重要關系。這一步驟帶來了巨大的影響,推動了機器翻譯、文本生成(如GPT)甚至計算機視覺任務等領域的巨大飛躍。

在深入Transformer模型的實現之前,強烈建議對深度學習概念有基本的了解。熟悉神經網絡、embedding、激活函數和優化技術等主題,將更容易理解代碼并掌握Transformer的工作原理。如果你對這些概念不熟悉,可以考慮探索深度學習框架的入門資源,以及反向傳播等基礎主題。

導入庫

我們將使用PyTorch作為深度學習框架。PyTorch提供了構建和訓練神經網絡所需的所有基本工具:

import torch
import torch.nn as nn
import math

這些導入包括:

  • torch:PyTorch主庫。
  • torch.nn:包含與神經網絡相關的類和函數,如nn.Linear、nn.Dropout等。
  • math:用于常見的數學操作。

Transformer架構

輸入embedding

什么是embedding?

embedding是單詞或詞元的密集向量表示。與將單詞表示為獨熱編碼向量不同,embedding將每個單詞映射到低維連續向量空間。這些embedding捕捉了單詞之間的語義關系。例如,“男人”和“女人”的詞embedding在向量空間中的距離可能比“男人”和“狗”更近。

以下是embedding層的代碼:

class InputEmbedding(nn.Module):
    def __init__(self, d_model: int, vocab_size: int):
        super().__init__()
        self.d_model = d_model
        self.vocab_size = vocab_size
        self.embedding = nn.Embedding(vocab_size, d_model)

    def forward(self, x):
        return self.embedding(x) * math.sqrt(self.d_model)

解釋:

  • nn.Embedding:將單詞索引轉換為大小為d_model的密集向量。
  • 縮放因子sqrt(d_model):論文中使用此方法在訓練期間穩定梯度。

示例:

如果詞匯表大小為6(例如,詞元如[“Bye”, “Hello”等]),且d_model為512,embedding層將每個詞元映射到一個512維向量。

位置編碼

什么是位置編碼?

Transformer并行處理輸入序列,因此它們缺乏固有的順序概念(與RNN不同,RNN是順序處理詞元的)。位置編碼被添加到embedding中,以向模型提供詞元在序列中的相對或絕對位置信息。

以下是位置編碼的代碼:

class PositionalEncoding(nn.Module):
    def __init__(self, d_model: int, seq_len: int, dropout: float) -> None:
        super().__init__()
        self.d_model = d_model
        self.seq_len = seq_len  
        self.dropout = nn.Dropout(dropout)

        pe = torch.zeros(seq_len, d_model)
        position = torch.arange(0, seq_len).unsqueeze(1).float()
        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.shape[1], :]).requires_grad_(False)
        return self.dropout(x)

解釋:

  • 正弦函數:編碼在偶數和奇數維度之間交替使用正弦和余弦函數。
  • 為什么使用正弦函數?這些函數允許模型泛化到比訓練時更長的序列。
  • register_buffer:確保位置編碼與模型一起保存,但在訓練期間不更新。

我們只需要計算一次位置編碼,然后為每個句子重復使用。

層歸一化

什么是層歸一化?

層歸一化是一種通過跨特征維度歸一化輸入來穩定和加速訓練的技術。它確保每個輸入向量的均值為0,方差為1。

我們還引入了兩個參數,gamma(乘法)和beta(加法),它們會在數據中引入波動。網絡將學習調整這兩個可學習參數,以在需要時引入波動。

以下是代碼:

class LayerNormalization(nn.Module):
    def __init__(self, eps: float = 1e-6) -> None:
        super().__init__()
        self.eps = eps
        self.alpha = nn.Parameter(torch.ones(1))
        self.bias = nn.Parameter(torch.zeros(1))

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

解釋:

  • alpha和bias:可學習參數,用于縮放和偏移歸一化后的輸出。
  • eps:添加到分母中的小值,以防止除以零。

前饋塊

什么是前饋塊?

它是一個簡單的兩層神經網絡,獨立應用于序列中的每個位置。它幫助模型學習復雜的變換。以下是代碼:

class FeedForwardBlock(nn.Module):
    def __init__(self, d_model: int, d_ff: int, dropout: float) -> None:
        super().__init__()
        self.linear_1 = nn.Linear(d_model, d_ff)
        self.dropout = nn.Dropout(dropout)
        self.linear_2 = nn.Linear(d_ff, d_model)

    def forward(self, x):
        return self.linear_2(self.dropout(torch.relu(self.linear_1(x))))

解釋:

  • 第一層線性層:將輸入維度從d_model擴展到d_ff,即512 → 2048。
  • ReLU激活:增加非線性。
  • 第二層線性層:投影回d_model。

多頭注意力

什么是注意力?

注意力機制允許模型在做出預測時關注輸入的相關部分。它計算值(V)的加權和,其中權重由查詢(Q)和鍵(K)之間的相似性決定。

什么是多頭注意力?

與計算單一注意力分數不同,多頭注意力將輸入分成多個“頭h”,以學習不同類型的關系。

注意力

按頭計算的注意力

所有頭的注意力

  • Q(查詢):代表當前單詞或詞元。
  • K(鍵):代表序列中的所有單詞或詞元。
  • V(值):代表與每個單詞或詞元相關的信息。
  • Softmax:將相似性分數轉換為概率,使其總和為1。
  • 縮放因子sqrt(d_k):防止點積變得過大,從而破壞softmax函數的穩定性。

每個頭計算自己的注意力,結果被拼接并投影回原始維度。以下是多頭注意力塊的代碼:

class MultiHeadAttentionBlock(nn.Module):
    def __init__(self, d_model: int, h: int, dropout: float) -> None:  # h is number of heads
        super().__init__()
        self.d_model = d_model
        self.h = h

        # Check if d_model is divisible by num_heads
        assert d_model % h == 0, "d_model must be divisible by num_heads"

        self.d_k = d_model // h

        # Define matrices W_q, W_k, W_v , W_o
        self.w_q = nn.Linear(d_model, d_model)  # W_q
        self.w_k = nn.Linear(d_model, d_model)  # W_k
        self.w_v = nn.Linear(d_model, d_model)  # W_v
        self.w_o = nn.Linear(d_model, d_model)  # W_o
        self.dropout = nn.Dropout(dropout)

    @staticmethod
    def attention(query, key, value, d_k, mask=None, dropout=nn.Dropout):
        # Compute attention scores
        attention_scores = (query @ key.transpose(-2, -1)) / math.sqrt(d_k)
        if mask is not None:
            attention_scores.masked_fill(mask == 0, -1e9)  # Mask padding tokens
        attention_scores = torch.softmax(attention_scores, dim=-1)
        if dropout is not None:
            attention_scores = dropout(attention_scores)
        return (attention_scores @ value), attention_scores

    def forward(self, q, k, v, mask):
        # Compute Q, K, V
        query = self.w_q(q)
        key = self.w_k(k)
        value = self.w_v(v)

        # Split into multiple heads
        query = query.view(query.shape[0], query.shape[1], self.h, self.d_k).transpose(1, 2)
        key = key.view(key.shape[0], key.shape[1], self.h, self.d_k).transpose(1, 2)
        value = value.view(value.shape[0], value.shape[1], self.h, self.d_k).transpose(1, 2)

        # Compute attention
        x, self.attention_scores = MultiHeadAttentionBlock.attention(query, key, value, self.d_k, mask, self.dropout)

        # Concatenate heads
        x = x.transpose(1, 2).contiguous().view(x.shape[0], -1, self.h * self.d_k)

        # Final linear projection
        return self.w_o(x)

解釋:

  • 線性層(W_q, W_k, W_v):這些層將輸入轉換為查詢、鍵和值。
  • 分割成頭:輸入被分割成h個頭,每個頭的維度較小(d_k = d_model / h)。
  • 拼接:所有頭的輸出被拼接,并使用w_o投影回原始維度。

示例:

如果d_model=512且h=8,每個頭的維度為d_k=64。輸入被分割成8個頭,每個頭獨立計算注意力。結果被拼接回一個512維向量。

殘差連接和層歸一化

什么是殘差連接?

殘差連接將層的輸入添加到其輸出中。這有助于防止“梯度消失”問題。以下是殘差連接的代碼:

class ResidualConnection(nn.Module):
    def __init__(self, dropout: float) -> None:
        super().__init__()
        self.dropout = nn.Dropout(dropout)
        self.norm = LayerNormalization()

    def forward(self, x, sublayer):
        return x + self.dropout(sublayer(self.norm(x)))

解釋:

  • x:層的輸入。
  • sublayer:表示層的函數(如注意力或前饋塊)。
  • 輸出是輸入和子層輸出的和,隨后進行dropout。

1. 編碼器塊

編碼器塊結合了我們迄今為止討論的所有組件:多頭注意力、前饋塊、殘差連接和層歸一化。以下是編碼器塊的代碼:

class EncoderBlock(nn.Module):
    def __init__(self, self_attention_block: MultiHeadAttentionBlock, feed_forward_block: FeedForwardBlock, dropout: float) -> None:
        super().__init__()
        self.self_attention_block = self_attention_block
        self.feed_forward_block = feed_forward_block
        self.residual_connections = nn.ModuleList([ResidualConnection(dropout) for _ in range(2)])

    def forward(self, x, src_mask):
        x = self.residual_connections[0](x, lambda x: self.self_attention_block(x, x, x, src_mask))
        x = self.residual_connections[1](x, self.feed_forward_block)
        return x

解釋:

  • 自注意力:輸入對自身進行注意力計算,以捕捉詞元之間的關系。
  • 前饋塊:對每個詞元應用全連接網絡。
  • 殘差連接:將輸入添加回每個子層的輸出。

2. 解碼器塊

解碼器塊與編碼器塊類似,但包含一個額外的交叉注意力層。這使得解碼器能夠關注編碼器的輸出。以下是解碼器塊的代碼:

class DecoderBlock(nn.Module):
    def __init__(self, self_attention_block: MultiHeadAttentionBlock, cross_attention_block: MultiHeadAttentionBlock, feed_forward_block: FeedForwardBlock, dropout: float) -> None:
        super().__init__()
        self.self_attention_block = self_attention_block
        self.cross_attention_block = cross_attention_block
        self.feed_forward_block = feed_forward_block
        self.residual_connections = nn.ModuleList([ResidualConnection(dropout) for _ in range(3)])

    def forward(self, x, encoder_output, src_mask, tgt_mask):
        x = self.residual_connections[0](x, lambda x: self.self_attention_block(x, x, x, tgt_mask))
        x = self.residual_connections[1](x, lambda x: self.cross_attention_block(x, encoder_output, encoder_output, src_mask))
        x = self.residual_connections[2](x, self.feed_forward_block)
        return x

解釋:

  • 自注意力:解碼器對其自身輸出進行注意力計算。
  • 交叉注意力:解碼器對編碼器的輸出進行注意力計算。
  • 前饋塊:對每個詞元應用全連接網絡。

3. 最終線性層

該層將解碼器的輸出投影到詞匯表大小,將embedding為每個單詞的概率。該層將包含一個線性層和一個softmax層。這里使用log_softmax以避免下溢并使此步驟在數值上更穩定。以下是最終線性層的代碼:

class ProjectionLayer(nn.Module):
    def __init__(self, d_model: int, vocab_size: int) -> None:
        super().__init__()
        self.proj = nn.Linear(d_model, vocab_size)

    def forward(self, x):
        return torch.log_softmax(self.proj(x), dim=-1)

解釋:

  • 輸入:解碼器的輸出(形狀:[batch, seq_len, d_model])。
  • 輸出:詞匯表中每個單詞的對數概率(形狀:[batch, seq_len, vocab_size])。

Transformer模型

Transformer將所有內容結合在一起:編碼器、解碼器、embedding、位置編碼和投影層。以下是代碼:

class Transformer(nn.Module):
    def __init__(self, encoder: Encoder, decoder: Decoder, src_embed: InputEmbedding, tgt_embed: InputEmbedding, src_pos: PositionalEncoding, tgt_pos: PositionalEncoding, projection_layer: ProjectionLayer) -> None:
        super().__init__()
        self.encoder = encoder
        self.decoder = decoder
        self.src_embed = src_embed
        self.tgt_embed = tgt_embed
        self.src_pos = src_pos
        self.tgt_pos = tgt_pos
        self.projection_layer = projection_layer

    def encode(self, src, src_mask):
        src = self.src_embed(src)
        src = self.src_pos(src)
        return self.encoder(src, src_mask)

    def decode(self, encoder_output, tgt, src_mask, tgt_mask):
        tgt = self.tgt_embed(tgt)
        tgt = self.tgt_pos(tgt)
        return self.decoder(tgt, encoder_output, src_mask, tgt_mask)

    def project(self, decoder_output):
        return self.projection_layer(decoder_output)

最終構建函數塊

最后一個塊是一個輔助函數,通過組合我們迄今為止看到的所有組件來構建整個Transformer模型。它允許我們指定論文中使用的超參數。以下是代碼:

def build_transformer(src_vocab_size: int, tgt_vocab_size: int, src_seq_len: int, tgt_seq_len: int, 
                      d_model: int = 512, N: int = 6, h: int = 8, dropout: float = 0.1, d_ff: int = 2048) -> Transformer:
    # Create the embedding layers for source and target
    src_embed = InputEmbedding(d_model, src_vocab_size)
    tgt_embed = InputEmbedding(d_model, tgt_vocab_size)

    # Create positional encoding layers for source and target
    src_pos = PositionalEncoding(d_model, src_seq_len, dropout)
    tgt_pos = PositionalEncoding(d_model, tgt_seq_len, dropout)

    # Create the encoder blocks
    encoder_blocks = []
    for _ in range(N):
        encoder_self_attention_block = MultiHeadAttentionBlock(d_model, h, dropout)
        feed_forward_block = FeedForwardBlock(d_model, d_ff, dropout)
        encoder_block = EncoderBlock(encoder_self_attention_block, feed_forward_block, dropout)
        encoder_blocks.append(encoder_block)

    # Create the decoder blocks
    decoder_blocks = []
    for _ in range(N):
        decoder_self_attention_block = MultiHeadAttentionBlock(d_model, h, dropout)
        decoder_cross_attention_block = MultiHeadAttentionBlock(d_model, h, dropout)
        feed_forward_block = FeedForwardBlock(d_model, d_ff, dropout)
        decoder_block = DecoderBlock(decoder_self_attention_block, decoder_cross_attention_block, feed_forward_block, dropout)
        decoder_blocks.append(decoder_block)

    # Create the encoder and decoder
    encoder = Encoder(nn.ModuleList(encoder_blocks))
    decoder = Decoder(nn.ModuleList(decoder_blocks))

    # Create the projection layer
    projection_layer = ProjectionLayer(d_model, tgt_vocab_size)

    # Create the Transformer model
    transformer = Transformer(encoder, decoder, src_embed, tgt_embed, src_pos, tgt_pos, projection_layer)

    # Initialize the parameters using Xavier initialization
    for p in transformer.parameters():
        if p.dim() > 1:
            nn.init.xavier_uniform_(p)

    return transformer

該函數接受以下超參數作為輸入:

  • src_vocab_size:源詞匯表的大小(源語言中唯一詞元的數量)。
  • tgt_vocab_size:目標詞匯表的大小(目標語言中唯一詞元的數量)。
  • src_seq_len:源輸入的最大序列長度。
  • tgt_seq_len:目標輸入的最大序列長度。
  • d_model:模型的維度(默認:512)。
  • N:編碼器和解碼器塊的數量(默認:6)。
  • h:多頭注意力機制中的注意力頭數量(默認:8)。
  • dropout:防止過擬合的dropout率(默認:0.1)。
  • d_ff:前饋網絡的維度(默認:2048)。

我們可以使用此函數創建具有所需超參數的Transformer模型。例如:

src_vocab_size = 10000
tgt_vocab_size = 10000
src_seq_len = 50
tgt_seq_len = 50

transformer = build_transformer(src_vocab_size, tgt_vocab_size, src_seq_len, tgt_seq_len)

總結 — “Attention is All You Need” 核心貢獻

  • 并行化:RNN按順序處理單詞,而Transformer并行處理整個句子。這大大減少了訓練時間。
  • 多功能性:注意力機制可以適應各種任務,如翻譯、文本分類、問答、計算機視覺、語音識別等。
  • 為基礎模型鋪路:該架構為BERT、GPT和T5等大規模語言模型鋪平了道路。
責任編輯:趙寧寧 來源: 小白玩轉Python
相關推薦

2025-05-20 08:15:00

AI大模型架構

2010-03-19 13:53:36

云計算

2015-07-23 11:36:28

GIT入門

2022-09-13 08:00:00

協議緩存區編程語言系統

2011-07-28 13:47:20

Xcode Interface

2022-07-22 07:18:53

代碼DeepMind

2017-11-29 15:21:53

PythonScrapy爬蟲

2009-12-16 14:51:26

Ruby nil

2023-09-14 09:54:01

Btrfs文件系統

2010-05-17 13:39:10

MySQL Clust

2024-07-15 09:58:03

OpenRestyNginx日志

2017-09-04 22:41:31

深度學習矩陣運算代碼

2024-04-18 08:28:06

2016-08-18 01:26:22

數據挖掘

2010-06-17 15:30:27

WAP協議

2010-02-02 11:16:28

C++異常

2010-07-12 13:20:18

UML協作圖

2010-07-14 15:23:19

Perl文件句柄

2009-12-28 16:54:30

WPF注釋

2009-12-18 15:24:52

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 殴美成人在线视频 | 欧美日韩精品一区二区三区蜜桃 | 国产精品久久久一区二区三区 | 一级黄色在线 | 中文字幕精品一区 | 久久久久久成人 | 亚洲国产精品久久人人爱 | 国产精品国产三级国产aⅴ原创 | 国产精品成人一区二区三区吃奶 | 先锋资源吧 | 在线免费观看色 | 午夜精品久久久久久不卡欧美一级 | 天堂视频一区 | 综合国产在线 | 亚洲天堂精品一区 | 日本一区高清 | 欧美a在线| 亚洲区一区二 | 亚洲一区 中文字幕 | 国产视频一区二区在线观看 | 涩涩操 | 欧美日韩专区 | 狠狠av| 特黄特黄a级毛片免费专区 av网站免费在线观看 | www.久| 人人干人人爽 | 国产九九九九 | 综合久久久久 | 日本一区二区三区四区 | 日韩欧美在线不卡 | 欧美视频一区 | 欧美日韩国产免费 | 一区二区国产在线 | 国产高清免费视频 | 婷婷丁香综合网 | 一二三在线视频 | 免费一区二区三区 | 精品国产乱码久久久久久闺蜜 | 秋霞a级毛片在线看 | 成人免费大片黄在线播放 | 免费午夜视频在线观看 |