成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

成都創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括博山網(wǎng)站建設(shè)、博山網(wǎng)站制作、博山網(wǎng)頁制作以及博山網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,博山網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到博山省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

大數(shù)據(jù)文摘出品

編譯:林安安、錢天培

與基于RNN的方法相比,Transformer 不需要循環(huán),主要是由Attention 機(jī)制組成,因而可以充分利用python的高效線性代數(shù)函數(shù)庫,大量節(jié)省訓(xùn)練時(shí)間。

可是,文摘菌卻經(jīng)常聽到同學(xué)抱怨,Transformer學(xué)過就忘,總是不得要領(lǐng)。

怎么辦?那就自己搭一個(gè)Transformer吧!

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

上圖是谷歌提出的transformer 架構(gòu),其本質(zhì)上是一個(gè)Encoder-Decoder的結(jié)構(gòu)。把英文句子輸入模型,模型會(huì)輸出法文句子。

要搭建Transformer,我們必須要了解5個(gè)過程:

  • 詞向量層

  • 位置編碼

  • 創(chuàng)建Masks

  • 多頭注意層(The Multi-Head Attention layer)

  • Feed Forward層

詞向量

詞向量是神經(jīng)網(wǎng)絡(luò)機(jī)器翻譯(NMT)的標(biāo)準(zhǔn)訓(xùn)練方法,能夠表達(dá)豐富的詞義信息。

在pytorch里很容易實(shí)現(xiàn)詞向量:

class Embedderdef __init__def forwardreturn self.embed(x)

當(dāng)每個(gè)單詞進(jìn)入后,代碼就會(huì)查詢和檢索詞向量。模型會(huì)把這些向量當(dāng)作參數(shù)進(jìn)行學(xué)習(xí),并隨著梯度下降的每次迭代而調(diào)整。

給單詞賦予上下文語境:位置編程

模型理解一個(gè)句子有兩個(gè)要素:一是單詞的含義,二是單詞在句中所處的位置。

每個(gè)單詞的嵌入向量會(huì)學(xué)習(xí)單詞的含義,所以我們需要輸入一些信息,讓神經(jīng)網(wǎng)絡(luò)知道單詞在句中所處的位置。

Vasmari用下面的函數(shù)創(chuàng)建位置特異性常量來解決這類問題:

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

這個(gè)常量是一個(gè)2D矩陣。Pos代表了句子的順序,i代表了嵌入向量所處的維度位置。在pos/i矩陣中的每一個(gè)值都可以通過上面的算式計(jì)算出來。

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

位置編碼矩陣是一個(gè)常量,它的值可以用上面的算式計(jì)算出來。把常量嵌入矩陣,然后每個(gè)嵌入的單詞會(huì)根據(jù)它所處的位置發(fā)生特定轉(zhuǎn)變。

位置編輯器的代碼如下所示:

class PositionalEncoderdef __init__80):
       super().__init__()
       self.d_model = d_model
       
       # create constant 'pe' matrix with values dependant on
       # pos and i
       pe = torch.zeros(max_seq_len, d_model)
       for pos in range(max_seq_len):
           for i in range(0, d_model, 2):
               pe[pos, i] = \
               math.sin(pos / (10000 ** ((2 * i)/d_model)))
               pe[pos, i + 1] = \
               math.cos(pos / (10000 ** ((2 * (i + 1))/d_model)))
               
       pe = pe.unsqueeze(0)
       self.register_buffer('pe', pe)

   def forward# make embeddings relatively larger
       x = x * math.sqrt(self.d_model)
       #add constant to embedding
       seq_len = x.size(1)
       x = x + Variable(self.pe[:,:seq_len], \
       requires_grad=False).cuda()
       return x

以上模塊允許我們向嵌入向量添加位置編碼(positional encoding),為模型架構(gòu)提供信息。

在給詞向量添加位置編碼之前,我們要擴(kuò)大詞向量的數(shù)值,目的是讓位置編碼相對較小。這意味著向詞向量添加位置編碼時(shí),詞向量的原始含義不會(huì)丟失。

創(chuàng)建Masks

Masks在transformer模型中起重要作用,主要包括兩個(gè)方面:

在編碼器和解碼器中:當(dāng)輸入為padding,注意力會(huì)是0。

在解碼器中:預(yù)測下一個(gè)單詞,避免解碼器偷偷看到后面的翻譯內(nèi)容。

輸入端生成一個(gè)mask很簡單:

batch = next(iter(train_iter))
input_seq = batch.English.transpose(0,1)
input_pad = EN_TEXT.vocab.stoi['<pad>']

