成人av在线资源一区,亚洲av日韩av一区,欧美丰满熟妇乱XXXXX图片,狠狠做五月深爱婷婷伊人,桔子av一区二区三区,四虎国产精品永久在线网址,国产尤物精品人妻在线,中文字幕av一区二区三区欲色
    您正在使用IE低版瀏覽器,為了您的雷峰網賬號安全和更好的產品體驗,強烈建議使用更快更安全的瀏覽器
    此為臨時鏈接,僅用于文章預覽,將在時失效
    人工智能開發者 正文
    發私信給skura
    發送

    0

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    本文作者: skura 2020-02-25 09:18
    導語:讓我們在現有資源的基礎上,用代碼實現 GPT-2 吧~

    歡迎來到「帶注釋的 GPT-2」。              

    我讀過的最精彩、解釋最清楚的文章之一是「The Annotated Transformer」https://nlp.seas.harvard.edu/2018/04/03/attention.html 。它引起了前所未有的關注,一個簡單的想法就是用一個文件注釋你所需要的代碼。

    我在機器學習方面的經驗讓我意識到,當你將事情寫成代碼的時候,其實現和秘密會變得更清晰,而不再是魔法了。              

    魔法沒有什么神奇的。魔術師只是理解一些簡單的東西,而這些東西對未經訓練的觀眾來說似乎并不簡單。一旦你學會了操作魔術卡片,你也可以變魔術。 

    ——Jeffrey Friedl 在《Mastering Regular Expressions》一書中寫道              

    GPT-2 一開始看起來像是魔術,它的看起來太美麗了,但希望我能為你解釋魔術,在你讀完這篇文章時揭示所有的技巧,這是我的目標。使那些熱衷于理解 GPT-2 模型是如何工作的人更好理解。 

    注:幾乎所有代碼都是從Hugging Face(https://github.com/huggingface/transformers/blob/master/src/transformers/modeling_gpt2.py )的 GPT-2 實現中復制、啟發和引用的,只保留了簡單的基本要素。如果你想在并行 GPU 上訓練 GPT-2 模型,在微調時保存檢查點,在多個 CPU 上運行推理任務等等,我建議你使用 Hugging Face API。最近,Hugging Face 發布了一個簡單的教程,它教你如何做到這一點:https://huggingface.co/blog/how-to-train 。              

    在這篇文章中,我并不是在試圖重新發明輪子,而是僅僅把一系列已經存在的優秀資源放在一起,讓讀者更容易掌握 GPT-2。我讓讀者在他們所選擇的任何領域進一步建立這些基礎。              

    你不能在弱小的基礎上建造一座偉大的建筑。如果你要有一個強大的上層建筑,你必須有堅實的基礎。    

    ——Gordon B. Hinckley        

    學習本教程的先決條件              

    本文假設讀者對注意力機制和 tansformers 有著扎實的理解。GPT-2 采用 12 層的,僅有解碼器的 transformer 架構。如果你想復習一下或了解注意力機制和 transformers,這里有一個很好的資源列表:

    如果你剛剛開始你的 NLP 之旅,或者你是一個專家,我絕對會推薦 Rachel Thomas 和 Jeremy Howard 教授的 fast.ai NLP 課程(https://www.fast.ai/2019/07/08/fastai-nlp/ )。本課程從基礎開始,包括使用樸素貝葉斯和 Logistic 回歸進行語義分類,接著是 RNN,后面還討論了遷移學習、ULMFiT、Seq2Seq 翻譯和 transformers 等。它是 fast.ai 團隊免費提供的優秀資源。 

    另一個關于 GPT-2 本身的優秀資源,是 Jay Alammar 的 The Illustrated GPT-2(http://jalammar.github.io/illustrated-gpt2/ )。本文從語言模型的基本介紹開始,以一種非常容易理解的方式逐步解釋 GPT-2 模型。我強烈建議讀者閱讀這篇文章。              

    哈佛大學 The Annotated Transformer 使用 PyTorch 實現了完整的 transformer 架構,是深入理解 transformer 的好方法。 

    然后,讓我們在這些優秀的現有資源的基礎上,用代碼實現 GPT-2 吧~              

    摘要              

    自然語言處理任務,如問答、機器翻譯、閱讀理解等,通常是在特定任務的數據集上進行有監督的學習。我們證明,當語言模型在一個名為 WebText 的數百萬網頁的新數據集上訓練時,它開始學習這些任務,而不需要任何明確的監督。我們最大的模型,GPT-2,是一個 1.5B 參數的 transformer,它可以獲得最先進的語言建模成果,但仍然不適合 WebText。模型中的示例反映了這些改進,并包含連貫的文本段落。這些發現為構建語言處理系統提供了一條有希望的途徑,該系統可以從自然發生的演示中學習執行任務。              

    Zero-shot 設置是不微調語言模型并直接在目標數據集上運行推理的設置。例如,在 WebText 上預覽一個 LM,并直接嘗試預測 Amazon 影評數據集的下一個單詞。              

    模型架構(GPT-2)              

    我們的 LM 使用基于 transformer 的架構。該模型主要遵循 OpenAI GPT 模型的細節,并進行了一些修改。層規范化被移動到每個子塊的輸入,類似于預激活剩余網絡,并且在最終的自關注塊之后添加了額外的層規范化。我們在初始化時將剩余層的權重按 1/√N 的因子進行縮放,其中 N 是剩余層的數量。詞匯量擴大到 50257 個單詞。我們還將上下文大小從 512 增加到 1024 個,并使用更大的批大小——512。 

    模型規格(GPT)              

    我們的模型基本上遵循了最初 transformer 的工作原理。我們訓練了一個 12 層的只解碼的 transformer,它有隱藏的自注意力頭(768 維狀態和 12 個注意力頭)。對于位置前饋網絡,我們使用了 3072 維的內部狀態。我們使用 Adam 優化方案,最大學習速率為 2.5e-4。學習速率在前 2000 次更新中從零線性增加,并使用余弦調度將其退火為 0。我們在 64 個隨機抽樣的小批量、512 個令牌的連續序列上訓練了 100 個階段。由于 layernorm 在整個模型中廣泛使用,簡單的 N(0,0.02)權重初始化就足夠了。我們使用了一個 bytepair 編碼(BPE)詞匯表。我們還采用了在中提出的 L2 正則化的改進版本,在所有非偏倚或增益權重上的 w=0.01。對于激活函數,我們使用高斯誤差線性單位(GELU)。  

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    導入

    import torch
    import copy
    import torch.nn as nn
    import torch.nn.functional as F
    from torch.nn.modules import ModuleList
    from torch.nn.modules.normalization import LayerNorm
    import numpy as np
    import os
    from tqdm import tqdm_notebook, trange
    import logging
    logging.basicConfig(level = logging.INFO)
    logger = logging.getLogger()

    GPT-2 內部的 transformer 解碼器              

    要重用用于描述 transformer 的術語,注意是一個查詢(Q)和一組鍵(K)和值(V)對的函數。為了處理更長的序列,我們修改了 transformer 的多頭自注意力機制,通過限制 Q 和 K 之間的點積來減少內存使用:

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    注意力是查詢、鍵和值的組合

    class Conv1D(nn.Module):
        def __init__(self, nx, nf):        super().__init__()
            self.nf = nf
            w = torch.empty(nx, nf)
            nn.init.normal_(w, std=0.02)
            self.weight = nn.Parameter(w)
            self.bias = nn.Parameter(torch.zeros(nf))

        def forward(self, x):
            size_out = x.size()[:-1] + (self.nf,)
            x = torch.addmm(self.bias, x.view(-1, x.size(-1)), self.weight)
            x = x.view(*size_out)
            return x

    CONV1D 層解釋              

    CONV1D 層本身可以看作是一個線性層。本質上,它是投射一個初始張量 x(最終尺寸為 x.size(-1))并傳遞給它,最終尺寸為 self.nf。              

    下面是相同的輸出示例:

    d_model = 768
    conv1d  = Conv1D(d_model, d_model*3)
    x       = torch.rand(1,4,d_model) #represents a sequence of batch_size=1, seq_len=4 and embedding_sz=768, something like "Hello how are you"
    x       = conv1d(x)
    x.shape

    >> torch.Size([1, 4, 2304])

    如上例所示,CONV1D 返回的張量的最終維數是初始大小的 3 倍。我們這樣做是為了能夠將輸入轉換為查詢、鍵和值矩陣。              

    然后可以檢索查詢、鍵和值矩陣,如下所示:

    query, key, value = x.split(d_model, dim=-1)

    query.shape, key.shape, value.shape
    >> (torch.Size([1, 4, 768]), torch.Size([1, 4, 768]), torch.Size([1, 4, 768]))

    將輸入轉換為 Q、K 和 V 矩陣的另一種方法是必須有單獨的 Wq、Wk 和 Wv 矩陣。我已經在這篇文章底部的附加部分解釋了這一點。我發現這種方法更直觀、更具相關性,但在本文中我們使用了 CONV1D 層,因為我們重用了 Hugging Face 的 CONV1D 預訓練權重。              

    前向層解釋

    class FeedForward(nn.Module):
        def __init__(self, dropout, d_model=768, nx=768*4):
            super().__init__()
            self.c_fc    = Conv1D(d_model, nx)
            self.c_proj  = Conv1D(nx, d_model)
            self.act     = F.gelu
            self.dropout = nn.Dropout(dropout)
           
        def forward(self, x):
            return self.dropout(self.c_proj(self.act(self.c_fc(x))))

    在 Jay Alammar 的文章中有一個很好的解釋,也就是上面提到的,輸入是如何先經過注意力層,然后再進入前向層的。前饋網絡是一個正常的網絡,它接受來自注意力層(768)的輸出,將其投射到 nx(768×4)維,添加一個激活函數 self.act(GELU),將其投射回 d_model (768) 并添加 dropout(0.1)。            

    注意力層解釋              

    下面的摘錄是從論文上摘取的:https://arxiv.org/abs/1706.03762 。              

    標度點產品注意力              

    我們稱我們的注意力為「標度點產品注意力」。輸入包括維度 dk 的查詢和鍵以及維度 dv 的值。我們使用所有鍵計算查詢的點積,用√dk除以每個鍵,然后應用 softmax 函數獲得值的權重。

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    在實際應用中,我們同時計算一組查詢的注意力函數,將它們組合成一個矩陣 Q,并將鍵和值組合成矩陣 K 和 V。我們將輸出矩陣計算為:

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    輸出矩陣為 Q、K 和 V 的組合

    最常用的兩個注意力函數是加性注意力函數和點積(乘法)力函數注意。除了比例因子 1/√dk 外,點積注意力與我們的算法相同。附加注意力使用具有單個隱藏層的前饋網絡計算兼容性函數。雖然二者在理論復雜度上相似,但在實際應用中,點積注意力速度更快,空間效率更高,因為它可以使用高度優化的矩陣乘法碼來實現。當 dk 值較小時,兩種機制的表現相似,但在 dk 值較大時,加性注意力優于點積注意力。我們懷疑,對于 dk 的較大值,點積在數量上增長較大,將 softmax 函數推入具有極小梯度的區域。為了抵消這一影響,我們將網點產品縮放至 1/√dk。              

    為了在代碼中實現注意力層,我們首先利用 CONV1D 層,得到前面解釋的 Q、K 和 V 矩陣。              

    一旦我們有了 Q、K 和 V 矩陣,我們就可以使用函數 _attn 來執行注意力。此函數復制了上述注意力點積公式。

    class Attention(nn.Module):
        def __init__(self, d_model=768, n_head=12, n_ctx=1024, d_head=64, bias=True, scale=False):
            super().__init__()
            self.n_head  = n_head
            self.d_model = d_model
            self.c_attn  = Conv1D(d_model, d_model*3)
            self.scale   = scale
            self.softmax = nn.Softmax(dim=-1)
            self.register_buffer("bias", torch.tril(torch.ones(n_ctx, n_ctx)).view(1, 1, n_ctx, n_ctx))
            self.dropout = nn.Dropout(0.1)
            self.c_proj  = Conv1D(d_model, d_model)
           
        def split_heads(self, x):
            "return shape [`batch`, `head`, `sequence`, `features`]"
            new_shape = x.size()[:-1] + (self.n_head, x.size(-1)//self.n_head)
            x = x.view(*new_shape)
            return x.permute(0, 2, 1, 3)
       
        def _attn(self, q, k, v, attn_mask=None):
            scores  = torch.matmul(q, k.transpose(-2, -1))
            if self.scale: scores = scores/math.sqrt(v.size(-1))
            nd, ns  = scores.size(-2), scores.size(-1)
            if attn_mask is not None: scores = scores + attn_mask
            scores  = self.softmax(scores)
            scores  = self.dropout(scores)
            outputs = torch.matmul(scores, v)
            return outputs
       
        def merge_heads(self, x):
            x         = x.permute(0, 2, 1, 3).contiguous()
            new_shape = x.size()[:-2] + (x.size(-2)*x.size(-1),)
            return x.view(*new_shape)
           
        def forward(self, x):
            x        = self.c_attn(x) #new `x` shape - `[1,3,2304]`
            q, k, v  = x.split(self.d_model, dim=2)
            q, k, v  = self.split_heads(q), self.split_heads(k), self.split_heads(v)
            out      = self._attn(q, k, v)
            out      = self.merge_heads(out)
            out      = self.c_proj(out)
            return out

    另一種實現注意力的方法在本博客底部的附加部分進行了說明。我發現它更直觀,更容易與研究論文進行比較。它利用線性層而不是 CONV1D 將輸入轉換為 Q、K 和 V 矩陣。我們之所以沒有使用它,是因為我們使用了預訓練的權重,從 Hugging Face 轉換為一維層。              

    多頭注意力              

    下面一段是從論文「Attention is all you need」上摘取的。              

    我們發現,使用不同的、學習到的線性映射將查詢、鍵和值分別線性映射到 dk、dk 和 dv 維度更好。然后,在這些查詢、鍵和值的隱射版本中,我們并行地執行注意力函數,生成 dv 維輸出值。這些值被連接起來,然后再次進行映射,得到最終值,如下圖所示:

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    多頭注意力機制允許模型在不同的位置共同關注來自不同表示子空間的信息。

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    多頭注意力等式              

    在這項工作中,我們使用了 h=8 個平行的注意力層,或者說頭。其中,我們使用的都是 dk=dv=dmodel/h=64。由于每個頭的維數減少,總的計算成本與全維度的單頭部注意的計算成本相似。              

    不要被這個弄糊涂了,本質上,我們所做的就是給 Q,K 和 V 矩陣增加一個維數。也就是說,如果這些矩陣之前的大小是 [1, 4, 768],表示 [bs, seq_len, d_model],則這些矩陣被投影到[bs, n_head, seq_len, d_model//n_head],大小為 [1, 12, 4, 64]。GPT-2 使用 12 個平行頭。我們將 Q,K,V 矩陣分解到 split_heads 函數中。最后,當我們通過應用并行注意力得到一個輸出時,我們將它連接到合并頭中,返回到維度矩陣 [bs,seq_len,d_model]。              

    代碼中的 GPT-2 模型體系結構

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    到目前為止,我們已經實現了多頭注意和前饋層。如上圖所示,這兩層構成 transformer 解碼器塊的構建塊。GPT-2 由 12 個 transformer 組組成。              

    這在 Jay Alammar 的文章中顯示如下:

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    由 12 個解碼塊組成的 GPT 體系結構              

    transformer 解碼器塊說明

    class TransformerBlock(nn.Module):
        def __init__(self, d_model=768, n_head=12, dropout=0.1):
            super(TransformerBlock, self).__init__()
            self.attn        = Attention(d_model=768, n_head=12, d_head=64, n_ctx=1024, bias=True, scale=False)
            self.feedforward = FeedForward(dropout=0.1, d_model=768, nx=768*4)
            self.ln_1        = LayerNorm(d_model)
            self.ln_2        = LayerNorm(d_model)
                   
        def forward(self, x):
            x = x + self.attn(self.ln_1(x))
            x = x + self.feedforward(self.ln_2(x))
            return x

    transformer 組由注意力層和前饋層組成,如 GPT-2 架構模型規范所述:層規范化被移動到每個子塊的輸入,這里的子塊是注意力和前饋。              

    因此,在 transformer 解碼器塊中,我們首先將輸入傳遞給一個 LayerNorm,然后是第一個子注意力塊。接下來,我們將這個子塊的輸出再次傳遞給 LayerNorm,最后傳遞給前饋層。              

    GPT-2架構說明              

    如 GPT 論文所述:我們訓練了一個 12 層的只解碼的 transformer,它有隱藏的自注意力頭(768 個維度和 12 個注意力頭)。              

    因此,完整的 GPT-2 體系結構是經過 12 次復制的 TransformerBlock。

    def _get_clones(module, n):
        return ModuleList([copy.deepcopy(module) for i in range(n)])

    class GPT2(nn.Module):
        def __init__(self, nlayers=12, n_ctx=1024, d_model=768, vcb_sz=50257):
            super(GPT2, self).__init__()
            self.nlayers = nlayers
            block        = TransformerBlock(d_model=768, n_head=12, dropout=0.1)
            self.h       = _get_clones(block, 12)
            self.wte     = nn.Embedding(vcb_sz, d_model)
            self.wpe     = nn.Embedding(n_ctx, d_model)
            self.drop    = nn.Dropout(0.1)
            self.ln_f    = LayerNorm(d_model)
            self.out     = nn.Linear(d_model, vcb_sz, bias=False)
            self.loss_fn = nn.CrossEntropyLoss()
            self.init_weights()
       
        def init_weights(self):
            self.out.weight = self.wte.weight
            self.apply(self._init_weights)
       
        def _init_weights(self, module):
            if isinstance(module, (nn.Linear, nn.Embedding, Conv1D)):
                module.weight.data.normal_(mean=0.0, std=0.02)
                if isinstance(module, (nn.Linear, Conv1D)) and module.bias is not None:
                    module.bias.data.zero_()
            elif isinstance(module, nn.LayerNorm):
                module.bias.data.zero_()
                module.weight.data.fill_(1.0)
       
        def forward(self, src, labels=None, pos_ids=None):
            if pos_ids is None: pos_ids = torch.arange(0, src.size(-1)).unsqueeze(0)
            inp = self.drop((self.wte(src)+self.wpe(pos_ids)))
            for i in range(self.nlayers): inp = self.h[i](inp)
            inp     = self.ln_f(inp)
            logits  = self.out(inp)
            outputs = (logits,) + (inp,)
           
            if labels is not None:
                shift_logits = logits[..., :-1, :].contiguous()
                shift_labels = labels[..., 1:].contiguous()
                loss = self.loss_fn(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
                outputs = (loss,) + outputs
                return outputs
            return logits

    我還沒有提到的是位置編碼和標記嵌入。因為,我們不能將諸如「hey」或「hello」之類的詞直接傳遞給模型,所以我們首先將輸入標記化。接下來,我們使用嵌入將標記表示為數字。Jay Alammar 的這篇文章(http://jalammar.github.io/illustrated-word2vec/ )很好地解釋了嵌入。              

    此外,與按順序傳遞輸入詞的 RNN 不同,transformer 并行地接受輸入矩陣,從而失去了被輸入詞的位置感。為了彌補這一損失,在將標記嵌入處理到模型之前,我們添加了 Positional Encoding——一種指示序列中單詞順序的信號。如前所述,由于 GPT-2 的上下文大小是 1024,因此位置編碼的維度是 [1024, 768]。

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    從[The Illustrated GPT-2]引用的位置編碼(http://jalammar.github.io/Illustrated-gpt2/)              

    因此,GPT-2 體系結構的輸入是通過一個 Dropout 的標記嵌入和位置編碼的總和。一旦我們有了輸入矩陣,我們就讓其通過 GPT-2 架構的 12 層中的每一層,其中每一層都是一個由兩個子層組成的 transformer 譯碼器塊——注意力和前饋網絡。              

    語言建模或分類              

    當使用 GPT-2 作為語言模型時,我們將輸入傳遞到最終層形式,并通過最終大小為[768, vocab_sz](50257)的線性層,得到大小為[1,4,50257]的輸出。這個輸出表示下一個詞匯輸入,我們現在可以很容易地通過一個 softmax 層,并使用 argmax 以最大的概率獲得單詞在詞匯表中的位置。              

    對于分類任務,我們可以通過大小為 [768, n] 的線性層來傳遞從 GPT-2 架構接收到的輸出,以獲得每個類別的概率(其中 n 表示類別的數量),然后通過 softmax 傳遞,得到最高的預測類別,并使用 CrossEntropyLoss 來訓練架構進行分類。              

    這就是 GPT-2 背后的全部魔法。它是一種基于解碼器的 transformer 式結構,與 RNN 不同,它采用與位置編碼并行的輸入,通過 12 個 transformer 解碼器層(由多頭注意力和前饋網絡組成)中的每一層來返回最終輸出。   

    讓我們在語言模型任務中看看這個模型的實際作用。              

    使用 Hugging Face 預訓練權重生成示例文本              

    首先,讓我們用 Hugging Face 提供的預訓練權重初始化模型。

    model = GPT2()
    # load pretrained_weights from hugging face
    # download file https://s3.amazonaws.com/models.huggingface.co/bert/gpt2-pytorch_model.bin to `.`

    model_dict = model.state_dict() #currently with random initialization
    state_dict = torch.load("./gpt2-pytorch_model.bin") #pretrained weights

    old_keys = []
    new_keys = []
    for key in state_dict.keys():
        if "mlp" in key: #The hugging face state dict references the feedforward network as mlp, need to replace to `feedforward` be able to reuse these weights
            new_key = key.replace("mlp", "feedforward")
            new_keys.append(new_key)
            old_keys.append(key)

    for old_key, new_key in zip(old_keys, new_keys):
        state_dict[new_key]=state_dict.pop(old_key)

    pretrained_dict = {k: v for k, v in state_dict.items() if k in model_dict}

    model_dict.update(pretrained_dict)
    model.load_state_dict(model_dict)
    model.eval() #model in inference mode as it's now initialized with pretrained weights

    現在讓我們生成文本。我們將使用 Hugging Face 的預訓練標記器將單詞轉換為輸入嵌入。

    from transformers import GPT2Tokenizer
    tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
    context   = torch.tensor([tokenizer.encode("The planet earth")])


    def generate(context, ntok=20):
        for _ in range(ntok):
            out = model(context)
            logits = out[:, -1, :]
            indices_to_remove = logits < torch.topk(logits, 10)[0][..., -1, None]
            logits[indices_to_remove] = np.NINF
            next_tok = torch.multinomial(F.softmax(logits, dim=-1), num_samples=1).squeeze(1)
            context = torch.cat([context, next_tok.unsqueeze(-1)], dim=-1)
        return context

    out = generate(context, ntok=20)
    tokenizer.decode(out[0])

    >> 'The planet earth is the source of all of all the light," says the study that the government will'

    附加內容              

    另一種實現注意力的方法,在 fast.ai 的 NLP 課程(https://github.com/fastai/course-nlp/blob/master/8-translation-transformer.ipynb )中有,我發現更直觀的方法如下:

    class Attention_FASTAI(nn.Module):
        def __init__(self, d_model=768, n_head=12, d_head=64, n_ctx=1024, bias=True, scale=False):
            super().__init__()
            self.n_head   = n_head
            self.d_head   = d_head
            self.softmax  = nn.Softmax(dim=-1)
            self.scale    = scale
            self.atn_drop = nn.Dropout(0.1)
            self.wq, self.wk, self.wv = [nn.Linear(d_model, n_head*d_head,
                                                   bias=bias) for o in range(3)]
       

        def split_heads(self, x, layer, bs):
            x = layer(x)
            return x.view(bs, x.size(1), self.n_head, self.d_head).permute(0,2,1,3)
           
        def _attn(self, q, k, v, attn_mask=None):
            scores  = torch.matmul(q, k.transpose(-2, -1))
            if self.scale: scores = scores/math.sqrt(v.size(-1))
            if attn_mask is not None:
                scores = scores.float().masked_fill(attn_mask, -float('inf')).type_as(scores)
            attn_prob  = self.atn_drop(self.softmax(scores))
            attn_vec   = attn_prob @ v
            return attn_vec
       
        def merge_heads(self, x, bs, seq_len):
            x         = x.permute(0, 2, 1, 3).contiguous()
            return x.view(bs, seq_len, -1)
           
        def forward(self, q, k, v, mask=None):
            bs, seq_len = q.size(0), q.size(1)
            wq, wk, wv  = map(lambda o:self.split_heads(*o, bs),
                            zip((q,k,v), (self.wq, self.wk, self.wv)))
            attn_vec    = self._attn(wq, wk, wv)
            attn_vec    = self.merge_heads(attn_vec, bs, seq_len)
            return attn_vec

    上面的實現與我們采用的實現方法的關鍵區別在于,這個實現沒有使用 CONV1D,而是先將輸入 x 傳遞給 self.wq、self.wk 和 self.wv 線性層,得到 wq、wk 和 wv 矩陣,然后接下來和前面一樣。              

    寫在最后             

    特別感謝 Hugging Face 創建了一個開源的 NLP 庫,并提供了許多可使用的預訓練模型。如前所述,本文中的代碼直接來自 Hugging Face 庫。The Illustrated GPT-2(http://jalammar.github.io/illustrated-gpt2/ )是關于 GPT-2 知識最全的博客之一。最后,Harvard NLP 的 The Annotated Transformer(https://nlp.seas.harvard.edu/2018/04/03/attention.html )完成了一個很棒且易于學習的 PyTorch 中 Transformers 的實現。          

    via:https://amaarora.github.io/2020/02/18/annotatedGPT2.html

    雷鋒網雷鋒網雷鋒網 

    雷峰網版權文章,未經授權禁止轉載。詳情見轉載須知

    GPT-2沒什么神奇的,PyTorch 就可以復現代碼

    分享:
    相關文章
    當月熱門文章
    最新文章
    請填寫申請人資料
    姓名
    電話
    郵箱
    微信號
    作品鏈接
    個人簡介
    為了您的賬戶安全,請驗證郵箱
    您的郵箱還未驗證,完成可獲20積分喲!
    請驗證您的郵箱
    立即驗證
    完善賬號信息
    您的賬號已經綁定,現在您可以設置密碼以方便用郵箱登錄
    立即設置 以后再說