<jupyter_start><jupyter_text>IntroEste tutorial é baseado no tutorial [Atenção é tudo que você precisa](http://nlp.seas.harvard.edu/2018/04/03/attention.html)O transformador descrito no artigo `Attention is All You Need` tem sido de grande interesse para vários pesquisadores em redes neurais. Além de produzir ganhos significativos em tarefas de tradução, este modelo provê uma nova arquitetura para muitas tarefas de processamento de linguagem natural (`NLP`).O artigo é extremamente bem escrito, mas é de conhecimento comum que esta arquitetura é bastante difícil de implementar de forma correta. Sendo assim, <NAME> escreveu esta versão linha-a-linha de como o modelo é implementado. Ele também teve ajuda de <NAME> e <NAME>.De toda forma, eu espero que eu possa capturar de forma correta os argumentos/ideias descritas no artigo e explanadas pelos pesquisadores citados acima. Dependências* torchtext* spacy* seaborn* torchtext* matplotlib<jupyter_code>import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import math, copy, time
from torch.autograd import Variable
import matplotlib.pyplot as plt
import seaborn
seaborn.set_context(context="talk")

# Removendo warning por conta da issue: https://github.com/NVIDIA/DeepRecommender/issues/21
import warnings
warnings.filterwarnings('ignore')<jupyter_output><empty_output><jupyter_text>ContextoIntra-atenção (`self-attention`) é um mecanismo de atenção que relaciona diferentes posições de uma única sequência de modo a computar a representação daquela sequência. Intra-antenção foi utilizado com sucesso em uma variedade de tarefas cujos nomes eu não me arrisco a traduzir (reading comprehension, abstractive summarization, textual entailment and learning task-independent sentence representations).Redes neurais nesta arquitetura são baseadas em atenção recorrente ao invés de alinhamentos sequenciais e foi comprovado que esta arquitetura performa bem em tarefas de perguntas e respostas ou na representação de modelos de linguage.   ArquiteturaAssim como a maioria dos modelos de sequência mais robustos, a arquiterura deste modelo segue a estrutura de um codificador-decodificador. Aqui, o codificador mapeia uma sequência de entrada com símbolos ($x_1, ..., x_n$) para uma sequência de representação continua z = ($z_1, ..., z_n$). Dado z, o decodificador gera então uma sequência de símbolos de saída ($y_1, ..., y_n$) - um símbolo a cada intervalo de tempo. A cada intervalo de tempo, o modelo é auto-regressivo, consumindo a última sequência de símbolos como parte da entrada.<jupyter_code>class EncoderDecoder(nn.Module):
    """
    A standard Encoder-Decoder architecture. Base for this and many 
    other models.
    """
    def __init__(self, encoder, decoder, src_embed, tgt_embed, generator):
        super(EncoderDecoder, self).__init__()
        self.encoder = encoder
        self.decoder = decoder
        self.src_embed = src_embed
        self.tgt_embed = tgt_embed
        self.generator = generator
        
    def forward(self, src, tgt, src_mask, tgt_mask):
        "Take in and process masked src and target sequences."
        return self.decode(self.encode(src, src_mask), src_mask,
                            tgt, tgt_mask)
    
    def encode(self, src, src_mask):
        return self.encoder(self.src_embed(src), src_mask)
    
    def decode(self, memory, src_mask, tgt, tgt_mask):
        return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask)
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):
        return F.log_softmax(self.proj(x), dim=-1)<jupyter_output><empty_output><jupyter_text>O transformador segue esta arquitetura empilhando uma série de camadas de intra-atenção, além de camadas densamente conectadas tanto no codificador quanto no decodificador, conforme exibidor no lado esquerdo e lado direito inferior da figura: ![alt text](images/ModalNet21.png "Title") `mais detalhes explicando a figura em breve....`  Pilha de Codificadores e Decodificadores   CodificadorO codificador é composto de uma pilha de $N = 6$ camadas idênticas<jupyter_code>def clones(module, N):
    "Produce N identical layers."
    return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])
