Open In Colab

Mecanismos de Atenção e Transformers#

Transformers surgiram com o artigo Attention Is All You Need (Vaswani et al., 2017), que propôs uma ruptura em relação às arquiteturas sequenciais tradicionais, como RNNs e LSTMs. A ideia central é o mecanismo de atenção, que permite ao modelo relacionar diretamente diferentes partes de uma sequência sem depender de processamento passo a passo, favorecendo paralelismo e eficiência em grandes volumes de dados. Desde então, transformers tornaram-se a base de modelos de linguagem de larga escala (como BERT, GPT e T5), além de expandirem para outras áreas como visão computacional (Vision Transformers), bioinformática e previsão de séries temporais. Sua flexibilidade em lidar com dados sequenciais e estruturados os consolidou como a arquitetura dominante em aprendizado profundo atual.

Mecanismos de Atenção (2014)#

Para superar as limitações das RNNs tradicionais, foi introduzido o mecanismo de atenção, que permite que a rede “preste atenção” em diferentes partes da sequência, independentemente da posição (Bahdanau et al., 2014). O mecanismo de atenção atua como um complemento ao processo recorrente, permitindo que a rede atribua pesos diferentes a diferentes elementos da entrada e capture informações relevantes, mesmo que estejam distantes na sequência. Isso melhora significativamente a capacidade das RNNs de lidar com dependências de longo alcance. Aplicações como a atenção em tradução automática demonstraram que esse mecanismo é eficaz para focar em palavras específicas de uma sentença ao traduzir, superando parte das limitações das RNNs puras.

Na arquitetura de Bahdanu (2014) mostrada acima, vemos um modelo Sequência a Sequência (Sequence to Sequence - Seq2seq), onde temos um codificador e um decodificador. No exemplo, o codificador é uma RNN bidirecional que extrai características \(h_i\) para cada token \(x_i\) na sequência de entrada \(\textbf{x}\) com comprimento \(T\). O decodificador é uma RNN que calcula a saída \(y_i\) de forma iterativa, passo-a-passo, utilizando o estado interno \(s_i\), a saída anterior \(y_{i-1}\) e um contexto \(c_i\), enquanto o estado interno \(s_i\) é uma função da saída anterior \(y_{i-1}\), o estado anterior \(s_{i-1}\) e o contexto \(c_i\). Este contexto \(c_i\) é uma média ponderada de todas as características \(h_i\) calculadas pelo codificador, em função de pesos de atenção \(a_i\). Cada um dos pesos de atenção \(a_i\) é calculado como uma função das características \(h_i\) e o estado interno do decodificador \(s_{i-1}\). Neste caso, todas as funções são redes neurais aprendidas com os próprios dados.

Transformers (2017)#

Com base no sucesso dos mecanismos de atenção, os Transformers, introduzidos em 2017 por Vasvani et al., levaram essa ideia ao próximo nível ao eliminar completamente a necessidade de recorrência. Os Transformers utilizam atenção auto-regressiva para processar toda a sequência de dados de uma vez, permitindo que a rede identifique dependências de curto e longo prazo de maneira eficiente, sem os problemas de gradientes desvanecentes. Isso também permite uma alta paralelização do processamento, acelerando consideravelmente o treinamento em grandes volumes de dados. Desde sua introdução, os Transformers se tornaram a arquitetura dominante em tarefas de processamento de linguagem natural e outras áreas, como visão computacional, devido à sua flexibilidade e eficiência.

A arquitetura do Transformer é baseada inteiramente em mecanismos de atenção, sem o uso de recorrência ou convolução. Ela consiste em dois blocos principais: um encoder e um decoder, ambos compostos de múltiplas camadas. Cada camada do encoder é formada por duas subcamadas principais: atenção auto-regressiva (self-attention), que permite que a rede atribua pesos a diferentes partes da sequência de entrada, e uma camada feed-forward que processa as características extraídas. O decoder segue uma estrutura similar, com a adição de uma subcamada de atenção cruzada que se conecta ao encoder, além de sua própria atenção auto-regressiva. Uma característica chave do Transformer é o uso de embeddings posicionais, que adicionam informações sobre a ordem dos elementos da sequência, algo que as RNNs capturam de forma nativa, mas que o Transformer precisa compensar. Essa estrutura permite que o Transformer processe sequências inteiras de forma paralelizada, tornando-o extremamente eficiente em tarefas como tradução automática, processamento de linguagem natural e até visão computacional.

