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>

        從源碼學(xué)習(xí)Transformer!

        共 15445字,需瀏覽 31分鐘

         ·

        2021-07-28 00:06

        點(diǎn)擊上方小白學(xué)視覺(jué)”,選擇加"星標(biāo)"或“置頂

        重磅干貨,第一時(shí)間送達(dá)

        本文轉(zhuǎn)自|機(jī)器學(xué)習(xí)算法工程師

        Transformer總體結(jié)構(gòu)

               近幾年NLP領(lǐng)域有了突飛猛進(jìn)的發(fā)展,預(yù)訓(xùn)練模型功不可沒(méi)。當(dāng)前利用預(yù)訓(xùn)練模型(pretrain models)在下游任務(wù)中進(jìn)行fine-tune,已經(jīng)成為了大部分NLP任務(wù)的固定范式。Transformer摒棄了RNN的序列結(jié)構(gòu),完全采用attention和全連接,嚴(yán)格來(lái)說(shuō)不屬于預(yù)訓(xùn)練模型。但它卻是當(dāng)前幾乎所有pretrain models的基本結(jié)構(gòu),為pretrain models打下了堅(jiān)實(shí)的基礎(chǔ),并逐步發(fā)展出了transformer-XL,reformer等優(yōu)化架構(gòu)。本文結(jié)合論文和源碼,對(duì)transformer基本結(jié)構(gòu),進(jìn)行詳細(xì)分析。

        Transformer是谷歌在2017年6月提出,發(fā)表在NIPS2017上。論文地址

        Attention Is All You Needarxiv.org

        分析的代碼為Harvardnlp的代碼,基于PyTorch, 地址

        annotated-transformergithub.com

        Transformer主體框架是一個(gè)encoder-decoder結(jié)構(gòu),去掉了RNN序列結(jié)構(gòu),完全基于attention和全連接。在WMT2014英語(yǔ)翻譯德語(yǔ)任務(wù)上,bleu值達(dá)到了28.4,達(dá)到當(dāng)時(shí)的SOTA。其總體結(jié)構(gòu)如下所示

        總體為一個(gè)典型的encoder-decoder結(jié)構(gòu)。代碼如下

        # 整個(gè)模型入口
        def make_model(src_vocab, tgt_vocab, N=6,
        d_model=512, d_ff=2048, h=8, dropout=0.1):
        "Helper: Construct a model from hyperparameters."
        c = copy.deepcopy

        # multiHead attention
        attn = MultiHeadedAttention(h, d_model)

        # feed-forward
        ff = PositionwiseFeedForward(d_model, d_ff, dropout)

        # position-encoding
        position = PositionalEncoding(d_model, dropout)

        # 整體為一個(gè)encoder-decoder
        model = EncoderDecoder(
        # encoder編碼層
        Encoder(EncoderLayer(d_model, c(attn), c(ff), dropout), N),

        # decoder解碼層
        Decoder(DecoderLayer(d_model, c(attn), c(attn), c(ff), dropout), N),

        # 編碼層輸入,輸入語(yǔ)句進(jìn)行token embedding和position embedding
        nn.Sequential(Embeddings(d_model, src_vocab), c(position)),

        # 解碼層輸入,同樣需要做token embedding和position embedding
        nn.Sequential(Embeddings(d_model, tgt_vocab), c(position)),

        # linear + softmax,查找vocab中概率最大的字
        Generator(d_model, tgt_vocab))

        # This was important from their code.
        # Initialize parameters with Glorot / fan_avg.
        for p in model.parameters():
        if p.dim() &gt; 1:
        nn.init.xavier_uniform(p)
        return model

        make_model為T(mén)ransformer模型定義的入口,它先定義了multi-head attention、feed-forward、position-encoding等一系列子模塊,然后定義了一個(gè)encoder-decoder結(jié)構(gòu)并返回。下面來(lái)看encoder-decoder定義。

        class EncoderDecoder(nn.Module):
        """
        一個(gè)標(biāo)準(zhǔn)的encoder和decoder框架,可以自定義embedding、encoder、decoder等
        """
        def __init__(self, encoder, decoder, src_embed, tgt_embed, generator):
        super(EncoderDecoder, self).__init__()

        # encoder和decoder通過(guò)構(gòu)造函數(shù)傳入,可靈活更改
        self.encoder = encoder
        self.decoder = decoder

        # src和target的embedding,也是通過(guò)構(gòu)造函數(shù)傳入,方便靈活更改
        self.src_embed = src_embed
        self.tgt_embed = tgt_embed

        # linear + softmax
        self.generator = generator

        def forward(self, src, tgt, src_mask, tgt_mask):
        "Take in and process masked src and target sequences."
        # 先對(duì)輸入進(jìn)行encode,然后再通過(guò)decode輸出
        return self.decode(self.encode(src, src_mask), src_mask,
        tgt, tgt_mask)

        def encode(self, src, src_mask):
        # 先對(duì)輸入進(jìn)行embedding,然后再經(jīng)過(guò)encoder
        return self.encoder(self.src_embed(src), src_mask)

        def decode(self, memory, src_mask, tgt, tgt_mask):
        # 先對(duì)目標(biāo)進(jìn)行embedding,然后經(jīng)過(guò)decoder
        return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask)

        encoder-decoder定義了一個(gè)標(biāo)準(zhǔn)的編碼解碼框架,其中編碼器、解碼器均可以自定義,有很強(qiáng)的泛化能力。模塊運(yùn)行時(shí)會(huì)調(diào)用forward函數(shù),它先對(duì)輸入進(jìn)行encode,然后再通過(guò)decode輸出。我們就不詳細(xì)展開(kāi)了。


        2 encoder

        2.1 encoder定義

        encoder分為兩部分

        1. 輸入層embedding。輸入層對(duì)inputs文本做token embedding,并對(duì)每個(gè)字做position encoding,然后疊加在一起,作為最終的輸入。

        2. 編碼層encoding。編碼層是多層結(jié)構(gòu)相同的layer堆疊而成。每個(gè)layer又包括兩部分,multi-head self-attention和feed-forward全連接,并在每部分加入了殘差連接和歸一化。

        代碼實(shí)現(xiàn)上也驗(yàn)證了這一點(diǎn)。我們看EncoderDecoder類(lèi)中的encode函數(shù),它先利用輸入embedding層對(duì)原始輸入進(jìn)行embedding,然后再通過(guò)編碼層進(jìn)行encoding。

        class EncoderDecoder(nn.Module):
        def encode(self, src, src_mask):
        # 先對(duì)輸入進(jìn)行embedding,然后再經(jīng)過(guò)encoder
        return self.encoder(self.src_embed(src), src_mask)


        2.2 輸入層embedding

        原始文本經(jīng)過(guò)embedding層進(jìn)行向量化,它包括token embedding和position embedding兩層。

        2.2.1 token embedding

        token embedding對(duì)文本進(jìn)行向量化,一般來(lái)說(shuō)有兩種方式

        1. 采用固定詞向量,比如利用Word2vec預(yù)先訓(xùn)練好的。這種方式是LSTM時(shí)代常用的方式,比較簡(jiǎn)單省事,無(wú)需訓(xùn)練。但由于詞向量是固定的,不能解決一詞多義的問(wèn)題,詞語(yǔ)本身也不是contextual的,沒(méi)有結(jié)合上下文語(yǔ)境信息,另外對(duì)于不在詞向量中的詞語(yǔ),比如特定領(lǐng)域詞語(yǔ)或者新詞,容易出現(xiàn)OOV問(wèn)題。

        2. 隨機(jī)初始化,然后訓(xùn)練。這種方式比較麻煩,需要大規(guī)模訓(xùn)練語(yǔ)料,但能解決固定詞向量的一系列問(wèn)題。Transformer采用了這種方式。

        另外,基于Transformer的BERT模型在中文處理時(shí),直接基于字做embedding,優(yōu)點(diǎn)有

        1. 無(wú)需分詞,故不會(huì)引入分詞誤差。事實(shí)上,只要訓(xùn)練語(yǔ)料充分,模型自然就可以學(xué)到分詞信息了。

        2. 中文字個(gè)數(shù)固定,不會(huì)導(dǎo)致OOV問(wèn)題

        3. 中文字相對(duì)詞,數(shù)量少很多,embedding層參數(shù)大大縮小,減小了模型體積,并加快了訓(xùn)練速度。

        事實(shí)上,就算在LSTM時(shí)代,很多case中,我們也碰到過(guò)基于字的embedding的效果比基于詞的要好一些。

        class Embeddings(nn.Module):
        # token embedding,隨機(jī)初始化訓(xùn)練,然后查表找到每個(gè)字的embedding
        def __init__(self, d_model, vocab):
        super(Embeddings, self).__init__()
        # 構(gòu)建一個(gè)隨機(jī)初始化的詞向量表,[vocab_size, d_model]。bert中的設(shè)置為[21128, 768]
        self.lut = nn.Embedding(vocab, d_model)
        self.d_model = d_model

        def forward(self, x):
        # 從詞向量表中查找字對(duì)應(yīng)的embedding向量
        return self.lut(x) * math.sqrt(self.d_model)

        由代碼可見(jiàn),Transformer采用的是隨機(jī)初始化,然后訓(xùn)練的方式。詞向量維度為[vocab_size, d_model]。例如BERT中為[21128, 768],參數(shù)量還是很大的。ALBert針對(duì)embedding層進(jìn)行矩陣分解,大大減小了embedding層體積。

        2.2.2 position encoding

        首先一個(gè)問(wèn)題,為啥要進(jìn)行位置編碼呢。原因在于self-attention,將任意兩個(gè)字之間距離縮小為1,丟失了字的位置信息,故我們需要加上這一信息。我們也可以想到兩種方法

        1. 固定編碼。Transformer采用了這一方式,通過(guò)奇數(shù)列cos函數(shù),偶數(shù)列sin函數(shù)方式,利用三角函數(shù)對(duì)位置進(jìn)行固定編碼。

        2. 動(dòng)態(tài)訓(xùn)練。BERT采用了這種方式。先隨機(jī)初始化一個(gè)embedding table,然后訓(xùn)練得到table 參數(shù)值。predict時(shí)通過(guò)embedding_lookup找到每個(gè)位置的embedding。這種方式和token embedding類(lèi)似。

        哪一種方法好呢?個(gè)人以為各有利弊

        1. 固定編碼方式簡(jiǎn)潔,不需要訓(xùn)練。且不受embedding table維度影響,理論上可以支持任意長(zhǎng)度文本。(但要盡量避免預(yù)測(cè)文本很長(zhǎng),但訓(xùn)練集文本較短的case)

        2. 動(dòng)態(tài)訓(xùn)練方式,在語(yǔ)料比較大時(shí),準(zhǔn)確度比較好。但需要訓(xùn)練,且最致命的是,限制了輸入文本長(zhǎng)度。當(dāng)文本長(zhǎng)度大于position embedding table維度時(shí),超出的position無(wú)法查表得到embedding(可以理解為OOV了)。這也是為什么BERT模型文本長(zhǎng)度最大512的原因。

        class PositionalEncoding(nn.Module):
        # 位置編碼。transformer利用編碼方式實(shí)現(xiàn),無(wú)需訓(xùn)練。bert則采用訓(xùn)練embedding_lookup方式
        # 編碼方式文本語(yǔ)句長(zhǎng)度不受限,但準(zhǔn)確度不高
        # 訓(xùn)練方式文本長(zhǎng)度會(huì)受position維度限制(這也是為什么bert只能處理最大512個(gè)字原因),但訓(xùn)練數(shù)據(jù)多時(shí),準(zhǔn)確率高
        def __init__(self, d_model, dropout, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)

        # 采用sin和cos進(jìn)行position encoding
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2) *
        -(math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term) # 偶數(shù)列
        pe[:, 1::2] = torch.cos(position * div_term) # 奇數(shù)列
        pe = pe.unsqueeze(0)
        self.register_buffer('pe', pe)

        def forward(self, x):
        # token embedding和position encoding加在一起
        x = x + Variable(self.pe[:, :x.size(1)],
        requires_grad=False)
        return self.dropout(x)

        由代碼可見(jiàn),position encoding直接采用了三角函數(shù)。對(duì)偶數(shù)列采用sin,奇數(shù)列采用cos。

        2.3 編碼層

        Encoder層是Transformer的核心,它由N層相同結(jié)構(gòu)的layer(默認(rèn)6層)堆疊而成。

        class Encoder(nn.Module):
        "Core encoder is a stack of N layers"
        def __init__(self, layer, N):
        super(Encoder, self).__init__()
        # N層堆疊而成,每一層結(jié)構(gòu)都是相同的,訓(xùn)練參數(shù)不同
        self.layers = clones(layer, N)

        # layer normalization
        self.norm = LayerNorm(layer.size)

        def forward(self, x, mask):
        # 1 經(jīng)過(guò)N層堆疊的multi-head attention + feed-forward
        for layer in self.layers:
        x = layer(x, mask)

        # 2 對(duì)encoder最終輸出結(jié)果進(jìn)行l(wèi)ayer-norm歸一化。層間和層內(nèi)子模塊都做過(guò) add + dropout + layer-norm
        return self.norm(x)

        encoder的定義很簡(jiǎn)潔。先經(jīng)過(guò)N層相同結(jié)構(gòu)的layer,然后再進(jìn)行歸一化輸出。重點(diǎn)我們來(lái)看layer的定義。

        class EncoderLayer(nn.Module):
        "Encoder is made up of self-attn and feed forward (defined below)"
        def __init__(self, size, self_attn, feed_forward, dropout):
        super(EncoderLayer, self).__init__()
        # 1 self_attention
        self.self_attn = self_attn

        # 2 feed_forward
        self.feed_forward = feed_forward

        # 3 殘差連接。encoder和decoder,每層結(jié)構(gòu),每個(gè)子結(jié)構(gòu),都有殘差連接。
        # add + drop-out + layer-norm
        self.sublayer = clones(SublayerConnection(size, dropout), 2)
        self.size = size

        def forward(self, x, mask):
        # 經(jīng)過(guò)self_attention, 然后和輸入進(jìn)行add + layer-norm
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))

        # 經(jīng)過(guò)feed_forward, 此模塊也有add + layer-norm
        return self.sublayer[1](x, self.feed_forward)

        encoder layer分為兩個(gè)子模塊

        1. self attention, 并對(duì)輸入attention前的和經(jīng)過(guò)attention輸出的,做殘差連接。殘差連接先經(jīng)過(guò)layer-norm歸一化,然后進(jìn)行dropout,最后再做add。后面我們?cè)敿?xì)分析

        2. feed-forward全連接,也有殘差連接的存在,方式和self attention相同。

        2.3.1 MultiHeadedAttention

        MultiHeadedAttention采用多頭self-attention。它先將隱向量切分為h個(gè)頭,然后每個(gè)頭內(nèi)部進(jìn)行self-attention計(jì)算,最后再concat再一起。

        代碼如下

        class MultiHeadedAttention(nn.Module):
        def __init__(self, h, d_model, dropout=0.1):
        super(MultiHeadedAttention, self).__init__()
        assert d_model % h == 0
        # d_model為隱層維度,也是embedding的維度,h為多頭個(gè)數(shù)。
        # d_k為每個(gè)頭的隱層維度,要除以多頭個(gè)數(shù)。也就是加入了多頭,總隱層維度不變。
        self.d_k = d_model // h
        self.h = h

        # 線性連接
        self.linears = clones(nn.Linear(d_model, d_model), 4)
        self.attn = None
        self.dropout = nn.Dropout(p=dropout)

        def forward(self, query, key, value, mask=None):
        if mask is not None:
        # 輸入mask,在decoder的時(shí)候有用到。decode時(shí)不能看到要生成字之后的字,所以需要mask
        mask = mask.unsqueeze(1)
        nbatches = query.size(0)

        # 1) q, k, v形狀變化,加入多頭, [batch, L, d_model] =&gt; [batch, h, L, d_model/h]
        query, key, value = [l(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2)
        for l, x in zip(self.linears, (query, key, value))]

        # 2) attention計(jì)算
        x, self.attn = attention(query, key, value, mask=mask,
        dropout=self.dropout)

        # 3) 多頭結(jié)果concat在一起,還原為初始形狀
        x = x.transpose(1, 2).contiguous().view(nbatches, -1, self.h * self.d_k)

        # 4)最后經(jīng)過(guò)一個(gè)線性層
        return self.linears[-1](x)

        下面重點(diǎn)來(lái)看單個(gè)頭的self-attention。也就是論文中的“Scaled Dot-Product Attention”。attention本質(zhì)上是一個(gè)向量的加權(quán)求和。它探討的是每個(gè)位置對(duì)當(dāng)前位置的貢獻(xiàn)。步驟如下

        1. q向量和每個(gè)位置的k向量計(jì)算點(diǎn)積,然后除以向量長(zhǎng)度的根號(hào)。計(jì)算點(diǎn)積可以認(rèn)為是進(jìn)行權(quán)重計(jì)算。除以向量長(zhǎng)度原因是向量越長(zhǎng),q*k值理論上會(huì)越大,故需要在向量長(zhǎng)度上做歸一化。

        2. attention-mask。mask和輸入矩陣shape相同,mask矩陣中值為0位置對(duì)應(yīng)的輸入矩陣的值更改為-1e9,一個(gè)非常非常小的數(shù),經(jīng)過(guò)softmax后趨近于0。decoder中使用了mask,后面我們?cè)敿?xì)分析。

        3. softmax歸一化,使得q向量和每個(gè)位置的k向量的score分布到(0, 1)之間

        4. 加權(quán)系數(shù)乘以每個(gè)位置v向量,然后加起來(lái)。

        公式如下:

        代碼如下

        def attention(query, key, value, mask=None, dropout=None):
        # attention計(jì)算,self_attention和soft-attention都是使用這個(gè)函數(shù)
        # self-attention, q k v 均來(lái)自同一文本。要么是encoder,要么是decoder
        # soft-attention, q來(lái)自decoder,k和v來(lái)自encoder,從而按照decoder和encoder相關(guān)性,將encoder信息融合進(jìn)來(lái)
        d_k = query.size(-1)

        # 利用q * k計(jì)算兩向量間相關(guān)度,相關(guān)度高則權(quán)重大。
        # 除以根號(hào)dk的原因是,對(duì)向量長(zhǎng)度進(jìn)行歸一化。q和k的向量長(zhǎng)度越長(zhǎng),q*k的值越大
        scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)

        # attention-mask,將 mask中為1的 元素所在的索引,在a中相同的的索引處替換為 value
        if mask is not None:
        scores = scores.masked_fill(mask == 0, -1e9)

        # softmax歸一化
        p_attn = F.softmax(scores, dim = -1)

        # dropout
        if dropout is not None:
        p_attn = dropout(p_attn)

        # 最后利用歸一化后的加權(quán)系數(shù),乘以每一個(gè)v向量,再加和在一起,作為attention后的向量。每個(gè)字對(duì)應(yīng)一個(gè)向量
        return torch.matmul(p_attn, value), p_attn

        self-attention和soft-attention共用了這個(gè)函數(shù),他們之間的唯一區(qū)別是q k v向量的來(lái)源不同。self-attention中q k v 均來(lái)自同一文本。而decoder的soft-attention,q來(lái)自于decoder,k和v來(lái)自于encoder。它體現(xiàn)的是encoder對(duì)decoder的加權(quán)貢獻(xiàn)。

        2.3.2 PositionwiseFeedForward

        feed-forward本質(zhì)是一個(gè)兩層的全連接,全連接之間加入了relu非線性和dropout。比較簡(jiǎn)單,代碼如下

        class PositionwiseFeedForward(nn.Module):
        # 全連接層
        def __init__(self, d_model, d_ff, dropout=0.1):
        super(PositionwiseFeedForward, self).__init__()
        # 第一層全連接 [d_model, d_ff]
        self.w_1 = nn.Linear(d_model, d_ff)

        # 第二層全連接 [d_ff, d_model]
        self.w_2 = nn.Linear(d_ff, d_model)

        # dropout
        self.dropout = nn.Dropout(dropout)

        def forward(self, x):
        # 全連接1 -&gt; relu -&gt; dropout -&gt; 全連接2
        return self.w_2(self.dropout(F.relu(self.w_1(x))))

        總體過(guò)程是:全連接1 -> relu -> dropout -> 全連接2。兩層全連接內(nèi)部沒(méi)有shortcut,這兒不要搞混了。

        2.3.3 SublayerConnection

        在每層的self-attention和feed-forward模塊中,均應(yīng)用了殘差連接。殘差連接先對(duì)輸入進(jìn)行l(wèi)ayerNorm歸一化,然后送入attention或feed-forward模塊,然后經(jīng)過(guò)dropout,最后再和原始輸入相加。這樣做的好處是,讓每一層attention和feed-forward模塊的輸入值,均是經(jīng)過(guò)歸一化的,保持在一個(gè)量級(jí)上,從而可以加快收斂速度。

        class SublayerConnection(nn.Module):
        """
        A residual connection followed by a layer norm.
        Note for code simplicity the norm is first as opposed to last.
        """
        def __init__(self, size, dropout):
        super(SublayerConnection, self).__init__()
        # layer-norm 歸一化
        self.norm = LayerNorm(size)

        # dropout
        self.dropout = nn.Dropout(dropout)

        def forward(self, x, sublayer):
        # 先對(duì)輸入進(jìn)行l(wèi)ayer-norm, 然后經(jīng)過(guò)attention等相關(guān)模塊,再經(jīng)過(guò)dropout,最后再和輸入相加
        return x + self.dropout(sublayer(self.norm(x)))

        從forward函數(shù)可見(jiàn),先對(duì)輸入進(jìn)行l(wèi)ayer-norm, 然后經(jīng)過(guò)attention等相關(guān)模塊,再經(jīng)過(guò)dropout,最后再和輸入相加。殘差連接的作用就不說(shuō)了,參考ResNet。

        3 decoder

        decoder結(jié)構(gòu)和encoder大體相同,也是堆疊了N層相同結(jié)構(gòu)的layer(默認(rèn)6層)。不同的是,decoder的每個(gè)子層包括三層。

        1. masked multi-head self-attention。這一部分和encoder基本相同,區(qū)別在于decoder為了保證模型不能看見(jiàn)要預(yù)測(cè)字的后面位置的字,加入了mask,從而避免未來(lái)信息的穿越問(wèn)題。mask為一個(gè)上三角矩陣,上三角全為1,下三角和對(duì)角線全為0

        2. multi-head soft-attention。soft-attention和self-attention結(jié)構(gòu)基本相同,甚至實(shí)現(xiàn)函數(shù)都是同一個(gè)。唯一的區(qū)別在于,self-attention的q k v矩陣來(lái)自同一個(gè),所以叫self-attention。而soft-attention的q來(lái)自decoder,k和v來(lái)自encoder。表征的是encoder的整體輸出對(duì)于decoder的貢獻(xiàn)。

        3. feed-forward。這一塊基本相同。

        另外三個(gè)模塊均使用了殘差連接,步驟仍然為 layerNorm -> attention等模塊 -> dropout -> 和輸入進(jìn)行add decoder每個(gè)layer代碼如下

        class DecoderLayer(nn.Module):
        "Decoder is made of self-attn, src-attn, and feed forward (defined below)"
        def __init__(self, size, self_attn, src_attn, feed_forward, dropout):
        super(DecoderLayer, self).__init__()
        self.size = size

        # self-attention 自注意力
        self.self_attn = self_attn

        # soft-attenton, encoder的輸出對(duì)decoder的作用
        self.src_attn = src_attn

        # feed-forward 全連接
        self.feed_forward = feed_forward

        # 殘差連接
        self.sublayer = clones(SublayerConnection(size, dropout), 3)

        def forward(self, x, memory, src_mask, tgt_mask):
        # memory為encoder最終輸出
        m = memory

        # 1 對(duì)decoder輸入做self-attention, 再和輸入做殘差連接
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, tgt_mask))

        # 2 對(duì)encoder輸出和decoder當(dāng)前進(jìn)行soft-attention,此處也有殘差連接
        x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, src_mask))

        # 3 feed-forward全連接,也有殘差連接
        return self.sublayer[2](x, self.feed_forward)

        4 輸出層

        decoder的輸出作為最終輸出層的輸入,經(jīng)過(guò)兩步

        1. linear線性連接,也即是w * x + b

        2. softmax歸一化,向量長(zhǎng)度等于vocabulary的長(zhǎng)度,得到vocabulary中每個(gè)字的概率。利用beam-search等方法,即可得到生成結(jié)果。

        這一層比較簡(jiǎn)單,代碼如下

        class Generator(nn.Module):
        "Define standard linear + softmax generation step."
        def __init__(self, d_model, vocab):
        super(Generator, self).__init__()
        self.proj = nn.Linear(d_model, vocab)

        def forward(self, x):
        # 先經(jīng)過(guò)linear線性層,然后經(jīng)過(guò)softmax得到歸一化概率分布
        # 輸出向量長(zhǎng)度等于vocabulary的維度
        return F.log_softmax(self.proj(x), dim=-1)

        5 總結(jié)

        Transformer相比LSTM的優(yōu)點(diǎn)

        1. 完全的并行計(jì)算,Transformer的attention和feed-forward,均可以并行計(jì)算。而LSTM則依賴上一時(shí)刻,必須串行

        2. 減少長(zhǎng)程依賴,利用self-attention將每個(gè)字之間距離縮短為1,大大緩解了長(zhǎng)距離依賴問(wèn)題

        3. 提高網(wǎng)絡(luò)深度。由于大大緩解了長(zhǎng)程依賴梯度衰減問(wèn)題,Transformer網(wǎng)絡(luò)可以很深,基于Transformer的BERT甚至可以做到24層。而LSTM一般只有2層或者4層。網(wǎng)絡(luò)越深,高階特征捕獲能力越好,模型performance也可以越高。

        4. 真正的雙向網(wǎng)絡(luò)。Transformer可以同時(shí)融合前后位置的信息,而雙向LSTM只是簡(jiǎn)單的將兩個(gè)方向的結(jié)果相加,嚴(yán)格來(lái)說(shuō)仍然是單向的。

        5. 可解釋性強(qiáng)。完全基于attention的Transformer,可以表達(dá)字與字之間的相關(guān)關(guān)系,可解釋性更強(qiáng)。

        Transformer也不是一定就比LSTM好,它的缺點(diǎn)如下

        1. 文本長(zhǎng)度很長(zhǎng)時(shí),比如篇章級(jí)別,計(jì)算量爆炸。self-attention的計(jì)算量為O(n^2), n為文本長(zhǎng)度。Transformer-xl利用層級(jí)方式,將計(jì)算速度提升了1800倍

        2. Transformer位置信息只靠position encoding,效果比較一般。當(dāng)語(yǔ)句較短時(shí),比如小于10個(gè)字,Transformer效果不一定比LSTM好

        3. Transformer參數(shù)量較大,在大規(guī)模數(shù)據(jù)集上,效果遠(yuǎn)好于LSTM。但在小規(guī)模數(shù)據(jù)集上,如果不是利用pretrain models,效果不一定有LSTM好。


        下載1:OpenCV-Contrib擴(kuò)展模塊中文版教程

        在「小白學(xué)視覺(jué)」公眾號(hào)后臺(tái)回復(fù):擴(kuò)展模塊中文教程,即可下載全網(wǎng)第一份OpenCV擴(kuò)展模塊教程中文版,涵蓋擴(kuò)展模塊安裝、SFM算法、立體視覺(jué)、目標(biāo)跟蹤、生物視覺(jué)、超分辨率處理等二十多章內(nèi)容。

        下載2:Python視覺(jué)實(shí)戰(zhàn)項(xiàng)目52講
        小白學(xué)視覺(jué)公眾號(hào)后臺(tái)回復(fù):Python視覺(jué)實(shí)戰(zhàn)項(xiàng)目即可下載包括圖像分割、口罩檢測(cè)、車(chē)道線檢測(cè)、車(chē)輛計(jì)數(shù)、添加眼線、車(chē)牌識(shí)別、字符識(shí)別、情緒檢測(cè)、文本內(nèi)容提取、面部識(shí)別等31個(gè)視覺(jué)實(shí)戰(zhàn)項(xiàng)目,助力快速學(xué)校計(jì)算機(jī)視覺(jué)。

        下載3:OpenCV實(shí)戰(zhàn)項(xiàng)目20講
        小白學(xué)視覺(jué)公眾號(hào)后臺(tái)回復(fù):OpenCV實(shí)戰(zhàn)項(xiàng)目20講即可下載含有20個(gè)基于OpenCV實(shí)現(xiàn)20個(gè)實(shí)戰(zhàn)項(xiàng)目,實(shí)現(xiàn)OpenCV學(xué)習(xí)進(jìn)階。

        交流群


        歡迎加入公眾號(hào)讀者群一起和同行交流,目前有SLAM、三維視覺(jué)、傳感器自動(dòng)駕駛、計(jì)算攝影、檢測(cè)、分割、識(shí)別、醫(yī)學(xué)影像、GAN、算法競(jìng)賽等微信群(以后會(huì)逐漸細(xì)分),請(qǐng)掃描下面微信號(hào)加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺(jué)SLAM“。請(qǐng)按照格式備注,否則不予通過(guò)。添加成功后會(huì)根據(jù)研究方向邀請(qǐng)進(jìn)入相關(guān)微信群。請(qǐng)勿在群內(nèi)發(fā)送廣告,否則會(huì)請(qǐng)出群,謝謝理解~


        瀏覽 63
        點(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>
            我和老妇女做爰 | 奶水被挤得到处喷hnp | 男人的天堂色 | 毛片1000部免费看 | 四虎永久在线观看 | 动漫美女靠逼 | 久本草精品| 国产精品尤物 | 欧美老妇潮喷 | 天堂网最新网址 |