class Encoder(nn.Module):
    "Core encoder is a stack of N layers"
    def __init__(self, layer, N):
        super(Encoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = LayerNorm(layer.size)
        
    def forward(self, x, mask):
        "Pass the input (and mask) through each layer in turn."
        for layer in self.layers:
            x = layer(x, mask)
        return self.norm(x)<jupyter_output><empty_output><jupyter_text>Aplicamos uma camada residual a cada duas camadas, e em seguida, utilizamos uma camada de normalização:<jupyter_code>class LayerNorm(nn.Module):
    "Construct a layernorm module (See citation for details)."
    def __init__(self, features, eps=1e-6):
        super(LayerNorm, self).__init__()
        self.a_2 = nn.Parameter(torch.ones(features))
        self.b_2 = nn.Parameter(torch.zeros(features))
        self.eps = eps

    def forward(self, x):
        mean = x.mean(-1, keepdim=True)
        std = x.std(-1, keepdim=True)
        return self.a_2 * (x - mean) / (std + self.eps) + self.b_2<jupyter_output><empty_output><jupyter_text>Em outras palavras, a saída de cada uma dessas camadas é uma $CamadaNormalizacao(x + SubCamada(x))$, onde $SubCamada(x)$ é a função implementada pela sub-camada. Também aplicamos dropout na saída de cada sub-camada antes de adicionarmos e normalizarmos os valores de entrada.Para facilitar a conexão entre estas camadas, todas as sub-camadas do modelo assim como as camadas intermediárias produzem saídas cujas dimensões são $d_{model} = 512$<jupyter_code>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__()
        self.norm = LayerNorm(size)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, sublayer):
        "Apply residual connection to any sublayer with the same size."
        return x + self.dropout(sublayer(self.norm(x)))<jupyter_output><empty_output><jupyter_text>Cada camada possui duas sub-camadas. A primeira, implementa um mecanismo de intra-atenção múltipla enquanto que a segunda camada implementa um algoritmo de feed-forward posicional<jupyter_code>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__()
        self.self_attn = self_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(size, dropout), 2)
        self.size = size

    def forward(self, x, mask):
        "Follow Figure 1 (left) for connections."
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))
        return self.sublayer[1](x, self.feed_forward)<jupyter_output><empty_output><jupyter_text>Decodificador O decodificador também é composto de uma pilha de $N = 6$ camadas idênticas<jupyter_code>class Decoder(nn.Module):
    "Generic N layer decoder with masking."
    def __init__(self, layer, N):
        super(Decoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = LayerNorm(layer.size)
        
    def forward(self, x, memory, src_mask, tgt_mask):
        for layer in self.layers:
            x = layer(x, memory, src_mask, tgt_mask)
        return self.norm(x)<jupyter_output><empty_output><jupyter_text>Em adição as duas sub-camadas existentes no codificador, o decodificador também possui uma terceira sub-camada, que computa atenção múltipla na saída da pilha de codificadores. De forma similar ao codificador, também utilizamos conexões residuais em cada subcamada, seguidas por uma camada de normalização.<jupyter_code>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.self_attn = self_attn
        self.src_attn = src_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(size, dropout), 3)
 
    def forward(self, x, memory, src_mask, tgt_mask):
        "Follow Figure 1 (right) for connections."
        m = memory
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, tgt_mask))
        x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, src_mask))
        return self.sublayer[2](x, self.feed_forward)<jupyter_output><empty_output><jupyter_text>Também é necessário que modifiquemos a camada de intra-atenção na pilha de decodificadores para previnir que uma determinada posição atenda à posições subsequentes. Esta modificação introduz um mecanismo de obfuscação (ou máscara) que combinado com o fato dos vetores estarem deslocados uma posição para a direita assegura que as predições na posição $i$ dependerão só e somente de saídas menores que $i$<jupyter_code>def subsequent_mask(size):
    "Mask out subsequent positions."
    attn_shape = (1, size, size)
    subsequent_mask = np.triu(np.ones(attn_shape), k=1).astype('uint8')
    return torch.from_numpy(subsequent_mask) == 0<jupyter_output><empty_output><jupyter_text>> Below the attention mask shows the position each tgt word (row) is allowed to look at (column). Words are blocked for attending to future words during training.<jupyter_code>plt.figure(figsize=(5,5))