Self-Attention#

A self-attention (atenção auto-regressiva) é um mecanismo usado para permitir que modelos atribuam pesos diferentes a diferentes partes de uma sequência de entrada, dependendo da relevância contextual entre elas. Em vez de processar uma sequência de dados de forma sequencial, a self-attention permite que o modelo “preste atenção” em todas as posições da sequência simultaneamente, calculando a importância de cada elemento em relação aos outros. Isso é feito ao gerar três vetores para cada token da sequência: query (consulta), key (chave) e value (valor). O modelo calcula as similaridades entre as queries e keys, produzindo uma pontuação de atenção, que é usada para ponderar os valores correspondentes. Com o self-attention, o modelo pode identificar relações importantes em toda a sequência, de maneira eficiente e paralelizada.

Codificação de Posicionamento#

Arquiteturas do tipo Transformer não possuem um viés indutivo explícito para a ordem das entradas, diferentemente de redes recorrentes ou convolucionais, que exploram a estrutura sequencial ou espacial dos dados. Como o mecanismo de atenção trata todos os elementos em paralelo, é necessário introduzir uma codificação de posicionamento (positional encoding) que injete informações sobre a posição relativa ou absoluta de cada token na sequência. Essa codificação pode ser fixa (como senóides de diferentes frequências) ou aprendida durante o treinamento, e é somada ou concatenada às embeddings dos tokens antes de serem processados pelo encoder. Sem esse componente, o Transformer não teria como distinguir a ordem dos elementos, comprometendo tarefas dependentes de sequência, como tradução, processamento de texto ou análise temporal.

Arquiteturas Discriminativas e Generativas de Transformers#

O Transformer revolucionou o processamento de linguagem natural (NLP) ao substituir arquiteturas sequenciais, como RNNs e LSTMs, por um mecanismo de atenção auto-regressiva que processa todo o contexto de uma sequência simultaneamente e de maneira paralela. Isso não apenas eliminou as limitações de capturar dependências de longo prazo, mas também permitiu uma paralelização eficiente do treinamento, acelerando significativamente o processamento em grandes volumes de dados. Modelos baseados no Transformer, como BERT e GPT, redefiniram o estado da arte em tarefas de NLP, como tradução, resposta a perguntas e geração de texto, graças à sua capacidade de capturar contextos complexos de maneira bidirecional ou autoregressiva.

Arquiteturas Discriminativas e BERT (2018)#

O BERT (Bidirectional Encoder Representations from Transformers) é uma aplicação avançada da arquitetura Transformer, especificamente do encoder do Transformer, desenvolvido pela Google em 2018. Enquanto o Transformer completo possui um componente de encoder e decoder para tarefas como tradução, o BERT utiliza apenas o encoder, focando em capturar o contexto bidirecional de uma palavra em uma sequência de texto. Isso significa que BERT analisa cada palavra levando em consideração as palavras à esquerda e à direita, ao contrário de modelos unidirecionais que só olham em uma direção. O uso dessa arquitetura Transformer bidirecional permite ao BERT obter representações de texto altamente contextualizadas, tornando-o extremamente eficaz para tarefas de processamento de linguagem natural, como perguntas e respostas, reconhecimento de entidades e análise de sentimento.

BERT é treinado usando duas tarefas principais: máscara de palavras (Masked Language Modeling), onde o modelo tenta prever palavras escondidas em uma sentença, e previsão de sentenças adjacentes (Next Sentence Prediction), onde ele prevê se uma frase segue logicamente a outra. Essa abordagem permite ao BERT capturar nuances complexas de significado e relações contextuais profundas, o que o torna altamente eficaz em tarefas como classificação de texto, resposta a perguntas e tradução automática.

Arquiteturas Generativas e GPT (2018-…)#