# creates mask with 0s wherever there is padding in the input
input_msk = (input_seq != input_pad).unsqueeze(1)

同樣的,Target_seq也可以生成一個(gè)mask,但是會(huì)額外增加一個(gè)步驟:

# create mask as before
target_seq = batch.French.transpose(0,1)
target_pad = FR_TEXT.vocab.stoi['<pad>']
target_msk = (target_seq != target_pad).unsqueeze(1)
size = target_seq.size(1) # get seq_len for matrix

nopeak_mask = np.triu(np.ones(1, size, size),
k=1).astype('uint8')
nopeak_mask = Variable(torch.from_numpy(nopeak_mask) == 0)

target_msk = target_msk & nopeak_mask

目標(biāo)語句(法語翻譯內(nèi)容)作為初始值輸進(jìn)解碼器中。解碼器通過編碼器的全部輸出,以及目前已翻譯的單詞來預(yù)測下一個(gè)單詞。

因此,我們需要防止解碼器偷看到還沒預(yù)測的單詞。為了達(dá)成這個(gè)目的,我們用到了nopeak_mask函數(shù):

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

當(dāng)在注意力函數(shù)中應(yīng)用mask,每一次預(yù)測都只會(huì)用到這個(gè)詞之前的句子。

多頭注意力

一旦我們有了詞向量(帶有位置編碼)和masks,我們就可以開始構(gòu)建模型層了。

下圖是多頭注意力的結(jié)構(gòu):

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

多頭注意力層,每一個(gè)輸入都會(huì)分成多頭(multiple heads),從而讓網(wǎng)絡(luò)同時(shí)“注意”每一個(gè)詞向量的不同部分。

V,K和Q分別代表“key”、“value”和“query”,這些是注意力函數(shù)的相關(guān)術(shù)語,但我不覺得解釋這些術(shù)語會(huì)對理解這個(gè)模型有任何幫助。

在編碼器中,V、K和G將作為詞向量(加上位置編碼)的相同拷貝。它們具有維度Batch_size * seq_len * d_model.

在多頭注意力中,我們把嵌入向量分進(jìn)N個(gè)頭中,它們就有了維度(batch_size * N * seq_len * (d_model / N).

我們定義最終維度 (d_model / N )為d_k。

讓我們來看看解碼器模塊的代碼:

class MultiHeadAttentiondef __init__0.1):
       super().__init__()
       self.d_model = d_model
       self.d_k = d_model // heads
       self.h = heads
       self.q_linear = nn.Linear(d_model, d_model)
       self.v_linear = nn.Linear(d_model, d_model)
       self.k_linear = nn.Linear(d_model, d_model)
       self.dropout = nn.Dropout(dropout)
       self.out = nn.Linear(d_model, d_model)

def forward0)
       
       # perform linear operation and split into h heads
       
       k = self.k_linear(k).view(bs, -1, self.h, self.d_k)
       q = self.q_linear(q).view(bs, -1, self.h, self.d_k)
       v = self.v_linear(v).view(bs, -1, self.h, self.d_k)
       
       # transpose to get dimensions bs * h * sl * d_model
     
       k = k.transpose(1,2)
       q = q.transpose(1,2)
       v = v.transpose(1,2)
# calculate attention using function we will define next
       scores = attention(q, k, v, self.d_k, mask, self.dropout)
       # concatenate heads and put through final linear layer
       concat = scores.transpose(1,2).contiguous()\
       .view(bs, -1, self.d_model)
       output = self.out(concat)
       return output

計(jì)算注意力

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

計(jì)算注意力的公式

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

圖解公式

這是另一個(gè)我們需要了解的公式,上面這幅圖很好地解釋了這個(gè)公式。

圖中的每個(gè)箭頭代表了公式的一部分。

首先,我們要用Q乘以K的轉(zhuǎn)置函數(shù)(transpose),然后通過除以d_k的平方根來實(shí)現(xiàn)scaled函數(shù)。

方程中沒有顯示的一個(gè)步驟是masking。在執(zhí)行Softmax之前,我們使用mask,減少輸入填充(padding)的值。

另一個(gè)未顯示的步驟是dropout,我們將在Softmax之后使用它。

最后一步是在目前為止的結(jié)果和V之間做點(diǎn)積(dot product)。

下面是注意力函數(shù)的代碼:

def attention-2, -1)) /  math.sqrt(d_k)
if mask is not None:
       mask = mask.unsqueeze(1)
       scores = scores.masked_fill(mask == 0, -1e9)
scores = F.softmax(scores, dim=-1)
   
   if dropout is not None:
       scores = dropout(scores)
       
   output = torch.matmul(scores, v)
   return output

