Fine tuning SMLs

Fine tuning SMLs Fine tuning SMLs

Aviso: Este post foi traduzido para o portuguĂȘs usando um modelo de tradução automĂĄtica. Por favor, me avise se encontrar algum erro.

Neste post, vamos ver como fazer fine tuning em pequenos modelos de linguagem, vamos ver como fazer fine tuning para classificação de texto e para geração de texto. Primeiro, vamos ver como fazer isso com as bibliotecas do Hugging Face, jå que o Hugging Face se tornou um ator muito importante no ecossistema de IA neste momento.

Mas embora as bibliotecas do Hugging Face sejam muito importantes e Ășteis, Ă© muito importante saber como o treinamento realmente acontece e o que estĂĄ acontecendo por baixo dos panos, entĂŁo vamos repetir o treinamento para classificação e geração de texto, mas com Pytorch.

Ajuste fino para classificação de texto com Hugging Facelink image 81

Loginlink image 82

Para poder subir o resultado do treinamento ao hub devemos nos autenticar primeiro, para isso precisamos de um token

Para criar um token, Ă© necessĂĄrio ir Ă  pĂĄgina de settings/tokens da nossa conta, onde aparecerĂĄ algo assim

User-Access-Token-dark

Damos a New token e serĂĄ exibida uma janela para criar um novo token

new-token-dark

Damos um nome ao token e o criamos com o papel write, ou com o papel Fine-grained, que nos permite selecionar exatamente quais permissÔes o token terå.

Uma vez criado, copiamos e colamos a seguir.

	
from huggingface_hub import notebook_login
notebook_login()
Copy

Conjunto de Dadoslink image 83

Agora vamos baixar um dataset, neste caso vamos baixar um de avaliaçÔes do Amazon

	
from datasets import load_dataset
dataset = load_dataset("mteb/amazon_reviews_multi", "en")
Copy

Vamos vĂȘ-lo um pouco

	
dataset
Copy
	
DatasetDict({
train: Dataset({
features: ['id', 'text', 'label', 'label_text'],
num_rows: 200000
})
validation: Dataset({
features: ['id', 'text', 'label', 'label_text'],
num_rows: 5000
})
test: Dataset({
features: ['id', 'text', 'label', 'label_text'],
num_rows: 5000
})
})

Vemos que tem um conjunto de treinamento com 200.000 amostras, um de validação com 5.000 amostras e um de teste com 5.000 amostras.

Vamos a ver um exemplo do conjunto de treinamento

	
from random import randint
idx = randint(0, len(dataset['train']) - 1)
dataset['train'][idx]
Copy
	
{'id': 'en_0907914',
'text': 'Mixed with fir it’s passable Not the scent I had hoped for . Love the scent of cedar, but this one missed',
'label': 3,
'label_text': '3'}

Vemos que tem a avaliação no campo text e a pontuação que o usuårio deu no campo label

Como vamos a fazer um modelo de classificação de textos, precisamos saber quantas classes vamos ter.

	
num_classes = len(dataset['train'].unique('label'))
num_classes
Copy
	
5

Vamos a ter 5 classes, agora vamos a ver o valor mínimo dessas classes para saber se a pontuação começa em 0 ou em 1. Para isso, usamos o método unique

	
dataset.unique('label')
Copy
	
{'train': [0, 1, 2, 3, 4],
'validation': [0, 1, 2, 3, 4],
'test': [0, 1, 2, 3, 4]}

O valor mĂ­nimo serĂĄ 0

Para treinar, as etiquetas precisam estar em um campo chamado labels, enquanto no nosso conjunto de dados estĂĄ em um campo chamado label, entĂŁo criamos o novo campo labels com o mesmo valor que label

Criamos uma função que faça o que quisermos

	
def set_labels(example):
example['labels'] = example['label']
return example
Copy

Aplicamos a função ao conjunto de dados

	
dataset = dataset.map(set_labels)
Copy

Vamos ver como fica o dataset

	
dataset['train'][idx]
Copy
	
{'id': 'en_0907914',
'text': 'Mixed with fir it’s passable Not the scent I had hoped for . Love the scent of cedar, but this one missed',
'label': 3,
'label_text': '3',
'labels': 3}

Tokenizadorlink image 84

Como temos as avaliaçÔes em texto no conjunto de dados, precisamos tokenizå-las para poder inserir os tokens no modelo.

	
from transformers import AutoTokenizer
checkpoint = "openai-community/gpt2"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
Copy

Agora criamos uma função para tokenizar o texto. Vamos fazer isso de maneira que todas as frases tenham o mesmo comprimento, de modo que o tokenizador truncarå quando necessårio e adicionarå tokens de padding quando necessårio. Além disso, indicamos que retorne tensores do pytorch.

Fazemos com que o comprimento de cada sentença seja de 768 tokens porque estamos usando o modelo pequeno do GPT2, que como vimos no post de GPT2 tem uma dimensão de embedding de 768 tokens

	
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=768, return_tensors="pt")
Copy

Vamos a provar a tokenizar um texto

	
tokens = tokenize_function(dataset['train'][idx])
Copy
	
---------------------------------------------------------------------------ValueError Traceback (most recent call last)Cell In[11], line 1
----> 1 tokens = tokenize_function(dataset['train'][idx])
Cell In[10], line 2, in tokenize_function(examples)
1 def tokenize_function(examples):
----> 2 return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=768, return_tensors="pt")
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/tokenization_utils_base.py:2883, in PreTrainedTokenizerBase.__call__(self, text, text_pair, text_target, text_pair_target, add_special_tokens, padding, truncation, max_length, stride, is_split_into_words, pad_to_multiple_of, return_tensors, return_token_type_ids, return_attention_mask, return_overflowing_tokens, return_special_tokens_mask, return_offsets_mapping, return_length, verbose, **kwargs)
2881 if not self._in_target_context_manager:
2882 self._switch_to_input_mode()
-> 2883 encodings = self._call_one(text=text, text_pair=text_pair, **all_kwargs)
2884 if text_target is not None:
2885 self._switch_to_target_mode()
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/tokenization_utils_base.py:2989, in PreTrainedTokenizerBase._call_one(self, text, text_pair, add_special_tokens, padding, truncation, max_length, stride, is_split_into_words, pad_to_multiple_of, return_tensors, return_token_type_ids, return_attention_mask, return_overflowing_tokens, return_special_tokens_mask, return_offsets_mapping, return_length, verbose, **kwargs)
2969 return self.batch_encode_plus(
2970 batch_text_or_text_pairs=batch_text_or_text_pairs,
2971 add_special_tokens=add_special_tokens,
(...)
2986 **kwargs,
2987 )
2988 else:
-> 2989 return self.encode_plus(
2990 text=text,
2991 text_pair=text_pair,
2992 add_special_tokens=add_special_tokens,
2993 padding=padding,
2994 truncation=truncation,
2995 max_length=max_length,
2996 stride=stride,
2997 is_split_into_words=is_split_into_words,
2998 pad_to_multiple_of=pad_to_multiple_of,
2999 return_tensors=return_tensors,
3000 return_token_type_ids=return_token_type_ids,
3001 return_attention_mask=return_attention_mask,
3002 return_overflowing_tokens=return_overflowing_tokens,
3003 return_special_tokens_mask=return_special_tokens_mask,
3004 return_offsets_mapping=return_offsets_mapping,
3005 return_length=return_length,
3006 verbose=verbose,
3007 **kwargs,
3008 )
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/tokenization_utils_base.py:3053, in PreTrainedTokenizerBase.encode_plus(self, text, text_pair, add_special_tokens, padding, truncation, max_length, stride, is_split_into_words, pad_to_multiple_of, return_tensors, return_token_type_ids, return_attention_mask, return_overflowing_tokens, return_special_tokens_mask, return_offsets_mapping, return_length, verbose, **kwargs)
3032 """
3033 Tokenize and prepare for the model a sequence or a pair of sequences.
3034
(...)
3049 method).
3050 """
3052 # Backward compatibility for 'truncation_strategy', 'pad_to_max_length'
-> 3053 padding_strategy, truncation_strategy, max_length, kwargs = self._get_padding_truncation_strategies(
3054 padding=padding,
3055 truncation=truncation,
3056 max_length=max_length,
3057 pad_to_multiple_of=pad_to_multiple_of,
3058 verbose=verbose,
3059 **kwargs,
3060 )
3062 return self._encode_plus(
3063 text=text,
3064 text_pair=text_pair,
(...)
3080 **kwargs,
3081 )
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/tokenization_utils_base.py:2788, in PreTrainedTokenizerBase._get_padding_truncation_strategies(self, padding, truncation, max_length, pad_to_multiple_of, verbose, **kwargs)
2786 # Test if we have a padding token
2787 if padding_strategy != PaddingStrategy.DO_NOT_PAD and (self.pad_token is None or self.pad_token_id < 0):
-> 2788 raise ValueError(
2789 "Asking to pad but the tokenizer does not have a padding token. "
2790 "Please select a token to use as `pad_token` `(tokenizer.pad_token = tokenizer.eos_token e.g.)` "
2791 "or add a new pad token via `tokenizer.add_special_tokens({'pad_token': '[PAD]'})`."
2792 )
2794 # Check that we will truncate to a multiple of pad_to_multiple_of if both are provided
2795 if (
2796 truncation_strategy != TruncationStrategy.DO_NOT_TRUNCATE
2797 and padding_strategy != PaddingStrategy.DO_NOT_PAD
(...)
2800 and (max_length % pad_to_multiple_of != 0)
2801 ):
ValueError: Asking to pad but the tokenizer does not have a padding token. Please select a token to use as `pad_token` `(tokenizer.pad_token = tokenizer.eos_token e.g.)` or add a new pad token via `tokenizer.add_special_tokens({'pad_token': '[PAD]'})`.