O GPT-1 (Generative Pre-trained Transformer) foi o primeiro modelo da série GPT, introduzido pela OpenAI em 2018. Ele se baseia na arquitetura do decoder do Transformer, utilizando uma abordagem unidirecional para processar texto, ou seja, cada token é gerado com base apenas nos tokens anteriores da sequência. Ao contrário do BERT, que é bidirecional, o GPT-1 foca na tarefa de modelagem de linguagem em que o modelo é pré-treinado para prever a próxima palavra em uma sequência de texto. O GPT-1 foi pré-treinado em uma grande quantidade de dados não supervisionados, sendo posteriormente ajustado para tarefas específicas de NLP, como tradução e classificação, com um processo simples de fine-tuning. Sua principal inovação foi a demonstração de que o pré-treinamento de modelos de linguagem em grandes volumes de dados seguido por ajuste fino pode produzir excelentes resultados em diversas tarefas de linguagem natural.

Com o lançamento de versões mais avançadas, como GPT-3 e GPT-4, o modelo se destacou pela sua capacidade de realizar uma ampla gama de tarefas, como tradução, resposta a perguntas, escrita criativa e geração de código, sem precisar de grandes quantidades de dados anotados. O uso de zero-shot e few-shot learning com GPT permitiu ao modelo generalizar para novas tarefas apenas com descrições mínimas ou alguns exemplos, transformando a maneira como abordamos o processamento de linguagem natural e outras aplicações de IA.

O Transformer como um Mixer de Informações#

O encoder do transformer pode ser entendido como um mecanismo de mistura de informações entre elementos de uma sequência ou conjunto, seja ela composta por palavras em um texto, patches de uma imagem, sinais em séries temporais ou mesmo conjuntos de atributos. Isso ocorre porque cada camada do encoder aplica self-attention, permitindo que cada entrada seja contextualizada em relação a todas as outras, redistribuindo a informação de forma dinâmica e dependente da tarefa. Esse processo resulta em representações ricas e globais, que não ficam limitadas a relações locais, como em convoluções ou recorrências. Por isso, o encoder é amplamente reutilizado como um bloco genérico de extração de representações.

Escalabilidade em Transformers#

Transformers capturam dependências globais, mas a auto-atenção tem custo quadrático, dificultando o uso em sequências longas, imagens e vídeos. Avanços recentes, como o Flash Attention, otimizam esse cálculo, reduzindo memória e acelerando o processamento em GPUs.

Linformer#

Reduz a complexidade quadrática da auto-atenção para linear ao projetar sequências em uma dimensão fixa. É eficiente em tarefas de NLP e visão com sequências longas, útil em ambientes com poucos recursos.

Reformer#

Emprega Locality-Sensitive Hashing para aproximar a atenção e codificação reversível para economizar memória, tornando possível treinar com sequências muito extensas.

Big Bird#

Introduz atenção esparsa (local, global e aleatória), reduzindo a complexidade para linear. Suporta documentos longos, genomas e outras sequências massivas, mantendo bom desempenho.

Flash Attention#

Reorganiza os cálculos da auto-atenção para reduzir memória e acelerar GPUs, viabilizando Transformers maiores e mais eficientes em NLP e visão.

Exemplo#

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