前饋網(wǎng)絡(luò)

好了,如果你現(xiàn)在已經(jīng)理解以上部分,我們就進(jìn)入最后一步!

這一層由兩個(gè)線性運(yùn)算組成,兩層中夾有relu和dropout 運(yùn)算。

class FeedForwarddef __init__2048, dropout = 0.1):
       super().__init__()
       # We set d_ff as a default to 2048
       self.linear_1 = nn.Linear(d_model, d_ff)
       self.dropout = nn.Dropout(dropout)
       self.linear_2 = nn.Linear(d_ff, d_model)
   def forwardreturn x

最后一件事:歸一化

在深度神經(jīng)網(wǎng)絡(luò)中,歸一化是非常重要的。它可以防止層中值變化太多,這意味著模型訓(xùn)練速度更快,具有更好的泛化。

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

我們在編碼器/解碼器的每一層之間歸一化我們的結(jié)果,所以在構(gòu)建我們的模型之前,讓我們先定義這個(gè)函數(shù):

class Normdef __init__1e-6):
       super().__init__()
   
       self.size = d_model
       # create two learnable parameters to calibrate normalisation
       self.alpha = nn.Parameter(torch.ones(self.size))
       self.bias = nn.Parameter(torch.zeros(self.size))
       self.eps = eps
   def forward-1, keepdim=True)) \
       / (x.std(dim=-1, keepdim=True) + self.eps) + self.bias
       return norm

把所有內(nèi)容結(jié)合起來!

如果你已經(jīng)清楚了上述相關(guān)細(xì)節(jié),那么你就能理解Transformer模型啦。剩下的就是把一切都組裝起來。

讓我們再來看看整體架構(gòu),然后開始構(gòu)建:

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

最后一個(gè)變量:如果你仔細(xì)看圖,你可以看到編碼器和解碼器旁邊有一個(gè)“Nx”。實(shí)際上,上圖中的編碼器和解碼器分別表示編碼器的一層和解碼器的一層。N是層數(shù)的變量。比如,如果N=6,數(shù)據(jù)經(jīng)過6個(gè)編碼器層(如上所示的結(jié)構(gòu)),然后將這些輸出傳給解碼器,解碼器也由6個(gè)重復(fù)的解碼器層組成。

現(xiàn)在,我們將使用上面模型中所示的結(jié)構(gòu)構(gòu)建編碼器層和解碼器層模塊。在我們構(gòu)建編碼器和解碼器時(shí),我們可以決定層的數(shù)量。

# build an encoder layer with one multi-head attention layer and one # feed-forward layer

class EncoderLayerdef __init__0.1):
       super().__init__()
       self.norm_1 = Norm(d_model)
       self.norm_2 = Norm(d_model)
       self.attn = MultiHeadAttention(heads, d_model)
       self.ff = FeedForward(d_model)
       self.dropout_1 = nn.Dropout(dropout)
       self.dropout_2 = nn.Dropout(dropout)
       
   def forwardreturn x
   
# build a decoder layer with two multi-head attention layers and
# one feed-forward layer

class DecoderLayerdef __init__0.1):
       super().__init__()
       self.norm_1 = Norm(d_model)
       self.norm_2 = Norm(d_model)
       self.norm_3 = Norm(d_model)
       
       self.dropout_1 = nn.Dropout(dropout)
       self.dropout_2 = nn.Dropout(dropout)
       self.dropout_3 = nn.Dropout(dropout)
       
       self.attn_1 = MultiHeadAttention(heads, d_model)
       self.attn_2 = MultiHeadAttention(heads, d_model)
       self.ff = FeedForward(d_model).cuda()

def forwardreturn x

# We can then build a convenient cloning function that can generate multiple layers:

def get_clonesreturn nn.ModuleList([copy.deepcopy(module) for i in range(N)])

我們現(xiàn)在可以構(gòu)建編碼器和解碼器了:

class Encoderdef __init__def forwardfor i in range(N):
           x = self.layers[i](x, mask)
       return self.norm(x)
   
class Decoderdef __init__def forwardfor i in range(self.N):
           x = self.layers[i](x, e_outputs, src_mask, trg_mask)
       return self.norm(x)

Transformer模型構(gòu)建完畢!

class Transformerdef __init__def forwardreturn output

# we don't perform softmax on the output as this will be handled
# automatically by our loss function

訓(xùn)練模型

構(gòu)建完transformer,接下來要做的是用EuroParl數(shù)據(jù)集進(jìn)行訓(xùn)練。編碼部分非常簡單,但是要等兩天,模型才會(huì)開始converge!

讓我們先來定義一些參數(shù):