Då-nos um erro porque o tokenizador do GPT2 não tem um token para preenchimento e pede que atribuamos um, além de sugerir fazer tokenizer.pad_token = tokenizer.eos_token, então fazemos isso.

	
tokenizer.pad_token = tokenizer.eos_token
Copy

Voltamos a testar a função de tokenização

	
tokens = tokenize_function(dataset['train'][idx])
tokens['input_ids'].shape, tokens['attention_mask'].shape
Copy
	
(torch.Size([1, 768]), torch.Size([1, 768]))

Agora que verificamos que a função tokeniza corretamente, aplicamos essa função ao conjunto de dados, mas também a aplicamos em lotes para que seja executada mais rapidamente.

Além disso, aproveitamos e eliminamos as colunas que não vamos precisar.

	
dataset = dataset.map(tokenize_function, batched=True, remove_columns=['text', 'label', 'id', 'label_text'])
Copy

Vamos ver agora como fica o dataset

	
dataset
Copy
	
DatasetDict({
train: Dataset({
features: ['id', 'text', 'label', 'label_text', 'labels'],
num_rows: 200000
})
validation: Dataset({
features: ['id', 'text', 'label', 'label_text', 'labels'],
num_rows: 5000
})
test: Dataset({
features: ['id', 'text', 'label', 'label_text', 'labels'],
num_rows: 5000
})
})

Vemos que temos os campos 'labels', 'input_ids' e 'attention_mask', que Ă© o que nos interessa para treinar.

Modelolink image 85

Instanciamos um modelo para classificação de sequĂȘncias e indicamos o nĂșmero de classes que temos

	
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=num_classes)
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.

Diz que os pesos da camada score foram inicializados de maneira aleatĂłria e que temos que reentreinĂĄ-los, vamos ver por que isso acontece.

O modelo GPT2 seria este

	
from transformers import AutoModelForCausalLM
casual_model = AutoModelForCausalLM.from_pretrained(checkpoint)
Copy

Enquanto o modelo do GPT2 para gerar texto Ă© este

Vamos a ver sua arquitetura

	
casual_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)
)

E agora a arquitetura do modelo que vamos usar para classificar as reviews

	
model
Copy
	
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)
)

Aqui hĂĄ duas coisas para mencionar

  • A primeira Ă© que em ambos, a primeira camada tem dimensĂ”es de 50257x768, o que corresponde a 50257 possĂ­veis tokens do vocabulĂĄrio do GPT2 e a 768 dimensĂ”es do embedding, por isso fizemos bem em tokenizar as avaliaçÔes com um tamanho de 768 tokens
  • A segunda Ă© que o modelo casual (o de geração de texto) tem no final uma camada Linear que gera 50257 valores, ou seja, Ă© responsĂĄvel por prever o prĂłximo token e dar um valor a um possĂ­vel token. Enquanto isso, o modelo de classificação tem uma camada Linear que gera apenas 5 valores, um para cada classe, o que nos fornecerĂĄ a probabilidade de a review pertencer a cada classe.

Por isso recebĂ­amos a mensagem de que os pesos da camada score haviam sido inicializados de forma aleatĂłria, porque a biblioteca transformers removeu a camada Linear de 768x50257 e adicionou uma camada Linear de 768x5, inicializou-a com valores aleatĂłrios e nĂłs temos que treinĂĄ-la para o nosso problema especĂ­fico.

Apagamos o modelo casual, pois nĂŁo vamos usĂĄ-lo.

	
del casual_model
Copy

Treinadorlink image 86

Vamos agora a configurar os argumentos do treinamento

	
from transformers import TrainingArguments
metric_name = "accuracy"
model_name = "GPT2-small-finetuned-amazon-reviews-en-classification"
LR = 2e-5
BS_TRAIN = 28
BS_EVAL = 40
EPOCHS = 3
WEIGHT_DECAY = 0.01
training_args = TrainingArguments(
model_name,
eval_strategy="epoch",
save_strategy="epoch",
learning_rate=LR,
per_device_train_batch_size=BS_TRAIN,
per_device_eval_batch_size=BS_EVAL,
num_train_epochs=EPOCHS,
weight_decay=WEIGHT_DECAY,
lr_scheduler_type="cosine",
warmup_ratio = 0.1,
fp16=True,
load_best_model_at_end=True,
metric_for_best_model=metric_name,
push_to_hub=True,
)
Copy

Definimos uma métrica para o dataloader de validação

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

Definimos agora o treinador

	
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset=dataset['train'],
eval_dataset=dataset['validation'],
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
Copy

Treinamos

	
trainer.train()
Copy
	
0%| | 0/600000 [00:00<?, ?it/s]
	
