GPT1 - Aprimorando a compreensão da linguagem por meio de pré-treinamento generativo
Papel
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..
[Improving Language Understanding by Generative Pre-Training] (https://s3-us-west-2.amazonaws.com/openai-assets/research-covers/language-unsupervised/language_understanding_paper.pdf) é o artigo GPT1. Antes de ler a postagem, é necessário se colocar na situação: antes do GPT, os modelos de linguagem eram baseados em redes recorrentes (RNN), que eram redes que funcionavam relativamente bem para tarefas específicas, mas com as quais não era possível reutilizar o pré-treinamento para fazer um ajuste fino para outras tarefas. Elas também não tinham muita memória, portanto, se você colocasse frases muito longas nelas, elas não se lembravam muito bem do início da frase.
Arquitetura
Antes de falarmos sobre a arquitetura do GPT1, vamos nos lembrar de como era a arquitetura dos Transformers.
O GPT1 é um modelo baseado nos decodificadores de transformador, portanto, como não temos um codificador, a arquitetura de um único decodificador é a seguinte
O mecanismo de atenção entre a sentença do codificador e do decodificador é eliminado.
No documento GPT1, eles propõem a seguinte arquitetura
O que corresponde ao decodificador de um transformador, como vimos anteriormente, executado 12 vezes.
Resumo do artigo
As ideias mais interessantes do artigo são:
- O modelo é treinado em um grande corpus de texto não supervisionado. Isso cria um modelo de linguagem. Um modelo de linguagem de alta capacidade é criado em um grande corpus de texto.
- O ajuste fino é então realizado em tarefas supervisionadas de NLP com conjuntos de dados rotulados. O ajuste fino é realizado em uma tarefa-alvo supervisionada. Além disso, quando o modelo é avaliado na tarefa supervisionada, ele não é avaliado apenas nessa tarefa, mas em quão bem ele prevê o próximo token, o que ajuda a aprimorar a generalização do modelo supervisionado e faz com que o modelo converse mais rapidamente.
- Embora já tenhamos mencionado isso, o documento diz que a arquitetura do transformador é usada, já que até aquele momento os RNNs eram usados para os modelos de linguagem. Isso levou a uma melhoria no sentido de que o que foi aprendido no primeiro treinamento (treinamento no corpus de texto não supervisionado) é mais fácil de transferir para tarefas supervisionadas. Ou seja, graças ao uso de transformadores, foi possível treinar em um corpus inteiro de texto e depois fazer o ajuste fino em tarefas supervisionadas.
- Eles testaram o modelo em quatro tipos de tarefas de compreensão de linguagem:
- Inferência de linguagem natural
- Resposta às perguntas
- Similaridade semântica
- Classificação de textos.
- O modelo geral (aquele treinado em todo o corpus de texto não supervisionado) supera os modelos RNN treinados de forma discriminatória que empregam arquiteturas específicas de tarefas, melhorando significativamente o estado da arte em 9 das 12 tarefas estudadas. Eles também analisaram os comportamentos de "disparo zero" do modelo pré-treinado em quatro ambientes diferentes e mostraram que ele adquire conhecimento linguístico útil para tarefas subsequentes.
- Nos últimos anos, os pesquisadores demonstraram os benefícios do uso de embeddings, que são treinados em corpora não rotulados, para melhorar o desempenho em várias tarefas. No entanto, essas abordagens transferem informações principalmente no nível da palavra, enquanto o uso de transformadores treinados em grandes corpora de texto não supervisionados captura a semântica de nível superior, no nível da frase.
Geração de texto
Vamos ver como gerar texto com um GPT1 pré-treinado.
Primeiro, você precisa instalar o ftfy
e o spacy
via
pip install ftfy spacy
```
Depois de instalado, você deve fazer o download do modelo de idioma spacy que deseja usar. Por exemplo, para fazer o download do modelo em inglês, você pode executar:
python -m spacy download en_core_web_sm
```
Para gerar texto, usaremos o modelo do repositório GPT1 do Hugging Face.
Importamos as bibliotecas
import torchfrom transformers import OpenAIGPTTokenizer, OpenAIGPTLMHeadModel, AutoTokenizer
Se você notar, importamos o OpenAIGPTTokenizer
e o AutoTokenizer
. Isso ocorre porque no model card do GPT1 diz para usar o OpenAIGPTTokenizer
, mas na postagem da biblioteca transformers explicamos que você deve usar o AutoTokenizer
para carregar o tokenizador. Então, vamos tentar os dois
import torchfrom transformers import OpenAIGPTTokenizer, OpenAIGPTLMHeadModel, AutoTokenizerckeckpoints = "openai-community/openai-gpt"tokenizer = OpenAIGPTTokenizer.from_pretrained(ckeckpoints)auto_tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)input_tokens = tokenizer("Hello, my dog is cute and", return_tensors="pt")input_auto_tokens = auto_tokenizer("Hello, my dog is cute and", return_tensors="pt")print(f"input tokens: {input_tokens}")print(f"input auto tokens: {input_auto_tokens}")
input tokens:{'input_ids': tensor([[3570, 240, 547, 2585, 544, 4957, 488]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1]])}input auto tokens:{'input_ids': tensor([[3570, 240, 547, 2585, 544, 4957, 488]]), 'attention_mask': tensor([[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.
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)model = OpenAIGPTLMHeadModel.from_pretrained(ckeckpoints).to(device)
Como instanciamos o modelo, vamos ver quantos parâmetros ele tem
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)model = OpenAIGPTLMHeadModel.from_pretrained(ckeckpoints).to(device)params = sum(p.numel() for p in model.parameters())print(f"Number of parameters: {round(params/1e6)}M")
Number of parameters: 117M
Na era de bilhões de parâmetros, podemos ver que o GPT1 tinha apenas 117 milhões de parâmetros.
Criamos os tokens de entrada para o modelo
input_sentence = "Hello, my dog is cute and"input_tokens = tokenizer(input_sentence, return_tensors="pt").to(device)input_tokens
{'input_ids': tensor([[3570, 240, 547, 2585, 544, 4957, 488]], device='cuda:0'), 'attention_mask': tensor([[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}")
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}")
output tokens:tensor([[ 3570, 240, 547, 2585, 544, 4957, 488, 249, 719, 797,485, 921, 575, 562, 246, 1671, 239, 244, 40477, 244]],device='cuda:0')decoded output:hello, my dog is cute and i'm going to take him for a walk. ""
Já conseguimos gerar texto com o GPT1
Gerar token para o texto do token
Greedy search
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
CausalLMOutput(loss=None, logits=tensor([[[ -5.9486, -5.8697, -18.4258, ..., -9.7371, -10.4495, 0.8814],[ -6.1212, -4.8031, -14.3970, ..., -6.5411, -9.5051, -1.2015],[ -7.4231, -6.3615, -14.7297, ..., -10.4575, -8.4600, -1.5183],...,[ -5.4751, -5.8803, -13.7767, ..., -10.5048, -12.4167, -6.1584],[ -7.2052, -6.0198, -21.5040, ..., -16.2941, -14.0494, -1.2416],[ -7.7240, -7.3631, -17.3174, ..., -12.1546, -12.3327, -1.7169]]],device='cuda:0', grad_fn=<UnsafeViewBackward0>), hidden_states=None, attentions=None)
Vemos que isso gera muitos dados, mas primeiro vamos dar uma olhada nas chaves de saída.
outputs.keys()
odict_keys(['logits'])
Nesse caso, temos apenas os logits do modelo, vamos ver seu tamanho.
logits = outputs.logitslogits.shape
torch.Size([1, 7, 40478])
Vamos ver quantos tokens tínhamos na entrada.
input_tokens.input_ids.shape
torch.Size([1, 7])
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
torch.Size([40478])
Há um total de 40478 logits, ou seja, há um vocabulário de 40478 tokens e temos que ver qual token tem a maior probabilidade.
softmax_logits = torch.softmax(nex_token_logits, dim=0)softmax_logits.shape
torch.Size([40478])
next_token_prob, next_token_id = torch.max(softmax_logits, dim=0)next_token_prob, next_token_id
(tensor(0.1898, device='cuda:0', grad_fn=<MaxBackward0>),tensor(249, device='cuda:0'))
Obtivemos o seguinte token, agora vamos decodificá-lo
tokenizer.decode(next_token_id.item())
'i'
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, as [formas de gerar textos] (https://maximofn.com/hugging-face-transformers/#Formas-de-generaci%C3%B3n-de-texto) que podem ser feitas por amostragem, 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.logitsnex_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_next_greedy_token(input_sentence, tokenizer, model, device):input_tokens = tokenizer(input_sentence, return_tensors="pt").to(device)outputs = model(**input_tokens)logits = outputs.logitsnex_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_iddef generate_greedy_text(input_sentence, tokenizer, model, device, max_length=20):generated_text = input_sentencefor _ 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
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.logitsnex_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_iddef generate_greedy_text(input_sentence, tokenizer, model, device, max_length=20):generated_text = input_sentencefor _ 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_textgenerate_greedy_text("Hello, my dog is cute and", tokenizer, model, device)
'Hello, my dog is cute andi." '
O resultado é bastante repetitivo, como já foi visto em [ways to generate text] (https://maximofn.com/hugging-face-transformers/#Formas-de-generaci%C3%B3n-de-texto).
Ajuste fino do GPT
Cálculo da perda
Antes de começarmos a fazer o ajuste fino do GPT1, vamos dar uma olhada em um aspecto. Antes, quando costumávamos obter a saída do modelo, fazíamos o seguinte
outputs = model(**input_tokens)outputs
CausalLMOutput(loss=None, logits=tensor([[[ -5.9486, -5.8697, -18.4258, ..., -9.7371, -10.4495, 0.8814],[ -6.1212, -4.8031, -14.3970, ..., -6.5411, -9.5051, -1.2015],[ -7.4231, -6.3615, -14.7297, ..., -10.4575, -8.4600, -1.5183],...,[ -5.4751, -5.8803, -13.7767, ..., -10.5048, -12.4167, -6.1584],[ -7.2052, -6.0198, -21.5040, ..., -16.2941, -14.0494, -1.2416],[ -7.7240, -7.3631, -17.3174, ..., -12.1546, -12.3327, -1.7169]]],device='cuda:0', grad_fn=<UnsafeViewBackward0>), hidden_states=None, attentions=None)
Você pode ver que obtemos loss=None
.
print(outputs.loss)
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 OpenAIGPTLMHeadModel
, veremos que ele diz que a saída retorna um objeto do tipo transformers.modeling_outputs.CausalLMOutput
, portanto, se consultarmos a documentação de transformers.modeling_outputs.CausalLMOutput, veremos 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:
# 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
tensor(4.2607, device='cuda:0', grad_fn=<NllLossBackward0>)
Conjunto de dados
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.
Baixamos o conjunto de dados
from datasets import load_datasetjokes = load_dataset("Maximofn/short-jokes-dataset")jokes
DatasetDict({train: Dataset({features: ['ID', 'Joke'],num_rows: 231657})})
Vamos dar uma olhada nisso
jokes["train"][0]
{'ID': 1,'Joke': '[me narrating a documentary about narrators] "I can't hear what they're saying cuz I'm talking"'}
Treinamento Pytorch
Primeiro, vamos dar uma olhada em como o treinamento puro do Pytorch seria feito.
Reinicie o notebook para que não haja problemas de memória da GPU
import torchfrom transformers import OpenAIGPTLMHeadModel, AutoTokenizerdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")ckeckpoints = "openai-community/openai-gpt"tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)model = OpenAIGPTLMHeadModel.from_pretrained(ckeckpoints)model = model.to(device)
Conjunto de dados Pytorch
Criar uma classe de conjunto de dados do Pytorch
import torchfrom transformers import OpenAIGPTLMHeadModel, AutoTokenizerdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")ckeckpoints = "openai-community/openai-gpt"tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)model = OpenAIGPTLMHeadModel.from_pretrained(ckeckpoints)model = model.to(device)from torch.utils.data import Datasetclass JokesDataset(Dataset):def __init__(self, dataset, tokenizer):self.dataset = datasetself.joke = "JOKE: "self.end_of_text_token = "<|endoftext|>"self.tokenizer = tokenizerdef __len__(self):return len(self.dataset["train"])def __getitem__(self, item):sentence = self.joke + self.dataset["train"][item]["Joke"] + self.end_of_text_tokentokens = self.tokenizer(sentence, return_tensors="pt")return sentence, tokens
Nós o instanciamos
import torchfrom transformers import OpenAIGPTLMHeadModel, AutoTokenizerdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")ckeckpoints = "openai-community/openai-gpt"tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)model = OpenAIGPTLMHeadModel.from_pretrained(ckeckpoints)model = model.to(device)from torch.utils.data import Datasetclass JokesDataset(Dataset):def __init__(self, dataset, tokenizer):self.dataset = datasetself.joke = "JOKE: "self.end_of_text_token = "<|endoftext|>"self.tokenizer = tokenizerdef __len__(self):return len(self.dataset["train"])def __getitem__(self, item):sentence = self.joke + self.dataset["train"][item]["Joke"] + self.end_of_text_tokentokens = self.tokenizer(sentence, return_tensors="pt")return sentence, tokensdataset = JokesDataset(jokes, tokenizer=tokenizer)
Aqui está um exemplo
sentence, tokens = dataset[5]
print(sentence)
tokens.input_ids.shape, tokens.attention_mask.shape
Dataloader
Agora, criamos um carregador de dados do Pytorch
import torchfrom transformers import OpenAIGPTLMHeadModel, AutoTokenizerdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")ckeckpoints = "openai-community/openai-gpt"tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)model = OpenAIGPTLMHeadModel.from_pretrained(ckeckpoints)model = model.to(device)from torch.utils.data import Datasetclass JokesDataset(Dataset):def __init__(self, dataset, tokenizer):self.dataset = datasetself.joke = "JOKE: "self.end_of_text_token = "<|endoftext|>"self.tokenizer = tokenizerdef __len__(self):return len(self.dataset["train"])def __getitem__(self, item):sentence = self.joke + self.dataset["train"][item]["Joke"] + self.end_of_text_tokentokens = self.tokenizer(sentence, return_tensors="pt")return sentence, tokensdataset = JokesDataset(jokes, tokenizer=tokenizer)sentence, tokens = dataset[5]print(sentence)tokens.input_ids.shape, tokens.attention_mask.shapefrom torch.utils.data import DataLoaderBS = 1joke_dataloader = DataLoader(dataset, batch_size=BS, shuffle=True)
Vemos um lote
import torchfrom transformers import OpenAIGPTLMHeadModel, AutoTokenizerdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")ckeckpoints = "openai-community/openai-gpt"tokenizer = AutoTokenizer.from_pretrained(ckeckpoints)model = OpenAIGPTLMHeadModel.from_pretrained(ckeckpoints)model = model.to(device)from torch.utils.data import Datasetclass JokesDataset(Dataset):def __init__(self, dataset, tokenizer):self.dataset = datasetself.joke = "JOKE: "self.end_of_text_token = "<|endoftext|>"self.tokenizer = tokenizerdef __len__(self):return len(self.dataset["train"])def __getitem__(self, item):sentence = self.joke + self.dataset["train"][item]["Joke"] + self.end_of_text_tokentokens = self.tokenizer(sentence, return_tensors="pt")return sentence, tokensdataset = JokesDataset(jokes, tokenizer=tokenizer)sentence, tokens = dataset[5]print(sentence)tokens.input_ids.shape, tokens.attention_mask.shapefrom torch.utils.data import DataLoaderBS = 1joke_dataloader = DataLoader(dataset, batch_size=BS, shuffle=True)sentences, tokens = next(iter(joke_dataloader))len(sentences), tokens.input_ids.shape, tokens.attention_mask.shape
JOKE: Why can't Barbie get pregnant? Because Ken comes in a different box. Heyooooooo<|endoftext|>(1, torch.Size([1, 1, 29]), torch.Size([1, 1, 29]))
Treinamento
from transformers import AdamW, get_linear_schedule_with_warmup
import tqdm
BATCH_SIZE = 32
EPOCHS = 5
LEARNING_RATE = 3e-5
WARMUP_STEPS = 5000
MAX_SEQ_LEN = 500
model.train()
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
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]})
if batch_count == 10:
batch_count = 0
Inferência
Vamos ver como o modelo faz piadas.
from transformers import AdamW, get_linear_schedule_with_warmupimport tqdmBATCH_SIZE = 32EPOCHS = 5LEARNING_RATE = 3e-5WARMUP_STEPS = 5000MAX_SEQ_LEN = 500model.train()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 = 0batch_count = 0tmp_jokes_tens = Nonefor 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_LENif joke_tens.size()[1] > MAX_SEQ_LEN:continue# The first joke sequence in the sequenceif not torch.is_tensor(tmp_jokes_tens):tmp_jokes_tens = joke_tenscontinueelse:# The next joke does not fit in so we process the sequence and leave the last joke# as the start for next sequenceif tmp_jokes_tens.size()[1] + joke_tens.size()[1] > MAX_SEQ_LEN:work_jokes_tens = tmp_jokes_tenstmp_jokes_tens = joke_tenselse:#Add the joke to sequence, continue and try to add moretmp_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.lossloss.backward()proc_seq_count = proc_seq_count + 1if proc_seq_count == BATCH_SIZE:proc_seq_count = 0batch_count += 1optimizer.step()scheduler.step()optimizer.zero_grad()model.zero_grad()progress_bar.set_postfix({'loss': loss.item(), 'lr': scheduler.get_last_lr()[0]})if batch_count == 10:batch_count = 0sentence_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}")
/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 warningwarnings.warn(decoded joke:joke : what do you call a group of people who are not afraid of the dark? a group
Você pode ver que você passa uma sequência com a palavra joke
e ele retorna uma piada. Mas se você retornar outra string, ela 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: {decoded_output_joke}")
decoded joke:my dog is cute and i'm not sure if i should be offended or not. "