GPT-2 – Language Models are Unsupervised Multitask Learners

GPT-2 – Language Models are Unsupervised Multitask Learners GPT-2 – Language Models are Unsupervised Multitask Learners

GPT-2 - Modelos de linguagem são aprendizes multitarefa não supervisionadoslink image 64

Papellink image 65

Este caderno foi traduzido automaticamente para torná-lo acessível a mais pessoas, por favor me avise se você vir algum erro de digitação..

[Language Models are Unsupervised Multitask Learners] (https://d4mucfpksywv.cloudfront.net/better-language-models/language-models.pdf) é o documento GPT-2. Essa é a segunda versão do modelo [GPT-1] (https://maximofn.com/gpt1/) que já vimos.

Arquiteturalink image 66

Antes de falarmos sobre a arquitetura da GPT-2, vamos nos lembrar de como era a arquitetura da GPT-1.

arquitetura gpt1

Uma arquitetura baseada em transformador é usada no GPT-2, como no [GPT-1] (https://maximofn.com/gpt1/), com os seguintes tamanhos

Parameters Layers d_model
117M 12 768
345M 24 1024
762M 36 1280
1542M 48 1600

O modelo menor é equivalente ao GPT original, e o segundo modelo menor é equivalente ao modelo BERT maior. O modelo maior tem mais de uma ordem de magnitude de parâmetros a mais do que o GPT.

Além disso, foram feitas as seguintes modificações arquitetônicas

  • Uma camada de normalização é adicionada antes do bloco de atenção. Isso pode ajudar a estabilizar o treinamento do modelo e melhorar a capacidade do modelo de aprender representações mais profundas. Ao normalizar as entradas para cada bloco, a variabilidade nas saídas é reduzida e o treinamento do modelo é facilitado.
  • Uma normalização adicional foi adicionada após o bloco de autoatenção final. Isso pode ajudar a reduzir a variabilidade nos resultados do modelo e melhorar a estabilidade do modelo.
  • Na maioria dos modelos, os pesos das camadas são inicializados aleatoriamente, seguindo uma distribuição normal ou uniforme. No entanto, no caso do GPT-2, os autores decidiram usar uma inicialização modificada que leva em conta a profundidade do modelo. A ideia por trás dessa inicialização modificada é que, à medida que o modelo se torna mais profundo, o sinal que flui pelas camadas residuais fica mais fraco. Isso ocorre porque cada camada residual é adicionada à entrada original, o que pode fazer com que o sinal seja atenuado com a profundidade do modelo. Para neutralizar esse efeito, eles decidiram dimensionar os pesos da camada residual na inicialização por um fator de 1/√N, em que N é o número de camadas residuais. Isso significa que, à medida que o modelo se torna mais profundo, os pesos das camadas residuais ficam menores. Esse truque de inicialização pode ajudar a estabilizar o treinamento do modelo e melhorar sua capacidade de aprender representações mais profundas. Ao dimensionar os pesos da camada residual, a variabilidade nas saídas de cada camada é reduzida e o fluxo de sinal pelo modelo é facilitado. Em resumo, a inicialização modificada na GPT-2 é usada para neutralizar o efeito de atenuação do sinal nas camadas residuais, o que ajuda a estabilizar o treinamento do modelo e a melhorar sua capacidade de aprender representações mais profundas.
  • O tamanho do vocabulário foi ampliado para 50.257. Isso significa que o modelo pode aprender a representar um conjunto maior de palavras e tokens.
  • O tamanho do contexto foi aumentado de 512 para 1024 tokens. Isso permite que o modelo leve em conta um contexto maior ao gerar o texto.

Arquitetura GPT1 vs. GPT-2](https://pub-fb664c455eca46a2ba762a065ac900f7.r2.dev/GPT1_vs_GPT2_architecture.webp)

Resumo do artigolink image 67

As ideias mais interessantes do artigo são:

  • Para o pré-treinamento do modelo, eles pensaram em usar uma fonte de texto diversificada e quase ilimitada, a raspagem da Web como Common Crawl. No entanto, eles descobriram que o texto era de qualidade quase muito ruim. Por isso, usaram o conjunto de dados WebText, que também era proveniente de raspagem da Web, mas com um filtro de qualidade, como a quantidade de links de saída do reddit etc. Eles também removeram o texto proveniente da Wikipédia, pois ele poderia ser repetido em outras páginas.
  • Eles usaram um tokenizador BPE, que explicamos em uma [postagem] anterior (https://maximofn.com/bpe/).

Geração de textolink image 68

Vamos ver como gerar texto com um GPT-2 pré-treinado.

Para gerar o texto, usaremos o modelo do repositório [GPT-2] (https://huggingface.co/openai-community/gpt2) do Hugging Face.

Geração de texto com pipelinelink image 69

Com esse modelo, agora podemos usar o pipeline de transformadores.

from transformers import pipeline
      
      checkpoints = "openai-community/gpt2-xl"
      generator = pipeline('text-generation', model=checkpoints)
      output = generator("Hello, I'm a language model,", max_length=30, num_return_sequences=5)
      for i, o in enumerate(output):
          print(f"Output {i+1}: {o['generated_text']}")
      
Truncation was not explicitly activated but `max_length` is provided a specific value, please use `truncation=True` to explicitly truncate examples to max length. Defaulting to 'longest_first' truncation strategy. If you encode pairs of sequences (GLUE-style) with the tokenizer you can select this strategy more precisely by providing a specific strategy to `truncation`.
      Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
      
Output 1: Hello, I'm a language model, and I want to change the way you read
      
      A little in today's post I want to talk about
      Output 2: Hello, I'm a language model, with two roles: the language model and the lexicographer-semantics expert. The language models are going
      Output 3: Hello, I'm a language model, and this is your brain. Here is your brain, and all this data that's stored in there, that
      Output 4: Hello, I'm a language model, and I like to talk... I want to help you talk to your customers
      
      Are you using language model
      Output 5: Hello, I'm a language model, I'm gonna tell you about what type of language you're using. We all know a language like this,
      

Geração de texto com modelo automáticolink image 70

Mas se quisermos usar o Automodel, podemos fazer o seguinte

	
from transformers import pipeline
checkpoints = "openai-community/gpt2-xl"
generator = pipeline('text-generation', model=checkpoints)
output = generator("Hello, I'm a language model,", max_length=30, num_return_sequences=5)
for i, o in enumerate(output):
print(f"Output {i+1}: {o['generated_text']}")
import torch
from transformers import GPT2Tokenizer, AutoTokenizer
checkpoints = "openai-community/gpt2-xl"
tokenizer = GPT2Tokenizer.from_pretrained(checkpoints)
auto_tokenizer = AutoTokenizer.from_pretrained(checkpoints)
Copy

Assim como no GPT-1, podemos importar o GPT2Tokenizer e o AutoTokenizer. Isso ocorre porque no model card do GPT-2 diz para usar o GPT2Tokenizer, mas na postagem da biblioteca transformers explicamos que você deve usar o AutoTokenizer para carregar o tokenizer. Portanto, vamos tentar os dois

	
from transformers import pipeline
checkpoints = "openai-community/gpt2-xl"
generator = pipeline('text-generation', model=checkpoints)
output = generator("Hello, I'm a language model,", max_length=30, num_return_sequences=5)
for i, o in enumerate(output):
print(f"Output {i+1}: {o['generated_text']}")
import torch
from transformers import GPT2Tokenizer, AutoTokenizer
checkpoints = "openai-community/gpt2-xl"
tokenizer = GPT2Tokenizer.from_pretrained(checkpoints)
auto_tokenizer = AutoTokenizer.from_pretrained(checkpoints)
checkpoints = "openai-community/gpt2-xl"
tokenizer = GPT2Tokenizer.from_pretrained(checkpoints)
auto_tokenizer = AutoTokenizer.from_pretrained(checkpoints)
input_tokens = tokenizer("Hello, I'm a language model,", return_tensors="pt")
input_auto_tokens = auto_tokenizer("Hello, I'm a language model,", return_tensors="pt")
print(f"input tokens: {input_tokens}")
print(f"input auto tokens: {input_auto_tokens}")
Copy
	
Truncation was not explicitly activated but `max_length` is provided a specific value, please use `truncation=True` to explicitly truncate examples to max length. Defaulting to 'longest_first' truncation strategy. If you encode pairs of sequences (GLUE-style) with the tokenizer you can select this strategy more precisely by providing a specific strategy to `truncation`.
Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
input tokens:
{'input_ids': tensor([[15496, 11, 314, 1101, 257, 3303, 2746, 11]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1]])}
input auto tokens:
{'input_ids': tensor([[15496, 11, 314, 1101, 257, 3303, 2746, 11]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1]])}

Como você pode ver, com os dois tokenizadores você obtém os mesmos tokens. Portanto, para tornar o código mais geral, de modo que, se você alterar os pontos de verificação, não precisará alterar o código, vamos usar o AutoTokenizer.

Em seguida, criamos o dispositivo, o tokenizador e o modelo.

	
import torch
from transformers import AutoTokenizer, GPT2LMHeadModel
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
checkpoints = "openai-community/gpt2-xl"
tokenizer = AutoTokenizer.from_pretrained(checkpoints)
model = GPT2LMHeadModel.from_pretrained(checkpoints).to(device)
Copy

Como instanciamos o modelo, vamos ver quantos parâmetros ele tem

	
import torch
from transformers import AutoTokenizer, GPT2LMHeadModel
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
checkpoints = "openai-community/gpt2-xl"
tokenizer = AutoTokenizer.from_pretrained(checkpoints)
model = GPT2LMHeadModel.from_pretrained(checkpoints).to(device)
params = sum(p.numel() for p in model.parameters())
print(f"Number of parameters: {round(params/1e6)}M")
Copy
	
Number of parameters: 1558M

Como podemos ver, carregamos o modelo de parâmetro 1.5B, mas se quiséssemos carregar os outros modelos, teríamos que fazer o seguinte

	
checkpoints_small = "openai-community/gpt2"
model_small = GPT2LMHeadModel.from_pretrained(checkpoints_small)
print(f"Number of parameters of small model: {round(sum(p.numel() for p in model_small.parameters())/1e6)}M")
checkpoints_medium = "openai-community/gpt2-medium"
model_medium = GPT2LMHeadModel.from_pretrained(checkpoints_medium)
print(f"Number of parameters of medium model: {round(sum(p.numel() for p in model_medium.parameters())/1e6)}M")
checkpoints_large = "openai-community/gpt2-large"
model_large = GPT2LMHeadModel.from_pretrained(checkpoints_large)
print(f"Number of parameters of large model: {round(sum(p.numel() for p in model_large.parameters())/1e6)}M")
checkpoints_xl = "openai-community/gpt2-xl"
model_xl = GPT2LMHeadModel.from_pretrained(checkpoints_xl)
print(f"Number of parameters of xl model: {round(sum(p.numel() for p in model_xl.parameters())/1e6)}M")
Copy
	
Number of parameters of small model: 124M
Number of parameters of medium model: 355M
Number of parameters of large model: 774M
Number of parameters of xl model: 1558M

Criamos os tokens de entrada para o modelo

	
input_sentence = "Hello, I'm a language model,"
input_tokens = tokenizer(input_sentence, return_tensors="pt").to(device)
input_tokens
Copy
	
{'input_ids': tensor([[15496, 11, 314, 1101, 257, 3303, 2746, 11]],
device='cuda:0'), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1]], device='cuda:0')}