from datasets import load_dataset
from sklearn.metrics import accuracy_score, f1_score
from transformers import DistilBertTokenizer, DistilBertModel
from torch.utils.data import DataLoader
from tqdm import tqdm
dataset = load_dataset("imdb")
dataset['train'][0]['text']
'I rented I AM CURIOUS-YELLOW from my video store because of all the controversy that surrounded it when it was first released in 1967. I also heard that at first it was seized by U.S. customs if it ever tried to enter this country, therefore being a fan of films considered "controversial" I really had to see this for myself.<br /><br />The plot is centered around a young Swedish drama student named Lena who wants to learn everything she can about life. In particular she wants to focus her attentions to making some sort of documentary on what the average Swede thought about certain political issues such as the Vietnam War and race issues in the United States. In between asking politicians and ordinary denizens of Stockholm about their opinions on politics, she has sex with her drama teacher, classmates, and married men.<br /><br />What kills me about I AM CURIOUS-YELLOW is that 40 years ago, this was considered pornographic. Really, the sex and nudity scenes are few and far between, even then it\'s not shot like some cheaply made porno. While my countrymen mind find it shocking, in reality sex and nudity are a major staple in Swedish cinema. Even Ingmar Bergman, arguably their answer to good old boy John Ford, had sex scenes in his films.<br /><br />I do commend the filmmakers for the fact that any sex shown in the film is shown for artistic purposes rather than just to shock people and make money to be shown in pornographic theaters in America. I AM CURIOUS-YELLOW is a good film for anyone wanting to study the meat and potatoes (no pun intended) of Swedish cinema. But really, this film doesn\'t have much of a plot.'
tokenizer = DistilBertTokenizer.from_pretrained("distilbert-base-uncased")
model = DistilBertModel.from_pretrained("distilbert-base-uncased")

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
def encode_batch(model, batch):
    enc = tokenizer(batch["text"], padding=True, truncation=True, return_tensors="pt", max_length=256)
    enc = enc.to(model.device)
    with torch.no_grad():
        out = model(**enc)
    # Pega embedding do token [CLS], que está em out.last_hidden_state[:,0,:]
    return out.last_hidden_state[:,0,:].cpu()

def build_embeddings(model, dataset_split):
    loader = DataLoader(dataset_split, batch_size=128, shuffle=False)
    all_embs, all_labels = [], []
    for batch in tqdm(loader):
        embs = encode_batch(model, batch)
        all_embs.append(embs)
        all_labels.append(batch["label"])
    return torch.vstack(all_embs), torch.hstack(all_labels)

model.eval()
train_embs, train_labels = build_embeddings(model, dataset["train"])
test_embs, test_labels = build_embeddings(model, dataset["test"])
class MLP(nn.Module):
    def __init__(self, input_dim=768, hidden_dim=256, num_classes=2):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, num_classes)
        )

    def forward(self, x):
        return self.net(x)
train_tensor = torch.utils.data.TensorDataset(torch.tensor(train_embs, dtype=torch.float32),
                                              torch.tensor(train_labels))
test_tensor = torch.utils.data.TensorDataset(torch.tensor(test_embs, dtype=torch.float32),
                                             torch.tensor(test_labels))

train_loader = DataLoader(train_tensor, batch_size=64, shuffle=True)
test_loader = DataLoader(test_tensor, batch_size=64)
model_mlp = MLP().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model_mlp.parameters(), lr=1e-3)

for epoch in range(5):
    model_mlp.train()
    total_loss = 0
    for X, y in train_loader:
        X, y = X.to(device), y.to(device)
        optimizer.zero_grad()
        out = model_mlp(X)
        loss = criterion(out, y)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print(f"Epoch {epoch+1}: Loss {total_loss/len(train_loader):.4f}")
model_mlp.eval()
preds, trues = [], []
with torch.no_grad():
    for X, y in test_loader:
        X, y = X.to(device), y.to(device)
        out = model_mlp(X)
        pred = torch.argmax(out, dim=1)
        preds.extend(pred.cpu().numpy())
        trues.extend(y.cpu().numpy())

acc = accuracy_score(trues, preds)
f1 = f1_score(trues, preds, average="macro")

print("\nFinal Evaluation:")
print(f"Accuracy: {acc:.4f}")
print(f"F1-macro: {f1:.4f}")

Considerações Finais#

Neste capítulo, abordamos os Transformers, que revolucionaram o processamento de linguagem natural e, mais recentemente, a visão computacional, ao usar mecanismos de atenção auto-regressiva para capturar relações globais de forma eficiente e paralelizada. Cada uma dessas arquiteturas tem aplicações específicas e vantagens únicas, sendo marcos importantes no desenvolvimento da inteligência artificial.

Transformer vs RNN#

As RNNs e os Transformers são ambos usados para processar dados sequenciais, mas diferem fundamentalmente em seu funcionamento. As RNNs processam a sequência de dados de forma sequencial, etapa por etapa, o que limita a eficiência e dificulta a captura de dependências de longo prazo devido ao problema do gradiente desvanecente. Já os Transformers utilizam o mecanismo de self-attention, permitindo que o modelo capture dependências em qualquer posição da sequência de forma paralelizada e mais eficiente, superando as limitações das RNNs. Como resultado, os Transformers têm se tornado o padrão em muitas tarefas de processamento de linguagem natural e visão computacional, substituindo as RNNs em diversas aplicações.

