1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【NLP】圖解Transformer(完整版)

        共 18478字,需瀏覽 37分鐘

         ·

        2020-10-28 16:31

        譯者:張賢,哈爾濱工程大學(xué),Datawhale原創(chuàng)作者
        本文約16000字,是NLP專欄第一篇,建議收藏閱讀
        審稿人:Jepson,Datawhale成員,畢業(yè)于中國(guó)科學(xué)院,目前在騰訊從事推薦算法工作。


        結(jié)構(gòu)總覽

        前言

        本文翻譯自http://jalammar.github.io/illustrated-transformer,是筆者看過的把 Transformer 講解得最好的文章。這篇文章從輸入開始,一步一步演示了數(shù)據(jù)在 Transformer 中的流動(dòng)過程。由于看過一些中文翻譯的文章,感覺不夠好,所以我自己翻譯了一個(gè)版本,在一些難以直譯的地方,我加入了一些原文沒有的文字說明,來更好地解釋概念。另外,我添加了一些簡(jiǎn)單的代碼,實(shí)現(xiàn)了一個(gè)基本的 Self Attention 以及 multi-head attention 的矩陣運(yùn)算。

        Transformer 依賴于 Self Attention 的知識(shí)。Attention 是一種在深度學(xué)習(xí)中廣泛使用的方法,Attention的思想提升了機(jī)器翻譯的效果。如果你還沒學(xué)習(xí) Attention,請(qǐng)查看這篇 Attention 的精彩講解:https://zhuanlan.zhihu.com/p/265182368。

        2017 年,Google 提出了 Transformer 模型,用 Self Attention 的結(jié)構(gòu),取代了以往 NLP 任務(wù)中的 RNN 網(wǎng)絡(luò)結(jié)構(gòu),在 WMT 2014 Englishto-GermanWMT 2014 English-to-French兩個(gè)機(jī)器翻譯任務(wù)上都取得了當(dāng)時(shí) SOTA 的效果。

        這個(gè)模型的其中一個(gè)優(yōu)點(diǎn),就是使得模型訓(xùn)練過程能夠并行計(jì)算。在 RNN 中,每一個(gè) time step 的計(jì)算都依賴于上一個(gè) time step 的輸出,這就使得所有的 time step 必須串行化,無法并行計(jì)算,如下圖所示。

        而在 Transformer 中,所有 time step 的數(shù)據(jù),都是經(jīng)過 Self Attention 計(jì)算,使得整個(gè)運(yùn)算過程可以并行化計(jì)算。

        這篇文章的目的是從上到下,一步一步拆解 Transformer 的各種概念,希望有助于初學(xué)者更加容易地理解 Transformer 到底是什么。

        Transformer 使用了 Seq2Seq任務(wù)中常用的結(jié)構(gòu)——包括兩個(gè)部分:Encoder 和 Decoder。一般的結(jié)構(gòu)圖,都是像下面這樣。

        如果你看到上圖不知所措,不要擔(dān)心,下面我們來一步步拆解 Transformer。

        一、從整體宏觀來理解 Transformer

        首先,我們將整個(gè)模型視為黑盒。在機(jī)器翻譯任務(wù)中,接收一種語(yǔ)言的句子作為輸入,然后將其翻譯成其他語(yǔ)言輸出。

        中間部分的 Transformer 可以拆分為 2 部分:左邊是編碼部分(encoding component),右邊是解碼部分(decoding component)。

        其中編碼部分是多層的編碼器(Encoder)組成(Transformer 的論文中使用了 6 層編碼器,這里的層數(shù) 6 并不是固定的,你也可以根據(jù)實(shí)驗(yàn)效果來修改層數(shù))。同理,解碼部分也是由多層的解碼器(Decoder)組成(論文里也使用了 6 層的解碼器)。

        每一個(gè)編碼器 在結(jié)構(gòu)上都是一樣的,但它們的權(quán)重參數(shù)是不同的。每一個(gè)編碼器里面,可以分為 2 層
        • Self-Attention Layer
        • Feed Forward Neural Network(前饋神經(jīng)網(wǎng)絡(luò),縮寫為 FFNN)

        輸入編碼器的文本數(shù)據(jù),首先會(huì)經(jīng)過一個(gè) Self Attention 層,這個(gè)層處理一個(gè)詞的時(shí)候,不僅會(huì)使用這個(gè)詞本身的信息,也會(huì)使用句子中其他詞的信息(你可以類比為:當(dāng)我們翻譯一個(gè)詞的時(shí)候,不僅會(huì)只關(guān)注當(dāng)前的詞,也會(huì)關(guān)注這個(gè)詞的上下文的其他詞的信息)。本文后面將會(huì)詳細(xì)介紹 Self Attention 的內(nèi)部結(jié)構(gòu)。

        接下來,Self Attention 層的輸出會(huì)經(jīng)過前饋神經(jīng)網(wǎng)絡(luò)。

        同理,解碼器也具有這兩層,但是這兩層中間還插入了一個(gè) Encoder-Decoder Attention 層,這個(gè)層能幫助解碼器聚焦于輸入句子的相關(guān)部分(類似于 seq2seq 模型 中的 Attention)。

        二、從細(xì)節(jié)來理解 Transformer

        上面,我們從宏觀理解了 Transformer 的主要部分。下面,我們來看輸入的張量數(shù)據(jù),在 Transformer 中運(yùn)算最終得到輸出的過程。

        2.1 Transformer 的輸入

        和通常的 NLP 任務(wù)一樣,我們首先會(huì)使用詞嵌入算法(embedding algorithm),將每個(gè)詞轉(zhuǎn)換為一個(gè)詞向量。實(shí)際中向量一般是 256 或者 512 維。為了簡(jiǎn)化起見,這里將每個(gè)詞的轉(zhuǎn)換為一個(gè) 4 維的詞向量。

        那么整個(gè)輸入的句子是一個(gè)向量列表,其中有 3 個(gè)詞向量。在實(shí)際中,每個(gè)句子的長(zhǎng)度不一樣,我們會(huì)取一個(gè)適當(dāng)?shù)闹?,作為向量列表的長(zhǎng)度。如果一個(gè)句子達(dá)不到這個(gè)長(zhǎng)度,那么就填充全為 0 的詞向量;如果句子超出這個(gè)長(zhǎng)度,則做截?cái)?。句子長(zhǎng)度是一個(gè)超參數(shù),通常是訓(xùn)練集中的句子的最大長(zhǎng)度,你可以嘗試不同長(zhǎng)度的效果。

        編碼器(Encoder)接收的輸入都是一個(gè)向量列表,輸出也是大小同樣的向量列表,然后接著輸入下一個(gè)編碼器。

        第一個(gè)編碼器的輸入是詞向量,而后面的編碼器的輸入是上一個(gè)編碼器的輸出。

        下面,我們來看這個(gè)向量列表在編碼器里面是如何流動(dòng)的。

        這里我們可以注意到 Transformer 的一個(gè)重要特性:每個(gè)位置的詞向量經(jīng)過編碼器都有自己?jiǎn)为?dú)的路徑。具體來說,在 Self Attention 層中,這些路徑之間是有依賴關(guān)系的;而在 Feed Forward (前饋神經(jīng)網(wǎng)絡(luò))層中,這些路徑之間是沒有依賴關(guān)系的。因此這些詞向量在經(jīng)過 Feed Forward 層中可以并行計(jì)算(這句話會(huì)造成困擾,我認(rèn)為在 Self Attention 層中,也能并行計(jì)算,沒有必要單獨(dú)說 Feed Forward 層也可以并行計(jì)算)。

        下面我們用一個(gè)更短的句子,來說明數(shù)據(jù)在編碼器的編碼過程。

        2.2 Encoder(編碼器)

        上面我們提到,一個(gè)編碼器接收的輸入是一個(gè)向量列表,它會(huì)把向量列表輸入到 Self Attention 層,然后經(jīng)過 feed-forward neural network (前饋神經(jīng)網(wǎng)絡(luò))層,最后得到輸出,傳入下一個(gè)編碼器。

        每個(gè)位置的詞都經(jīng)過 Self Attention 層,得到的每個(gè)輸出向量都單獨(dú)經(jīng)過前饋神經(jīng)網(wǎng)絡(luò)層,每個(gè)向量經(jīng)過的前饋神經(jīng)網(wǎng)絡(luò)都是一樣的

        三、 Self-Attention 整體理解

        別被“Self-Attention”這么高大上的詞給唬住了,乍一聽好像每個(gè)人都應(yīng)該對(duì)這個(gè)詞熟悉一樣。但我在讀論文《Attention is All You Need》 之前就沒有聽過這個(gè)詞。下面來分析 Self-Attention 的具體機(jī)制。

        假設(shè)我們想要翻譯的句子是:

        The animal didn't cross the street because it was too tired

        這個(gè)句子中的 it 是一個(gè)指代詞,那么 it 指的是什么呢?它是指animal還是street?這個(gè)問題對(duì)人來說,是很簡(jiǎn)單的,但是對(duì)算法來說并不是那么容易。

        當(dāng)模型在處理(翻譯)it 的時(shí)候,Self Attention機(jī)制能夠讓模型把itanimal關(guān)聯(lián)起來。

        同理,當(dāng)模型處理句子中的每個(gè)詞時(shí),Self Attention機(jī)制使得模型不僅能夠關(guān)注這個(gè)位置的詞,而且能夠關(guān)注句子中其他位置的詞,作為輔助線索,進(jìn)而可以更好地編碼當(dāng)前位置的詞。

        如果你熟悉 RNN,回憶一下:RNN 在處理一個(gè)詞時(shí),會(huì)考慮前面?zhèn)鬟^來的hidden state,而hidden state就包含了前面的詞的信息。而 Transformer 使用Self Attention機(jī)制,會(huì)把其他單詞的理解融入處理當(dāng)前的單詞。

        當(dāng)我們?cè)诘谖鍖泳幋a器中(編碼部分中的最后一層編碼器)編碼“it”時(shí),有一部分注意力集中在“The animal”上,并且把這兩個(gè)詞的信息融合到了"it"這個(gè)單詞中。

        你可以查看 【Tensor2Tensor notebook】。在這個(gè) notebook 里,你可以加載 Transformer 模型,并通過交互式的可視化,來理解 Self Attention。

        四、Self-Attention 的細(xì)節(jié)

        4.1 計(jì)算Query 向量,Key 向量,Value 向量

        下面我們先看下如何使用向量來計(jì)算 Self Attention,然后再看下如何使用矩陣來實(shí)現(xiàn) Self Attention。(矩陣運(yùn)算的方式,使得 Self Attention 的計(jì)算能夠并行化,這也是 Self Attention 最終的實(shí)現(xiàn)方式)。

        計(jì)算 Self Attention 的第 1 步是:對(duì)輸入編碼器的每個(gè)詞向量,都創(chuàng)建 3 個(gè)向量,分別是:Query 向量,Key 向量,Value 向量。這 3 個(gè)向量是詞向量分別和 3 個(gè)矩陣相乘得到的,而這個(gè)矩陣是我們要學(xué)習(xí)的參數(shù)。

        注意,這 3 個(gè)新得到的向量一般比原來的詞向量的長(zhǎng)度更小。假設(shè)這 3 個(gè)向量的長(zhǎng)度是?,而原始的詞向量或者最終輸出的向量的長(zhǎng)度是 512(這 3 個(gè)向量的長(zhǎng)度,和最終輸出的向量長(zhǎng)度,是有倍數(shù)關(guān)系的)。關(guān)于 Multi-head Attention,后面會(huì)給出實(shí)際代碼。這里為了簡(jiǎn)化,假設(shè)只有一個(gè) head 的 Self-Attention。

        上圖中,有兩個(gè)詞向量:Thinking 的詞向量 x1 和 Machines 的詞向量 x2。以 x1 為例,X1 乘以 WQ 得到 q1,q1 就是 X1 對(duì)應(yīng)的 Query 向量。同理,X1 乘以 WK 得到 k1,k1 是 X1 對(duì)應(yīng)的 Key 向量;X1 乘以 WV 得到 v1,v1 是 X1 對(duì)應(yīng)的 Value 向量。

        Query 向量,Key 向量,Value 向量是什么含義呢?

        其實(shí)它們就是 3 個(gè)向量,給它們加上一個(gè)名稱,可以讓我們更好地理解 Self-Attention 的計(jì)算過程和邏輯含義。繼續(xù)往下讀,你會(huì)知道 attention 是如何計(jì)算出來的,Query 向量,Key 向量,Value 向量又分別扮演了什么角色。

        4.2 計(jì)算 Attention Score(注意力分?jǐn)?shù))

        第 2 步,是計(jì)算 Attention Score(注意力分?jǐn)?shù))。假設(shè)我們現(xiàn)在計(jì)算第一個(gè)詞 Thinking 的 Attention Score(注意力分?jǐn)?shù)),需要根據(jù) Thinking 這個(gè)詞,對(duì)句子中的其他每個(gè)詞都計(jì)算一個(gè)分?jǐn)?shù)。這些分?jǐn)?shù)決定了我們?cè)诰幋aThinking這個(gè)詞時(shí),需要對(duì)句子中其他位置的每個(gè)詞放置多少的注意力。

        這些分?jǐn)?shù),是通過計(jì)算 "Thinking" 對(duì)應(yīng)的 Query 向量和其他位置的每個(gè)詞的 Key 向量的點(diǎn)積,而得到的。如果我們計(jì)算句子中第一個(gè)位置單詞的 Attention Score(注意力分?jǐn)?shù)),那么第一個(gè)分?jǐn)?shù)就是 q1 和 k1 的內(nèi)積,第二個(gè)分?jǐn)?shù)就是 q1 和 k2 的點(diǎn)積。

        第 3 步就是把每個(gè)分?jǐn)?shù)除以? 是 Key 向量的長(zhǎng)度)。你也可以除以其他數(shù),除以一個(gè)數(shù)是為了在反向傳播時(shí),求取梯度更加穩(wěn)定。

        第 4 步,接著把這些分?jǐn)?shù)經(jīng)過一個(gè) Softmax 層,Softmax可以將分?jǐn)?shù)歸一化,這樣使得分?jǐn)?shù)都是正數(shù)并且加起來等于 1。

        這些分?jǐn)?shù)決定了在編碼當(dāng)前位置(這里的例子是第一個(gè)位置)的詞時(shí),對(duì)所有位置的詞分別有多少的注意力。很明顯,在上圖的例子中,當(dāng)前位置(這里的例子是第一個(gè)位置)的詞會(huì)有最高的分?jǐn)?shù),但有時(shí),關(guān)注到其他位置上相關(guān)的詞也很有用。

        第 5 步,得到每個(gè)位置的分?jǐn)?shù)后,將每個(gè)分?jǐn)?shù)分別與每個(gè) Value 向量相乘。這種做法背后的直覺理解就是:對(duì)于分?jǐn)?shù)高的位置,相乘后的值就越大,我們把更多的注意力放到了它們身上;對(duì)于分?jǐn)?shù)低的位置,相乘后的值就越小,這些位置的詞可能是相關(guān)性不大的,這樣我們就忽略了這些位置的詞。

        第 6 步是把上一步得到的向量相加,就得到了 Self Attention 層在這個(gè)位置(這里的例子是第一個(gè)位置)的輸出。

        上面這張圖,包含了 Self Attention 的全過程,最終得到的當(dāng)前位置(這里的例子是第一個(gè)位置)的向量會(huì)輸入到前饋神經(jīng)網(wǎng)絡(luò)。但這樣每次只能計(jì)算一個(gè)位置的輸出向量,在實(shí)際的代碼實(shí)現(xiàn)中,Self Attention 的計(jì)算過程是使用矩陣來實(shí)現(xiàn)的,這樣可以加速計(jì)算,一次就得到所有位置的輸出向量。下面讓我們來看,如何使用矩陣來計(jì)算所有位置的輸出向量。

        五、使用矩陣計(jì)算 Self-Attention

        第一步是計(jì)算 Query,Key,Value 的矩陣。首先,我們把所有詞向量放到一個(gè)矩陣 X 中,然后分別和 3 個(gè)權(quán)重矩陣, 相乘,得到 Q,K,V 矩陣。

        矩陣 X 中的每一行,表示句子中的每一個(gè)詞的詞向量,長(zhǎng)度是 512。Q,K,V 矩陣中的每一行表示 Query 向量,Key 向量,Value 向量,向量長(zhǎng)度是 64。

        接著,由于我們使用了矩陣來計(jì)算,我們可以把上面的第 2 步到第 6 步壓縮為一步,直接得到 Self Attention 的輸出。

        六、多頭注意力機(jī)制(multi-head attention)

        Transformer 的論文通過增加多頭注意力機(jī)制(一組注意力稱為一個(gè) attention head),進(jìn)一步完善了 Self Attention 層。這種機(jī)制從如下兩個(gè)方面增強(qiáng)了 attention 層的能力:

        1. 它擴(kuò)展了模型關(guān)注不同位置的能力。在上面的例子中,第一個(gè)位置的輸出 z1 包含了句子中其他每個(gè)位置的很小一部分信息,但 z1 可能主要是由第一個(gè)位置的信息決定的。當(dāng)我們翻譯句子:The animal didn’t cross the street because it was too tired時(shí),我們想讓機(jī)器知道其中的it指代的是什么。這時(shí),多頭注意力機(jī)制會(huì)有幫助。

        2. 多頭注意力機(jī)制賦予 attention 層多個(gè)“子表示空間”。下面我們會(huì)看到,多頭注意力機(jī)制會(huì)有多組? 的權(quán)重矩陣(在 Transformer 的論文中,使用了 8 組注意力(attention heads)。因此,接下來我也是用 8 組注意力頭 (attention heads))。每一組注意力的?的權(quán)重矩陣都是隨機(jī)初始化的。經(jīng)過訓(xùn)練之后,每一組注意力可以看作是把輸入的向量映射到一個(gè)”子表示空間“。


        在多頭注意力機(jī)制中,我們?yōu)槊拷M注意力維護(hù)單獨(dú)的 WQ, WK, WV 權(quán)重矩陣。將輸入 X 和每組注意力的WQ, WK, WV 相乘,得到 8 組 Q, K, V 矩陣。

        接著,我們把每組 K, Q, V 計(jì)算得到每組的 Z 矩陣,就得到 8 個(gè) Z 矩陣。

        接下來就有點(diǎn)麻煩了,因?yàn)榍梆伾窠?jīng)網(wǎng)絡(luò)層接收的是 1 個(gè)矩陣(其中每行的向量表示一個(gè)詞),而不是 8 個(gè)矩陣。所以我們需要一種方法,把 8 個(gè)矩陣整合為一個(gè)矩陣。

        怎么才能做到呢?我們把矩陣拼接起來,然后和另一個(gè)權(quán)重矩陣? 相乘。

        1. 把 8 個(gè)矩陣 {Z0,Z1...,Z7} 拼接起來
        2. 把拼接后的矩陣和 WO 權(quán)重矩陣相乘
        3. 得到最終的矩陣 Z,這個(gè)矩陣包含了所有 attention heads(注意力頭) 的信息。這個(gè)矩陣會(huì)輸入到 FFNN (Feed Forward Neural Network)層。

        這就是多頭注意力的全部?jī)?nèi)容。我知道,在上面的講解中,出現(xiàn)了相當(dāng)多的矩陣。下面我把所有的內(nèi)容都放到一張圖中,這樣你可以總攬全局,在這張圖中看到所有的內(nèi)容。

        既然我們已經(jīng)談到了多頭注意力,現(xiàn)在讓我們重新回顧之前的翻譯例子,看下當(dāng)我們編碼單詞it時(shí),不同的 attention heads (注意力頭)關(guān)注的是什么部分。

        當(dāng)我們編碼單詞"it"時(shí),其中一個(gè) attention head (注意力頭)最關(guān)注的是"the animal",另外一個(gè) attention head 關(guān)注的是"tired"。因此在某種意義上,"it"在模型中的表示,融合了"animal"和"word"的部分表達(dá)。

        然而,當(dāng)我們把所有 attention heads(注意力頭) 都在圖上畫出來時(shí),多頭注意力又變得難以解釋了。

        七、代碼實(shí)現(xiàn)矩陣計(jì)算 Attention

        下面我們是用代碼來演示,如何使用矩陣計(jì)算 attention。首先使用 PyTorch 庫(kù)提供的函數(shù)實(shí)現(xiàn),然后自己再實(shí)現(xiàn)。

        7.1 使用 PyTorch 庫(kù)的實(shí)現(xiàn)

        PyTorch 提供了 MultiheadAttention 來實(shí)現(xiàn) attention 的計(jì)算。

        torch.nn.MultiheadAttention(embed_dim, num_heads, dropout=0.0, bias=True, add_bias_kv=False, add_zero_attn=False, kdim=None, vdim=None)

        參數(shù)說明如下:

        • embed_dim:最終輸出的 K、Q、V 矩陣的維度,這個(gè)維度需要和詞向量的維度一樣

        • num_heads:設(shè)置多頭注意力的數(shù)量。如果設(shè)置為 1,那么只使用一組注意力。如果設(shè)置為其他數(shù)值,那么 num_heads 的值需要能夠被 embed_dim 整除

        • dropout:這個(gè) dropout 加在 attention score 后面

        現(xiàn)在來解釋一下,為什么 ?num_heads 的值需要能夠被 embed_dim 整除。這是為了把詞的隱向量長(zhǎng)度平分到每一組,這樣多組注意力也能夠放到一個(gè)矩陣?yán)?,從而并行?jì)算多頭注意力。

        例如,我們前面說到,8 組注意力可以得到 8 組 Z 矩陣,然后把這些矩陣拼接起來,得到最終的輸出。如果最終輸出的每個(gè)詞的向量維度是 512,那么每組注意力的向量維度應(yīng)該是?

        如果不能夠整除,那么這些向量的長(zhǎng)度就無法平均分配。

        下面的會(huì)有代碼示例,如何使用矩陣實(shí)現(xiàn)多組注意力的并行計(jì)算。

        定義 MultiheadAttention 的對(duì)象后,調(diào)用時(shí)傳入的參數(shù)如下。

        forward(query, key, value, key_padding_mask=None, need_weights=True, attn_mask=None)
        • query:對(duì)應(yīng)于 Key 矩陣,形狀是 (L,N,E) 。其中 L 是輸出序列長(zhǎng)度,N 是 batch size,E 是詞向量的維度

        • key:對(duì)應(yīng)于 Key 矩陣,形狀是 (S,N,E) 。其中 S 是輸入序列長(zhǎng)度,N 是 batch size,E 是詞向量的維度

        • value:對(duì)應(yīng)于 Value 矩陣,形狀是 (S,N,E) 。其中 S 是輸入序列長(zhǎng)度,N 是 batch size,E 是詞向量的維度

        • key_padding_mask:如果提供了這個(gè)參數(shù),那么計(jì)算 attention score 時(shí),忽略 Key 矩陣中某些 padding 元素,不參與計(jì)算 attention。形狀是 (N,S)。其中 N 是 batch size,S 是輸入序列長(zhǎng)度。

          • 如果 key_padding_mask 是 ByteTensor,那么非 0 元素對(duì)應(yīng)的位置會(huì)被忽略
          • 如果 key_padding_mask 是 BoolTensor,那么 ?True 對(duì)應(yīng)的位置會(huì)被忽略
        • attn_mask:計(jì)算輸出時(shí),忽略某些位置。形狀可以是 2D ?(L,S),或者 3D (N?numheads,L,S)。其中 L 是輸出序列長(zhǎng)度,S 是輸入序列長(zhǎng)度,N 是 batch size。

          • 如果 attn_mask 是 ByteTensor,那么非 0 元素對(duì)應(yīng)的位置會(huì)被忽略
          • 如果 attn_mask 是 BoolTensor,那么 ?True 對(duì)應(yīng)的位置會(huì)被忽略

        需要注意的是:在前面的講解中,我們的 K、Q、V 矩陣的序列長(zhǎng)度都是一樣的。但是在實(shí)際中,K、V 矩陣的序列長(zhǎng)度是一樣的,而 Q 矩陣的序列長(zhǎng)度可以不一樣。

        這種情況發(fā)生在:在解碼器部分的Encoder-Decoder Attention層中,Q 矩陣是來自解碼器下層,而 K、V 矩陣則是來自編碼器的輸出。


        在完成了編碼(encoding)階段之后,我們開始解碼(decoding)階段。解碼(decoding )階段的每一個(gè)時(shí)間步都輸出一個(gè)翻譯后的單詞(這里的例子是英語(yǔ)翻譯)。

        輸出是:

        • attn_output:形狀是 (L,N,E)
        • attn_output_weights:形狀是 (N,L,S)

        代碼示例如下:

        ## nn.MultiheadAttention 輸入第0維為length
        # batch_size 為 64,有 12 個(gè)詞,每個(gè)詞的 Query 向量是 300 維
        query = torch.rand(12,64,300)
        # batch_size 為 64,有 10 個(gè)詞,每個(gè)詞的 Key 向量是 300 維
        key = torch.rand(10,64,300)
        # batch_size 為 64,有 10 個(gè)詞,每個(gè)詞的 Value 向量是 300 維
        value= torch.rand(10,64,300)

        embed_dim = 299
        num_heads = 1
        # 輸出是 (attn_output, attn_output_weights)
        multihead_attn = nn.MultiheadAttention(embed_dim, num_heads)
        attn_output = multihead_attn(query, key, value)[0]
        # output: torch.Size([12, 64, 300])
        # batch_size 為 64,有 12 個(gè)詞,每個(gè)詞的向量是 300 維
        print(attn_output.shape)

        7.2 手動(dòng)實(shí)現(xiàn)計(jì)算 Attention

        在 PyTorch 提供的 MultiheadAttention ?中,第 1 維是句子長(zhǎng)度,第 2 維是 batch size。這里我們的代碼實(shí)現(xiàn)中,第 1 維是 batch size,第 2 維是句子長(zhǎng)度。代碼里也包括:如何用矩陣實(shí)現(xiàn)多組注意力的并行計(jì)算。代碼中已經(jīng)有詳細(xì)注釋和說明。

        class MultiheadAttention(nn.Module):
        # n_heads:多頭注意力的數(shù)量
        # hid_dim:每個(gè)詞輸出的向量維度
        def __init__(self, hid_dim, n_heads, dropout):
        super(MultiheadAttention, self).__init__()
        self.hid_dim = hid_dim
        self.n_heads = n_heads

        # 強(qiáng)制 hid_dim 必須整除 h
        assert hid_dim % n_heads == 0
        # 定義 W_q 矩陣
        self.w_q = nn.Linear(hid_dim, hid_dim)
        # 定義 W_k 矩陣
        self.w_k = nn.Linear(hid_dim, hid_dim)
        # 定義 W_v 矩陣
        self.w_v = nn.Linear(hid_dim, hid_dim)
        self.fc = nn.Linear(hid_dim, hid_dim)
        self.do = nn.Dropout(dropout)
        # 縮放
        self.scale = torch.sqrt(torch.FloatTensor([hid_dim // n_heads]))

        def forward(self, query, key, value, mask=None):
        # K: [64,10,300], batch_size 為 64,有 12 個(gè)詞,每個(gè)詞的 Query 向量是 300 維
        # V: [64,10,300], batch_size 為 64,有 10 個(gè)詞,每個(gè)詞的 Query 向量是 300 維
        # Q: [64,12,300], batch_size 為 64,有 10 個(gè)詞,每個(gè)詞的 Query 向量是 300 維
        bsz = query.shape[0]
        Q = self.w_q(query)
        K = self.w_k(key)
        V = self.w_v(value)
        # 這里把 K Q V 矩陣拆分為多組注意力,變成了一個(gè) 4 維的矩陣
        # 最后一維就是是用 self.hid_dim // self.n_heads 來得到的,表示每組注意力的向量長(zhǎng)度, 每個(gè) head 的向量長(zhǎng)度是:300/6=50
        # 64 表示 batch size,6 表示有 6組注意力,10 表示有 10 詞,50 表示每組注意力的詞的向量長(zhǎng)度
        # K: [64,10,300] 拆分多組注意力 -> [64,10,6,50] 轉(zhuǎn)置得到 -> [64,6,10,50]
        # V: [64,10,300] 拆分多組注意力 -> [64,10,6,50] 轉(zhuǎn)置得到 -> [64,6,10,50]
        # Q: [64,12,300] 拆分多組注意力 -> [64,12,6,50] 轉(zhuǎn)置得到 -> [64,6,12,50]
        # 轉(zhuǎn)置是為了把注意力的數(shù)量 6 放到前面,把 10 和 50 放到后面,方便下面計(jì)算
        Q = Q.view(bsz, -1, self.n_heads, self.hid_dim //
        self.n_heads).permute(0, 2, 1, 3)
        K = K.view(bsz, -1, self.n_heads, self.hid_dim //
        self.n_heads).permute(0, 2, 1, 3)
        V = V.view(bsz, -1, self.n_heads, self.hid_dim //
        self.n_heads).permute(0, 2, 1, 3)

        # 第 1 步:Q 乘以 K的轉(zhuǎn)置,除以scale
        # [64,6,12,50] * [64,6,50,10] = [64,6,12,10]
        # attention:[64,6,12,10]
        attention = torch.matmul(Q, K.permute(0, 1, 3, 2)) / self.scale

        # 把 mask 不為空,那么就把 mask 為 0 的位置的 attention 分?jǐn)?shù)設(shè)置為 -1e10
        if mask is not None:
        attention = attention.masked_fill(mask == 0, -1e10)

        # 第 2 步:計(jì)算上一步結(jié)果的 softmax,再經(jīng)過 dropout,得到 attention。
        # 注意,這里是對(duì)最后一維做 softmax,也就是在輸入序列的維度做 softmax
        # attention: [64,6,12,10]
        attention = self.do(torch.softmax(attention, dim=-1))

        # 第三步,attention結(jié)果與V相乘,得到多頭注意力的結(jié)果
        # [64,6,12,10] * [64,6,10,50] = [64,6,12,50]
        # x: [64,6,12,50]
        x = torch.matmul(attention, V)

        # 因?yàn)?query 有 12 個(gè)詞,所以把 12 放到前面,把 5 和 60 放到后面,方便下面拼接多組的結(jié)果
        # x: [64,6,12,50] 轉(zhuǎn)置-> [64,12,6,50]
        x = x.permute(0, 2, 1, 3).contiguous()
        # 這里的矩陣轉(zhuǎn)換就是:把多組注意力的結(jié)果拼接起來
        # 最終結(jié)果就是 [64,12,300]
        # x: [64,12,6,50] -> [64,12,300]
        x = x.view(bsz, -1, self.n_heads * (self.hid_dim // self.n_heads))
        x = self.fc(x)
        return x


        # batch_size 為 64,有 12 個(gè)詞,每個(gè)詞的 Query 向量是 300 維
        query = torch.rand(64, 12, 300)
        # batch_size 為 64,有 12 個(gè)詞,每個(gè)詞的 Key 向量是 300 維
        key = torch.rand(64, 10, 300)
        # batch_size 為 64,有 10 個(gè)詞,每個(gè)詞的 Value 向量是 300 維
        value = torch.rand(64, 10, 300)
        attention = MultiheadAttention(hid_dim=300, n_heads=6, dropout=0.1)
        output = attention(query, key, value)
        ## output: torch.Size([64, 12, 300])
        print(output.shape)

        7.3 關(guān)鍵代碼

        其中用矩陣實(shí)現(xiàn)多頭注意力的關(guān)鍵代碼如下所示, K、Q、V 矩陣拆分為多組注意力,變成了一個(gè) 4 維的矩陣。

                # 這里把 K Q V 矩陣拆分為多組注意力,變成了一個(gè) 4 維的矩陣
        # 最后一維就是是用 self.hid_dim // self.n_heads 來得到的,表示每組注意力的向量長(zhǎng)度, 每個(gè) head 的向量長(zhǎng)度是:300/6=50
        # 64 表示 batch size,6 表示有 6組注意力,10 表示有 10 個(gè)詞,50 表示每組注意力的詞的向量長(zhǎng)度
        # K: [64,10,300] 拆分多組注意力 -> [64,10,6,50] 轉(zhuǎn)置得到 -> [64,6,10,50]
        # V: [64,10,300] 拆分多組注意力 -> [64,10,6,50] 轉(zhuǎn)置得到 -> [64,6,10,50]
        # Q: [64,12,300] 拆分多組注意力 -> [64,12,6,50] 轉(zhuǎn)置得到 -> [64,6,12,50]
        # 轉(zhuǎn)置是為了把注意力的數(shù)量 6 放到前面,把 10 和 50 放到后面,方便下面計(jì)算
        Q = Q.view(bsz, -1, self.n_heads, self.hid_dim //
        self.n_heads).permute(0, 2, 1, 3)
        K = K.view(bsz, -1, self.n_heads, self.hid_dim //
        self.n_heads).permute(0, 2, 1, 3)
        V = V.view(bsz, -1, self.n_heads, self.hid_dim //
        self.n_heads).permute(0, 2, 1, 3)
        經(jīng)過 attention 計(jì)算得到 x 的形狀是 `[64,12,6,50]`,64 表示 batch size,6 表示有 6組注意力,10 表示有 10 個(gè)詞,50 表示每組注意力的詞的向量長(zhǎng)度。把這個(gè)矩陣轉(zhuǎn)換為 `[64,12,300]`的矩陣,就是相當(dāng)于把多組注意力的結(jié)果拼接起來。
        e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e ee

        這里的矩陣轉(zhuǎn)換就是:把多組注意力的結(jié)果拼接起來,最終結(jié)果就是 [64,12,300],x: [64,12,6,50] -> [64,12,300]
        x = x.view(bsz, -1, self.n_heads * (self.hid_dim // self.n_heads))

        八、使用位置編碼來表示序列的順序

        到目前為止,我們闡述的模型中缺失了一個(gè)東西,那就是表示序列中單詞順序的方法。

        為了解決這個(gè)問題,Transformer 模型對(duì)每個(gè)輸入的向量都添加了一個(gè)向量。這些向量遵循模型學(xué)習(xí)到的特定模式,有助于確定每個(gè)單詞的位置,或者句子中不同單詞之間的距離。這種做法背后的直覺是:將這些表示位置的向量添加到詞向量中,得到了新的向量,這些新向量映射到 Q/K/V,然后計(jì)算點(diǎn)積得到 attention 時(shí),可以提供有意義的信息。

        為了讓模型了解單詞的順序,我們添加了帶有位置編碼的向量--這些向量的值遵循特定的模式。

        如果我們假設(shè)詞向量的維度是 4,那么帶有位置編碼的向量可能如下所示:

        上圖為帶有位置編碼的向量長(zhǎng)度為 4 的例子。

        那么帶有位置編碼的向量到底遵循什么模式?

        在下圖中,每一行表示一個(gè)帶有位置編碼的向量。所以,第一行對(duì)應(yīng)于序列中第一個(gè)單詞的位置編碼向量。每一行都包含 512 個(gè)值,每個(gè)值的范圍在 -1 和 1 之間。我對(duì)這些向量進(jìn)行了涂色可視化,你可以從中看到向量遵循的模式。

        這是一個(gè)真實(shí)的例子,包含了 20 個(gè)詞,每個(gè)詞向量的維度是 512。你可以看到,它看起來像從中間一分為二。這是因?yàn)樽蟀氩糠值闹凳怯?sine 函數(shù)產(chǎn)生的,而右半部分的值是由 cosine 函數(shù)產(chǎn)生的,然后將他們拼接起來,得到每個(gè)位置編碼向量。

        你可以在get_timing_signal_1d()上查看生成位置編碼的代碼。這種方法來自于Tranformer2Transformer 的實(shí)現(xiàn)。

        而論文中的方法和上面圖中的稍有不同,它不是直接拼接兩個(gè)向量,而是將兩個(gè)向量交織在一起。如下圖所示。

        此為生成位置編碼的公式,在 Transformer 論文的 3.5 節(jié)中有詳細(xì)說明。

        這不是唯一一種生成位置編碼的方法。但這種方法的優(yōu)點(diǎn)是:可以擴(kuò)展到未知的序列長(zhǎng)度。例如:當(dāng)我們的模型需要翻譯一個(gè)句子,而這個(gè)句子的長(zhǎng)度大于訓(xùn)練集中所有句子的長(zhǎng)度,這時(shí),這種位置編碼的方法也可以生成一樣長(zhǎng)的位置編碼向量。

        九、殘差連接

        在我們繼續(xù)講解之前,編碼器結(jié)構(gòu)中有一個(gè)需要注意的細(xì)節(jié)是:編碼器的每個(gè)子層(Self Attention 層和 FFNN)都有一個(gè)殘差連接和層標(biāo)準(zhǔn)化(layer-normalization)。

        將 Self-Attention 層的層標(biāo)準(zhǔn)化(layer-normalization)和向量都進(jìn)行可視化,如下所示:

        在解碼器的子層里面也有層標(biāo)準(zhǔn)化(layer-normalization)。假設(shè)一個(gè) Transformer 是由 2 層編碼器和兩層解碼器組成的,如下圖所示。

        十、Decoder(解碼器)

        現(xiàn)在我們已經(jīng)介紹了解碼器中的大部分概念,我們也基本知道了解碼器的原理。現(xiàn)在讓我們來看下, 編碼器和解碼器是如何協(xié)同工作的。

        上面說了,編碼器一般有多層,第一個(gè)編碼器的輸入是一個(gè)序列,最后一個(gè)編碼器輸出是一組注意力向量 K 和 V。這些注意力向量將會(huì)輸入到每個(gè)解碼器的Encoder-Decoder Attention層,這有助于解碼器把注意力集中中輸入序列的合適位置。

        在完成了編碼(encoding)階段之后,我們開始解碼(decoding)階段。解碼(decoding )階段的每一個(gè)時(shí)間步都輸出一個(gè)翻譯后的單詞(這里的例子是英語(yǔ)翻譯)。

        接下來會(huì)重復(fù)這個(gè)過程,直到輸出一個(gè)結(jié)束符,Transformer 就完成了所有的輸出。每一步的輸出都會(huì)在下一個(gè)時(shí)間步輸入到下面的第一個(gè)解碼器。Decoder 就像 Encoder 那樣,從下往上一層一層地輸出結(jié)果。正對(duì)如編碼器的輸入所做的處理,我們把解碼器的輸入向量,也加上位置編碼向量,來指示每個(gè)詞的位置。

        解碼器中的 Self Attention 層,和編碼器中的 Self Attention 層不太一樣:在解碼器里,Self Attention 層只允許關(guān)注到輸出序列中早于當(dāng)前位置之前的單詞。具體做法是:在 Self Attention 分?jǐn)?shù)經(jīng)過 Softmax 層之前,屏蔽當(dāng)前位置之后的那些位置。

        Encoder-Decoder Attention層的原理和多頭注意力(multiheaded Self Attention)機(jī)制類似,不同之處是:Encoder-Decoder Attention層是使用前一層的輸出來構(gòu)造 Query 矩陣,而 Key 矩陣和 Value 矩陣來自于解碼器最終的輸出。

        十一、 最后的線性層和 Softmax 層

        Decoder 最終的輸出是一個(gè)向量,其中每個(gè)元素是浮點(diǎn)數(shù)。我們?cè)趺窗堰@個(gè)向量轉(zhuǎn)換為單詞呢?這是由 Softmax 層后面的線性層來完成的。

        線性層就是一個(gè)普通的全連接神經(jīng)網(wǎng)絡(luò),可以把解碼器輸出的向量,映射到一個(gè)更長(zhǎng)的向量,這個(gè)向量稱為 logits 向量。

        現(xiàn)在假設(shè)我們的模型有 10000 個(gè)英語(yǔ)單詞(模型的輸出詞匯表),這些單詞是從訓(xùn)練集中學(xué)到的。因此 logits 向量有 10000 個(gè)數(shù)字,每個(gè)數(shù)表示一個(gè)單詞的分?jǐn)?shù)。我們就是這樣去理解線性層的輸出。

        然后,Softmax 層會(huì)把這些分?jǐn)?shù)轉(zhuǎn)換為概率(把所有的分?jǐn)?shù)轉(zhuǎn)換為正數(shù),并且加起來等于 1)。然后選擇最高概率的那個(gè)數(shù)字對(duì)應(yīng)的詞,就是這個(gè)時(shí)間步的輸出單詞。

        在上圖中,最下面的向量,就是編碼器的輸出,這個(gè)向量輸入到線性層和 Softmax 層,最終得到輸出的詞。

        十二、 Transformer 的訓(xùn)練過程

        現(xiàn)在我們已經(jīng)了解了 Transformer 的前向傳播過程,下面講講 Transformer 的訓(xùn)練過程,這也是非常有用的知識(shí)。

        在訓(xùn)練過程中,模型會(huì)經(jīng)過上面講的所有前向傳播的步驟。但是,當(dāng)我們?cè)谝粋€(gè)標(biāo)注好的數(shù)據(jù)集上訓(xùn)練這個(gè)模型的時(shí)候,我們可以對(duì)比模型的輸出和真實(shí)的標(biāo)簽。

        為了可視化這個(gè)對(duì)比,讓我們假設(shè)輸出詞匯表只包含 6 個(gè)單詞(“a”, “am”, “i”, “thanks”, “student”, and “”(“”表示句子末尾))。

        我們模型的輸出詞匯表,是在訓(xùn)練之前的數(shù)據(jù)預(yù)處理階段構(gòu)造的。當(dāng)我們確定了輸出詞匯表,我們可以用向量來表示詞匯表中的每個(gè)單詞。這個(gè)表示方法也稱為 ?one-hot encoding。例如,我們可以把單詞 “am” 用下面的向量來表示:

        介紹了訓(xùn)練過程,我們接著討論模型的損失函數(shù),這我們?cè)谟?xùn)練時(shí)需要優(yōu)化的目標(biāo),通過優(yōu)化這個(gè)目標(biāo)來得到一個(gè)訓(xùn)練好的、非常精確的模型。

        十三、 損失函數(shù)

        用一個(gè)簡(jiǎn)單的例子來說明訓(xùn)練過程,比如:把“merci”翻譯為“thanks”。

        這意味著我們希望模型最終輸出的概率分布,會(huì)指向單詞 ”thanks“(在“thanks”這個(gè)詞的概率最高)。但模型還沒訓(xùn)練好,它輸出的概率分布可能和我們希望的概率分布相差甚遠(yuǎn)。

        由于模型的參數(shù)都是隨機(jī)初始化的。模型在每個(gè)詞輸出的概率都是隨機(jī)的。我們可以把這個(gè)概率和正確的輸出概率做對(duì)比,然后使用反向傳播來調(diào)整模型的權(quán)重,使得輸出的概率分布更加接近震數(shù)輸出。

        那我們要怎么比較兩個(gè)概率分布呢?我們可以簡(jiǎn)單地用一個(gè)概率分布減去另一個(gè)概率分布。關(guān)于更多細(xì)節(jié),你可以查看交叉熵(cross-entropy)]和KL 散度(Kullback–Leibler divergence)的相關(guān)概念。

        但上面的例子是經(jīng)過簡(jiǎn)化的,因?yàn)槲覀兊木渥又挥幸粋€(gè)單詞。在實(shí)際中,我們使用的句子不只有一個(gè)單詞。例如--輸入是:“je suis étudiant” ,輸出是:“i am a student”。這意味著,我們的模型需要輸出多個(gè)概率分布,滿足如下條件:

        • 每個(gè)概率分布都是一個(gè)向量,長(zhǎng)度是 vocab_size(我們的例子中,向量長(zhǎng)度是 6,但實(shí)際中更可能是 30000 或者 50000)
        • 第一個(gè)概率分布中,最高概率對(duì)應(yīng)的單詞是 i
        • 第二個(gè)概率分布中,最高概率對(duì)應(yīng)的單詞是 am
        • 以此類推,直到第 5 個(gè)概率分布中,最高概率對(duì)應(yīng)的單詞是 ,表示沒有下一個(gè)單詞了

        我們用例子中的句子訓(xùn)練模型,希望產(chǎn)生圖中所示的概率分布

        我們的模型在一個(gè)足夠大的數(shù)據(jù)集上,經(jīng)過足夠長(zhǎng)時(shí)間的訓(xùn)練后,希望輸出的概率分布如下圖所示:

        希望經(jīng)過訓(xùn)練,模型會(huì)輸出我們希望的正確翻譯。當(dāng)然,如果你要翻譯的句子是訓(xùn)練集中的一部分,那輸出的結(jié)果并不能說明什么。我們希望的是模型在沒見過的句子上也能夠準(zhǔn)確翻譯。需要注意的是:概率分布向量中,每個(gè)位置都會(huì)有一點(diǎn)概率,即使這個(gè)位置不是輸出對(duì)應(yīng)的單詞--這是 Softmax 中一個(gè)很有用的特性,有助于幫助訓(xùn)練過程。

        現(xiàn)在,由于模型每個(gè)時(shí)間步只產(chǎn)生一個(gè)輸出,我們可以認(rèn)為:模型是從概率分布中選擇概率最大的詞,并且丟棄其他詞。這種方法叫做貪婪解碼(greedy decoding)。另一種方法是每個(gè)時(shí)間步保留兩個(gè)最高概率的輸出詞,然后在下一個(gè)時(shí)間步,重復(fù)執(zhí)行這個(gè)過程:假設(shè)第一個(gè)位置概率最高的兩個(gè)輸出的詞是”I“和”a“,這兩個(gè)詞都保留,然后根據(jù)第一個(gè)詞計(jì)算第二個(gè)位置的詞的概率分布,再取出 2 個(gè)概率最高的詞,對(duì)于第二個(gè)位置和第三個(gè)位置,我們也重復(fù)這個(gè)過程。這種方法稱為集束搜索(beam search),在我們的例子中,beam_size 的值是 2(含義是:在所有時(shí)間步,我們保留兩個(gè)最高概率),top_beams 的值也是 2(表示我們最終會(huì)返回兩個(gè)翻譯的結(jié)果)。beam_size ?和 top_beams 都是你可以在實(shí)驗(yàn)中嘗試的超參數(shù)。

        更進(jìn)一步理解

        我希望上面講的內(nèi)容,可以幫助你理解 Transformer 中的主要概念。如果你想更深一步地理解,我建議你可以參考下面這些:

        • 閱讀 Transformer 的論文:
          《Attention Is All You Need》
          鏈接地址:https://arxiv.org/abs/1706.03762
        • 閱讀Transformer 的博客文章:
          《Transformer: A Novel Neural Network Architecture for Language Understanding》
          鏈接地址:https://ai.googleblog.com/2017/08/transformer-novel-neural-network.html
        • 閱讀《Tensor2Tensor announcement》
          鏈接地址:https://ai.googleblog.com/2017/06/accelerating-deep-learning-research.html
        • 觀看視頻 【?ukasz Kaiser’s talk】來理解模型和其中的細(xì)節(jié)
          鏈接地址:https://www.youtube.com/watch?v=rBCqOTEfxvg
        • 運(yùn)行這份代碼:【Jupyter Notebook provided as part of the Tensor2Tensor repo】
          鏈接地址:https://colab.research.google.com/github/tensorflow/tensor2tensor/blob/master/tensor2tensor/notebooks/hello_t2t.ipynb。
        • 查看這個(gè)項(xiàng)目:【Tensor2Tensor repo】
          鏈接地址:https://github.com/tensorflow/tensor2tensor

        往期精彩回顧





        獲取一折本站知識(shí)星球優(yōu)惠券,復(fù)制鏈接直接打開:

        https://t.zsxq.com/y7uvZF6

        本站qq群704220115。

        加入微信群請(qǐng)掃碼:

        瀏覽 57
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            潘金莲武松再深一点 | 黄a视频在线观看 | 欧美交换乱婬粗大 | 视频一区三区 | 日韩成人AV一区二区 | 伸进内裤揉到高潮嗯啊渺渺视频 | 亚洲日本在线观看 | 男女做视频网站免费观看 | 久草久久 | 欧美日韩美女操逼视频 |