d_model = 512
heads = 8
N = 6
src_vocab = len(EN_TEXT.vocab)
trg_vocab = len(FR_TEXT.vocab)

model = Transformer(src_vocab, trg_vocab, d_model, N, heads)

for p in model.parameters():
   if p.dim() > 1:
       nn.init.xavier_uniform_(p)

# this code is very important! It initialises the parameters with a
# range of values that stops the signal fading or getting too big.
# See this blog for a mathematical explanation.

optim = torch.optim.Adam(model.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)

現(xiàn)在,我們可以開始訓(xùn)練了:

def train_model100):
   model.train()

   start = time.time()
   temp = start
   
   total_loss = 0
   
   for epoch in range(epochs):
     
       for i, batch in enumerate(train_iter):

           src = batch.English.transpose(0,1)
           trg = batch.French.transpose(0,1)

           # the French sentence we input has all words except
           # the last, as it is using each word to predict the next
           
           trg_input = trg[:, :-1]
           
           # the words we are trying to predict
           
           targets = trg[:, 1:].contiguous().view(-1)
           
           # create function to make masks using mask code above
           
           src_mask, trg_mask = create_masks(src, trg_input)
           
           preds = model(src, trg_input, src_mask, trg_mask)
           
           optim.zero_grad()
           
           loss = F.cross_entropy(preds.view(-1, preds.size(-1)),
           results, ignore_index=target_pad)

           loss.backward()
           optim.step()
           
           total_loss += loss.data[0]
           if (i + 1) % print_every == 0:
               loss_avg = total_loss / print_every
               print("time = %dm, epoch %d, iter = %d, loss = %.3f,
               %ds per %d iters" % ((time.time() - start) // 60,
               epoch + 1, i + 1, loss_avg, time.time() - temp,
               print_every))
               total_loss = 0
               temp = time.time()

百聞不如一碼!手把手教你用Python搭一個(gè)Transformer

示例訓(xùn)練輸出:經(jīng)過幾天的訓(xùn)練后,模型的損失函數(shù)收斂到了大約1.3。

測試模型

我們可以使用下面的函數(shù)來翻譯句子。我們可以直接輸入句子,或者輸入自定義字符串。

翻譯器通過運(yùn)行一個(gè)循環(huán)來工作。我們對英語句子進(jìn)行編碼。把<sos> token輸進(jìn)解碼器,編碼器輸出。然后,解碼器對第一個(gè)單詞進(jìn)行預(yù)測,使用<sos> token將其加進(jìn)解碼器的輸入。接著,重新運(yùn)行循環(huán),獲取下一個(gè)單詞預(yù)測,將其加入解碼器的輸入,直到<sos> token完成翻譯。

def translate80, custom_string=False):

   model.eval()

if custom_sentence == True:
       src = tokenize_en(src)
       sentence=\
       Variable(torch.LongTensor([[EN_TEXT.vocab.stoi[tok] for tok
       in sentence]])).cuda()

src_mask = (src != input_pad).unsqueeze(-2)
   e_outputs = model.encoder(src, src_mask)
   
   outputs = torch.zeros(max_len).type_as(src.data)
   outputs[0] = torch.LongTensor([FR_TEXT.vocab.stoi['<sos>']])

for i in range(1, max_len):
           
       trg_mask = np.triu(np.ones((1, i, i),
       k=1).astype('uint8')
       trg_mask= Variable(torch.from_numpy(trg_mask) == 0).cuda()
       
       out = model.out(model.decoder(outputs[:i].unsqueeze(0),
       e_outputs, src_mask, trg_mask))
       out = F.softmax(out, dim=-1)
       val, ix = out[:, -1].data.topk(1)
       
       outputs[i] = ix[0][0]
       if ix[0][0] == FR_TEXT.vocab.stoi['<eos>']:
           break

return ' '.join(
   [FR_TEXT.vocab.itos[ix] for ix in outputs[:i]]
   )

Transformer模型的構(gòu)建過程大致就是這樣。想要獲取完整代碼,可以進(jìn)入下面這個(gè)Github頁面:

https://github.com/SamLynnEvans/Transformer

相關(guān)報(bào)道:

https://towardsdatascience.com/how-to-code-the-transformer-in-pytorch-24db27c8f9ec

當(dāng)前標(biāo)題:百聞不如一碼!手把手教你用Python搭一個(gè)Transformer
轉(zhuǎn)載源于:http://jinyejixie.com/article16/gpspdg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊企業(yè)建站、網(wǎng)站建設(shè)、企業(yè)網(wǎng)站制作搜索引擎優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

手機(jī)網(wǎng)站建設(shè)