---------------------------------------------------------------------------ValueError Traceback (most recent call last)Cell In[21], line 1
----> 1 trainer.train()
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/trainer.py:1876, in Trainer.train(self, resume_from_checkpoint, trial, ignore_keys_for_eval, **kwargs)
1873 try:
1874 # Disable progress bars when uploading models during checkpoints to avoid polluting stdout
1875 hf_hub_utils.disable_progress_bars()
-> 1876 return inner_training_loop(
1877 args=args,
1878 resume_from_checkpoint=resume_from_checkpoint,
1879 trial=trial,
1880 ignore_keys_for_eval=ignore_keys_for_eval,
1881 )
1882 finally:
1883 hf_hub_utils.enable_progress_bars()
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/trainer.py:2178, in Trainer._inner_training_loop(self, batch_size, args, resume_from_checkpoint, trial, ignore_keys_for_eval)
2175 rng_to_sync = True
2177 step = -1
-> 2178 for step, inputs in enumerate(epoch_iterator):
2179 total_batched_samples += 1
2181 if self.args.include_num_input_tokens_seen:
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/accelerate/data_loader.py:454, in DataLoaderShard.__iter__(self)
452 # We iterate one batch ahead to check when we are at the end
453 try:
--> 454 current_batch = next(dataloader_iter)
455 except StopIteration:
456 yield
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/torch/utils/data/dataloader.py:631, in _BaseDataLoaderIter.__next__(self)
628 if self._sampler_iter is None:
629 # TODO(https://github.com/pytorch/pytorch/issues/76750)
630 self._reset() # type: ignore[call-arg]
--> 631 data = self._next_data()
632 self._num_yielded += 1
633 if self._dataset_kind == _DatasetKind.Iterable and \
634 self._IterableDataset_len_called is not None and \
635 self._num_yielded > self._IterableDataset_len_called:
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/torch/utils/data/dataloader.py:675, in _SingleProcessDataLoaderIter._next_data(self)
673 def _next_data(self):
674 index = self._next_index() # may raise StopIteration
--> 675 data = self._dataset_fetcher.fetch(index) # may raise StopIteration
676 if self._pin_memory:
677 data = _utils.pin_memory.pin_memory(data, self._pin_memory_device)
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/torch/utils/data/_utils/fetch.py:54, in _MapDatasetFetcher.fetch(self, possibly_batched_index)
52 else:
53 data = self.dataset[possibly_batched_index]
---> 54 return self.collate_fn(data)
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/data/data_collator.py:271, in DataCollatorWithPadding.__call__(self, features)
270 def __call__(self, features: List[Dict[str, Any]]) -> Dict[str, Any]:
--> 271 batch = pad_without_fast_tokenizer_warning(
272 self.tokenizer,
273 features,
274 padding=self.padding,
275 max_length=self.max_length,
276 pad_to_multiple_of=self.pad_to_multiple_of,
277 return_tensors=self.return_tensors,
278 )
279 if "label" in batch:
280 batch["labels"] = batch["label"]
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/data/data_collator.py:66, in pad_without_fast_tokenizer_warning(tokenizer, *pad_args, **pad_kwargs)
63 tokenizer.deprecation_warnings["Asking-to-pad-a-fast-tokenizer"] = True
65 try:
---> 66 padded = tokenizer.pad(*pad_args, **pad_kwargs)
67 finally:
68 # Restore the state of the warning.
69 tokenizer.deprecation_warnings["Asking-to-pad-a-fast-tokenizer"] = warning_state
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/tokenization_utils_base.py:3299, in PreTrainedTokenizerBase.pad(self, encoded_inputs, padding, max_length, pad_to_multiple_of, return_attention_mask, return_tensors, verbose)
3297 # The model's main input name, usually `input_ids`, has be passed for padding
3298 if self.model_input_names[0] not in encoded_inputs:
-> 3299 raise ValueError(
3300 "You should supply an encoding or a list of encodings to this method "
3301 f"that includes {self.model_input_names[0]}, but you provided {list(encoded_inputs.keys())}"
3302 )
3304 required_input = encoded_inputs[self.model_input_names[0]]
3306 if required_input is None or (isinstance(required_input, Sized) and len(required_input) == 0):
ValueError: You should supply an encoding or a list of encodings to this method that includes input_ids, but you provided ['label', 'labels']

NĂłs recebemos novamente um erro porque o modelo nĂŁo tem um token de padding atribuĂ­do, entĂŁo, assim como com o tokenizador, nĂłs o atribuĂ­mos.

	
model.config.pad_token_id = model.config.eos_token_id
Copy

Recriamos os argumentos do treinador com o novo modelo, que agora tem um token de padding, o treinador e voltamos a treinar.

	
training_args = TrainingArguments(
model_name,
eval_strategy="epoch",
save_strategy="epoch",
learning_rate=LR,
per_device_train_batch_size=BS_TRAIN,
per_device_eval_batch_size=BS_EVAL,
num_train_epochs=EPOCHS,
weight_decay=WEIGHT_DECAY,
lr_scheduler_type="cosine",
warmup_ratio = 0.1,
fp16=True,
load_best_model_at_end=True,
metric_for_best_model=metric_name,
push_to_hub=True,
logging_dir="./runs",
)
trainer = Trainer(
model,
training_args,
train_dataset=dataset['train'],
eval_dataset=dataset['validation'],
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
Copy

Agora que vimos que tudo estĂĄ bem, podemos treinar

	
trainer.train()
Copy
	
<IPython.core.display.HTML object>
	
<IPython.core.display.HTML object>
	
<transformers.trainer_utils.EvalPrediction object at 0x782767ea1450>
<transformers.trainer_utils.EvalPrediction object at 0x782767eeefe0>
<transformers.trainer_utils.EvalPrediction object at 0x782767eecfd0>
	
TrainOutput(global_step=21429, training_loss=0.7846888848762739, metrics={'train_runtime': 26367.7801, 'train_samples_per_second': 22.755, 'train_steps_per_second': 0.813, 'total_flos': 2.35173445632e+17, 'train_loss': 0.7846888848762739, 'epoch': 3.0})

Avaliaçãolink image 87

Uma vez treinado, avaliamos sobre o dataset de teste

	
trainer.evaluate(eval_dataset=dataset['test'])
Copy
	
<IPython.core.display.HTML object>
	
<transformers.trainer_utils.EvalPrediction object at 0x7826ddfded40>
	
{'eval_loss': 0.7973636984825134,
'eval_accuracy': 0.6626,
'eval_runtime': 76.3016,
'eval_samples_per_second': 65.529,
'eval_steps_per_second': 1.638,
'epoch': 3.0}

Publicar o modelolink image 88

JĂĄ temos nosso modelo treinado, agora podemos compartilhĂĄ-lo com o mundo, entĂŁo primeiro criamos uma **ficha do modelo**

	
trainer.create_model_card()
Copy

E jĂĄ podemos publicĂĄ-lo. Como a primeira coisa que fizemos foi fazer login no hub do huggingface, podemos enviĂĄ-lo para o nosso hub sem nenhum problema.

	
trainer.push_to_hub()
Copy

Uso do modelolink image 89

Limpamos tudo o que for possĂ­vel

	
import torch
import gc
def clear_hardwares():
torch.clear_autocast_cache()
torch.cuda.ipc_collect()
torch.cuda.empty_cache()
gc.collect()
clear_hardwares()
clear_hardwares()
Copy

Como subimos o modelo ao nosso hub, podemos baixĂĄ-lo e usĂĄ-lo

	
from transformers import pipeline
user = "maximofn"
checkpoints = f"{user}/{model_name}"
task = "text-classification"
classifier = pipeline(task, model=checkpoints, tokenizer=checkpoints)
Copy

Agora, se quisermos que nos retorne a probabilidade de todas as classes, simplesmente usamos o classificador que acabamos de instanciar, com o parĂąmetro top_k=None

	
labels = classifier("I love this product", top_k=None)
labels
Copy
	
[{'label': 'LABEL_4', 'score': 0.8253807425498962},
{'label': 'LABEL_3', 'score': 0.15411493182182312},
{'label': 'LABEL_2', 'score': 0.013907806016504765},
{'label': 'LABEL_0', 'score': 0.003939222544431686},
{'label': 'LABEL_1', 'score': 0.0026572425849735737}]

Se quisermos apenas a classe com a maior probabilidade, fazemos o mesmo mas com o parĂąmetro top_k=1

	
label = classifier("I love this product", top_k=1)
label
Copy
	
[{'label': 'LABEL_4', 'score': 0.8253807425498962}]

E se quisermos n classes, fazemos o mesmo mas com o parĂąmetro top_k=n

	
two_labels = classifier("I love this product", top_k=2)
two_labels
Copy
	
[{'label': 'LABEL_4', 'score': 0.8253807425498962},
{'label': 'LABEL_3', 'score': 0.15411493182182312}]

Também podemos testar o modelo com Automodel e AutoTokenizer

	
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
model_name = "GPT2-small-finetuned-amazon-reviews-en-classification"
user = "maximofn"
checkpoint = f"{user}/{model_name}"
num_classes = 5
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=num_classes).half().eval().to("cuda")
Copy
	
tokens = tokenizer.encode("I love this product", return_tensors="pt").to(model.device)
with torch.no_grad():
output = model(tokens)
logits = output.logits
lables = torch.softmax(logits, dim=1).cpu().numpy().tolist()
lables[0]
Copy
	
[0.003963470458984375,
0.0026721954345703125,
0.01397705078125,
0.154541015625,
0.82470703125]

Se vocĂȘ quiser testar mais o modelo, vocĂȘ pode vĂȘ-lo em Maximofn/GPT2-small-finetuned-amazon-reviews-en-classification