plt.imshow(subsequent_mask(20)[0])
None<jupyter_output><empty_output><jupyter_text>AtençãoUma função de atenção pode ser descrita como uma função que mapeia um consulta (`Q`) e um conjunto de chaves e valores (`K` e `V `) onde a consulta, chaves e valores assim como a saída da função são todos vetores. A saída é computada como uma soma ponderada dos valores, onde os pesos atribuídos a cada valor são computados através de uma função de compatibilidade entre a consulta e a sua correspondente chave.Cada mecanismo individual de atenção é chamado de **atenção de produto escalar** (`Scaled Dot-Product Attention`).Os dados de entrada consistem de uma consulta e chaves de dimensão $d_K$ e valores de dimensão $d_v$. Nos executamos o produto de cada termo da consulta com cada chave, dividindo cada um por $\sqrt{d_K}$ e aplicando softmax para obter os pesos de cada valor computado. ![alt text](images/attention.png "Title") ___**nota pessoal:** Há muita confusão quanto aos valores que `Q`, `K` e `V` podem assumir. No grupo de discussões de redes neurais, o aluno que explicou da seguinte forma:`TODO`___ Na prática, computamos a função de atenção em um conjunto de consultas simultaneamente. As consultas são agrupadas em uma matriz `Q` assim como as chaves e valores são agrupados em matrizes `K` e `V`. As matrizes de saída são computadas como:\begin{equation}    Attention(Q, K, V) = softmax\Big(\frac{QK^T}{\sqrt{d_k}}\Big)V\end{equation}<jupyter_code>def attention(query, key, value, mask=None, dropout=None):
    "Compute 'Scaled Dot Product Attention'"
    d_k = query.size(-1)
    scores = torch.matmul(query, key.transpose(-2, -1)) \
             / math.sqrt(d_k) # primeira caixa roxa (baixo para cima)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, -1e9)
    p_attn = F.softmax(scores, dim = -1) # caixa verde na figura
    if dropout is not None:
        p_attn = dropout(p_attn)
    return torch.matmul(p_attn, value), p_attn 