ViT vs CNN#

As CNNs (Redes Neurais Convolucionais) e os Vision Transformers (ViT) são ambas arquiteturas utilizadas para visão computacional, mas diferem na forma como processam imagens. As CNNs utilizam convoluções locais, aplicando filtros para detectar padrões como bordas e texturas em pequenas regiões da imagem, com forte viés indutivo sobre a estrutura espacial. Já o ViT trata a imagem como uma sequência de patches (pequenos blocos), utilizando o mecanismo de self-attention para capturar relações globais e locais de forma mais flexível. As CNNs são mais eficientes em conjuntos de dados menores, mas o ViT se destaca em grandes volumes de dados, onde pode superar as CNNs em termos de desempenho, graças à sua capacidade de modelar dependências globais de maneira eficiente.

Transformer vs MLP#

As MLPs (Multilayer Perceptrons) e os Transformers são ambas redes neurais, mas têm diferenças fundamentais em como processam dados. O MLP é uma arquitetura mais simples, composta por camadas totalmente conectadas que tratam cada entrada de forma independente, sem considerar a estrutura dos dados. Isso limita sua capacidade de capturar relações complexas ou estruturais entre os elementos dos dados, como sequências temporais ou imagens. Em contraste, os Transformers utilizam o mecanismo de self-attention, permitindo que o modelo capture dependências globais entre diferentes partes de uma sequência, como palavras em um texto ou patches em uma imagem, de forma mais eficiente. Enquanto os MLPs são eficientes em tarefas simples e estruturadas, os Transformers se destacam em tarefas que exigem capturar relações contextuais complexas, como processamento de linguagem natural e visão computacional.

Próximos Capítulos#

Nos próximos capítulos, trataremos sobre paradigmas avançados de redes neurais artificiais, como aprendizagem auto-supervisionada, contrastive learning e meta-learning.

Exercícios#

Após extrair os embeddings do exemplo, teste:

  1. Treinar o modelo por mais épocas.

  2. Adicionar mais camadas na MLP.

  3. Adicionar mais neurônios na camada oculta da MLP.

Quais foram os resultados e o que surtiu mais efeito?

Referências#

  • Bahdanau, D. (2014). Neural machine translation by jointly learning to align and translate. arXiv preprint arXiv:1409.0473.

  • Vaswani, A. (2017). Attention is all you need. Advances in Neural Information Processing Systems.

  • Devlin, J. (2018). Bert: Pre-training of deep bidirectional transformers for language understanding. arXiv preprint arXiv:1810.04805.

  • Radford, A. (2018). Improving language understanding by generative pre-training.

  • Radford, A., Wu, J., Child, R., Luan, D., Amodei, D., & Sutskever, I. (2019). Language models are unsupervised multitask learners. OpenAI blog, 1(8), 9.

  • Brown, T. B. (2020). Language models are few-shot learners. arXiv preprint arXiv:2005.14165.

  • Achiam, J., Adler, S., Agarwal, S., Ahmad, L., Akkaya, I., Aleman, F. L., … & McGrew, B. (2023). Gpt-4 technical report. arXiv preprint arXiv:2303.08774.

  • Wang, S., Li, B. Z., Khabsa, M., Fang, H., & Ma, H. (2020). Linformer: Self-attention with linear complexity. arXiv preprint arXiv:2006.04768.

  • Kitaev, N., Kaiser, Ł., & Levskaya, A. (2020). Reformer: The efficient transformer. arXiv preprint arXiv:2001.04451.

  • Zaheer, M., Guruganesh, G., Dubey, K. A., Ainslie, J., Alberti, C., Ontanon, S., … & Ahmed, A. (2020). Big bird: Transformers for longer sequences. Advances in neural information processing systems, 33, 17283-17297.

  • Dao, T., Fu, D., Ermon, S., Rudra, A., & Ré, C. (2022). Flashattention: Fast and memory-efficient exact attention with io-awareness. Advances in Neural Information Processing Systems, 35, 16344-16359.