Ajuste fino para geração de texto com Hugging Facelink image 90

Para garantizar que nĂŁo tenho problemas de memĂłria VRAM, reinicio o notebook

Loginlink image 91

Para poder fazer o upload do resultado do treinamento para o hub, devemos nos autenticar primeiro, para isso precisamos de um token

Para criar um token Ă© necessĂĄrio ir Ă  pĂĄgina de settings/tokens da nossa conta, aparecerĂĄ algo assim

User-Access-Token-dark

Damos a New token e uma janela aparecerĂĄ para criar um novo token

new-token-dark

Damos um nome ao token e o criamos com o papel write, ou com o papel Fine-grained, que nos permite selecionar exatamente quais permissÔes terå o token.

Uma vez criado, copiamos e colamos a seguir.

	
from huggingface_hub import notebook_login
notebook_login()
Copy

Conjunto de Dadoslink image 92

Vamos a usar um dataset de chistes em inglĂȘs

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

Vamos vĂȘ-lo um pouco

	
jokes
Copy
	
DatasetDict({
train: Dataset({
features: ['ID', 'Joke'],
num_rows: 231657
})
})

Vemos que Ă© um conjunto de treinamento Ășnico com mais de 200 mil piadas. EntĂŁo, mais tarde teremos que dividi-lo em treinamento e avaliação.

Vamos a ver um exemplo

	
from random import randint
idx = randint(0, len(jokes['train']) - 1)
jokes['train'][idx]
Copy
	
{'ID': 198387,
'Joke': 'My hot dislexic co-worker said she had an important massage to give me in her office... When I got there, she told me it can wait until I put on some clothes.'}

Vemos que tem um ID de piada que nĂŁo nos interessa em absoluto e a prĂłpria piada

Caso vocĂȘ tenha pouca memĂłria na GPU, vou fazer um subconjunto do conjunto de dados, escolha o percentual de piadas que deseja usar

	
percent_of_train_dataset = 1 # If you want 50% of the dataset, set this to 0.5
subset_dataset = jokes["train"].select(range(int(len(jokes["train"]) * percent_of_train_dataset)))
subset_dataset
Copy
	
Dataset({
features: ['ID', 'Joke'],
num_rows: 231657
})

Agora dividimos o subconjunto em um conjunto de treinamento e outro de validação.

	
percent_of_train_dataset = 0.90
split_dataset = subset_dataset.train_test_split(train_size=int(subset_dataset.num_rows * percent_of_train_dataset), seed=19, shuffle=False)
train_dataset = split_dataset["train"]
validation_test_dataset = split_dataset["test"]
split_dataset = validation_test_dataset.train_test_split(train_size=int(validation_test_dataset.num_rows * 0.5), seed=19, shuffle=False)
validation_dataset = split_dataset["train"]
test_dataset = split_dataset["test"]
print(f"Size of the train set: {len(train_dataset)}. Size of the validation set: {len(validation_dataset)}. Size of the test set: {len(test_dataset)}")
Copy
	
Size of the train set: 208491. Size of the validation set: 11583. Size of the test set: 11583

Tokenizadorlink image 93

Instanciamos o tokenizador. Instanciamos o token de padding do tokenizador para que nĂŁo nos dĂȘ erro como antes.

	
from transformers import AutoTokenizer
checkpoints = "openai-community/gpt2"
tokenizer = AutoTokenizer.from_pretrained(checkpoints)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
Copy

Vamos adicionar dois novos tokens de inĂ­cio de piada e fim de piada para ter mais controle

	
new_tokens = ['<SJ>', '<EJ>'] # Start and end of joke tokens
num_added_tokens = tokenizer.add_tokens(new_tokens)
print(f"Added {num_added_tokens} tokens")
Copy
	
Added 2 tokens

Criamos uma função para adicionar os novos tokens às frases.

	
joke_column = "Joke"
def format_joke(example):
example[joke_column] = '<SJ> ' + example['Joke'] + ' <EJ>'
return example
Copy

Selecionamos as colunas que nĂŁo precisamos

	
remove_columns = [column for column in train_dataset.column_names if column != joke_column]
remove_columns
Copy
	
['ID']

Formatamos o conjunto de dados e eliminamos as colunas que nĂŁo precisamos

	
train_dataset = train_dataset.map(format_joke, remove_columns=remove_columns)
validation_dataset = validation_dataset.map(format_joke, remove_columns=remove_columns)
test_dataset = test_dataset.map(format_joke, remove_columns=remove_columns)
train_dataset, validation_dataset, test_dataset
Copy
	
(Dataset({
features: ['Joke'],
num_rows: 208491
}),
Dataset({
features: ['Joke'],
num_rows: 11583
}),
Dataset({
features: ['Joke'],
num_rows: 11583
}))

Agora criamos uma função para tokenizar os piadas

	
def tokenize_function(examples):
return tokenizer(examples[joke_column], padding="max_length", truncation=True, max_length=768, return_tensors="pt")
Copy

Tokenizamos o conjunto de dados e removemos a coluna com o texto

	
train_dataset = train_dataset.map(tokenize_function, batched=True, remove_columns=[joke_column])
validation_dataset = validation_dataset.map(tokenize_function, batched=True, remove_columns=[joke_column])
test_dataset = test_dataset.map(tokenize_function, batched=True, remove_columns=[joke_column])
train_dataset, validation_dataset, test_dataset
Copy
	
(Dataset({
features: ['input_ids', 'attention_mask'],
num_rows: 208491
}),
Dataset({
features: ['input_ids', 'attention_mask'],
num_rows: 11583
}),
Dataset({
features: ['input_ids', 'attention_mask'],
num_rows: 11583
}))

Modelolink image 94

Agora instanciamos o modelo para geração de texto e atribuímos ao token de padding o token de end of string

	
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(checkpoints)
model.config.pad_token_id = model.config.eos_token_id
Copy

Vemos o tamanho do vocabulĂĄrio do modelo

	
vocab_size = model.config.vocab_size
vocab_size
Copy
	
50257

Tem 50257 tokens, que Ă© o tamanho do vocabulĂĄrio do GPT2. Mas como dissemos que irĂ­amos criar dois novos tokens com o inĂ­cio da piada e o fim da piada, os adicionamos ao modelo.

	
model.resize_token_embeddings(len(tokenizer))
new_vocab_size = model.config.vocab_size
print(f"Old vocab size: {vocab_size}. New vocab size: {new_vocab_size}. Added {new_vocab_size - vocab_size} tokens")
Copy
	
Old vocab size: 50257. New vocab size: 50259. Added 2 tokens

Foram adicionados os dois novos tokens

Treinamentolink image 95

Configuramos os parĂąmetros de treinamento

	
from transformers import TrainingArguments
metric_name = "accuracy"
model_name = "GPT2-small-finetuned-Maximofn-short-jokes-dataset-casualLM"
output_dir = f"./training_results"
LR = 2e-5
BS_TRAIN = 28
BS_EVAL = 32
EPOCHS = 3
WEIGHT_DECAY = 0.01
WARMUP_STEPS = 100
training_args = TrainingArguments(
model_name,
eval_strategy="epoch",
save_strategy="epoch",
learning_rate=LR,
per_device_train_batch_size=BS_TRAIN,
per_device_eval_batch_size=BS_EVAL,
warmup_steps=WARMUP_STEPS,
num_train_epochs=EPOCHS,
weight_decay=WEIGHT_DECAY,
lr_scheduler_type="cosine",
warmup_ratio = 0.1,
fp16=True,
load_best_model_at_end=True,
# metric_for_best_model=metric_name,
push_to_hub=True,
)
Copy

Agora nĂŁo usamos metric_for_best_model, depois de definir o treinador explicamos por que

Definimos o treinador

	
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset=train_dataset,
eval_dataset=validation_dataset,
tokenizer=tokenizer,
# compute_metrics=compute_metrics,
)
Copy

Neste caso, não passamos uma função compute_metrics, mas sim, durante a avaliação, serå usada a loss para avaliar o modelo. Por isso, ao definir os argumentos, não definimos metric_for_best_model, pois não vamos usar uma métrica para avaliar o modelo, mas sim a loss.