# são retornados dois valores, o primeiro é o resultado da função de anteção (multiplicação de matrizes)
# o segundo é são os vetores de atenção, obtidos após o softmax.
# TODO: indicar onde o vetor de atenção é utilizado em etapas posteriores do notebook<jupyter_output><empty_output><jupyter_text>As duas funções mais comuns de atenção são atenção-aditiva e atenção-produto-escalar. A atenção por produto escalar é idêntica ao algoritmo acima exceto por um fator escalar (caixa amarela na imagem?) de $\frac{1}{\sqrt{d_k}}$. Apesar de teoricamente serem semelhantes, a atenção por produto escalar é mais rápida uma vez que ela pode se beneficiar de códigos que optimizam a multiplicação de matrizes. No entanto, há cenários onde a atenção aditiva é superior. Para uma discussão mais detalhada, direcionamos o leitor ao artigo original.  Atenção múltipla ![alt text](images/ModalNet20.png "Title") Atenção múltipla permite ao modelo "atender" mutuamente a diferentes representações em diferentes posições do mesmo sub-espaço.\begin{equation}    MultiHead(Q, K, V) = Concat(head_1, head-2,.... head_h)W^O\end{equation}onde :\begin{equation}    head_i = Attention(QW_i^Q, KW_i^K, VW_i^V)\end{equation}e as projeções são matrizes parametrizadas na forma:* $W_i^Q \in \mathbb{R}^{d_{model} \times d_k}$* $W_i^K \in \mathbb{R}^{d_{model} \times d_k}$* $W_i^V \in \mathbb{R}^{d_{model} \times d_k}$* $W^O \in \mathbb{R}^{hd_v \times d_{model}}$No artigo, é utilizado $h = 8$ atenções múltiplas, ou cabeças. Para cada uma, nós utilizamos $d_k = d_v = \frac{d_{model}}{h} = 64$. Devido as reduções de dimensionalidade de cada cabeça, o custo computacional total é relativamente similar ao de um mecanismo de atenção com uma única cabeça utilizando as dimensões originais do modelo (i.e. $d_{model} = 512$).<jupyter_code>class MultiHeadedAttention(nn.Module):
    def __init__(self, h, d_model, dropout=0.1):
        "Take in model size and number of heads."
        super(MultiHeadedAttention, self).__init__()
        assert d_model % h == 0
        # We assume d_v always equals d_k
        self.d_k = d_model // h
        self.h = h
        # Não estou certo porque é preciso dessas 4 linear layers aqui
        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):
        "Implements Figure 2"
        if mask is not None:
            # Same mask applied to all h heads.
            mask = mask.unsqueeze(1)
        nbatches = query.size(0)
        
        # 1) Do all the linear projections in batch from d_model => h x d_k 
        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) Apply attention on all the projected vectors in batch. 
        x, self.attn = attention(query, key, value, mask=mask, 
                                 dropout=self.dropout)
        
        # 3) "Concat" using a view and apply a final linear. 
        x = x.transpose(1, 2).contiguous() \
             .view(nbatches, -1, self.h * self.d_k)
        return self.linears[-1](x)<jupyter_output><empty_output><jupyter_text>Aplicações do mecanismo de atenção em nosso modelo ___**nota pessoal:** copiando e colando a figura para faciliar a explicação a seguir:![alt text](images/ModalNet21.png "Title") O transformador utiliza atenção múltipla de 3 formas distintas:**1.** Há um mecanismo de atenção entre o codificador e o decodificador, onde as consultas vem da camada de decodificação anterior e a memória com as chaves e valores vem da saída do codificador. Isto serve para replicar o mecanismo de atenção normalmente utilizado em arquiteturas típicas encontradas em codificadores-decodificadores.> **nota pessoal**: o codificador é o lado esquerdo da figura, o decodificador o lado direito. Então apesar das duas setas que saem do codificador indicarem que elas seriam (Q e K) na verdade a ordem das setas no decodificador é invertida. No caso as setas da caixa laranja superior do decodificador devem ser lidas na seguinte ordem: V, K, Q**2.** O codificador contém camadas de intra-atenção. Em uma camada de intra-atenção, todas as consultas, chaves e valores vem do mesmo lugar, neste caso, a saída da camada anterior do codificador.**3.** De forma análoga as camadas do decodificador usam intra-atenção para que cada posição atenda a si mesma e a posições anteriores. No decodificador, é necessário previnir que informações vindas de posições a esquerda da posição atual possam comprometer a propriedade auto-regrassiva de nosso modelo. Para isto, mascaramos qualquer dado de entrada que são oriundos de conexões ilegais.> **nota pessoal**: explicar melhor essa propriedade  Mecanismo de Feed-Forward (`Position-wise Feed-Forward Networks`)Em adição aos mecanismos de atenção nas sub-camadas do modelo, cada uma das camadas de nosso codificador e decodificador contem uma rede densamente conecta com um mecanismo de feed-forward. Este mecanismo é aplicado a cada posição individualmente e separadamente. Este mecanismo consiste de duas camadas lineares com uma ativação ReLU entre as camadas. \begin{equation}    FNN(x) = max(0, xW_1 +b_1)W_2 + b_2\end{equation} Apesar das transformações lineares serem sempre as mesmas, elas usam diferentes parâmetros em cada camada.Outra forma de descrever esse mecanismo é pensar nele como duas camadas convolucionais com kernels de tamanho 1. A dimensionalidade da entrada e saída é $d_{model} = 512$ e cada camada linear tem dimensões $d_{ff} = 2048$<jupyter_code>class PositionwiseFeedForward(nn.Module):
    "Implements FFN equation."
    def __init__(self, d_model, d_ff, dropout=0.1):
        super(PositionwiseFeedForward, self).__init__()
        self.w_1 = nn.Linear(d_model, d_ff)
        self.w_2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        return self.w_2(self.dropout(F.relu(self.w_1(x))))<jupyter_output><empty_output><jupyter_text>Vetores e SofmaxSimilar a outros modelos de sequência, nós utilizamos representações (`embeddings`) previamente aprendidas para converter sequências de entrada em sequências de saída de dimensão $d_{model}$. Também utilizamos uma camada linear e uma função de softmax na saída do decodificador para predizer a probabilidade do próximo token na sequência.Em nosso modelo, nós compartilhamos as mesmas matrizes ?intermediárias? (`weight matrix`) entre as duas camadas de representações e a camada linear de softmax. Na camada de representação, nós multiplicamos os valores intermediários por $\sqrt{d_{model}}$<jupyter_code>class Embeddings(nn.Module):
    def __init__(self, d_model, vocab):
        super(Embeddings, self).__init__()
        self.lut = nn.Embedding(vocab, d_model)
        self.d_model = d_model

    def forward(self, x):
        return self.lut(x) * math.sqrt(self.d_model)<jupyter_output><empty_output><jupyter_text>Vetor de Posição Nosso modelo não contém nenhuma recorrência ou convolução. Entretanto, precisamos utilizar informações sobre a ordem dos tokens na sequência. Para isto, precisamos inferir algo como a posição relativa ou absoluta de um token em nossa sequência de entrada.Para isto, nós adicionamos vetores posicionais aos vetores de entrada de nossos codificadores e decodificadores. Estes vetores posicionais tem a mesma dimensionalidade dos vetores de entrada de modo que eles possam ser somados. Neste artigo, são utilizados duas funções de diferentes frequências:\begin{equation}    PE(pos, 2i) = sin(pos/10000^{2i/d_{model}})    \\    PE(pos, 2i+1) = cos(pos/10000^{2i/d_{model}})\end{equation}Onde $pos$ é a i-ésima posição de um token na sequência. `Mais detalhes no artigo.`<jupyter_code>class PositionalEncoding(nn.Module):
    "Implement the PE function."
    def __init__(self, d_model, dropout, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        
        # Compute the positional encodings once in log space.
        pe = torch.zeros(max_len, d_model)
        
        # https://stackoverflow.com/questions/52922445/runtimeerror-exp-not-implemented-for-torch-longtensor
        # Pequeno fix no artigo original devido ao problema acima
        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)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)
        self.register_buffer('pe', pe)
        
    def forward(self, x):
        x = x + Variable(self.pe[:, :x.size(1)], 
                         requires_grad=False)
        return self.dropout(x)<jupyter_output><empty_output><jupyter_text>___> **nota pessoal:** Eu preferi abstrair toda a discussão sobre as funções de seno e cosseno. Intuitivamente, basta que você compreenda que eu preciso codificar a posição do token na sequência. Isto não pode ser feito de forma hard-coded utilizando apenas um índice $i$ porque as posições relativas terão $i$s que conflitarão. Além disso, no final a rede é apenas uma sequência de funções que são utilizadas para computar as probabilidades. Sendo assim, precisamos de uma função que codifique a posição dos tokens.> Dado um valor suficientemente grande, i.e. 10000.0, se utilizarmos $i$ como a potência deste valor e duas funções distintas para $i$s pares ou ímpares, é provável que as posições nunca colidam. ___A intuição acima é exemplificada plotando as funções de seno e cosseno (nesse caso com $d_{model} = 20$):<jupyter_code>plt.figure(figsize=(15, 5))