Nós os passamos para o modelo para gerar os tokens de saída.

output_tokens = model.generate(**input_tokens)
      
      print(f"output tokens: \n{output_tokens}")
      
Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
      /home/wallabot/miniconda3/envs/nlp/lib/python3.11/site-packages/transformers/generation/utils.py:1178: UserWarning: Using the model-agnostic default `max_length` (=20) to control the generation length. We recommend setting `max_new_tokens` to control the maximum length of the generation.
        warnings.warn(
      
output tokens: 
      tensor([[15496,    11,   314,  1101,   257,  3303,  2746,    11,   290,   314,
                1101,  1016,   284,  1037,   345,   351,   534,  1917,    13,   198]],
             device='cuda:0')
      

Decodificamos os tokens para obter a declaração de saída

	
output_tokens = model.generate(**input_tokens)
print(f"output tokens: {output_tokens}")
decoded_output = tokenizer.decode(output_tokens[0], skip_special_tokens=True)
print(f"decoded output: {decoded_output}")
Copy
	
Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
/home/wallabot/miniconda3/envs/nlp/lib/python3.11/site-packages/transformers/generation/utils.py:1178: UserWarning: Using the model-agnostic default `max_length` (=20) to control the generation length. We recommend setting `max_new_tokens` to control the maximum length of the generation.
warnings.warn(
decoded output:
Hello, I'm a language model, and I'm going to help you with your problem.

Já conseguimos gerar texto com o GPT-2

Gerar token para o texto do tokenlink image 71

Usamos o model.generate para gerar os tokens de saída de uma só vez, mas vamos ver como gerá-los um a um. Para fazer isso, em vez de usar model.generate, usaremos model, que, na verdade, chama o método model.forward.

	
outputs = model(**input_tokens)
outputs
Copy
	
CausalLMOutputWithCrossAttentions(loss=None, logits=tensor([[[ 6.6288, 5.1421, -0.8002, ..., -6.3998, -4.4113, 1.8240],
[ 2.7250, 1.9371, -1.2293, ..., -5.0979, -5.1617, 2.2694],
[ 2.6891, 4.3089, -1.6074, ..., -7.6321, -2.0448, 0.4042],
...,
[ 6.0513, 3.8020, -2.8080, ..., -6.7754, -8.3176, 1.1541],
[ 6.8402, 5.6952, 0.2002, ..., -9.1281, -6.7818, 2.7576],
[ 1.0255, -0.2201, -2.5484, ..., -6.2137, -7.2322, 0.1665]]],
device='cuda:0', grad_fn=<UnsafeViewBackward0>), past_key_values=((tensor([[[[ 0.4779, 0.7671, -0.7532, ..., -0.3551, 0.4590, 0.3073],
[ 0.2034, -0.6033, 0.2484, ..., 0.7760, -0.3546, 0.0198],
[-0.1968, -0.9029, 0.5570, ..., 0.9985, -0.5028, -0.3508],
...,
[-0.5007, -0.4009, 0.1604, ..., -0.3693, -0.1158, 0.1320],
[-0.4854, -0.1369, 0.7377, ..., -0.8043, -0.1054, 0.0871],
[ 0.1610, -0.8358, -0.5534, ..., 0.9951, -0.3085, 0.4574]],
[[ 0.6288, -0.1374, -0.3467, ..., -1.0003, -1.1518, 0.3114],
[-1.7269, 1.2920, -0.0734, ..., 1.0572, 1.4698, -2.0412],
[ 0.2714, -0.0670, -0.4769, ..., 0.6305, 0.6890, -0.8158],
...,
[-0.0499, -0.0721, 0.4580, ..., 0.6797, 0.2331, 0.0210],
[-0.1894, 0.2077, 0.6722, ..., 0.6938, 0.2104, -0.0574],
[ 0.3661, -0.0218, 0.2618, ..., 0.8750, 1.2205, -0.6103]],
[[ 0.5964, 1.1178, 0.3604, ..., 0.8426, 0.4881, -0.4094],
[ 0.3186, -0.3953, 0.2687, ..., -0.1110, -0.5640, 0.5900],
...,
[ 0.2092, 0.3898, -0.6061, ..., -0.2859, -0.3136, -0.1002],
[ 0.0539, 0.8941, 0.3423, ..., -0.6326, -0.1053, -0.6679],
[ 0.5628, 0.6687, -0.2720, ..., -0.1073, -0.9792, -0.0302]]]],
device='cuda:0', grad_fn=<PermuteBackward0>))), hidden_states=None, attentions=None, cross_attentions=None)

Vemos que isso gera muitos dados, mas primeiro vamos dar uma olhada nas chaves de saída.

	
outputs.keys()
Copy
	
odict_keys(['logits', 'past_key_values'])

Nesse caso, temos apenas os logits do modelo, vamos ver seu tamanho.

	
logits = outputs.logits
logits.shape
Copy
	
torch.Size([1, 8, 50257])

Vamos ver quantos tokens tínhamos na entrada.

	
input_tokens.input_ids.shape
Copy
	
torch.Size([1, 8])

Bem, temos o mesmo número de logits na saída e na entrada. Isso é normal

Obtemos os logits da última posição da saída

	
nex_token_logits = logits[0,-1]
nex_token_logits.shape
Copy
	
torch.Size([50257])

Há um total de 50257 logits, ou seja, há um vocabulário de 50257 tokens e temos que ver qual token tem a maior probabilidade.

	
softmax_logits = torch.softmax(nex_token_logits, dim=0)
softmax_logits.shape
Copy
	
torch.Size([50257])

Depois de calcularmos o softmax, obtemos o token mais provável procurando aquele com a maior probabilidade, ou seja, aquele com o maior valor após o softmax.

	
next_token_prob, next_token_id = torch.max(softmax_logits, dim=0)
next_token_prob, next_token_id
Copy
	
(tensor(0.1732, device='cuda:0', grad_fn=<MaxBackward0>),
tensor(290, device='cuda:0'))

Obtivemos o seguinte token, agora vamos decodificá-lo

	
tokenizer.decode(next_token_id.item())
Copy
	
' and'

Obtivemos o seguinte token usando o método guloso, ou seja, o token com a maior probabilidade. Mas já vimos na postagem sobre a biblioteca de transformadores, a [ways to generate texts] (https://maximofn.com/hugging-face-transformers/#Formas-de-generaci%C3%B3n-de-texto) que você pode fazer sampling, top-k, top-p, etc.

Vamos colocar tudo em uma função e ver o que acontece se gerarmos alguns tokens.

	
def generate_next_greedy_token(input_sentence, tokenizer, model, device):
input_tokens = tokenizer(input_sentence, return_tensors="pt").to(device)
outputs = model(**input_tokens)
logits = outputs.logits
nex_token_logits = logits[0,-1]
softmax_logits = torch.softmax(nex_token_logits, dim=0)
next_token_prob, next_token_id = torch.max(softmax_logits, dim=0)
return next_token_prob, next_token_id
Copy
	
def generate_next_greedy_token(input_sentence, tokenizer, model, device):
input_tokens = tokenizer(input_sentence, return_tensors="pt").to(device)
outputs = model(**input_tokens)
logits = outputs.logits
nex_token_logits = logits[0,-1]
softmax_logits = torch.softmax(nex_token_logits, dim=0)
next_token_prob, next_token_id = torch.max(softmax_logits, dim=0)
return next_token_prob, next_token_id
def generate_greedy_text(input_sentence, tokenizer, model, device, max_length=20):
generated_text = input_sentence
for _ in range(max_length):
next_token_prob, next_token_id = generate_next_greedy_token(generated_text, tokenizer, model, device)
generated_text += tokenizer.decode(next_token_id.item())
return generated_text
Copy

Agora vamos gerar o texto

	
def generate_next_greedy_token(input_sentence, tokenizer, model, device):
input_tokens = tokenizer(input_sentence, return_tensors="pt").to(device)
outputs = model(**input_tokens)
logits = outputs.logits
nex_token_logits = logits[0,-1]
softmax_logits = torch.softmax(nex_token_logits, dim=0)
next_token_prob, next_token_id = torch.max(softmax_logits, dim=0)
return next_token_prob, next_token_id
def generate_greedy_text(input_sentence, tokenizer, model, device, max_length=20):
generated_text = input_sentence
for _ in range(max_length):
next_token_prob, next_token_id = generate_next_greedy_token(generated_text, tokenizer, model, device)
generated_text += tokenizer.decode(next_token_id.item())
return generated_text
generate_greedy_text("Hello, I'm a language model,", tokenizer, model, device)
Copy
	
"Hello, I'm a language model, and I'm going to help you with your problem. I'm going to help you"

O resultado é bastante repetitivo, como já visto em [ways to generate text] (https://maximofn.com/hugging-face-transformers/#Formas-de-generaci%C3%B3n-de-texto). Mas, ainda assim, é um resultado melhor do que o que obtivemos com o [GPT-1] (https://maximofn.com/gpt1/#Generaci%C3%B3n-de-texto).

Arquitetura dos modelos disponíveis no Hugging Facelink image 73

Se acessarmos a documentação do Hugging Face do GPT2, veremos que temos as opções GPT2Model, GPT2LMHeadModel, GPT2ForSequenceClassification, GPT2ForQuestionAnswering, GPT2ForTokenClassification. Vamos dar uma olhada neles

	
import torch
ckeckpoints = "openai-community/gpt2"
Copy

GPT2Modellink image 74

Esse é o modelo básico, ou seja, o decodificador de transformador.

	
import torch
ckeckpoints = "openai-community/gpt2"
from transformers import GPT2Model
model = GPT2Model.from_pretrained(ckeckpoints)
model
Copy
	
GPT2Model(
(wte): Embedding(50257, 768)
(wpe): Embedding(1024, 768)
(drop): Dropout(p=0.1, inplace=False)
(h): ModuleList(
(0-11): 12 x GPT2Block(
(ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(attn): GPT2Attention(
(c_attn): Conv1D()
(c_proj): Conv1D()
(attn_dropout): Dropout(p=0.1, inplace=False)
(resid_dropout): Dropout(p=0.1, inplace=False)
)
(ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): GPT2MLP(
(c_fc): Conv1D()
(c_proj): Conv1D()
(act): NewGELUActivation()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(ln_f): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)

Como você pode ver na saída, um tensor de dimensão 768, que é a dimensão dos embeddings do modelo pequeno. Se tivéssemos usado o modelo openai-community/gpt2-xl, teríamos obtido um resultado de 1600.

Dependendo da tarefa em questão, seria necessário adicionar mais camadas.

Podemos adicioná-las manualmente, mas os pesos dessas camadas seriam inicializados de forma aleatória. Por outro lado, se usarmos os modelos Hugging Face com essas camadas, os pesos serão pré-treinados.

GPT2LMHeadModellink image 75

É o mesmo que usamos anteriormente para gerar texto

	
from transformers import GPT2LMHeadModel
model = GPT2LMHeadModel.from_pretrained(ckeckpoints)
model
Copy
	
GPT2LMHeadModel(
(transformer): GPT2Model(
(wte): Embedding(50257, 768)
(wpe): Embedding(1024, 768)
(drop): Dropout(p=0.1, inplace=False)
(h): ModuleList(
(0-11): 12 x GPT2Block(
(ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(attn): GPT2Attention(
(c_attn): Conv1D()
(c_proj): Conv1D()
(attn_dropout): Dropout(p=0.1, inplace=False)
(resid_dropout): Dropout(p=0.1, inplace=False)
)
(ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): GPT2MLP(
(c_fc): Conv1D()
(c_proj): Conv1D()
(act): NewGELUActivation()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(ln_f): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)
(lm_head): Linear(in_features=768, out_features=50257, bias=False)
)

Como você pode ver, é o mesmo modelo anterior, só que no final foi adicionada uma camada linear com uma entrada de 768 (os embeddings) e uma saída de 50257, que corresponde ao tamanho do vocabulário.

GPT2ForSequenceClassificationlink image 76

Essa opção serve para classificar sequências de texto; nesse caso, devemos especificar com num_labels o número de classes que queremos classificar.

from transformers import GPT2ForSequenceClassification
      model = GPT2ForSequenceClassification.from_pretrained(ckeckpoints, num_labels=5)
      model
      
Some weights of GPT2ForSequenceClassification were not initialized from the model checkpoint at openai-community/gpt2 and are newly initialized: ['score.weight']
      You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
      
Out[10]:
GPT2ForSequenceClassification(
        (transformer): GPT2Model(
          (wte): Embedding(50257, 768)
          (wpe): Embedding(1024, 768)
          (drop): Dropout(p=0.1, inplace=False)
          (h): ModuleList(
            (0-11): 12 x GPT2Block(
              (ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
              (attn): GPT2Attention(
                (c_attn): Conv1D()
                (c_proj): Conv1D()
                (attn_dropout): Dropout(p=0.1, inplace=False)
                (resid_dropout): Dropout(p=0.1, inplace=False)
              )
              (ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
              (mlp): GPT2MLP(
                (c_fc): Conv1D()
                (c_proj): Conv1D()
                (act): NewGELUActivation()
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
          )
          (ln_f): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
        )
        (score): Linear(in_features=768, out_features=5, bias=False)
      )

Agora, em vez de termos uma saída de 50257, temos uma saída de 5, que é o número que inserimos em num_labels e é o número de classes que queremos classificar.

GPT2ForQuestionAnsweringlink image 77

Na postagem transformers, explicamos que, nesse modo, você passa um contexto para o modelo e uma pergunta sobre o contexto e ele retorna a resposta.

from transformers import GPT2ForQuestionAnswering
      model = GPT2ForQuestionAnswering.from_pretrained(ckeckpoints)
      model
      
Some weights of GPT2ForQuestionAnswering were not initialized from the model checkpoint at openai-community/gpt2 and are newly initialized: ['qa_outputs.bias', 'qa_outputs.weight']
      You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
      
Out[13]:
GPT2ForQuestionAnswering(
        (transformer): GPT2Model(
          (wte): Embedding(50257, 768)
          (wpe): Embedding(1024, 768)
          (drop): Dropout(p=0.1, inplace=False)
          (h): ModuleList(
            (0-11): 12 x GPT2Block(
              (ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
              (attn): GPT2Attention(
                (c_attn): Conv1D()
                (c_proj): Conv1D()
                (attn_dropout): Dropout(p=0.1, inplace=False)
                (resid_dropout): Dropout(p=0.1, inplace=False)
              )
              (ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
              (mlp): GPT2MLP(
                (c_fc): Conv1D()
                (c_proj): Conv1D()
                (act): NewGELUActivation()
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
          )
          (ln_f): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
        )
        (qa_outputs): Linear(in_features=768, out_features=2, bias=True)
      )

Vemos que o resultado nos dá um tensor bidimensional.

GPT2ForTokenClassificationlink image 78

Também na postagem transformers dissemos o que era a calsificação de tokens, explicamos que ela classificava a qual categoria cada token pertencia. Temos que passar o número de classes que queremos classificar com num_labels.

from transformers import GPT2ForTokenClassification
      model = GPT2ForTokenClassification.from_pretrained(ckeckpoints, num_labels=5)
      model
      
Some weights of GPT2ForTokenClassification were not initialized from the model checkpoint at openai-community/gpt2 and are newly initialized: ['classifier.bias', 'classifier.weight']
      You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
      
Out[2]:
GPT2ForTokenClassification(
        (transformer): GPT2Model(
          (wte): Embedding(50257, 768)
          (wpe): Embedding(1024, 768)
          (drop): Dropout(p=0.1, inplace=False)
          (h): ModuleList(
            (0-11): 12 x GPT2Block(
              (ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
              (attn): GPT2Attention(
                (c_attn): Conv1D()
                (c_proj): Conv1D()
                (attn_dropout): Dropout(p=0.1, inplace=False)
                (resid_dropout): Dropout(p=0.1, inplace=False)
              )
              (ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
              (mlp): GPT2MLP(
                (c_fc): Conv1D()
                (c_proj): Conv1D()
                (act): NewGELUActivation()
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
          )
          (ln_f): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
        )
        (dropout): Dropout(p=0.1, inplace=False)
        (classifier): Linear(in_features=768, out_features=5, bias=True)
      )

Na saída, obtemos as 5 classes que especificamos com num_labels.

Ajuste fino do GPT-2link image 79

Ajuste fino para geração de textolink image 80

Primeiro, vamos dar uma olhada em como o treinamento puro do Pytorch seria feito.

Cálculo de perdaslink image 81

Antes de começarmos a fazer o ajuste fino do GPT-2, vamos dar uma olhada em um aspecto. Antes, quando obtivemos a saída do modelo, fizemos o seguinte

	
from transformers import GPT2ForSequenceClassification
model = GPT2ForSequenceClassification.from_pretrained(ckeckpoints, num_labels=5)
model
from transformers import GPT2ForQuestionAnswering
model = GPT2ForQuestionAnswering.from_pretrained(ckeckpoints)
model
from transformers import GPT2ForTokenClassification
model = GPT2ForTokenClassification.from_pretrained(ckeckpoints, num_labels=5)
model
outputs = model(**input_tokens)
outputs
Copy
	
Some weights of GPT2ForSequenceClassification were not initialized from the model checkpoint at openai-community/gpt2 and are newly initialized: ['score.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
Some weights of GPT2ForQuestionAnswering were not initialized from the model checkpoint at openai-community/gpt2 and are newly initialized: ['qa_outputs.bias', 'qa_outputs.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
Some weights of GPT2ForTokenClassification were not initialized from the model checkpoint at openai-community/gpt2 and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
CausalLMOutputWithCrossAttentions(loss=None, logits=tensor([[[ 6.6288, 5.1421, -0.8002, ..., -6.3998, -4.4113, 1.8240],
[ 2.7250, 1.9371, -1.2293, ..., -5.0979, -5.1617, 2.2694],
[ 2.6891, 4.3089, -1.6074, ..., -7.6321, -2.0448, 0.4042],
...,
[ 6.0513, 3.8020, -2.8080, ..., -6.7754, -8.3176, 1.1541],
[ 6.8402, 5.6952, 0.2002, ..., -9.1281, -6.7818, 2.7576],
[ 1.0255, -0.2201, -2.5484, ..., -6.2137, -7.2322, 0.1665]]],
device='cuda:0', grad_fn=<UnsafeViewBackward0>), past_key_values=((tensor([[[[ 0.4779, 0.7671, -0.7532, ..., -0.3551, 0.4590, 0.3073],
[ 0.2034, -0.6033, 0.2484, ..., 0.7760, -0.3546, 0.0198],
[-0.1968, -0.9029, 0.5570, ..., 0.9985, -0.5028, -0.3508],
...,
[-0.5007, -0.4009, 0.1604, ..., -0.3693, -0.1158, 0.1320],
[-0.4854, -0.1369, 0.7377, ..., -0.8043, -0.1054, 0.0871],
[ 0.1610, -0.8358, -0.5534, ..., 0.9951, -0.3085, 0.4574]],
[[ 0.6288, -0.1374, -0.3467, ..., -1.0003, -1.1518, 0.3114],
[-1.7269, 1.2920, -0.0734, ..., 1.0572, 1.4698, -2.0412],
[ 0.2714, -0.0670, -0.4769, ..., 0.6305, 0.6890, -0.8158],
...,
[-0.0499, -0.0721, 0.4580, ..., 0.6797, 0.2331, 0.0210],
[-0.1894, 0.2077, 0.6722, ..., 0.6938, 0.2104, -0.0574],
[ 0.3661, -0.0218, 0.2618, ..., 0.8750, 1.2205, -0.6103]],
[[ 0.5964, 1.1178, 0.3604, ..., 0.8426, 0.4881, -0.4094],
[ 0.3186, -0.3953, 0.2687, ..., -0.1110, -0.5640, 0.5900],
...,
[ 0.2092, 0.3898, -0.6061, ..., -0.2859, -0.3136, -0.1002],
[ 0.0539, 0.8941, 0.3423, ..., -0.6326, -0.1053, -0.6679],
[ 0.5628, 0.6687, -0.2720, ..., -0.1073, -0.9792, -0.0302]]]],
device='cuda:0', grad_fn=<PermuteBackward0>))), hidden_states=None, attentions=None, cross_attentions=None)

Você pode ver que obtemos loss=None.

	
print(outputs.loss)
Copy
	
None

Como precisaremos da perda para fazer o ajuste fino, vamos ver como obtê-la.

Se consultarmos a documentação do método forward de GPT2LMHeadModel, veremos que ele diz que a saída retorna um objeto do tipo transformers.modeling_outputs.CausalLMOutputWithCrossAttentions, portanto, se consultarmos a documentação de transformers.modeling_outputs.CausalLMOutputWithCrossAttentions, podemos ver que ele diz que retorna loss se labels for passado para o método forward.

Se acessarmos o código-fonte do método forward, veremos este bloco de código

perda = Nenhuma
              se labels não for None:
                  # mova os rótulos para o dispositivo correto para ativar o paralelismo do modelo
                  rótulos = rótulos.to(lm_logits.device)
                  # Deslocamento de modo que os tokens < n prevejam n
                  shift_logits = lm_logits[..., :-1, :].contiguous()
                  shift_labels = labels[..., 1:].contiguous()
                  # Achatar os tokens
                  loss_fct = CrossEntropyLoss()
                  loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
      ```
      
      Em outras palavras, a "perda" é calculada da seguinte forma
      
       * Deslocamento de logits e rótulos: a primeira parte é deslocar logits (`lm_logits`) e rótulos (`labels`) para que `tokens < n` prevejam `n`, ou seja, a partir de uma posição `n`, o próximo token é previsto a partir dos anteriores.
       * CrossEntropyLoss: é criada uma instância da função de perda `CrossEntropyLoss()`.
       * Achatar tokens: os logits e os rótulos são achatados usando `view(-1, shift_logits.size(-1))` e `view(-1)`, respectivamente. Isso é feito para que os logits e os rótulos tenham a mesma forma para a função de perda.
       * Cálculo da perda: finalmente, a perda é calculada usando a função de perda `CrossEntropyLoss()` com os logits achatados e os rótulos achatados como entradas.
      
      Em resumo, a "perda" é calculada como a perda de entropia cruzada entre os logits deslocados e achatados e os rótulos deslocados e achatados.
      
      Portanto, se passarmos os rótulos para o método `forward`, ele retornará a `perda`.
      
	
outputs = model(**input_tokens, labels=input_tokens.input_ids)
outputs.loss
Copy
	
tensor(3.8028, device='cuda:0', grad_fn=<NllLossBackward0>)

Conjunto de dadoslink image 82

Para o treinamento, usaremos um conjunto de dados de piadas em inglês [short-jokes-dataset] (https://huggingface.co/datasets/Maximofn/short-jokes-dataset), que é um conjunto de dados com 231 mil piadas em inglês.

Reinicie o notebook para que não haja problemas de memória da GPU

Baixamos o conjunto de dados

	
from datasets import load_dataset
jokes = load_dataset("Maximofn/short-jokes-dataset")
jokes
Copy
	
DatasetDict({
train: Dataset({
features: ['ID', 'Joke'],
num_rows: 231657
})
})

Vamos dar uma olhada nisso

	
jokes["train"][0]
Copy
	
{'ID': 1,
'Joke': '[me narrating a documentary about narrators] "I can't hear what they're saying cuz I'm talking"'}

Instância do modelolink image 83

Para usar o modelo xl, ou seja, aquele com parâmetros de 1,5B, eu o mudo para FP16 para não ficar sem memória.

	
import torch
from transformers import AutoTokenizer, GPT2LMHeadModel
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
ckeckpoints = "openai-community/gpt2-xl"
tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)
model = GPT2LMHeadModel.from_pretrained(ckeckpoints)
model = model.half().to(device)
Copy

Conjunto de dados Pytorchlink image 84

Criar uma classe de conjunto de dados do Pytorch

	
import torch
from transformers import AutoTokenizer, GPT2LMHeadModel
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
ckeckpoints = "openai-community/gpt2-xl"
tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)
model = GPT2LMHeadModel.from_pretrained(ckeckpoints)
model = model.half().to(device)
from torch.utils.data import Dataset
class JokesDataset(Dataset):
def __init__(self, dataset, tokenizer):
self.dataset = dataset
self.joke = "JOKE: "
self.end_of_text_token = "<|endoftext|>"
self.tokenizer = tokenizer
def __len__(self):
return len(self.dataset["train"])
def __getitem__(self, item):
sentence = self.joke + self.dataset["train"][item]["Joke"] + self.end_of_text_token
tokens = self.tokenizer(sentence, return_tensors="pt")
return sentence, tokens
Copy

Nós o instanciamos

	
import torch
from transformers import AutoTokenizer, GPT2LMHeadModel
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
ckeckpoints = "openai-community/gpt2-xl"
tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)
model = GPT2LMHeadModel.from_pretrained(ckeckpoints)
model = model.half().to(device)
from torch.utils.data import Dataset
class JokesDataset(Dataset):
def __init__(self, dataset, tokenizer):
self.dataset = dataset
self.joke = "JOKE: "
self.end_of_text_token = "<|endoftext|>"
self.tokenizer = tokenizer
def __len__(self):
return len(self.dataset["train"])
def __getitem__(self, item):
sentence = self.joke + self.dataset["train"][item]["Joke"] + self.end_of_text_token
tokens = self.tokenizer(sentence, return_tensors="pt")
return sentence, tokens
dataset = JokesDataset(jokes, tokenizer=tokenizer)
Copy

Aqui está um exemplo

sentence, tokens = dataset[5]
      print(sentence)
      tokens.input_ids.shape, tokens.attention_mask.shape
      
JOKE: Why can't Barbie get pregnant? Because Ken comes in a different box. Heyooooooo<|endoftext|>
      
Out[5]:
(torch.Size([1, 22]), torch.Size([1, 22]))

Dataloaderlink image 85

Agora, criamos um carregador de dados do Pytorch

	
import torch
from transformers import AutoTokenizer, GPT2LMHeadModel
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
ckeckpoints = "openai-community/gpt2-xl"
tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)
model = GPT2LMHeadModel.from_pretrained(ckeckpoints)
model = model.half().to(device)
from torch.utils.data import Dataset
class JokesDataset(Dataset):
def __init__(self, dataset, tokenizer):
self.dataset = dataset
self.joke = "JOKE: "
self.end_of_text_token = "<|endoftext|>"
self.tokenizer = tokenizer
def __len__(self):
return len(self.dataset["train"])
def __getitem__(self, item):
sentence = self.joke + self.dataset["train"][item]["Joke"] + self.end_of_text_token
tokens = self.tokenizer(sentence, return_tensors="pt")
return sentence, tokens
dataset = JokesDataset(jokes, tokenizer=tokenizer)
sentence, tokens = dataset[5]
print(sentence)
tokens.input_ids.shape, tokens.attention_mask.shape
from torch.utils.data import DataLoader
BS = 1
joke_dataloader = DataLoader(dataset, batch_size=BS, shuffle=True)
Copy

Vemos um lote

	
import torch
from transformers import AutoTokenizer, GPT2LMHeadModel
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
ckeckpoints = "openai-community/gpt2-xl"
tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)
model = GPT2LMHeadModel.from_pretrained(ckeckpoints)
model = model.half().to(device)
from torch.utils.data import Dataset
class JokesDataset(Dataset):
def __init__(self, dataset, tokenizer):
self.dataset = dataset
self.joke = "JOKE: "
self.end_of_text_token = "<|endoftext|>"
self.tokenizer = tokenizer
def __len__(self):
return len(self.dataset["train"])
def __getitem__(self, item):
sentence = self.joke + self.dataset["train"][item]["Joke"] + self.end_of_text_token
tokens = self.tokenizer(sentence, return_tensors="pt")
return sentence, tokens
dataset = JokesDataset(jokes, tokenizer=tokenizer)
sentence, tokens = dataset[5]
print(sentence)
tokens.input_ids.shape, tokens.attention_mask.shape
from torch.utils.data import DataLoader
BS = 1
joke_dataloader = DataLoader(dataset, batch_size=BS, shuffle=True)
sentences, tokens = next(iter(joke_dataloader))
len(sentences), tokens.input_ids.shape, tokens.attention_mask.shape
Copy
	
JOKE: Why can't Barbie get pregnant? Because Ken comes in a different box. Heyooooooo<|endoftext|>
(1, torch.Size([1, 1, 36]), torch.Size([1, 1, 36]))

Treinamentolink image 86

from transformers import AdamW, get_linear_schedule_with_warmup
      import tqdm
      
      BATCH_SIZE = 32
      EPOCHS = 5
      LEARNING_RATE = 3e-6
      WARMUP_STEPS = 5000
      MAX_SEQ_LEN = 500
      
      optimizer = AdamW(model.parameters(), lr=LEARNING_RATE)
      scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=WARMUP_STEPS, num_training_steps=-1)
      proc_seq_count = 0
      batch_count = 0
      
      tmp_jokes_tens = None
      
      losses = []
      lrs = []
      
      for epoch in range(EPOCHS):
          
          print(f"EPOCH {epoch} started" + '=' * 30)
          progress_bar = tqdm.tqdm(joke_dataloader, desc="Training")
          
          for sample in progress_bar:
      
              sentence, tokens = sample
              
              #################### "Fit as many joke sequences into MAX_SEQ_LEN sequence as possible" logic start ####
              joke_tens = tokens.input_ids[0].to(device)
      
              # Skip sample from dataset if it is longer than MAX_SEQ_LEN
              if joke_tens.size()[1] > MAX_SEQ_LEN:
                  continue
              
              # The first joke sequence in the sequence
              if not torch.is_tensor(tmp_jokes_tens):
                  tmp_jokes_tens = joke_tens
                  continue
              else:
                  # The next joke does not fit in so we process the sequence and leave the last joke 
                  # as the start for next sequence 
                  if tmp_jokes_tens.size()[1] + joke_tens.size()[1] > MAX_SEQ_LEN:
                      work_jokes_tens = tmp_jokes_tens
                      tmp_jokes_tens = joke_tens
                  else:
                      #Add the joke to sequence, continue and try to add more
                      tmp_jokes_tens = torch.cat([tmp_jokes_tens, joke_tens[:,1:]], dim=1)
                      continue
              ################## Sequence ready, process it trough the model ##################
                  
              outputs = model(work_jokes_tens, labels=work_jokes_tens)
              loss = outputs.loss
              loss.backward()
                          
              proc_seq_count = proc_seq_count + 1
              if proc_seq_count == BATCH_SIZE:
                  proc_seq_count = 0    
                  batch_count += 1
                  optimizer.step()
                  scheduler.step() 
                  optimizer.zero_grad()
                  model.zero_grad()
      
              progress_bar.set_postfix({'loss': loss.item(), 'lr': scheduler.get_last_lr()[0]})
              losses.append(loss.item())
              lrs.append(scheduler.get_last_lr()[0])
              if batch_count == 10:
                  batch_count = 0
      
/home/wallabot/miniconda3/envs/nlp/lib/python3.11/site-packages/transformers/optimization.py:429: FutureWarning: This implementation of AdamW is deprecated and will be removed in a future version. Use the PyTorch implementation torch.optim.AdamW instead, or set `no_deprecation_warning=True` to disable this warning
        warnings.warn(
      
EPOCH 0 started==============================
      
Training:   0%|          | 0/231657 [00:00<?, ?it/s]
Training: 100%|██████████| 231657/231657 [32:29<00:00, 118.83it/s, loss=3.1, lr=2.31e-7] 
      
EPOCH 1 started==============================
      
Training: 100%|██████████| 231657/231657 [32:34<00:00, 118.55it/s, loss=2.19, lr=4.62e-7]
      
EPOCH 2 started==============================
      
Training: 100%|██████████| 231657/231657 [32:36<00:00, 118.42it/s, loss=2.42, lr=6.93e-7]
      
EPOCH 3 started==============================
      
Training: 100%|██████████| 231657/231657 [32:23<00:00, 119.18it/s, loss=2.16, lr=9.25e-7]
      
EPOCH 4 started==============================
      
Training: 100%|██████████| 231657/231657 [32:22<00:00, 119.25it/s, loss=2.1, lr=1.16e-6] 
      
import numpy as np
      import matplotlib.pyplot as plt
      
      losses_np = np.array(losses)
      lrs_np = np.array(lrs)
      
      plt.figure(figsize=(12,6))
      plt.plot(losses_np, label='loss')
      plt.plot(lrs_np, label='learning rate')
      plt.yscale('log')
      plt.legend()
      plt.show()
      
image gpt2 1

Inferêncialink image 87

Vamos ver como o modelo faz piadas.

sentence_joke = "JOKE:"
      input_tokens_joke = tokenizer(sentence_joke, return_tensors="pt").to(device)
      output_tokens_joke = model.generate(**input_tokens_joke)
      decoded_output_joke = tokenizer.decode(output_tokens_joke[0], skip_special_tokens=True)
      
      print(f"decoded joke: \n{decoded_output_joke}")
      
Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
      /home/wallabot/miniconda3/envs/nlp/lib/python3.11/site-packages/transformers/generation/utils.py:1178: UserWarning: Using the model-agnostic default `max_length` (=20) to control the generation length. We recommend setting `max_new_tokens` to control the maximum length of the generation.
        warnings.warn(
      
decoded joke: 
      JOKE:!!!!!!!!!!!!!!!!!
      

Você pode ver que você passa uma sequência com a palavra joke e ele retorna uma piada. Mas se você retornar outra string, ele não retornará

sentence_joke = "My dog is cute and"
      input_tokens_joke = tokenizer(sentence_joke, return_tensors="pt").to(device)
      output_tokens_joke = model.generate(**input_tokens_joke)
      decoded_output_joke = tokenizer.decode(output_tokens_joke[0], skip_special_tokens=True)
      
      print(f"decoded joke: \n{decoded_output_joke}")
      
Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
      
decoded joke: 
      My dog is cute and!!!!!!!!!!!!!!!
      

Ajuste fino do GPT-2 para classificação de sentençaslink image 88

Agora vamos fazer um treinamento com as bibliotecas Hugging Face.

Conjunto de dadoslink image 89

Usaremos o conjunto de dados imdb para classificar as frases em positivas e negativas.

	
from transformers import AdamW, get_linear_schedule_with_warmup
import tqdm
BATCH_SIZE = 32
EPOCHS = 5
LEARNING_RATE = 3e-6
WARMUP_STEPS = 5000
MAX_SEQ_LEN = 500
optimizer = AdamW(model.parameters(), lr=LEARNING_RATE)
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=WARMUP_STEPS, num_training_steps=-1)
proc_seq_count = 0
batch_count = 0
tmp_jokes_tens = None
losses = []
lrs = []
for epoch in range(EPOCHS):
print(f"EPOCH {epoch} started" + '=' * 30)
progress_bar = tqdm.tqdm(joke_dataloader, desc="Training")
for sample in progress_bar:
sentence, tokens = sample
#################### "Fit as many joke sequences into MAX_SEQ_LEN sequence as possible" logic start ####
joke_tens = tokens.input_ids[0].to(device)
# Skip sample from dataset if it is longer than MAX_SEQ_LEN
if joke_tens.size()[1] > MAX_SEQ_LEN:
continue
# The first joke sequence in the sequence
if not torch.is_tensor(tmp_jokes_tens):
tmp_jokes_tens = joke_tens
continue
else:
# The next joke does not fit in so we process the sequence and leave the last joke
# as the start for next sequence
if tmp_jokes_tens.size()[1] + joke_tens.size()[1] > MAX_SEQ_LEN:
work_jokes_tens = tmp_jokes_tens
tmp_jokes_tens = joke_tens
else:
#Add the joke to sequence, continue and try to add more
tmp_jokes_tens = torch.cat([tmp_jokes_tens, joke_tens[:,1:]], dim=1)
continue
################## Sequence ready, process it trough the model ##################
outputs = model(work_jokes_tens, labels=work_jokes_tens)
loss = outputs.loss
loss.backward()
proc_seq_count = proc_seq_count + 1
if proc_seq_count == BATCH_SIZE:
proc_seq_count = 0
batch_count += 1
optimizer.step()
scheduler.step()
optimizer.zero_grad()
model.zero_grad()
progress_bar.set_postfix({'loss': loss.item(), 'lr': scheduler.get_last_lr()[0]})
losses.append(loss.item())
lrs.append(scheduler.get_last_lr()[0])
if batch_count == 10:
batch_count = 0
import numpy as np
import matplotlib.pyplot as plt
losses_np = np.array(losses)
lrs_np = np.array(lrs)
plt.figure(figsize=(12,6))
plt.plot(losses_np, label='loss')
plt.plot(lrs_np, label='learning rate')
plt.yscale('log')
plt.legend()
plt.show()
sentence_joke = "JOKE:"
input_tokens_joke = tokenizer(sentence_joke, return_tensors="pt").to(device)
output_tokens_joke = model.generate(**input_tokens_joke)
decoded_output_joke = tokenizer.decode(output_tokens_joke[0], skip_special_tokens=True)
print(f"decoded joke: {decoded_output_joke}")
sentence_joke = "My dog is cute and"
input_tokens_joke = tokenizer(sentence_joke, return_tensors="pt").to(device)
output_tokens_joke = model.generate(**input_tokens_joke)
decoded_output_joke = tokenizer.decode(output_tokens_joke[0], skip_special_tokens=True)
print(f"decoded joke: {decoded_output_joke}")
from datasets import load_dataset
dataset = load_dataset("imdb")
dataset
Copy
	
/home/wallabot/miniconda3/envs/nlp/lib/python3.11/site-packages/transformers/optimization.py:429: FutureWarning: This implementation of AdamW is deprecated and will be removed in a future version. Use the PyTorch implementation torch.optim.AdamW instead, or set `no_deprecation_warning=True` to disable this warning
warnings.warn(
Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
/home/wallabot/miniconda3/envs/nlp/lib/python3.11/site-packages/transformers/generation/utils.py:1178: UserWarning: Using the model-agnostic default `max_length` (=20) to control the generation length. We recommend setting `max_new_tokens` to control the maximum length of the generation.
warnings.warn(
Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
DatasetDict({
train: Dataset({
features: ['text', 'label'],
num_rows: 25000
})
test: Dataset({
features: ['text', 'label'],
num_rows: 25000
})
unsupervised: Dataset({
features: ['text', 'label'],
num_rows: 50000
})
})

Vamos dar uma olhada nisso

	
dataset["train"].info
Copy
	
DatasetInfo(description='', citation='', homepage='', license='', features={'text': Value(dtype='string', id=None), 'label': ClassLabel(names=['neg', 'pos'], id=None)}, post_processed=None, supervised_keys=None, task_templates=None, builder_name='parquet', dataset_name='imdb', config_name='plain_text', version=0.0.0, splits={'train': SplitInfo(name='train', num_bytes=33435948, num_examples=25000, shard_lengths=None, dataset_name='imdb'), 'test': SplitInfo(name='test', num_bytes=32653810, num_examples=25000, shard_lengths=None, dataset_name='imdb'), 'unsupervised': SplitInfo(name='unsupervised', num_bytes=67113044, num_examples=50000, shard_lengths=None, dataset_name='imdb')}, download_checksums={'hf://datasets/imdb@e6281661ce1c48d982bc483cf8a173c1bbeb5d31/plain_text/train-00000-of-00001.parquet': {'num_bytes': 20979968, 'checksum': None}, 'hf://datasets/imdb@e6281661ce1c48d982bc483cf8a173c1bbeb5d31/plain_text/test-00000-of-00001.parquet': {'num_bytes': 20470363, 'checksum': None}, 'hf://datasets/imdb@e6281661ce1c48d982bc483cf8a173c1bbeb5d31/plain_text/unsupervised-00000-of-00001.parquet': {'num_bytes': 41996509, 'checksum': None}}, download_size=83446840, post_processing_size=None, dataset_size=133202802, size_in_bytes=216649642)

Vamos dar uma olhada nos recursos desse conjunto de dados.

	
dataset["train"].info.features
Copy
	
{'text': Value(dtype='string', id=None),
'label': ClassLabel(names=['neg', 'pos'], id=None)}

O conjunto de dados contém strings e classes. Além disso, há dois tipos de classes, pos e neg. Vamos criar uma variável com o número de classes

	
num_clases = len(dataset["train"].unique("label"))
num_clases
Copy
	
2

Tokeniserlink image 90

Criamos o tokenizador

	
from transformers import GPT2Tokenizer
checkpoints = "openai-community/gpt2"
tokenizer = GPT2Tokenizer.from_pretrained(checkpoints, bos_token='<|startoftext|>', eos_token='<|endoftext|>', pad_token='<|pad|>')
tokenizer.pad_token = tokenizer.eos_token
Copy

Agora que temos um tokenizador, podemos tokenizar o conjunto de dados, pois o modelo só entende tokens.

	
from transformers import GPT2Tokenizer
checkpoints = "openai-community/gpt2"
tokenizer = GPT2Tokenizer.from_pretrained(checkpoints, bos_token='<|startoftext|>', eos_token='<|endoftext|>', pad_token='<|pad|>')
tokenizer.pad_token = tokenizer.eos_token
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
Copy

Modelolink image 91

Instanciamos o modelo

	
from transformers import GPT2Tokenizer
checkpoints = "openai-community/gpt2"
tokenizer = GPT2Tokenizer.from_pretrained(checkpoints, bos_token='<|startoftext|>', eos_token='<|endoftext|>', pad_token='<|pad|>')
tokenizer.pad_token = tokenizer.eos_token
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
from transformers import GPT2ForSequenceClassification
model = GPT2ForSequenceClassification.from_pretrained(checkpoints, num_labels=num_clases).half()
model.config.pad_token_id = model.config.eos_token_id
Copy
	
Some weights of GPT2ForSequenceClassification were not initialized from the model checkpoint at openai-community/gpt2 and are newly initialized: ['score.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.

Avaliaçãolink image 92

Criamos uma métrica de avaliação

	
import numpy as np
import evaluate
metric = evaluate.load("accuracy")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
Copy

Trainerlink image 93

Criamos o instrutor

	
import numpy as np
import evaluate
metric = evaluate.load("accuracy")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir="./results",
learning_rate=2e-5,
per_device_train_batch_size=16,
per_device_eval_batch_size=64,
num_train_epochs=3,
weight_decay=0.01,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["test"],
compute_metrics=compute_metrics,
)
Copy

Treinamentolink image 94

Treinamos

trainer.train()
      
[4689/4689 1:27:50, Epoch 3/3]
Step Training Loss
500 0.379400
1000 0.000000
1500 0.000000
2000 0.000000
2500 0.000000
3000 0.000000
3500 0.000000
4000 0.000000
4500 0.000000

Out[25]:
TrainOutput(global_step=4689, training_loss=0.04045845954294626, metrics={'train_runtime': 5271.3532, 'train_samples_per_second': 14.228, 'train_steps_per_second': 0.89, 'total_flos': 3.91945125888e+16, 'train_loss': 0.04045845954294626, 'epoch': 3.0})

Inferêncialink image 95

Testamos o modelo após o treinamento

	
import numpy as np
import evaluate
metric = evaluate.load("accuracy")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir="./results",
learning_rate=2e-5,
per_device_train_batch_size=16,
per_device_eval_batch_size=64,
num_train_epochs=3,
weight_decay=0.01,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["test"],
compute_metrics=compute_metrics,
)
trainer.train()
import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
def get_sentiment(sentence):
inputs = tokenizer(sentence, return_tensors="pt").to(device)
outputs = model(**inputs)
prediction = outputs.logits.argmax(-1).item()
return "positive" if prediction == 1 else "negative"
Copy
	
import numpy as np
import evaluate
metric = evaluate.load("accuracy")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir="./results",
learning_rate=2e-5,
per_device_train_batch_size=16,
per_device_eval_batch_size=64,
num_train_epochs=3,
weight_decay=0.01,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["test"],
compute_metrics=compute_metrics,
)
trainer.train()
import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
def get_sentiment(sentence):
inputs = tokenizer(sentence, return_tensors="pt").to(device)
outputs = model(**inputs)
prediction = outputs.logits.argmax(-1).item()
return "positive" if prediction == 1 else "negative"
sentence = "I hate this movie!"
print(get_sentiment(sentence))
Copy
	
negative

Continuar lendo

DoLa – Decoding by Contrasting Layers Improves Factuality in Large Language Models

DoLa – Decoding by Contrasting Layers Improves Factuality in Large Language Models

Você já conversou com um LLM e ele lhe respondeu algo que parece ter bebido café de máquina a noite toda? 😂 Isso é o que chamamos de alucinação no mundo dos LLMs! Mas não se preocupe, pois não é que seu modelo de linguagem esteja louco (embora às vezes possa parecer isso 🤪). A verdade é que os LLMs podem ser um pouco... criativos quando se trata de gerar texto. Mas graças ao DoLa, um método que usa camadas de contraste para melhorar a viabilidade dos LLMs, podemos evitar que nossos modelos de linguagem se transformem em escritores de ficção científica 😂. Nesta publicação, explicarei como o DoLa funciona e mostrarei um exemplo de código para que você possa entender melhor como tornar seus LLMs mais confiáveis e menos propensos a inventar histórias. Vamos salvar nossos LLMs da loucura e torná-los mais úteis! 🚀

Últimos posts -->

Você viu esses projetos?

Subtify

Subtify Subtify

Gerador de legendas para vídeos no idioma que você desejar. Além disso, coloca uma legenda de cor diferente para cada pessoa

Ver todos os projetos -->

Quer aplicar IA no seu projeto? Entre em contato!

Quer melhorar com essas dicas?

Últimos tips -->

Use isso localmente

Os espaços do Hugging Face nos permitem executar modelos com demos muito simples, mas e se a demo quebrar? Ou se o usuário a deletar? Por isso, criei contêineres docker com alguns espaços interessantes, para poder usá-los localmente, aconteça o que acontecer. Na verdade, se você clicar em qualquer botão de visualização de projeto, ele pode levá-lo a um espaço que não funciona.

Ver todos os contêineres -->

Quer aplicar IA no seu projeto? Entre em contato!

Você quer treinar seu modelo com esses datasets?

short-jokes-dataset

Dataset com piadas em inglês

opus100

Dataset com traduções de inglês para espanhol

netflix_titles

Dataset com filmes e séries da Netflix

Ver mais datasets -->