Treinamos

	
trainer.train()
Copy
	
0%| | 0/625473 [00:00<?, ?it/s]
	
---------------------------------------------------------------------------ValueError Traceback (most recent call last)Cell In[19], line 1
----> 1 trainer.train()
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/trainer.py:1885, in Trainer.train(self, resume_from_checkpoint, trial, ignore_keys_for_eval, **kwargs)
1883 hf_hub_utils.enable_progress_bars()
1884 else:
-> 1885 return inner_training_loop(
1886 args=args,
1887 resume_from_checkpoint=resume_from_checkpoint,
1888 trial=trial,
1889 ignore_keys_for_eval=ignore_keys_for_eval,
1890 )
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/trainer.py:2216, in Trainer._inner_training_loop(self, batch_size, args, resume_from_checkpoint, trial, ignore_keys_for_eval)
2213 self.control = self.callback_handler.on_step_begin(args, self.state, self.control)
2215 with self.accelerator.accumulate(model):
-> 2216 tr_loss_step = self.training_step(model, inputs)
2218 if (
2219 args.logging_nan_inf_filter
2220 and not is_torch_xla_available()
2221 and (torch.isnan(tr_loss_step) or torch.isinf(tr_loss_step))
2222 ):
2223 # if loss is nan or inf simply add the average of previous logged losses
2224 tr_loss += tr_loss / (1 + self.state.global_step - self._globalstep_last_logged)
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/trainer.py:3238, in Trainer.training_step(self, model, inputs)
3235 return loss_mb.reduce_mean().detach().to(self.args.device)
3237 with self.compute_loss_context_manager():
-> 3238 loss = self.compute_loss(model, inputs)
3240 del inputs
3241 torch.cuda.empty_cache()
File ~/miniconda3/envs/nlp_/lib/python3.11/site-packages/transformers/trainer.py:3282, in Trainer.compute_loss(self, model, inputs, return_outputs)
3280 else:
3281 if isinstance(outputs, dict) and "loss" not in outputs:
-> 3282 raise ValueError(
3283 "The model did not return a loss from the inputs, only the following keys: "
3284 f"{','.join(outputs.keys())}. For reference, the inputs it received are {','.join(inputs.keys())}."
3285 )
3286 # We don't use .loss here since the model may return tuples instead of ModelOutput.
3287 loss = outputs["loss"] if isinstance(outputs, dict) else outputs[0]
ValueError: The model did not return a loss from the inputs, only the following keys: logits,past_key_values. For reference, the inputs it received are input_ids,attention_mask.

Como vemos, nos dĂĄ um erro, dizendo que o modelo nĂŁo retorna o valor do loss, que Ă© fundamental para poder treinar, vamos ver por quĂȘ.

Vamos ver como Ă© um exemplo do dataset

	
idx = randint(0, len(train_dataset) - 1)
sample = train_dataset[idx]
sample
Copy
	
{'input_ids': [50257,
4162,
750,
262,
18757,
6451,
2245,
2491,
30,
4362,
340,
373,
734,
10032,
13,
220,
50258,
50256,
50256,
...,
50256,
50256,
50256],
'attention_mask': [1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
...,
0,
0,
0]}

Como podemos ver, temos um dicionĂĄrio com os input_ids e as attention_mask. Se o passarmos para o modelo, obtemos isso

	
import torch
output = model(
input_ids=torch.Tensor(sample["input_ids"]).long().unsqueeze(0).to(model.device),
attention_mask=torch.Tensor(sample["attention_mask"]).long().unsqueeze(0).to(model.device),
)
print(output.loss)
Copy
	
None

Como vemos, não retorna o valor da loss porque estå esperando um valor para labels, que não foi fornecido. No exemplo anterior, em que fazíamos fine tuning para classificação de texto, dissemos que as etiquetas devem ser passadas para um campo do dataset chamado labels, mas neste caso não temos esse campo no dataset.

Se agora atribuirmos as labels aos input_ids e voltarmos a ver a loss

	
import torch
output = model(
input_ids=torch.Tensor(sample["input_ids"]).long().unsqueeze(0).to(model.device),
attention_mask=torch.Tensor(sample["attention_mask"]).long().unsqueeze(0).to(model.device),
labels=torch.Tensor(sample["input_ids"]).long().unsqueeze(0).to(model.device)
)
print(output.loss)
Copy
	
tensor(102.1873, device='cuda:0', grad_fn=<NllLossBackward0>)

Agora sim obtemos uma loss

Portanto, temos duas opçÔes: adicionar um campo labels ao dataset, com os valores de input_ids, ou utilizar uma função da biblioteca transformers chamada data_collator. Neste caso, usaremos DataCollatorForLanguageModeling. Vamos ver isso.

	
from transformers import DataCollatorForLanguageModeling
my_data_collator=DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
Copy

Passamos a amostra sample por este data_collator

	
collated_sample = my_data_collator([sample]).to(model.device)
Copy

Vemos como Ă© a saĂ­da

	
for key, value in collated_sample.items():
print(f"{key} ({value.shape}): {value}")
Copy
	
input_ids (torch.Size([1, 768])): tensor([[50257, 4162, 750, 262, 18757, 6451, 2245, 2491, 30, 4362,
340, 373, 734, 10032, 13, 220, 50258, 50256, ..., 50256, 50256]],
device='cuda:0')
attention_mask (torch.Size([1, 768])): tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, ..., 0, 0]],
device='cuda:0')
labels (torch.Size([1, 768])): tensor([[50257, 4162, 750, 262, 18757, 6451, 2245, 2491, 30, 4362,
340, 373, 734, 10032, 13, 220, 50258, -100, ..., -100, -100]],
device='cuda:0')

Como se pode ver, o data_collator criou um campo labels e lhe atribuiu os valores de input_ids. Os tokens que estĂŁo mascarados receberam o valor -100. Isso ocorre porque quando definimos o data_collator, passamos o parĂąmetro mlm=False, o que significa que nĂŁo estamos fazendo Masked Language Modeling, mas sim Language Modeling, por isso nenhum token original Ă© mascarado.

Vamos ver se agora obtemos uma loss com esse data_collator

	
output = model(**collated_sample)
output.loss
Copy
	
tensor(102.7181, device='cuda:0', grad_fn=<NllLossBackward0>)

EntĂŁo, redefinimos o trainer com o data_collator e treinamos novamente.

	
from transformers import DataCollatorForLanguageModeling
trainer = Trainer(
model,
training_args,
train_dataset=train_dataset,
eval_dataset=validation_dataset,
tokenizer=tokenizer,
data_collator=DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False),
)
Copy
	
trainer.train()
Copy
	
<IPython.core.display.HTML object>
	
There were missing keys in the checkpoint model loaded: ['lm_head.weight'].
	
TrainOutput(global_step=22341, training_loss=3.505178199598342, metrics={'train_runtime': 9209.5353, 'train_samples_per_second': 67.916, 'train_steps_per_second': 2.426, 'total_flos': 2.45146666696704e+17, 'train_loss': 3.505178199598342, 'epoch': 3.0})

Avaliaçãolink image 96

Uma vez treinado, avaliamos o modelo sobre o dataset de teste.

	
trainer.evaluate(eval_dataset=test_dataset)
Copy
	
<IPython.core.display.HTML object>
	
{'eval_loss': 3.201305866241455,
'eval_runtime': 65.0033,
'eval_samples_per_second': 178.191,
'eval_steps_per_second': 5.569,
'epoch': 3.0}

Publicar o modelolink image 97

Criamos o cartĂŁo do modelo

	
trainer.create_model_card()
Copy

Publicamos

	
trainer.push_to_hub()
Copy
	
events.out.tfevents.1720875425.8de3af1b431d.6946.1: 0%| | 0.00/364 [00:00<?, ?B/s]
	