pe = PositionalEncoding(20, 0)
y = pe.forward(Variable(torch.zeros(1, 100, 20)))
plt.plot(np.arange(100), y[0, :, 4:8].data.numpy())
plt.legend(["dim %d"%p for p in [4,5,6,7]])
None<jupyter_output><empty_output><jupyter_text>Modelo CompletoPara o modelo completo, precisamos definir uma função que recebe hiper-parâmetros de entrada e produz, como saída, o nosso modelo.Relembrando todos os parâmetros de entrada:`EncoderDecoder(encoder, decoder, src_embed, tgt_embed, generator)`onde:* `Encoder(layer, N)`    * `EncoderLayer(size, self_attn, feed_forward, dropout)`* `Decoder(layer, N)`   * `DecoderLayer(size, self_attn, src_attn, feed_forward, dropout)`<jupyter_code>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
    
    # mecanismos utilizados tanto no encoder quanto decoder
    attn = MultiHeadedAttention(h, d_model) # mecanismo de atenção múltipla
    ff = PositionwiseFeedForward(d_model, d_ff, dropout) # feed forward
    position = PositionalEncoding(d_model, dropout) # vetores posicionais
    
    model = EncoderDecoder(
        Encoder(EncoderLayer(d_model, c(attn), c(ff), dropout), N),
        Decoder(DecoderLayer(d_model, c(attn), c(attn), 
                             c(ff), dropout), N),
        nn.Sequential(Embeddings(d_model, src_vocab), c(position)),
        nn.Sequential(Embeddings(d_model, tgt_vocab), c(position)),
        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() > 1:
            nn.init.xavier_uniform(p)
    return model<jupyter_output><empty_output><jupyter_text>> **nota pessoal:** phew! Se você leu tudo até aqui, obrigado pelo esforço e paciência.<jupyter_code># Small example model.
tmp_model = make_model(10, 10, 2)
None<jupyter_output><empty_output><jupyter_text>Treino  Batches e obfuscação<jupyter_code>class Batch:
    "Object for holding a batch of data with mask during training."
    def __init__(self, src, trg=None, pad=0):
        self.src = src
        self.src_mask = (src != pad).unsqueeze(-2)
        if trg is not None:
            self.trg = trg[:, :-1]
            self.trg_y = trg[:, 1:]
            self.trg_mask = \
                self.make_std_mask(self.trg, pad)
            self.ntokens = (self.trg_y != pad).data.sum()
    
    @staticmethod
    def make_std_mask(tgt, pad):
        "Create a mask to hide padding and future words."
        tgt_mask = (tgt != pad).unsqueeze(-2)
        tgt_mask = tgt_mask & Variable(
            subsequent_mask(tgt.size(-1)).type_as(tgt_mask.data))
        return tgt_mask<jupyter_output><empty_output><jupyter_text>Loop de treino<jupyter_code>def run_epoch(data_iter, model, loss_compute):
    "Standard Training and Logging Function"
    start = time.time()
    total_tokens = 0
    total_loss = 0
    tokens = 0
    for i, batch in enumerate(data_iter):
        out = model.forward(batch.src, batch.trg, 
                            batch.src_mask, batch.trg_mask)
        loss = loss_compute(out, batch.trg_y, batch.ntokens)
        total_loss += loss
        total_tokens += batch.ntokens
        tokens += batch.ntokens
        if i % 50 == 1:
            elapsed = time.time() - start
            print("Epoch Step: %d Loss: %f Tokens per Sec: %f" %
                    (i, loss / batch.ntokens, tokens / elapsed))
            start = time.time()
            tokens = 0
    return total_loss / total_tokens<jupyter_output><empty_output><jupyter_text>Training Data and Batching<jupyter_code>global max_src_in_batch, max_tgt_in_batch
def batch_size_fn(new, count, sofar):
    "Keep augmenting batch and calculate total number of tokens + padding."
    global max_src_in_batch, max_tgt_in_batch
    if count == 1:
        max_src_in_batch = 0
        max_tgt_in_batch = 0
    max_src_in_batch = max(max_src_in_batch,  len(new.src))
    max_tgt_in_batch = max(max_tgt_in_batch,  len(new.trg) + 2)
    src_elements = count * max_src_in_batch
    tgt_elements = count * max_tgt_in_batch
    return max(src_elements, tgt_elements)<jupyter_output><empty_output><jupyter_text>Hardware  Optimizer<jupyter_code>class NoamOpt:
    "Optim wrapper that implements rate."
    def __init__(self, model_size, factor, warmup, optimizer):
        self.optimizer = optimizer
        self._step = 0
        self.warmup = warmup
        self.factor = factor
        self.model_size = model_size
        self._rate = 0
        
    def step(self):
        "Update parameters and rate"
        self._step += 1
        rate = self.rate()
        for p in self.optimizer.param_groups:
            p['lr'] = rate
        self._rate = rate
        self.optimizer.step()
        
    def rate(self, step = None):
        "Implement `lrate` above"
        if step is None:
            step = self._step
        return self.factor * \
            (self.model_size ** (-0.5) *
            min(step ** (-0.5), step * self.warmup ** (-1.5)))
        
def get_std_opt(model):
    return NoamOpt(model.src_embed[0].d_model, 2, 4000,
            torch.optim.Adam(model.parameters(), lr=0, betas=(0.9, 0.98), eps=1e-9))
# Three settings of the lrate hyperparameters.
opts = [NoamOpt(512, 1, 4000, None), 
        NoamOpt(512, 1, 8000, None),
        NoamOpt(256, 1, 4000, None)]
plt.plot(np.arange(1, 20000), [[opt.rate(i) for opt in opts] for i in range(1, 20000)])
plt.legend(["512:4000", "512:8000", "256:4000"])
None<jupyter_output><empty_output><jupyter_text>Regularização<jupyter_code>class LabelSmoothing(nn.Module):
    "Implement label smoothing."
    def __init__(self, size, padding_idx, smoothing=0.0):
        super(LabelSmoothing, self).__init__()
        self.criterion = nn.KLDivLoss(size_average=False)
        self.padding_idx = padding_idx
        self.confidence = 1.0 - smoothing
        self.smoothing = smoothing
        self.size = size
        self.true_dist = None
        
    def forward(self, x, target):
        assert x.size(1) == self.size
        true_dist = x.data.clone()
        true_dist.fill_(self.smoothing / (self.size - 2))
        true_dist.scatter_(1, target.data.unsqueeze(1), self.confidence)
        true_dist[:, self.padding_idx] = 0
        mask = torch.nonzero(target.data == self.padding_idx)
        if mask.dim() > 0:
            true_dist.index_fill_(0, mask.squeeze(), 0.0)
        self.true_dist = true_dist
        return self.criterion(x, Variable(true_dist, requires_grad=False))
# Example of label smoothing.
crit = LabelSmoothing(5, 0, 0.4)
predict = torch.FloatTensor([[0, 0.2, 0.7, 0.1, 0],
                             [0, 0.2, 0.7, 0.1, 0], 
                             [0, 0.2, 0.7, 0.1, 0]])
v = crit(Variable(predict.log()), 
         Variable(torch.LongTensor([2, 1, 0])))

# Show the target distributions expected by the system.
plt.imshow(crit.true_dist)
None
crit = LabelSmoothing(5, 0, 0.1)
def loss(x):
    d = x + 3 * 1
    predict = torch.FloatTensor([[0, x / d, 1 / d, 1 / d, 1 / d],
                                 ])
    #checar -- https://github.com/NVIDIA/flownet2-pytorch/issues/113    
    return crit(Variable(predict.log()),
                 Variable(torch.LongTensor([1]))).data
plt.plot(np.arange(1, 100), [loss(x) for x in range(1, 100)])
None<jupyter_output><empty_output><jupyter_text>Exemplo Inicial Dados sintéticos<jupyter_code>def data_gen(V, batch, nbatches):
    "Generate random data for a src-tgt copy task."
    for i in range(nbatches):
        data = torch.from_numpy(np.random.randint(1, V, size=(batch, 10)))
        data[:, 0] = 1
        src = Variable(data, requires_grad=False)
        tgt = Variable(data, requires_grad=False)
        yield Batch(src, tgt, 0)
class SimpleLossCompute:
    "A simple loss compute and train function."
    def __init__(self, generator, criterion, opt=None):
        self.generator = generator
        self.criterion = criterion
        self.opt = opt
        
    def __call__(self, x, y, norm):
        x = self.generator(x)
        loss = self.criterion(x.contiguous().view(-1, x.size(-1)), 
                              y.contiguous().view(-1)) / norm
        loss.backward()
        if self.opt is not None:
            self.opt.step()
            self.opt.optimizer.zero_grad()
        return loss.data * norm
# Train the simple copy task.
V = 11
criterion = LabelSmoothing(size=V, padding_idx=0, smoothing=0.0)
model = make_model(V, V, N=2)
model_opt = NoamOpt(model.src_embed[0].d_model, 1, 400,
        torch.optim.Adam(model.parameters(), lr=0, betas=(0.9, 0.98), eps=1e-9))

for epoch in range(10):
    model.train()
    run_epoch(data_gen(V, 30, 20), model, 
              SimpleLossCompute(model.generator, criterion, model_opt))
    model.eval()
    print(run_epoch(data_gen(V, 30, 5), model, 
                    SimpleLossCompute(model.generator, criterion, None)))
def greedy_decode(model, src, src_mask, max_len, start_symbol):
    memory = model.encode(src, src_mask)
    ys = torch.ones(1, 1).fill_(start_symbol).type_as(src.data)
    for i in range(max_len-1):
        out = model.decode(memory, src_mask, 
                           Variable(ys), 
                           Variable(subsequent_mask(ys.size(1))
                                    .type_as(src.data)))
        prob = model.generator(out[:, -1])
        _, next_word = torch.max(prob, dim = 1)
        next_word = next_word.data[0]
        ys = torch.cat([ys, 
                        torch.ones(1, 1).type_as(src.data).fill_(next_word)], dim=1)
    return ys

model.eval()
src = Variable(torch.LongTensor([[1,2,3,4,5,6,7,8,9,10]]) )
src_mask = Variable(torch.ones(1, 1, 10) )
print(greedy_decode(model, src, src_mask, max_len=10, start_symbol=1))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09728578693109585, 'fraction_numerical': 0.013571065344520723, 'mean_word_length': 3.5001430205949657, 'pattern_counts': {'":': 0, '<': 100, '<?xml version=': 0, '>': 109, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '24', 'dir': 'jupyter-structured-clean-dedup', 'id': '533087', 'license': 'Apache-2.0', 'n_tokens_mistral': 10853, 'n_tokens_neox': 10435, 'n_words': 3186, 'path': 'attention-is-all-you-need/atencao-eh-tudo-que-vc-precisa.ipynb', 'repo_name': 'marquesarthur/tensorflow-brasil', 'size': '31489'}