CommitInfo(commit_url='https://huggingface.co/Maximofn/GPT2-small-finetuned-Maximofn-short-jokes-dataset-casualLM/commit/d107b3bb0e02076483238f9975697761015ec390', commit_message='End of training', commit_description='', oid='d107b3bb0e02076483238f9975697761015ec390', pr_url=None, pr_revision=None, pr_num=None)

Uso do modelolink image 98

Limpez tudo o possĂ­vel

	
import torch
import gc
def clear_hardwares():
torch.clear_autocast_cache()
torch.cuda.ipc_collect()
torch.cuda.empty_cache()
gc.collect()
clear_hardwares()
clear_hardwares()
Copy

Baixamos o modelo e o tokenizador

	
from transformers import AutoTokenizer, AutoModelForCausalLM
user = "maximofn"
checkpoints = f"{user}/{model_name}"
tokenizer = AutoTokenizer.from_pretrained(checkpoints)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
model = AutoModelForCausalLM.from_pretrained(checkpoints)
model.config.pad_token_id = model.config.eos_token_id
Copy
	
Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.

Verificamos que o tokenizador e o modelo tenham os 2 tokens extras que adicionamos.

	
tokenizer_vocab = tokenizer.get_vocab()
model_vocab = model.config.vocab_size
print(f"tokenizer_vocab: {len(tokenizer_vocab)}. model_vocab: {model_vocab}")
Copy
	
tokenizer_vocab: 50259. model_vocab: 50259

Vemos que tĂȘm 50259 tokens, ou seja, os 50257 tokens do GPT2 mais os 2 que adicionamos.

Criamos uma função para gerar piadas

	
def generate_joke(prompt_text):
text = f"<SJ> {prompt_text}"
tokens = tokenizer(text, return_tensors="pt").to(model.device)
with torch.no_grad():
output = model.generate(**tokens, max_new_tokens=256, eos_token_id=tokenizer.encode("<EJ>")[-1])
return tokenizer.decode(output[0], skip_special_tokens=False)
Copy

Geramos uma piada

	
generate_joke("Why didn't the frog cross the road?")
Copy
	
Setting `pad_token_id` to `eos_token_id`:50258 for open-end generation.
	
"<SJ> Why didn't the frog cross the road? Because he was frog-in-the-face. <EJ>"

Se quiser testar mais o modelo, vocĂȘ pode vĂȘ-lo em Maximofn/GPT2-small-finetuned-Maximofn-short-jokes-dataset-casualLM

Ajuste fino para classificação de texto com Pytorchlink image 99

Repetimos o treinamento com Pytorch

Reiniciamos o notebook para nos assegurar

Conjunto de Dadoslink image 100

Baixamos o mesmo conjunto de dados que quando fizemos o treinamento com as bibliotecas do Hugging Face

	
from datasets import load_dataset
dataset = load_dataset("mteb/amazon_reviews_multi", "en")
Copy

Criamos uma variĂĄvel com o nĂșmero de classes

	
num_classes = len(dataset['train'].unique('label'))
num_classes
Copy
	
5

Antes processĂĄvamos todo o conjunto de dados para criar um campo chamado labels, mas agora nĂŁo Ă© necessĂĄrio porque, como vamos programar tudo nĂłs mesmos, nos adaptamos a como Ă© o conjunto de dados.

Tokenizadorlink image 101

Criamos o tokenizador. AtribuĂ­mos o token de padding para que nĂŁo nos dĂȘ erro como antes.

	
from transformers import AutoTokenizer
checkpoint = "openai-community/gpt2"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
tokenizer.pad_token = tokenizer.eos_token
Copy

Criamos uma função para tokenizar o dataset

	
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=768, return_tensors="pt")
Copy

Tokenizamos. Removemos colunas que nĂŁo sejam necessĂĄrias, mas agora mantemos a de texto.

	
dataset = dataset.map(tokenize_function, batched=True, remove_columns=['id', 'label_text'])
Copy
	
dataset
Copy
	
DatasetDict({
train: Dataset({
features: ['text', 'label', 'input_ids', 'attention_mask'],
num_rows: 200000
})
validation: Dataset({
features: ['text', 'label', 'input_ids', 'attention_mask'],
num_rows: 5000
})
test: Dataset({
features: ['text', 'label', 'input_ids', 'attention_mask'],
num_rows: 5000
})
})
	
percentage = 1
subset_train = dataset['train'].select(range(int(len(dataset['train']) * percentage)))
percentage = 1
subset_validation = dataset['validation'].select(range(int(len(dataset['validation']) * percentage)))
subset_test = dataset['test'].select(range(int(len(dataset['test']) * percentage)))
print(f"len subset_train: {len(subset_train)}, len subset_validation: {len(subset_validation)}, len subset_test: {len(subset_test)}")
Copy
	
len subset_train: 200000, len subset_validation: 5000, len subset_test: 5000

Modelolink image 102

Importamos os pesos e atribuĂ­mos o token de padding

	
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=num_classes)
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.

Dispositivolink image 103

Criamos o dispositivo onde tudo serĂĄ executado

	
import torch
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
Copy

De passagem, transferimos o modelo para o dispositivo e, de passagem, o convertemos para FP16 para ocupar menos memĂłria

	
model.half().to(device)
print()
Copy
	

Conjunto de Dados do Pytorchlink image 104

Criamos um dataset de pytorch

	
from torch.utils.data import Dataset
class ReviewsDataset(Dataset):
def __init__(self, huggingface_dataset):
self.dataset = huggingface_dataset
def __getitem__(self, idx):
label = self.dataset[idx]['label']
input_ids = torch.tensor(self.dataset[idx]['input_ids'])
attention_mask = torch.tensor(self.dataset[idx]['attention_mask'])
return input_ids, attention_mask, label
def __len__(self):
return len(self.dataset)
Copy

Instanciamos os datasets

	
train_dataset = ReviewsDataset(subset_train)
validatation_dataset = ReviewsDataset(subset_validation)
test_dataset = ReviewsDataset(subset_test)
Copy

Vamos dar uma olhada em um exemplo

	
input_ids, at_mask, label = train_dataset[0]
input_ids.shape, at_mask.shape, label
Copy
	
(torch.Size([768]), torch.Size([768]), 0)

Pytorch Dataloaderlink image 105

Criamos agora um DataLoader do PyTorch

	
from torch.utils.data import DataLoader
BS = 12
train_loader = DataLoader(train_dataset, batch_size=BS, shuffle=True)
validation_loader = DataLoader(validatation_dataset, batch_size=BS)
test_loader = DataLoader(test_dataset, batch_size=BS)
Copy

Vamos dar uma olhada em um exemplo

	
input_ids, at_mask, labels = next(iter(train_loader))
input_ids.shape, at_mask.shape, labels
Copy
	
(torch.Size([12, 768]),
torch.Size([12, 768]),
tensor([2, 1, 2, 0, 3, 3, 0, 4, 3, 3, 4, 2]))

Para verificar se tudo estĂĄ bem, passamos a amostra ao modelo para ver se tudo sai bem. Primeiro passamos os tokens para o dispositivo.

	
input_ids = input_ids.to(device)
at_mask = at_mask.to(device)
labels = labels.to(device)
Copy

Agora passamos isso para o modelo

	
output = model(input_ids=input_ids, attention_mask=at_mask, labels=labels)
output.keys()
Copy
	
odict_keys(['loss', 'logits', 'past_key_values'])

Como vemos, nos dĂĄ a loss e os logits

	
output['loss']
Copy
	
tensor(5.9414, device='cuda:0', dtype=torch.float16,
grad_fn=<NllLossBackward0>)
	
output['logits']
Copy
	
tensor([[ 6.1953e+00, -1.2275e+00, -2.4824e+00, 5.8867e+00, -1.4734e+01],
[ 5.4062e+00, -8.4570e-01, -2.3203e+00, 5.1055e+00, -1.1555e+01],
[ 6.1641e+00, -9.3066e-01, -2.5664e+00, 6.0039e+00, -1.4570e+01],
[ 5.2266e+00, -4.2358e-01, -2.0801e+00, 4.7461e+00, -1.1570e+01],
[ 3.8184e+00, -2.3460e-03, -1.7666e+00, 3.4160e+00, -7.7969e+00],
[ 4.1641e+00, -4.8169e-01, -1.6914e+00, 3.9941e+00, -8.7734e+00],
[ 4.6758e+00, -3.0298e-01, -2.1641e+00, 4.1055e+00, -9.3359e+00],
[ 4.1953e+00, -3.2471e-01, -2.1875e+00, 3.9375e+00, -8.3438e+00],
[-1.1650e+00, 1.3564e+00, -6.2158e-01, -6.8115e-01, 4.8672e+00],
[ 4.4961e+00, -8.7891e-02, -2.2793e+00, 4.2812e+00, -9.3359e+00],
[ 4.9336e+00, -2.6627e-03, -2.1543e+00, 4.3711e+00, -1.0742e+01],
[ 5.9727e+00, -4.3152e-02, -1.4551e+00, 4.3438e+00, -1.2117e+01]],
device='cuda:0', dtype=torch.float16, grad_fn=<IndexBackward0>)

Métricalink image 106

Vamos a criar uma função para obter a métrica, que neste caso vai ser a acuråcia.

	
def predicted_labels(logits):
percent = torch.softmax(logits, dim=1)
predictions = torch.argmax(percent, dim=1)
return predictions
Copy
	
def compute_accuracy(logits, labels):
predictions = predicted_labels(logits)
correct = (predictions == labels).float()
return correct.mean()
Copy

Vamos a ver se ele calcula bem.

	
compute_accuracy(output['logits'], labels).item()
Copy
	
0.1666666716337204

Otimizadorlink image 107

Como vamos a precisar um otimizador, criamos um

	
from transformers import AdamW
LR = 2e-5
optimizer = AdamW(model.parameters(), lr=LR)
Copy
	
/usr/local/lib/python3.10/dist-packages/transformers/optimization.py:588: 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(

Treinamentolink image 108

Criamos o loop de treinamento

	
from tqdm import tqdm
EPOCHS = 3
accuracy = 0
for epoch in range(EPOCHS):
model.train()
train_loss = 0
progresbar = tqdm(train_loader, total=len(train_loader), desc=f'Epoch {epoch + 1}')
for input_ids, at_mask, labels in progresbar:
input_ids = input_ids.to(device)
at_mask = at_mask.to(device)
label = labels.to(device)
output = model(input_ids=input_ids, attention_mask=at_mask, labels=label)
loss = output['loss']
train_loss += loss.item()
optimizer.zero_grad()
loss.backward()
optimizer.step()
progresbar.set_postfix({'train_loss': loss.item()})
train_loss /= len(train_loader)
progresbar.set_postfix({'train_loss': train_loss})
model.eval()
valid_loss = 0
progresbar = tqdm(validation_loader, total=len(validation_loader), desc=f'Epoch {epoch + 1}')
for input_ids, at_mask, labels in progresbar:
input_ids = input_ids.to(device)
at_mask = at_mask.to(device)
labels = labels.to(device)
output = model(input_ids=input_ids, attention_mask=at_mask, labels=labels)
loss = output['loss']
valid_loss += loss.item()
step_accuracy = compute_accuracy(output['logits'], labels)
accuracy += step_accuracy
progresbar.set_postfix({'valid_loss': loss.item(), 'accuracy': step_accuracy.item()})
valid_loss /= len(validation_loader)
accuracy /= len(validation_loader)
progresbar.set_postfix({'valid_loss': valid_loss, 'accuracy': accuracy})
Copy
	
Epoch 1: 100%|██████████| 16667/16667 [44:13<00:00, 6.28it/s, train_loss=nan]
Epoch 1: 100%|██████████| 417/417 [00:32<00:00, 12.72it/s, valid_loss=nan, accuracy=0]
Epoch 2: 100%|██████████| 16667/16667 [44:06<00:00, 6.30it/s, train_loss=nan]
Epoch 2: 100%|██████████| 417/417 [00:32<00:00, 12.77it/s, valid_loss=nan, accuracy=0]
Epoch 3: 100%|██████████| 16667/16667 [44:03<00:00, 6.30it/s, train_loss=nan]
Epoch 3: 100%|██████████| 417/417 [00:32<00:00, 12.86it/s, valid_loss=nan, accuracy=0]

Uso do modelolink image 109

Vamos a testar o modelo que treinamos

Primeiro tokenizamos um texto

	
input_tokens = tokenize_function({"text": "I love this product. It is amazing."})
input_tokens['input_ids'].shape, input_tokens['attention_mask'].shape
Copy
	
(torch.Size([1, 768]), torch.Size([1, 768]))

Agora passamos isso para o modelo.

	
output = model(input_ids=input_tokens['input_ids'].to(device), attention_mask=input_tokens['attention_mask'].to(device))
output['logits']
Copy
	
tensor([[nan, nan, nan, nan, nan]], device='cuda:0', dtype=torch.float16,
grad_fn=<IndexBackward0>)

Vemos as previsÔes desses logits

	
predicted = predicted_labels(output['logits'])
predicted
Copy
	
tensor([0], device='cuda:0')

Ajuste fino para geração de texto com Pytorchlink image 110

Repetimos o treinamento com Pytorch

Reiniciamos o notebook para nos assegurar

Conjunto de Dadoslink image 111

Voltamos a baixar o conjunto de dados de piadas

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

Criamos um subconjunto caso haja pouca memĂłria

	
percent_of_train_dataset = 1 # If you want 50% of the dataset, set this to 0.5
subset_dataset = jokes["train"].select(range(int(len(jokes["train"]) * percent_of_train_dataset)))
subset_dataset
Copy
	
Dataset({
features: ['ID', 'Joke'],
num_rows: 231657
})

Dividimos o conjunto de dados em subconjuntos de treinamento, validação e teste.

	
percent_of_train_dataset = 0.90
split_dataset = subset_dataset.train_test_split(train_size=int(subset_dataset.num_rows * percent_of_train_dataset), seed=19, shuffle=False)
train_dataset = split_dataset["train"]
validation_test_dataset = split_dataset["test"]
split_dataset = validation_test_dataset.train_test_split(train_size=int(validation_test_dataset.num_rows * 0.5), seed=19, shuffle=False)
validation_dataset = split_dataset["train"]
test_dataset = split_dataset["test"]
print(f"Size of the train set: {len(train_dataset)}. Size of the validation set: {len(validation_dataset)}. Size of the test set: {len(test_dataset)}")
Copy
	
Size of the train set: 208491. Size of the validation set: 11583. Size of the test set: 11583

Tokenizadorlink image 112

Iniciamos o tokenizador e atribuĂ­mos ao token de padding o de end of string

	
from transformers import AutoTokenizer
checkpoints = "openai-community/gpt2"
tokenizer = AutoTokenizer.from_pretrained(checkpoints)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
Copy

Adicionamos os tokens especiais de inĂ­cio e fim de piada

	
new_tokens = ['<SJ>', '<EJ>'] # Start and end of joke tokens
num_added_tokens = tokenizer.add_tokens(new_tokens)
print(f"Added {num_added_tokens} tokens")
Copy
	
Added 2 tokens

Os adicionamos ao dataset

	
joke_column = "Joke"
def format_joke(example):
example[joke_column] = '<SJ> ' + example['Joke'] + ' <EJ>'
return example
remove_columns = [column for column in train_dataset.column_names if column != joke_column]
train_dataset = train_dataset.map(format_joke, remove_columns=remove_columns)
validation_dataset = validation_dataset.map(format_joke, remove_columns=remove_columns)
test_dataset = test_dataset.map(format_joke, remove_columns=remove_columns)
train_dataset, validation_dataset, test_dataset
Copy
	
(Dataset({
features: ['Joke'],
num_rows: 208491
}),
Dataset({
features: ['Joke'],
num_rows: 11583
}),
Dataset({
features: ['Joke'],
num_rows: 11583
}))

Tokenizamos o conjunto de dados

	
def tokenize_function(examples):
return tokenizer(examples[joke_column], padding="max_length", truncation=True, max_length=768, return_tensors="pt")
train_dataset = train_dataset.map(tokenize_function, batched=True, remove_columns=[joke_column])
validation_dataset = validation_dataset.map(tokenize_function, batched=True, remove_columns=[joke_column])
test_dataset = test_dataset.map(tokenize_function, batched=True, remove_columns=[joke_column])
train_dataset, validation_dataset, test_dataset
Copy
	
(Dataset({
features: ['input_ids', 'attention_mask'],
num_rows: 208491
}),
Dataset({
features: ['input_ids', 'attention_mask'],
num_rows: 11583
}),
Dataset({
features: ['input_ids', 'attention_mask'],
num_rows: 11583
}))

Modelolink image 113

Instanciamos o modelo, atribuĂ­mos o token de padding e adicionamos os novos tokens de inĂ­cio de piada e fim de piada

	
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(checkpoints)
model.config.pad_token_id = model.config.eos_token_id
model.resize_token_embeddings(len(tokenizer))
Copy
	
Embedding(50259, 768)

Dispositivolink image 114

Criamos o dispositivo e passamos o modelo para o dispositivo

	
import torch
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.half().to(device)
print()
Copy
	

Conjunto de Dados do Pytorchlink image 115

Criamos um conjunto de dados do PyTorch

	
from torch.utils.data import Dataset
class JokesDataset(Dataset):
def __init__(self, huggingface_dataset):
self.dataset = huggingface_dataset
def __getitem__(self, idx):
input_ids = torch.tensor(self.dataset[idx]['input_ids'])
attention_mask = torch.tensor(self.dataset[idx]['attention_mask'])
return input_ids, attention_mask
def __len__(self):
return len(self.dataset)
Copy

Instanciamos os conjuntos de dados de treinamento, validação e teste

	
train_pytorch_dataset = JokesDataset(train_dataset)
validation_pytorch_dataset = JokesDataset(validation_dataset)
test_pytorch_dataset = JokesDataset(test_dataset)
Copy

Vamos ver um exemplo.

	
input_ids, attention_mask = train_pytorch_dataset[0]
input_ids.shape, attention_mask.shape
Copy
	
(torch.Size([768]), torch.Size([768]))

Pytorch Dataloaderlink image 116

Criamos os dataloaders

	
from torch.utils.data import DataLoader
BS = 28
train_loader = DataLoader(train_pytorch_dataset, batch_size=BS, shuffle=True)
validation_loader = DataLoader(validation_pytorch_dataset, batch_size=BS)
test_loader = DataLoader(test_pytorch_dataset, batch_size=BS)
Copy

Vemos uma amostra

	
input_ids, attention_mask = next(iter(train_loader))
input_ids.shape, attention_mask.shape
Copy
	
(torch.Size([28, 768]), torch.Size([28, 768]))

Passamos isso para o modelo.

	
output = model(input_ids.to(device), attention_mask=attention_mask.to(device))
output.keys()
Copy
	
odict_keys(['logits', 'past_key_values'])

Como podemos ver, nĂŁo temos um valor de loss. Como jĂĄ vimos, precisamos passar o input_ids e o labels.

	
output = model(input_ids.to(device), attention_mask=attention_mask.to(device), labels=input_ids.to(device))
output.keys()
Copy
	
odict_keys(['loss', 'logits', 'past_key_values'])

Agora sim temos loss

	
output['loss'].item()
Copy
	
80.5625

Otimizadorlink image 117

Criamos um otimizador

	
from transformers import AdamW
LR = 2e-5
optimizer = AdamW(model.parameters(), lr=5e-5)
Copy
	
/usr/local/lib/python3.10/dist-packages/transformers/optimization.py:588: 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(

Treinamentolink image 118

Criamos o loop de treinamento

	
from tqdm import tqdm
EPOCHS = 3
for epoch in range(EPOCHS):
model.train()
train_loss = 0
progresbar = tqdm(train_loader, total=len(train_loader), desc=f'Epoch {epoch + 1}')
for input_ids, at_mask in progresbar:
input_ids = input_ids.to(device)
at_mask = at_mask.to(device)
output = model(input_ids=input_ids, attention_mask=at_mask, labels=input_ids)
loss = output['loss']
train_loss += loss.item()
optimizer.zero_grad()
loss.backward()
optimizer.step()
progresbar.set_postfix({'train_loss': loss.item()})
train_loss /= len(train_loader)
progresbar.set_postfix({'train_loss': train_loss})
Copy
	
Epoch 1: 100%|██████████| 7447/7447 [51:07<00:00, 2.43it/s, train_loss=nan]
Epoch 2: 100%|██████████| 7447/7447 [51:06<00:00, 2.43it/s, train_loss=nan]
Epoch 3: 100%|██████████| 7447/7447 [51:07<00:00, 2.43it/s, train_loss=nan]

Uso do modelolink image 119

Testamos o modelo

	
def generate_text(decoded_joke, max_new_tokens=100, stop_token='<EJ>', top_k=0, temperature=1.0):
input_tokens = tokenize_function({'Joke': decoded_joke})
output = model(input_tokens['input_ids'].to(device), attention_mask=input_tokens['attention_mask'].to(device))
nex_token = torch.argmax(output['logits'][:, -1, :], dim=-1).item()
nex_token_decoded = tokenizer.decode(nex_token)
decoded_joke = decoded_joke + nex_token_decoded
for _ in range(max_new_tokens):
nex_token = torch.argmax(output['logits'][:, -1, :], dim=-1).item()
nex_token_decoded = tokenizer.decode(nex_token)
if nex_token_decoded == stop_token:
break
decoded_joke = decoded_joke + nex_token_decoded
input_tokens = tokenize_function({'Joke': decoded_joke})
output = model(input_tokens['input_ids'].to(device), attention_mask=input_tokens['attention_mask'].to(device))
return decoded_joke
Copy
	
generated_text = generate_text("<SJ> Why didn't the frog cross the road")
generated_text
Copy
	
"<SJ> Why didn't the frog cross the road!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"

Continuar lendo

MCP: Guia Completa para Criar Servidores e Clientes MCP (Model Context Protocol) com FastMCP

MCP: Guia Completa para Criar Servidores e Clientes MCP (Model Context Protocol) com FastMCP

Aprenda o que Ă© o Model Context Protocol (MCP), o padrĂŁo de cĂłdigo aberto desenvolvido pela Anthropic que revoluciona como os modelos de IA interagem com ferramentas externas. Nesta guia prĂĄtica e detalhada, eu te levo passo a passo na criação de um servidor e cliente MCP do zero usando a biblioteca fastmcp. VocĂȘ construirĂĄ um agente de IA "inteligente" com Claude Sonnet, capaz de interagir com a API do GitHub para consultar issues e informaçÔes de repositĂłrios. Vamos cobrir desde conceitos bĂĄsicos atĂ© recursos avançados como filtragem de ferramentas por tags, composição de servidores, recursos estĂĄticos e plantillas dinĂąmicas (resource templates), geração de prompts e autenticação segura. Descubra como MCP pode padronizar e simplificar a integração de ferramentas em suas aplicaçÔes de IA, de forma anĂĄloga ao como o USB unificou perifĂ©ricos!

Últimos posts -->

VocĂȘ viu esses projetos?

Horeca chatbot

Horeca chatbot Horeca chatbot
Python
LangChain
PostgreSQL
PGVector
React
Kubernetes
Docker
GitHub Actions

Chatbot conversacional para cozinheiros de hotéis e restaurantes. Um cozinheiro, gerente de cozinha ou serviço de quarto de um hotel ou restaurante pode falar com o chatbot para obter informaçÔes sobre receitas e menus. Mas também implementa agentes, com os quais pode editar ou criar novas receitas ou menus

Naviground

Naviground Naviground
Python
Pytorch
TensorRT
YOLO
Docker
Azure

Sistema de percepção para veículos autÎnomos

Subtify

Subtify Subtify
Python
Whisper
Spaces

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.

Flow edit

Flow edit Flow edit

Edite imagens com este modelo de Flow. Baseado em SD3 ou FLUX, vocĂȘ pode editar qualquer imagem e gerar novas

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 -->