diff --git a/src/AI/AI-llm-architecture/0.-basic-llm-concepts.md b/src/AI/AI-llm-architecture/0.-basic-llm-concepts.md
index c9e6b50ee..9bb14600a 100644
--- a/src/AI/AI-llm-architecture/0.-basic-llm-concepts.md
+++ b/src/AI/AI-llm-architecture/0.-basic-llm-concepts.md
@@ -201,14 +201,14 @@ Gradient w.r.t b: tensor([-0.0817])
Em redes neurais maiores com múltiplas camadas, o processo de computação de gradientes se torna mais complexo devido ao aumento do número de parâmetros e operações. No entanto, os princípios fundamentais permanecem os mesmos:
-- **Passo Direto:** Calcule a saída da rede passando as entradas por cada camada.
+- **Passagem Direta:** Calcule a saída da rede passando as entradas por cada camada.
- **Calcule a Perda:** Avalie a função de perda usando a saída da rede e os rótulos alvo.
-- **Passo Reverso (Retropropagação):** Calcule os gradientes da perda em relação a cada parâmetro na rede aplicando a regra da cadeia recursivamente da camada de saída de volta para a camada de entrada.
+- **Passagem Reversa (Retropropagação):** Calcule os gradientes da perda em relação a cada parâmetro na rede aplicando a regra da cadeia recursivamente da camada de saída de volta para a camada de entrada.
### **2. Algoritmo de Retropropagação**
- **Passo 1:** Inicialize os parâmetros da rede (pesos e viéses).
-- **Passo 2:** Para cada exemplo de treinamento, realize um passo direto para calcular as saídas.
+- **Passo 2:** Para cada exemplo de treinamento, realize uma passagem direta para calcular as saídas.
- **Passo 3:** Calcule a perda.
- **Passo 4:** Calcule os gradientes da perda em relação a cada parâmetro usando a regra da cadeia.
- **Passo 5:** Atualize os parâmetros usando um algoritmo de otimização (por exemplo, descida de gradiente).
@@ -274,9 +274,9 @@ Neste código:
Durante o backward pass:
-- PyTorch percorre o grafo computacional em ordem reversa.
+- O PyTorch percorre o grafo computacional em ordem reversa.
- Para cada operação, aplica a regra da cadeia para computar gradientes.
-- Gradientes são acumulados no atributo `.grad` de cada tensor de parâmetro.
+- Os gradientes são acumulados no atributo `.grad` de cada tensor de parâmetro.
### **6. Vantagens da Diferenciação Automática**
diff --git a/src/AI/AI-llm-architecture/1.-tokenizing.md b/src/AI/AI-llm-architecture/1.-tokenizing.md
index aee83539f..96fcf3428 100644
--- a/src/AI/AI-llm-architecture/1.-tokenizing.md
+++ b/src/AI/AI-llm-architecture/1.-tokenizing.md
@@ -19,7 +19,7 @@ Tokens: `["Olá", ",", "mundo", "!"]`
- **Tokens Especiais:** Estes são símbolos especiais adicionados ao vocabulário para lidar com vários cenários:
- `[BOS]` (Início da Sequência): Indica o início de um texto.
- `[EOS]` (Fim da Sequência): Indica o fim de um texto.
-- `[PAD]` (Preenchimento): Usado para fazer todas as sequências em um lote terem o mesmo comprimento.
+- `[PAD]` (Preenchimento): Usado para fazer com que todas as sequências em um lote tenham o mesmo comprimento.
- `[UNK]` (Desconhecido): Representa tokens que não estão no vocabulário.
- _Exemplo:_\
Se `"Olá"` é atribuído ao ID `64`, `","` é `455`, `"mundo"` é `78`, e `"!"` é `467`, então:\
@@ -55,7 +55,7 @@ Enquanto o tokenizador básico funciona bem para textos simples, ele tem limita
- Equilibra entre ter um tamanho de vocabulário gerenciável e representar palavras de forma eficaz.
- Lida eficientemente com palavras raras e compostas.
- _Exemplo:_\
-`"infelicidade"` pode ser tokenizado como `["in", "felicidade"]` ou `["in", "feliz", "dade"]` dependendo do vocabulário.
+`"infelicidade"` pode ser tokenizado como `["in", "felicidade"]` ou `["in", "feliz", "dade"]`, dependendo do vocabulário.
3. **Modelo de Linguagem Unigram:**
- **Usado Por:** Modelos como SentencePiece.
- **Propósito:** Usa um modelo probabilístico para determinar o conjunto mais provável de tokens de subpalavras.
diff --git a/src/AI/AI-llm-architecture/2.-data-sampling.md b/src/AI/AI-llm-architecture/2.-data-sampling.md
new file mode 100644
index 000000000..faf47dfbf
--- /dev/null
+++ b/src/AI/AI-llm-architecture/2.-data-sampling.md
@@ -0,0 +1,233 @@
+# 2. Amostragem de Dados
+
+## **Amostragem de Dados**
+
+**Amostragem de Dados** é um processo crucial na preparação de dados para treinar grandes modelos de linguagem (LLMs) como o GPT. Envolve organizar dados textuais em sequências de entrada e alvo que o modelo usa para aprender a prever a próxima palavra (ou token) com base nas palavras anteriores. A amostragem de dados adequada garante que o modelo capture efetivamente padrões e dependências da linguagem.
+
+> [!TIP]
+> O objetivo desta segunda fase é muito simples: **Amostrar os dados de entrada e prepará-los para a fase de treinamento, geralmente separando o conjunto de dados em frases de um comprimento específico e gerando também a resposta esperada.**
+
+### **Por que a Amostragem de Dados é Importante**
+
+LLMs como o GPT são treinados para gerar ou prever texto entendendo o contexto fornecido pelas palavras anteriores. Para alcançar isso, os dados de treinamento devem ser estruturados de uma maneira que o modelo possa aprender a relação entre sequências de palavras e suas palavras subsequentes. Essa abordagem estruturada permite que o modelo generalize e gere texto coerente e contextualmente relevante.
+
+### **Conceitos Chave na Amostragem de Dados**
+
+1. **Tokenização:** Dividir o texto em unidades menores chamadas tokens (por exemplo, palavras, subpalavras ou caracteres).
+2. **Comprimento da Sequência (max_length):** O número de tokens em cada sequência de entrada.
+3. **Janela Deslizante:** Um método para criar sequências de entrada sobrepostas movendo uma janela sobre o texto tokenizado.
+4. **Stride:** O número de tokens que a janela deslizante avança para criar a próxima sequência.
+
+### **Exemplo Passo a Passo**
+
+Vamos percorrer um exemplo para ilustrar a amostragem de dados.
+
+**Texto de Exemplo**
+```arduino
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+```
+**Tokenização**
+
+Assuma que usamos um **tokenizador básico** que divide o texto em palavras e sinais de pontuação:
+```vbnet
+Tokens: ["Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit."]
+```
+**Parâmetros**
+
+- **Comprimento Máximo da Sequência (max_length):** 4 tokens
+- **Passo da Janela Deslizante:** 1 token
+
+**Criando Sequências de Entrada e Alvo**
+
+1. **Abordagem da Janela Deslizante:**
+- **Sequências de Entrada:** Cada sequência de entrada consiste em `max_length` tokens.
+- **Sequências de Alvo:** Cada sequência de alvo consiste nos tokens que imediatamente seguem a sequência de entrada correspondente.
+2. **Gerando Sequências:**
+
+
+
+**Janela Deslizante com Passo 1:**
+
+- **Primeira Janela (Posições 1-4):** \["Lorem", "ipsum", "dolor", "sit"] → **Alvo:** \["ipsum", "dolor", "sit", "amet,"]
+- **Segunda Janela (Posições 2-5):** \["ipsum", "dolor", "sit", "amet,"] → **Alvo:** \["dolor", "sit", "amet,", "consectetur"]
+- **Terceira Janela (Posições 3-6):** \["dolor", "sit", "amet,", "consectetur"] → **Alvo:** \["sit", "amet,", "consectetur", "adipiscing"]
+- **Quarta Janela (Posições 4-7):** \["sit", "amet,", "consectetur", "adipiscing"] → **Alvo:** \["amet,", "consectetur", "adipiscing", "elit."]
+
+**Entendendo o Passo**
+
+- **Passo de 1:** A janela se move para frente um token a cada vez, resultando em sequências altamente sobrepostas. Isso pode levar a uma melhor aprendizagem das relações contextuais, mas pode aumentar o risco de overfitting, uma vez que pontos de dados semelhantes são repetidos.
+- **Passo de 2:** A janela se move para frente dois tokens a cada vez, reduzindo a sobreposição. Isso diminui a redundância e a carga computacional, mas pode perder algumas nuances contextuais.
+- **Passo Igual a max_length:** A janela se move para frente pelo tamanho total da janela, resultando em sequências não sobrepostas. Isso minimiza a redundância de dados, mas pode limitar a capacidade do modelo de aprender dependências entre sequências.
+
+**Exemplo com Passo de 2:**
+
+Usando o mesmo texto tokenizado e `max_length` de 4:
+
+- **Primeira Janela (Posições 1-4):** \["Lorem", "ipsum", "dolor", "sit"] → **Alvo:** \["ipsum", "dolor", "sit", "amet,"]
+- **Segunda Janela (Posições 3-6):** \["dolor", "sit", "amet,", "consectetur"] → **Alvo:** \["sit", "amet,", "consectetur", "adipiscing"]
+- **Terceira Janela (Posições 5-8):** \["amet,", "consectetur", "adipiscing", "elit."] → **Alvo:** \["consectetur", "adipiscing", "elit.", "sed"] _(Assumindo continuação)_
+
+## Exemplo de Código
+
+Vamos entender isso melhor a partir de um exemplo de código de [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb):
+```python
+# Download the text to pre-train the LLM
+import urllib.request
+url = ("https://raw.githubusercontent.com/rasbt/LLMs-from-scratch/main/ch02/01_main-chapter-code/the-verdict.txt")
+file_path = "the-verdict.txt"
+urllib.request.urlretrieve(url, file_path)
+
+with open("the-verdict.txt", "r", encoding="utf-8") as f:
+raw_text = f.read()
+
+"""
+Create a class that will receive some params lie tokenizer and text
+and will prepare the input chunks and the target chunks to prepare
+the LLM to learn which next token to generate
+"""
+import torch
+from torch.utils.data import Dataset, DataLoader
+
+class GPTDatasetV1(Dataset):
+def __init__(self, txt, tokenizer, max_length, stride):
+self.input_ids = []
+self.target_ids = []
+
+# Tokenize the entire text
+token_ids = tokenizer.encode(txt, allowed_special={"<|endoftext|>"})
+
+# Use a sliding window to chunk the book into overlapping sequences of max_length
+for i in range(0, len(token_ids) - max_length, stride):
+input_chunk = token_ids[i:i + max_length]
+target_chunk = token_ids[i + 1: i + max_length + 1]
+self.input_ids.append(torch.tensor(input_chunk))
+self.target_ids.append(torch.tensor(target_chunk))
+
+def __len__(self):
+return len(self.input_ids)
+
+def __getitem__(self, idx):
+return self.input_ids[idx], self.target_ids[idx]
+
+
+"""
+Create a data loader which given the text and some params will
+prepare the inputs and targets with the previous class and
+then create a torch DataLoader with the info
+"""
+
+import tiktoken
+
+def create_dataloader_v1(txt, batch_size=4, max_length=256,
+stride=128, shuffle=True, drop_last=True,
+num_workers=0):
+
+# Initialize the tokenizer
+tokenizer = tiktoken.get_encoding("gpt2")
+
+# Create dataset
+dataset = GPTDatasetV1(txt, tokenizer, max_length, stride)
+
+# Create dataloader
+dataloader = DataLoader(
+dataset,
+batch_size=batch_size,
+shuffle=shuffle,
+drop_last=drop_last,
+num_workers=num_workers
+)
+
+return dataloader
+
+
+"""
+Finally, create the data loader with the params we want:
+- The used text for training
+- batch_size: The size of each batch
+- max_length: The size of each entry on each batch
+- stride: The sliding window (how many tokens should the next entry advance compared to the previous one). The smaller the more overfitting, usually this is equals to the max_length so the same tokens aren't repeated.
+- shuffle: Re-order randomly
+"""
+dataloader = create_dataloader_v1(
+raw_text, batch_size=8, max_length=4, stride=1, shuffle=False
+)
+
+data_iter = iter(dataloader)
+first_batch = next(data_iter)
+print(first_batch)
+
+# Note the batch_size of 8, the max_length of 4 and the stride of 1
+[
+# Input
+tensor([[ 40, 367, 2885, 1464],
+[ 367, 2885, 1464, 1807],
+[ 2885, 1464, 1807, 3619],
+[ 1464, 1807, 3619, 402],
+[ 1807, 3619, 402, 271],
+[ 3619, 402, 271, 10899],
+[ 402, 271, 10899, 2138],
+[ 271, 10899, 2138, 257]]),
+# Target
+tensor([[ 367, 2885, 1464, 1807],
+[ 2885, 1464, 1807, 3619],
+[ 1464, 1807, 3619, 402],
+[ 1807, 3619, 402, 271],
+[ 3619, 402, 271, 10899],
+[ 402, 271, 10899, 2138],
+[ 271, 10899, 2138, 257],
+[10899, 2138, 257, 7026]])
+]
+
+# With stride=4 this will be the result:
+[
+# Input
+tensor([[ 40, 367, 2885, 1464],
+[ 1807, 3619, 402, 271],
+[10899, 2138, 257, 7026],
+[15632, 438, 2016, 257],
+[ 922, 5891, 1576, 438],
+[ 568, 340, 373, 645],
+[ 1049, 5975, 284, 502],
+[ 284, 3285, 326, 11]]),
+# Target
+tensor([[ 367, 2885, 1464, 1807],
+[ 3619, 402, 271, 10899],
+[ 2138, 257, 7026, 15632],
+[ 438, 2016, 257, 922],
+[ 5891, 1576, 438, 568],
+[ 340, 373, 645, 1049],
+[ 5975, 284, 502, 284],
+[ 3285, 326, 11, 287]])
+]
+```
+## Referências
+
+- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)
diff --git a/src/AI/AI-llm-architecture/4.-attention-mechanisms.md b/src/AI/AI-llm-architecture/4.-attention-mechanisms.md
index e525685d1..df2d46550 100644
--- a/src/AI/AI-llm-architecture/4.-attention-mechanisms.md
+++ b/src/AI/AI-llm-architecture/4.-attention-mechanisms.md
@@ -10,15 +10,15 @@ Os mecanismos de atenção permitem que redes neurais **focalizem partes especí
### Entendendo os Mecanismos de Atenção
-Em modelos tradicionais de sequência para sequência usados para tradução de linguagem, o modelo codifica uma sequência de entrada em um vetor de contexto de tamanho fixo. No entanto, essa abordagem tem dificuldades com frases longas porque o vetor de contexto de tamanho fixo pode não capturar todas as informações necessárias. Os mecanismos de atenção abordam essa limitação permitindo que o modelo considere todos os tokens de entrada ao gerar cada token de saída.
+Em modelos tradicionais de sequência para sequência usados para tradução de idiomas, o modelo codifica uma sequência de entrada em um vetor de contexto de tamanho fixo. No entanto, essa abordagem tem dificuldades com frases longas porque o vetor de contexto de tamanho fixo pode não capturar todas as informações necessárias. Os mecanismos de atenção abordam essa limitação permitindo que o modelo considere todos os tokens de entrada ao gerar cada token de saída.
#### Exemplo: Tradução Automática
-Considere traduzir a frase em alemão "Kannst du mir helfen diesen Satz zu übersetzen" para o inglês. Uma tradução palavra por palavra não produziria uma frase em inglês gramaticalmente correta devido a diferenças nas estruturas gramaticais entre as línguas. Um mecanismo de atenção permite que o modelo se concentre nas partes relevantes da frase de entrada ao gerar cada palavra da frase de saída, levando a uma tradução mais precisa e coerente.
+Considere traduzir a frase em alemão "Kannst du mir helfen diesen Satz zu übersetzen" para o inglês. Uma tradução palavra por palavra não produziria uma frase em inglês gramaticalmente correta devido a diferenças nas estruturas gramaticais entre os idiomas. Um mecanismo de atenção permite que o modelo se concentre nas partes relevantes da frase de entrada ao gerar cada palavra da frase de saída, levando a uma tradução mais precisa e coerente.
### Introdução à Auto-Atenção
-A auto-atensão, ou intra-atensão, é um mecanismo onde a atenção é aplicada dentro de uma única sequência para calcular uma representação dessa sequência. Isso permite que cada token na sequência atenda a todos os outros tokens, ajudando o modelo a capturar dependências entre tokens, independentemente da distância entre eles na sequência.
+Auto-atenção, ou intra-atenção, é um mecanismo onde a atenção é aplicada dentro de uma única sequência para computar uma representação dessa sequência. Isso permite que cada token na sequência atenda a todos os outros tokens, ajudando o modelo a capturar dependências entre tokens, independentemente da distância entre eles na sequência.
#### Conceitos Chave
@@ -34,7 +34,7 @@ Vamos considerar a frase **"Hello shiny sun!"** e representar cada palavra com u
- **shiny**: `[0.53, 0.34, 0.98]`
- **sun**: `[0.29, 0.54, 0.93]`
-Nosso objetivo é calcular o **vetor de contexto** para a palavra **"shiny"** usando auto-atensão.
+Nosso objetivo é computar o **vetor de contexto** para a palavra **"shiny"** usando auto-atenção.
#### Passo 1: Calcular Pontuações de Atenção
@@ -68,7 +68,7 @@ Aplique a **função softmax** às pontuações de atenção para convertê-las
Calculando os exponenciais:
-
+
Calculando a soma:
@@ -78,12 +78,12 @@ Calculando pesos de atenção:
-#### Passo 3: Calcular o Vetor de Contexto
+#### Passo 3: Computar o Vetor de Contexto
> [!TIP]
> Basta pegar cada peso de atenção e multiplicá-lo pelas dimensões do token relacionado e, em seguida, somar todas as dimensões para obter apenas 1 vetor (o vetor de contexto)
-O **vetor de contexto** é calculado como a soma ponderada dos embeddings de todas as palavras, usando os pesos de atenção.
+O **vetor de contexto** é computado como a soma ponderada dos embeddings de todas as palavras, usando os pesos de atenção.
@@ -103,7 +103,7 @@ Calculando cada componente:
Somando os embeddings ponderados:
-`context vector=[0.0779+0.2156+0.1057, 0.0504+0.1382+0.1972, 0.1237+0.3983+0.3390]=[0.3992,0.3858,0.8610]`
+`vetor de contexto=[0.0779+0.2156+0.1057, 0.0504+0.1382+0.1972, 0.1237+0.3983+0.3390]=[0.3992,0.3858,0.8610]`
**Este vetor de contexto representa o embedding enriquecido para a palavra "shiny", incorporando informações de todas as palavras na frase.**
@@ -111,11 +111,11 @@ Somando os embeddings ponderados:
1. **Calcular Pontuações de Atenção**: Use o produto escalar entre o embedding da palavra-alvo e os embeddings de todas as palavras na sequência.
2. **Normalizar Pontuações para Obter Pesos de Atenção**: Aplique a função softmax às pontuações de atenção para obter pesos que somem 1.
-3. **Calcular o Vetor de Contexto**: Multiplique o embedding de cada palavra pelo seu peso de atenção e some os resultados.
+3. **Computar Vetor de Contexto**: Multiplique o embedding de cada palavra pelo seu peso de atenção e some os resultados.
## Auto-Atenção com Pesos Treináveis
-Na prática, os mecanismos de auto-atensão usam **pesos treináveis** para aprender as melhores representações para consultas, chaves e valores. Isso envolve a introdução de três matrizes de peso:
+Na prática, os mecanismos de auto-atenção usam **pesos treináveis** para aprender as melhores representações para consultas, chaves e valores. Isso envolve a introdução de três matrizes de peso:
@@ -127,7 +127,7 @@ Cada token terá sua própria matriz de consulta, chave e valor multiplicando se
-Essas matrizes transformam os embeddings originais em um novo espaço adequado para calcular a atenção.
+Essas matrizes transformam os embeddings originais em um novo espaço adequado para computar a atenção.
**Exemplo**
@@ -404,7 +404,7 @@ print(context_vecs)
print("context_vecs.shape:", context_vecs.shape)
```
-Para uma implementação compacta e eficiente, você pode usar a [`torch.nn.MultiheadAttention`](https://pytorch.org/docs/stable/generated/torch.nn.MultiheadAttention.html) classe no PyTorch.
+Para uma implementação compacta e eficiente, você pode usar a classe [`torch.nn.MultiheadAttention`](https://pytorch.org/docs/stable/generated/torch.nn.MultiheadAttention.html) no PyTorch.
> [!TIP]
> Resposta curta do ChatGPT sobre por que é melhor dividir as dimensões dos tokens entre as cabeças em vez de fazer com que cada cabeça verifique todas as dimensões de todos os tokens:
diff --git a/src/AI/AI-llm-architecture/5.-llm-architecture.md b/src/AI/AI-llm-architecture/5.-llm-architecture.md
index 398229d1e..4aee84a9c 100644
--- a/src/AI/AI-llm-architecture/5.-llm-architecture.md
+++ b/src/AI/AI-llm-architecture/5.-llm-architecture.md
@@ -221,7 +221,7 @@ torch.sqrt(torch.tensor(2.0 / torch.pi)) *
### **Rede Neural FeedForward**
-_Formas foram adicionadas como comentários para entender melhor as formas das matrizes:_
+_As formas foram adicionadas como comentários para entender melhor as formas das matrizes:_
```python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class FeedForward(nn.Module):
@@ -306,7 +306,7 @@ return self.scale * norm_x + self.shift
### **Bloco Transformer**
-_Shapes foram adicionadas como comentários para entender melhor as formas das matrizes:_
+_Formas foram adicionadas como comentários para entender melhor as formas das matrizes:_
```python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
@@ -608,7 +608,7 @@ total_params = 163,009,536
```
## Gerar Texto
-Tendo um modelo que prevê o próximo token como o anterior, é necessário apenas pegar os últimos valores de token da saída (já que eles serão os do token previsto), que será um **valor por entrada no vocabulário** e então usar a função `softmax` para normalizar as dimensões em probabilidades que somam 1 e, em seguida, obter o índice da maior entrada, que será o índice da palavra dentro do vocabulário.
+Tendo um modelo que prevê o próximo token como o anterior, é necessário apenas pegar os últimos valores de token da saída (já que serão os do token previsto), que será um **valor por entrada no vocabulário** e então usar a função `softmax` para normalizar as dimensões em probabilidades que somam 1 e, em seguida, obter o índice da maior entrada, que será o índice da palavra dentro do vocabulário.
Código de [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb):
```python
diff --git a/src/AI/AI-llm-architecture/6.-pre-training-and-loading-models.md b/src/AI/AI-llm-architecture/6.-pre-training-and-loading-models.md
new file mode 100644
index 000000000..f868d30a1
--- /dev/null
+++ b/src/AI/AI-llm-architecture/6.-pre-training-and-loading-models.md
@@ -0,0 +1,943 @@
+# 6. Pré-treinamento e Carregamento de Modelos
+
+## Geração de Texto
+
+Para treinar um modelo, precisamos que esse modelo seja capaz de gerar novos tokens. Em seguida, compararemos os tokens gerados com os esperados para treinar o modelo a **aprender os tokens que precisa gerar**.
+
+Como nos exemplos anteriores, já previmos alguns tokens, é possível reutilizar essa função para esse propósito.
+
+> [!TIP]
+> O objetivo desta sexta fase é muito simples: **Treinar o modelo do zero**. Para isso, a arquitetura LLM anterior será utilizada com alguns loops sobre os conjuntos de dados usando as funções de perda e otimizador definidos para treinar todos os parâmetros do modelo.
+
+## Avaliação de Texto
+
+Para realizar um treinamento correto, é necessário medir as previsões obtidas para o token esperado. O objetivo do treinamento é maximizar a probabilidade do token correto, o que envolve aumentar sua probabilidade em relação a outros tokens.
+
+Para maximizar a probabilidade do token correto, os pesos do modelo devem ser modificados para que essa probabilidade seja maximizada. As atualizações dos pesos são feitas via **backpropagation**. Isso requer uma **função de perda a ser maximizada**. Neste caso, a função será a **diferença entre a previsão realizada e a desejada**.
+
+No entanto, em vez de trabalhar com as previsões brutas, trabalhará com um logaritmo de base n. Assim, se a previsão atual do token esperado for 7.4541e-05, o logaritmo natural (base *e*) de **7.4541e-05** é aproximadamente **-9.5042**.\
+Então, para cada entrada com um comprimento de contexto de 5 tokens, por exemplo, o modelo precisará prever 5 tokens, sendo os primeiros 4 tokens o último da entrada e o quinto o previsto. Portanto, para cada entrada teremos 5 previsões nesse caso (mesmo que os primeiros 4 estivessem na entrada, o modelo não sabe disso) com 5 tokens esperados e, portanto, 5 probabilidades a serem maximizadas.
+
+Portanto, após realizar o logaritmo natural em cada previsão, a **média** é calculada, o **sinal de menos removido** (isso é chamado de _cross entropy loss_) e esse é o **número a ser reduzido o mais próximo possível de 0** porque o logaritmo natural de 1 é 0:
+
+
+
+Outra maneira de medir quão bom é o modelo é chamada de perplexidade. **Perplexidade** é uma métrica usada para avaliar quão bem um modelo de probabilidade prevê uma amostra. Na modelagem de linguagem, representa a **incerteza do modelo** ao prever o próximo token em uma sequência.\
+Por exemplo, um valor de perplexidade de 48725 significa que, ao precisar prever um token, ele não tem certeza sobre qual entre 48.725 tokens no vocabulário é o correto.
+
+## Exemplo de Pré-Treinamento
+
+Este é o código inicial proposto em [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/01_main-chapter-code/ch05.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/01_main-chapter-code/ch05.ipynb) algumas vezes ligeiramente modificado
+
+
+
+Código anterior usado aqui, mas já explicado em seções anteriores
+```python
+"""
+This is code explained before so it won't be exaplained
+"""
+
+import tiktoken
+import torch
+import torch.nn as nn
+from torch.utils.data import Dataset, DataLoader
+
+
+class GPTDatasetV1(Dataset):
+def __init__(self, txt, tokenizer, max_length, stride):
+self.input_ids = []
+self.target_ids = []
+
+# Tokenize the entire text
+token_ids = tokenizer.encode(txt, allowed_special={"<|endoftext|>"})
+
+# Use a sliding window to chunk the book into overlapping sequences of max_length
+for i in range(0, len(token_ids) - max_length, stride):
+input_chunk = token_ids[i:i + max_length]
+target_chunk = token_ids[i + 1: i + max_length + 1]
+self.input_ids.append(torch.tensor(input_chunk))
+self.target_ids.append(torch.tensor(target_chunk))
+
+def __len__(self):
+return len(self.input_ids)
+
+def __getitem__(self, idx):
+return self.input_ids[idx], self.target_ids[idx]
+
+
+def create_dataloader_v1(txt, batch_size=4, max_length=256,
+stride=128, shuffle=True, drop_last=True, num_workers=0):
+# Initialize the tokenizer
+tokenizer = tiktoken.get_encoding("gpt2")
+
+# Create dataset
+dataset = GPTDatasetV1(txt, tokenizer, max_length, stride)
+
+# Create dataloader
+dataloader = DataLoader(
+dataset, batch_size=batch_size, shuffle=shuffle, drop_last=drop_last, num_workers=num_workers)
+
+return dataloader
+
+
+class MultiHeadAttention(nn.Module):
+def __init__(self, d_in, d_out, context_length, dropout, num_heads, qkv_bias=False):
+super().__init__()
+assert d_out % num_heads == 0, "d_out must be divisible by n_heads"
+
+self.d_out = d_out
+self.num_heads = num_heads
+self.head_dim = d_out // num_heads # Reduce the projection dim to match desired output dim
+
+self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
+self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
+self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)
+self.out_proj = nn.Linear(d_out, d_out) # Linear layer to combine head outputs
+self.dropout = nn.Dropout(dropout)
+self.register_buffer('mask', torch.triu(torch.ones(context_length, context_length), diagonal=1))
+
+def forward(self, x):
+b, num_tokens, d_in = x.shape
+
+keys = self.W_key(x) # Shape: (b, num_tokens, d_out)
+queries = self.W_query(x)
+values = self.W_value(x)
+
+# We implicitly split the matrix by adding a `num_heads` dimension
+# Unroll last dim: (b, num_tokens, d_out) -> (b, num_tokens, num_heads, head_dim)
+keys = keys.view(b, num_tokens, self.num_heads, self.head_dim)
+values = values.view(b, num_tokens, self.num_heads, self.head_dim)
+queries = queries.view(b, num_tokens, self.num_heads, self.head_dim)
+
+# Transpose: (b, num_tokens, num_heads, head_dim) -> (b, num_heads, num_tokens, head_dim)
+keys = keys.transpose(1, 2)
+queries = queries.transpose(1, 2)
+values = values.transpose(1, 2)
+
+# Compute scaled dot-product attention (aka self-attention) with a causal mask
+attn_scores = queries @ keys.transpose(2, 3) # Dot product for each head
+
+# Original mask truncated to the number of tokens and converted to boolean
+mask_bool = self.mask.bool()[:num_tokens, :num_tokens]
+
+# Use the mask to fill attention scores
+attn_scores.masked_fill_(mask_bool, -torch.inf)
+
+attn_weights = torch.softmax(attn_scores / keys.shape[-1]**0.5, dim=-1)
+attn_weights = self.dropout(attn_weights)
+
+# Shape: (b, num_tokens, num_heads, head_dim)
+context_vec = (attn_weights @ values).transpose(1, 2)
+
+# Combine heads, where self.d_out = self.num_heads * self.head_dim
+context_vec = context_vec.reshape(b, num_tokens, self.d_out)
+context_vec = self.out_proj(context_vec) # optional projection
+
+return context_vec
+
+
+class LayerNorm(nn.Module):
+def __init__(self, emb_dim):
+super().__init__()
+self.eps = 1e-5
+self.scale = nn.Parameter(torch.ones(emb_dim))
+self.shift = nn.Parameter(torch.zeros(emb_dim))
+
+def forward(self, x):
+mean = x.mean(dim=-1, keepdim=True)
+var = x.var(dim=-1, keepdim=True, unbiased=False)
+norm_x = (x - mean) / torch.sqrt(var + self.eps)
+return self.scale * norm_x + self.shift
+
+
+class GELU(nn.Module):
+def __init__(self):
+super().__init__()
+
+def forward(self, x):
+return 0.5 * x * (1 + torch.tanh(
+torch.sqrt(torch.tensor(2.0 / torch.pi)) *
+(x + 0.044715 * torch.pow(x, 3))
+))
+
+
+class FeedForward(nn.Module):
+def __init__(self, cfg):
+super().__init__()
+self.layers = nn.Sequential(
+nn.Linear(cfg["emb_dim"], 4 * cfg["emb_dim"]),
+GELU(),
+nn.Linear(4 * cfg["emb_dim"], cfg["emb_dim"]),
+)
+
+def forward(self, x):
+return self.layers(x)
+
+
+class TransformerBlock(nn.Module):
+def __init__(self, cfg):
+super().__init__()
+self.att = MultiHeadAttention(
+d_in=cfg["emb_dim"],
+d_out=cfg["emb_dim"],
+context_length=cfg["context_length"],
+num_heads=cfg["n_heads"],
+dropout=cfg["drop_rate"],
+qkv_bias=cfg["qkv_bias"])
+self.ff = FeedForward(cfg)
+self.norm1 = LayerNorm(cfg["emb_dim"])
+self.norm2 = LayerNorm(cfg["emb_dim"])
+self.drop_shortcut = nn.Dropout(cfg["drop_rate"])
+
+def forward(self, x):
+# Shortcut connection for attention block
+shortcut = x
+x = self.norm1(x)
+x = self.att(x) # Shape [batch_size, num_tokens, emb_size]
+x = self.drop_shortcut(x)
+x = x + shortcut # Add the original input back
+
+# Shortcut connection for feed-forward block
+shortcut = x
+x = self.norm2(x)
+x = self.ff(x)
+x = self.drop_shortcut(x)
+x = x + shortcut # Add the original input back
+
+return x
+
+
+class GPTModel(nn.Module):
+def __init__(self, cfg):
+super().__init__()
+self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])
+self.pos_emb = nn.Embedding(cfg["context_length"], cfg["emb_dim"])
+self.drop_emb = nn.Dropout(cfg["drop_rate"])
+
+self.trf_blocks = nn.Sequential(
+*[TransformerBlock(cfg) for _ in range(cfg["n_layers"])])
+
+self.final_norm = LayerNorm(cfg["emb_dim"])
+self.out_head = nn.Linear(cfg["emb_dim"], cfg["vocab_size"], bias=False)
+
+def forward(self, in_idx):
+batch_size, seq_len = in_idx.shape
+tok_embeds = self.tok_emb(in_idx)
+pos_embeds = self.pos_emb(torch.arange(seq_len, device=in_idx.device))
+x = tok_embeds + pos_embeds # Shape [batch_size, num_tokens, emb_size]
+x = self.drop_emb(x)
+x = self.trf_blocks(x)
+x = self.final_norm(x)
+logits = self.out_head(x)
+return logits
+```
+
+```python
+# Download contents to train the data with
+import os
+import urllib.request
+
+file_path = "the-verdict.txt"
+url = "https://raw.githubusercontent.com/rasbt/LLMs-from-scratch/main/ch02/01_main-chapter-code/the-verdict.txt"
+
+if not os.path.exists(file_path):
+with urllib.request.urlopen(url) as response:
+text_data = response.read().decode('utf-8')
+with open(file_path, "w", encoding="utf-8") as file:
+file.write(text_data)
+else:
+with open(file_path, "r", encoding="utf-8") as file:
+text_data = file.read()
+
+total_characters = len(text_data)
+tokenizer = tiktoken.get_encoding("gpt2")
+total_tokens = len(tokenizer.encode(text_data))
+
+print("Data downloaded")
+print("Characters:", total_characters)
+print("Tokens:", total_tokens)
+
+# Model initialization
+GPT_CONFIG_124M = {
+"vocab_size": 50257, # Vocabulary size
+"context_length": 256, # Shortened context length (orig: 1024)
+"emb_dim": 768, # Embedding dimension
+"n_heads": 12, # Number of attention heads
+"n_layers": 12, # Number of layers
+"drop_rate": 0.1, # Dropout rate
+"qkv_bias": False # Query-key-value bias
+}
+
+torch.manual_seed(123)
+model = GPTModel(GPT_CONFIG_124M)
+model.eval()
+print ("Model initialized")
+
+
+# Functions to transform from tokens to ids and from to ids to tokens
+def text_to_token_ids(text, tokenizer):
+encoded = tokenizer.encode(text, allowed_special={'<|endoftext|>'})
+encoded_tensor = torch.tensor(encoded).unsqueeze(0) # add batch dimension
+return encoded_tensor
+
+def token_ids_to_text(token_ids, tokenizer):
+flat = token_ids.squeeze(0) # remove batch dimension
+return tokenizer.decode(flat.tolist())
+
+
+
+# Define loss functions
+def calc_loss_batch(input_batch, target_batch, model, device):
+input_batch, target_batch = input_batch.to(device), target_batch.to(device)
+logits = model(input_batch)
+loss = torch.nn.functional.cross_entropy(logits.flatten(0, 1), target_batch.flatten())
+return loss
+
+
+def calc_loss_loader(data_loader, model, device, num_batches=None):
+total_loss = 0.
+if len(data_loader) == 0:
+return float("nan")
+elif num_batches is None:
+num_batches = len(data_loader)
+else:
+# Reduce the number of batches to match the total number of batches in the data loader
+# if num_batches exceeds the number of batches in the data loader
+num_batches = min(num_batches, len(data_loader))
+for i, (input_batch, target_batch) in enumerate(data_loader):
+if i < num_batches:
+loss = calc_loss_batch(input_batch, target_batch, model, device)
+total_loss += loss.item()
+else:
+break
+return total_loss / num_batches
+
+
+# Apply Train/validation ratio and create dataloaders
+train_ratio = 0.90
+split_idx = int(train_ratio * len(text_data))
+train_data = text_data[:split_idx]
+val_data = text_data[split_idx:]
+
+torch.manual_seed(123)
+
+train_loader = create_dataloader_v1(
+train_data,
+batch_size=2,
+max_length=GPT_CONFIG_124M["context_length"],
+stride=GPT_CONFIG_124M["context_length"],
+drop_last=True,
+shuffle=True,
+num_workers=0
+)
+
+val_loader = create_dataloader_v1(
+val_data,
+batch_size=2,
+max_length=GPT_CONFIG_124M["context_length"],
+stride=GPT_CONFIG_124M["context_length"],
+drop_last=False,
+shuffle=False,
+num_workers=0
+)
+
+
+# Sanity checks
+if total_tokens * (train_ratio) < GPT_CONFIG_124M["context_length"]:
+print("Not enough tokens for the training loader. "
+"Try to lower the `GPT_CONFIG_124M['context_length']` or "
+"increase the `training_ratio`")
+
+if total_tokens * (1-train_ratio) < GPT_CONFIG_124M["context_length"]:
+print("Not enough tokens for the validation loader. "
+"Try to lower the `GPT_CONFIG_124M['context_length']` or "
+"decrease the `training_ratio`")
+
+print("Train loader:")
+for x, y in train_loader:
+print(x.shape, y.shape)
+
+print("\nValidation loader:")
+for x, y in val_loader:
+print(x.shape, y.shape)
+
+train_tokens = 0
+for input_batch, target_batch in train_loader:
+train_tokens += input_batch.numel()
+
+val_tokens = 0
+for input_batch, target_batch in val_loader:
+val_tokens += input_batch.numel()
+
+print("Training tokens:", train_tokens)
+print("Validation tokens:", val_tokens)
+print("All tokens:", train_tokens + val_tokens)
+
+
+# Indicate the device to use
+if torch.cuda.is_available():
+device = torch.device("cuda")
+elif torch.backends.mps.is_available():
+device = torch.device("mps")
+else:
+device = torch.device("cpu")
+
+print(f"Using {device} device.")
+
+model.to(device) # no assignment model = model.to(device) necessary for nn.Module classes
+
+
+
+# Pre-calculate losses without starting yet
+torch.manual_seed(123) # For reproducibility due to the shuffling in the data loader
+
+with torch.no_grad(): # Disable gradient tracking for efficiency because we are not training, yet
+train_loss = calc_loss_loader(train_loader, model, device)
+val_loss = calc_loss_loader(val_loader, model, device)
+
+print("Training loss:", train_loss)
+print("Validation loss:", val_loss)
+
+
+# Functions to train the data
+def train_model_simple(model, train_loader, val_loader, optimizer, device, num_epochs,
+eval_freq, eval_iter, start_context, tokenizer):
+# Initialize lists to track losses and tokens seen
+train_losses, val_losses, track_tokens_seen = [], [], []
+tokens_seen, global_step = 0, -1
+
+# Main training loop
+for epoch in range(num_epochs):
+model.train() # Set model to training mode
+
+for input_batch, target_batch in train_loader:
+optimizer.zero_grad() # Reset loss gradients from previous batch iteration
+loss = calc_loss_batch(input_batch, target_batch, model, device)
+loss.backward() # Calculate loss gradients
+optimizer.step() # Update model weights using loss gradients
+tokens_seen += input_batch.numel()
+global_step += 1
+
+# Optional evaluation step
+if global_step % eval_freq == 0:
+train_loss, val_loss = evaluate_model(
+model, train_loader, val_loader, device, eval_iter)
+train_losses.append(train_loss)
+val_losses.append(val_loss)
+track_tokens_seen.append(tokens_seen)
+print(f"Ep {epoch+1} (Step {global_step:06d}): "
+f"Train loss {train_loss:.3f}, Val loss {val_loss:.3f}")
+
+# Print a sample text after each epoch
+generate_and_print_sample(
+model, tokenizer, device, start_context
+)
+
+return train_losses, val_losses, track_tokens_seen
+
+
+def evaluate_model(model, train_loader, val_loader, device, eval_iter):
+model.eval()
+with torch.no_grad():
+train_loss = calc_loss_loader(train_loader, model, device, num_batches=eval_iter)
+val_loss = calc_loss_loader(val_loader, model, device, num_batches=eval_iter)
+model.train()
+return train_loss, val_loss
+
+
+def generate_and_print_sample(model, tokenizer, device, start_context):
+model.eval()
+context_size = model.pos_emb.weight.shape[0]
+encoded = text_to_token_ids(start_context, tokenizer).to(device)
+with torch.no_grad():
+token_ids = generate_text(
+model=model, idx=encoded,
+max_new_tokens=50, context_size=context_size
+)
+decoded_text = token_ids_to_text(token_ids, tokenizer)
+print(decoded_text.replace("\n", " ")) # Compact print format
+model.train()
+
+
+# Start training!
+import time
+start_time = time.time()
+
+torch.manual_seed(123)
+model = GPTModel(GPT_CONFIG_124M)
+model.to(device)
+optimizer = torch.optim.AdamW(model.parameters(), lr=0.0004, weight_decay=0.1)
+
+num_epochs = 10
+train_losses, val_losses, tokens_seen = train_model_simple(
+model, train_loader, val_loader, optimizer, device,
+num_epochs=num_epochs, eval_freq=5, eval_iter=5,
+start_context="Every effort moves you", tokenizer=tokenizer
+)
+
+end_time = time.time()
+execution_time_minutes = (end_time - start_time) / 60
+print(f"Training completed in {execution_time_minutes:.2f} minutes.")
+
+
+
+# Show graphics with the training process
+import matplotlib.pyplot as plt
+from matplotlib.ticker import MaxNLocator
+import math
+def plot_losses(epochs_seen, tokens_seen, train_losses, val_losses):
+fig, ax1 = plt.subplots(figsize=(5, 3))
+ax1.plot(epochs_seen, train_losses, label="Training loss")
+ax1.plot(
+epochs_seen, val_losses, linestyle="-.", label="Validation loss"
+)
+ax1.set_xlabel("Epochs")
+ax1.set_ylabel("Loss")
+ax1.legend(loc="upper right")
+ax1.xaxis.set_major_locator(MaxNLocator(integer=True))
+ax2 = ax1.twiny()
+ax2.plot(tokens_seen, train_losses, alpha=0)
+ax2.set_xlabel("Tokens seen")
+fig.tight_layout()
+plt.show()
+
+# Compute perplexity from the loss values
+train_ppls = [math.exp(loss) for loss in train_losses]
+val_ppls = [math.exp(loss) for loss in val_losses]
+# Plot perplexity over tokens seen
+plt.figure()
+plt.plot(tokens_seen, train_ppls, label='Training Perplexity')
+plt.plot(tokens_seen, val_ppls, label='Validation Perplexity')
+plt.xlabel('Tokens Seen')
+plt.ylabel('Perplexity')
+plt.title('Perplexity over Training')
+plt.legend()
+plt.show()
+
+epochs_tensor = torch.linspace(0, num_epochs, len(train_losses))
+plot_losses(epochs_tensor, tokens_seen, train_losses, val_losses)
+
+
+torch.save({
+"model_state_dict": model.state_dict(),
+"optimizer_state_dict": optimizer.state_dict(),
+},
+"/tmp/model_and_optimizer.pth"
+)
+```
+Vamos ver uma explicação passo a passo
+
+### Funções para transformar texto <--> ids
+
+Estas são algumas funções simples que podem ser usadas para transformar textos do vocabulário em ids e vice-versa. Isso é necessário no início do manuseio do texto e no final das previsões:
+```python
+# Functions to transform from tokens to ids and from to ids to tokens
+def text_to_token_ids(text, tokenizer):
+encoded = tokenizer.encode(text, allowed_special={'<|endoftext|>'})
+encoded_tensor = torch.tensor(encoded).unsqueeze(0) # add batch dimension
+return encoded_tensor
+
+def token_ids_to_text(token_ids, tokenizer):
+flat = token_ids.squeeze(0) # remove batch dimension
+return tokenizer.decode(flat.tolist())
+```
+### Funções de geração de texto
+
+Na seção anterior, uma função que apenas obteve o **token mais provável** após obter os logits. No entanto, isso significa que para cada entrada a mesma saída sempre será gerada, o que a torna muito determinística.
+
+A seguinte função `generate_text` aplicará os conceitos de `top-k`, `temperature` e `multinomial`.
+
+- O **`top-k`** significa que começaremos a reduzir para `-inf` todas as probabilidades de todos os tokens, exceto os k tokens principais. Assim, se k=3, antes de tomar uma decisão, apenas os 3 tokens mais prováveis terão uma probabilidade diferente de `-inf`.
+- A **`temperature`** significa que cada probabilidade será dividida pelo valor da temperatura. Um valor de `0.1` melhorará a maior probabilidade em comparação com a menor, enquanto uma temperatura de `5`, por exemplo, a tornará mais uniforme. Isso ajuda a melhorar a variação nas respostas que gostaríamos que o LLM tivesse.
+- Após aplicar a temperatura, uma função **`softmax`** é aplicada novamente para fazer com que todos os tokens restantes tenham uma probabilidade total de 1.
+- Finalmente, em vez de escolher o token com a maior probabilidade, a função **`multinomial`** é aplicada para **prever o próximo token de acordo com as probabilidades finais**. Assim, se o token 1 tiver 70% de probabilidade, o token 2 tiver 20% e o token 3 tiver 10%, 70% das vezes o token 1 será selecionado, 20% das vezes será o token 2 e 10% das vezes será o token 3.
+```python
+# Generate text function
+def generate_text(model, idx, max_new_tokens, context_size, temperature=0.0, top_k=None, eos_id=None):
+
+# For-loop is the same as before: Get logits, and only focus on last time step
+for _ in range(max_new_tokens):
+idx_cond = idx[:, -context_size:]
+with torch.no_grad():
+logits = model(idx_cond)
+logits = logits[:, -1, :]
+
+# New: Filter logits with top_k sampling
+if top_k is not None:
+# Keep only top_k values
+top_logits, _ = torch.topk(logits, top_k)
+min_val = top_logits[:, -1]
+logits = torch.where(logits < min_val, torch.tensor(float("-inf")).to(logits.device), logits)
+
+# New: Apply temperature scaling
+if temperature > 0.0:
+logits = logits / temperature
+
+# Apply softmax to get probabilities
+probs = torch.softmax(logits, dim=-1) # (batch_size, context_len)
+
+# Sample from the distribution
+idx_next = torch.multinomial(probs, num_samples=1) # (batch_size, 1)
+
+# Otherwise same as before: get idx of the vocab entry with the highest logits value
+else:
+idx_next = torch.argmax(logits, dim=-1, keepdim=True) # (batch_size, 1)
+
+if idx_next == eos_id: # Stop generating early if end-of-sequence token is encountered and eos_id is specified
+break
+
+# Same as before: append sampled index to the running sequence
+idx = torch.cat((idx, idx_next), dim=1) # (batch_size, num_tokens+1)
+
+return idx
+```
+> [!TIP]
+> Existe uma alternativa comum ao `top-k` chamada [**`top-p`**](https://en.wikipedia.org/wiki/Top-p_sampling), também conhecida como amostragem de núcleo, que em vez de obter k amostras com a maior probabilidade, **organiza** todo o **vocabulário** resultante por probabilidades e **soma** elas da maior probabilidade para a menor até que um **limite seja alcançado**.
+>
+> Então, **apenas aquelas palavras** do vocabulário serão consideradas de acordo com suas probabilidades relativas.
+>
+> Isso permite não precisar selecionar um número de amostras `k`, já que o k ideal pode ser diferente em cada caso, mas **apenas um limite**.
+>
+> _Note que essa melhoria não está incluída no código anterior._
+
+> [!TIP]
+> Outra maneira de melhorar o texto gerado é usando **Beam search** em vez da busca gulosa usada neste exemplo.\
+> Ao contrário da busca gulosa, que seleciona a próxima palavra mais provável em cada passo e constrói uma única sequência, **beam search mantém o controle das 𝑘 k sequências parciais com as maiores pontuações** (chamadas de "beams") em cada passo. Ao explorar múltiplas possibilidades simultaneamente, equilibra eficiência e qualidade, aumentando as chances de **encontrar uma sequência geral melhor** que pode ser perdida pela abordagem gulosa devido a escolhas subótimas precoces.
+>
+> _Note que essa melhoria não está incluída no código anterior._
+
+### Funções de perda
+
+A função **`calc_loss_batch`** calcula a entropia cruzada de uma previsão de um único lote.\
+A **`calc_loss_loader`** obtém a entropia cruzada de todos os lotes e calcula a **entropia cruzada média**.
+```python
+# Define loss functions
+def calc_loss_batch(input_batch, target_batch, model, device):
+input_batch, target_batch = input_batch.to(device), target_batch.to(device)
+logits = model(input_batch)
+loss = torch.nn.functional.cross_entropy(logits.flatten(0, 1), target_batch.flatten())
+return loss
+
+def calc_loss_loader(data_loader, model, device, num_batches=None):
+total_loss = 0.
+if len(data_loader) == 0:
+return float("nan")
+elif num_batches is None:
+num_batches = len(data_loader)
+else:
+# Reduce the number of batches to match the total number of batches in the data loader
+# if num_batches exceeds the number of batches in the data loader
+num_batches = min(num_batches, len(data_loader))
+for i, (input_batch, target_batch) in enumerate(data_loader):
+if i < num_batches:
+loss = calc_loss_batch(input_batch, target_batch, model, device)
+total_loss += loss.item()
+else:
+break
+return total_loss / num_batches
+```
+> [!TIP]
+> **Recorte de gradiente** é uma técnica usada para melhorar a **estabilidade do treinamento** em grandes redes neurais, definindo um **limite máximo** para as magnitudes dos gradientes. Quando os gradientes excedem esse `max_norm` pré-definido, eles são reduzidos proporcionalmente para garantir que as atualizações dos parâmetros do modelo permaneçam dentro de um intervalo gerenciável, prevenindo problemas como gradientes explosivos e garantindo um treinamento mais controlado e estável.
+>
+> _Observe que essa melhoria não está incluída no código anterior._
+>
+> Confira o seguinte exemplo:
+
+
+
+### Carregando Dados
+
+As funções `create_dataloader_v1` e `create_dataloader_v1` já foram discutidas em uma seção anterior.
+
+A partir daqui, note como está definido que 90% do texto será usado para treinamento, enquanto 10% será usado para validação, e ambos os conjuntos são armazenados em 2 carregadores de dados diferentes.\
+Observe que, às vezes, parte do conjunto de dados também é reservada para um conjunto de testes para avaliar melhor o desempenho do modelo.
+
+Ambos os carregadores de dados estão usando o mesmo tamanho de lote, comprimento máximo, stride e número de trabalhadores (0 neste caso).\
+As principais diferenças são os dados usados por cada um, e o validador não está descartando o último nem embaralhando os dados, pois isso não é necessário para fins de validação.
+
+Além disso, o fato de que **o stride é tão grande quanto o comprimento do contexto** significa que não haverá sobreposição entre os contextos usados para treinar os dados (reduz o overfitting, mas também o conjunto de dados de treinamento).
+
+Além disso, note que o tamanho do lote neste caso é 2 para dividir os dados em 2 lotes, e o principal objetivo disso é permitir o processamento paralelo e reduzir o consumo por lote.
+```python
+train_ratio = 0.90
+split_idx = int(train_ratio * len(text_data))
+train_data = text_data[:split_idx]
+val_data = text_data[split_idx:]
+
+torch.manual_seed(123)
+
+train_loader = create_dataloader_v1(
+train_data,
+batch_size=2,
+max_length=GPT_CONFIG_124M["context_length"],
+stride=GPT_CONFIG_124M["context_length"],
+drop_last=True,
+shuffle=True,
+num_workers=0
+)
+
+val_loader = create_dataloader_v1(
+val_data,
+batch_size=2,
+max_length=GPT_CONFIG_124M["context_length"],
+stride=GPT_CONFIG_124M["context_length"],
+drop_last=False,
+shuffle=False,
+num_workers=0
+)
+```
+## Verificações de Sanidade
+
+O objetivo é verificar se há tokens suficientes para treinamento, se as formas são as esperadas e obter algumas informações sobre o número de tokens usados para treinamento e validação:
+```python
+# Sanity checks
+if total_tokens * (train_ratio) < GPT_CONFIG_124M["context_length"]:
+print("Not enough tokens for the training loader. "
+"Try to lower the `GPT_CONFIG_124M['context_length']` or "
+"increase the `training_ratio`")
+
+if total_tokens * (1-train_ratio) < GPT_CONFIG_124M["context_length"]:
+print("Not enough tokens for the validation loader. "
+"Try to lower the `GPT_CONFIG_124M['context_length']` or "
+"decrease the `training_ratio`")
+
+print("Train loader:")
+for x, y in train_loader:
+print(x.shape, y.shape)
+
+print("\nValidation loader:")
+for x, y in val_loader:
+print(x.shape, y.shape)
+
+train_tokens = 0
+for input_batch, target_batch in train_loader:
+train_tokens += input_batch.numel()
+
+val_tokens = 0
+for input_batch, target_batch in val_loader:
+val_tokens += input_batch.numel()
+
+print("Training tokens:", train_tokens)
+print("Validation tokens:", val_tokens)
+print("All tokens:", train_tokens + val_tokens)
+```
+### Selecionar dispositivo para treinamento e pré-cálculos
+
+O código a seguir apenas seleciona o dispositivo a ser usado e calcula uma perda de treinamento e uma perda de validação (sem ter treinado nada ainda) como ponto de partida.
+```python
+# Indicate the device to use
+
+if torch.cuda.is_available():
+device = torch.device("cuda")
+elif torch.backends.mps.is_available():
+device = torch.device("mps")
+else:
+device = torch.device("cpu")
+
+print(f"Using {device} device.")
+
+model.to(device) # no assignment model = model.to(device) necessary for nn.Module classes
+
+# Pre-calculate losses without starting yet
+torch.manual_seed(123) # For reproducibility due to the shuffling in the data loader
+
+with torch.no_grad(): # Disable gradient tracking for efficiency because we are not training, yet
+train_loss = calc_loss_loader(train_loader, model, device)
+val_loss = calc_loss_loader(val_loader, model, device)
+
+print("Training loss:", train_loss)
+print("Validation loss:", val_loss)
+```
+### Funções de treinamento
+
+A função `generate_and_print_sample` apenas obtém um contexto e gera alguns tokens para ter uma noção de quão bom é o modelo naquele ponto. Isso é chamado por `train_model_simple` em cada etapa.
+
+A função `evaluate_model` é chamada com a frequência indicada para a função de treinamento e é usada para medir a perda de treinamento e a perda de validação naquele ponto do treinamento do modelo.
+
+Então, a grande função `train_model_simple` é a que realmente treina o modelo. Ela espera:
+
+- O carregador de dados de treinamento (com os dados já separados e preparados para treinamento)
+- O carregador de validação
+- O **otimizador** a ser usado durante o treinamento: Esta é a função que usará os gradientes e atualizará os parâmetros para reduzir a perda. Neste caso, como você verá, `AdamW` é usado, mas há muitos mais.
+- `optimizer.zero_grad()` é chamado para redefinir os gradientes em cada rodada para não acumulá-los.
+- O parâmetro **`lr`** é a **taxa de aprendizado** que determina o **tamanho dos passos** dados durante o processo de otimização ao atualizar os parâmetros do modelo. Uma taxa de aprendizado **menor** significa que o otimizador **faz atualizações menores** nos pesos, o que pode levar a uma convergência mais **precisa**, mas pode **retardar** o treinamento. Uma taxa de aprendizado **maior** pode acelerar o treinamento, mas **risca ultrapassar** o mínimo da função de perda (**pular** o ponto onde a função de perda é minimizada).
+- **Weight Decay** modifica a etapa de **Cálculo da Perda** adicionando um termo extra que penaliza pesos grandes. Isso incentiva o otimizador a encontrar soluções com pesos menores, equilibrando entre ajustar bem os dados e manter o modelo simples, prevenindo overfitting em modelos de aprendizado de máquina, desencorajando o modelo de atribuir muita importância a qualquer recurso único.
+- Otimizadores tradicionais como SGD com regularização L2 acoplam o weight decay com o gradiente da função de perda. No entanto, **AdamW** (uma variante do otimizador Adam) desacopla o weight decay da atualização do gradiente, levando a uma regularização mais eficaz.
+- O dispositivo a ser usado para treinamento
+- O número de épocas: Número de vezes para percorrer os dados de treinamento
+- A frequência de avaliação: A frequência para chamar `evaluate_model`
+- A iteração de avaliação: O número de lotes a serem usados ao avaliar o estado atual do modelo ao chamar `generate_and_print_sample`
+- O contexto inicial: Qual a frase inicial a ser usada ao chamar `generate_and_print_sample`
+- O tokenizer
+```python
+# Functions to train the data
+def train_model_simple(model, train_loader, val_loader, optimizer, device, num_epochs,
+eval_freq, eval_iter, start_context, tokenizer):
+# Initialize lists to track losses and tokens seen
+train_losses, val_losses, track_tokens_seen = [], [], []
+tokens_seen, global_step = 0, -1
+
+# Main training loop
+for epoch in range(num_epochs):
+model.train() # Set model to training mode
+
+for input_batch, target_batch in train_loader:
+optimizer.zero_grad() # Reset loss gradients from previous batch iteration
+loss = calc_loss_batch(input_batch, target_batch, model, device)
+loss.backward() # Calculate loss gradients
+optimizer.step() # Update model weights using loss gradients
+tokens_seen += input_batch.numel()
+global_step += 1
+
+# Optional evaluation step
+if global_step % eval_freq == 0:
+train_loss, val_loss = evaluate_model(
+model, train_loader, val_loader, device, eval_iter)
+train_losses.append(train_loss)
+val_losses.append(val_loss)
+track_tokens_seen.append(tokens_seen)
+print(f"Ep {epoch+1} (Step {global_step:06d}): "
+f"Train loss {train_loss:.3f}, Val loss {val_loss:.3f}")
+
+# Print a sample text after each epoch
+generate_and_print_sample(
+model, tokenizer, device, start_context
+)
+
+return train_losses, val_losses, track_tokens_seen
+
+
+def evaluate_model(model, train_loader, val_loader, device, eval_iter):
+model.eval() # Set in eval mode to avoid dropout
+with torch.no_grad():
+train_loss = calc_loss_loader(train_loader, model, device, num_batches=eval_iter)
+val_loss = calc_loss_loader(val_loader, model, device, num_batches=eval_iter)
+model.train() # Back to training model applying all the configurations
+return train_loss, val_loss
+
+
+def generate_and_print_sample(model, tokenizer, device, start_context):
+model.eval() # Set in eval mode to avoid dropout
+context_size = model.pos_emb.weight.shape[0]
+encoded = text_to_token_ids(start_context, tokenizer).to(device)
+with torch.no_grad():
+token_ids = generate_text(
+model=model, idx=encoded,
+max_new_tokens=50, context_size=context_size
+)
+decoded_text = token_ids_to_text(token_ids, tokenizer)
+print(decoded_text.replace("\n", " ")) # Compact print format
+model.train() # Back to training model applying all the configurations
+```
+> [!TIP]
+> Para melhorar a taxa de aprendizado, existem algumas técnicas relevantes chamadas **linear warmup** e **cosine decay.**
+>
+> **Linear warmup** consiste em definir uma taxa de aprendizado inicial e uma máxima, e atualizá-la consistentemente após cada época. Isso ocorre porque começar o treinamento com atualizações de peso menores diminui o risco de o modelo encontrar atualizações grandes e desestabilizadoras durante sua fase de treinamento.\
+> **Cosine decay** é uma técnica que **reduz gradualmente a taxa de aprendizado** seguindo uma curva de meio cosseno **após a fase de warmup**, desacelerando as atualizações de peso para **minimizar o risco de ultrapassar** os mínimos de perda e garantir a estabilidade do treinamento em fases posteriores.
+>
+> _Observe que essas melhorias não estão incluídas no código anterior._
+
+### Iniciar treinamento
+```python
+import time
+start_time = time.time()
+
+torch.manual_seed(123)
+model = GPTModel(GPT_CONFIG_124M)
+model.to(device)
+optimizer = torch.optim.AdamW(model.parameters(), lr=0.0004, weight_decay=0.1)
+
+num_epochs = 10
+train_losses, val_losses, tokens_seen = train_model_simple(
+model, train_loader, val_loader, optimizer, device,
+num_epochs=num_epochs, eval_freq=5, eval_iter=5,
+start_context="Every effort moves you", tokenizer=tokenizer
+)
+
+end_time = time.time()
+execution_time_minutes = (end_time - start_time) / 60
+print(f"Training completed in {execution_time_minutes:.2f} minutes.")
+```
+### Impressão da evolução do treinamento
+
+Com a seguinte função, é possível imprimir a evolução do modelo enquanto ele estava sendo treinado.
+```python
+import matplotlib.pyplot as plt
+from matplotlib.ticker import MaxNLocator
+import math
+def plot_losses(epochs_seen, tokens_seen, train_losses, val_losses):
+fig, ax1 = plt.subplots(figsize=(5, 3))
+ax1.plot(epochs_seen, train_losses, label="Training loss")
+ax1.plot(
+epochs_seen, val_losses, linestyle="-.", label="Validation loss"
+)
+ax1.set_xlabel("Epochs")
+ax1.set_ylabel("Loss")
+ax1.legend(loc="upper right")
+ax1.xaxis.set_major_locator(MaxNLocator(integer=True))
+ax2 = ax1.twiny()
+ax2.plot(tokens_seen, train_losses, alpha=0)
+ax2.set_xlabel("Tokens seen")
+fig.tight_layout()
+plt.show()
+
+# Compute perplexity from the loss values
+train_ppls = [math.exp(loss) for loss in train_losses]
+val_ppls = [math.exp(loss) for loss in val_losses]
+# Plot perplexity over tokens seen
+plt.figure()
+plt.plot(tokens_seen, train_ppls, label='Training Perplexity')
+plt.plot(tokens_seen, val_ppls, label='Validation Perplexity')
+plt.xlabel('Tokens Seen')
+plt.ylabel('Perplexity')
+plt.title('Perplexity over Training')
+plt.legend()
+plt.show()
+
+epochs_tensor = torch.linspace(0, num_epochs, len(train_losses))
+plot_losses(epochs_tensor, tokens_seen, train_losses, val_losses)
+```
+### Salvar o modelo
+
+É possível salvar o modelo + otimizador se você quiser continuar o treinamento mais tarde:
+```python
+# Save the model and the optimizer for later training
+torch.save({
+"model_state_dict": model.state_dict(),
+"optimizer_state_dict": optimizer.state_dict(),
+},
+"/tmp/model_and_optimizer.pth"
+)
+# Note that this model with the optimizer occupied close to 2GB
+
+# Restore model and optimizer for training
+checkpoint = torch.load("/tmp/model_and_optimizer.pth", map_location=device)
+
+model = GPTModel(GPT_CONFIG_124M)
+model.load_state_dict(checkpoint["model_state_dict"])
+optimizer = torch.optim.AdamW(model.parameters(), lr=5e-4, weight_decay=0.1)
+optimizer.load_state_dict(checkpoint["optimizer_state_dict"])
+model.train(); # Put in training mode
+```
+Ou apenas o modelo se você planeja usá-lo apenas:
+```python
+# Save the model
+torch.save(model.state_dict(), "model.pth")
+
+# Load it
+model = GPTModel(GPT_CONFIG_124M)
+
+model.load_state_dict(torch.load("model.pth", map_location=device))
+
+model.eval() # Put in eval mode
+```
+## Carregando pesos do GPT2
+
+Existem 2 scripts rápidos para carregar os pesos do GPT2 localmente. Para ambos, você pode clonar o repositório [https://github.com/rasbt/LLMs-from-scratch](https://github.com/rasbt/LLMs-from-scratch) localmente, então:
+
+- O script [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/01_main-chapter-code/gpt_generate.py](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/01_main-chapter-code/gpt_generate.py) fará o download de todos os pesos e transformará os formatos de OpenAI para os esperados pelo nosso LLM. O script também está preparado com a configuração necessária e com o prompt: "Every effort moves you"
+- O script [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/02_alternative_weight_loading/weight-loading-hf-transformers.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch05/02_alternative_weight_loading/weight-loading-hf-transformers.ipynb) permite que você carregue qualquer um dos pesos do GPT2 localmente (basta mudar a variável `CHOOSE_MODEL`) e prever texto a partir de alguns prompts.
+
+## Referências
+
+- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)
diff --git a/src/AI/AI-llm-architecture/7.1.-fine-tuning-for-classification.md b/src/AI/AI-llm-architecture/7.1.-fine-tuning-for-classification.md
new file mode 100644
index 000000000..a541d4b93
--- /dev/null
+++ b/src/AI/AI-llm-architecture/7.1.-fine-tuning-for-classification.md
@@ -0,0 +1,110 @@
+# 7.1. Ajuste Fino para Classificação
+
+## O que é
+
+Ajuste fino é o processo de pegar um **modelo pré-treinado** que aprendeu **padrões gerais de linguagem** a partir de grandes quantidades de dados e **adaptá-lo** para realizar uma **tarefa específica** ou entender a linguagem específica de um domínio. Isso é alcançado continuando o treinamento do modelo em um conjunto de dados menor e específico para a tarefa, permitindo que ele ajuste seus parâmetros para se adequar melhor às nuances dos novos dados, aproveitando o amplo conhecimento que já adquiriu. O ajuste fino permite que o modelo forneça resultados mais precisos e relevantes em aplicações especializadas sem a necessidade de treinar um novo modelo do zero.
+
+> [!TIP]
+> Como pré-treinar um LLM que "entende" o texto é bastante caro, geralmente é mais fácil e barato ajustar modelos pré-treinados de código aberto para realizar uma tarefa específica que queremos que ele execute.
+
+> [!TIP]
+> O objetivo desta seção é mostrar como ajustar um modelo já pré-treinado, de modo que, em vez de gerar novo texto, o LLM selecione e forneça as **probabilidades do texto dado ser categorizado em cada uma das categorias dadas** (como se um texto é spam ou não).
+
+## Preparando o conjunto de dados
+
+### Tamanho do conjunto de dados
+
+Claro, para ajustar um modelo, você precisa de alguns dados estruturados para usar para especializar seu LLM. No exemplo proposto em [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb), o GPT2 é ajustado para detectar se um e-mail é spam ou não usando os dados de [https://archive.ics.uci.edu/static/public/228/sms+spam+collection.zip](https://archive.ics.uci.edu/static/public/228/sms+spam+collection.zip)_._
+
+Este conjunto de dados contém muito mais exemplos de "não spam" do que de "spam", portanto, o livro sugere **usar apenas tantos exemplos de "não spam" quanto de "spam"** (removendo assim todos os exemplos extras dos dados de treinamento). Neste caso, foram 747 exemplos de cada.
+
+Então, **70%** do conjunto de dados é usado para **treinamento**, **10%** para **validação** e **20%** para **teste**.
+
+- O **conjunto de validação** é usado durante a fase de treinamento para ajustar os **hiperparâmetros** do modelo e tomar decisões sobre a arquitetura do modelo, ajudando efetivamente a prevenir o overfitting ao fornecer feedback sobre como o modelo se comporta em dados não vistos. Ele permite melhorias iterativas sem enviesar a avaliação final.
+- Isso significa que, embora os dados incluídos neste conjunto de dados não sejam usados para o treinamento diretamente, eles são usados para ajustar os melhores **hiperparâmetros**, portanto, este conjunto não pode ser usado para avaliar o desempenho do modelo como o conjunto de teste.
+- Em contraste, o **conjunto de teste** é usado **apenas após** o modelo ter sido totalmente treinado e todos os ajustes estarem completos; ele fornece uma avaliação imparcial da capacidade do modelo de generalizar para novos dados não vistos. Esta avaliação final no conjunto de teste dá uma indicação realista de como o modelo deve se comportar em aplicações do mundo real.
+
+### Comprimento das entradas
+
+Como o exemplo de treinamento espera entradas (texto de e-mails, neste caso) do mesmo comprimento, decidiu-se fazer cada entrada tão grande quanto a maior, adicionando os ids de `<|endoftext|>` como preenchimento.
+
+### Inicializar o modelo
+
+Usando os pesos pré-treinados de código aberto, inicialize o modelo para treinar. Já fizemos isso antes e seguindo as instruções de [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb) você pode facilmente fazê-lo.
+
+## Cabeça de Classificação
+
+Neste exemplo específico (prevendo se um texto é spam ou não), não estamos interessados em ajustar de acordo com o vocabulário completo do GPT2, mas queremos que o novo modelo diga se o e-mail é spam (1) ou não (0). Portanto, vamos **modificar a camada final que** fornece as probabilidades por token do vocabulário para uma que apenas fornece as probabilidades de ser spam ou não (então, como um vocabulário de 2 palavras).
+```python
+# This code modified the final layer with a Linear one with 2 outs
+num_classes = 2
+model.out_head = torch.nn.Linear(
+
+in_features=BASE_CONFIG["emb_dim"],
+
+out_features=num_classes
+)
+```
+## Parâmetros a ajustar
+
+Para ajustar rapidamente, é mais fácil não ajustar todos os parâmetros, mas apenas alguns finais. Isso ocorre porque é sabido que as camadas inferiores geralmente capturam estruturas e semânticas básicas da linguagem aplicáveis. Portanto, apenas **ajustar as últimas camadas geralmente é suficiente e mais rápido**.
+```python
+# This code makes all the parameters of the model unrtainable
+for param in model.parameters():
+param.requires_grad = False
+
+# Allow to fine tune the last layer in the transformer block
+for param in model.trf_blocks[-1].parameters():
+param.requires_grad = True
+
+# Allow to fine tune the final layer norm
+for param in model.final_norm.parameters():
+
+param.requires_grad = True
+```
+## Entradas a serem usadas para treinamento
+
+Nas seções anteriores, o LLM foi treinado reduzindo a perda de cada token previsto, embora quase todos os tokens previstos estivessem na frase de entrada (apenas 1 no final foi realmente previsto) para que o modelo entendesse melhor a linguagem.
+
+Neste caso, só nos importa que o modelo seja capaz de prever se o modelo é spam ou não, então só nos importa o último token previsto. Portanto, é necessário modificar nossas funções de perda de treinamento anteriores para levar em conta apenas esse token.
+
+Isso é implementado em [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/ch06.ipynb) como:
+```python
+def calc_accuracy_loader(data_loader, model, device, num_batches=None):
+model.eval()
+correct_predictions, num_examples = 0, 0
+
+if num_batches is None:
+num_batches = len(data_loader)
+else:
+num_batches = min(num_batches, len(data_loader))
+for i, (input_batch, target_batch) in enumerate(data_loader):
+if i < num_batches:
+input_batch, target_batch = input_batch.to(device), target_batch.to(device)
+
+with torch.no_grad():
+logits = model(input_batch)[:, -1, :] # Logits of last output token
+predicted_labels = torch.argmax(logits, dim=-1)
+
+num_examples += predicted_labels.shape[0]
+correct_predictions += (predicted_labels == target_batch).sum().item()
+else:
+break
+return correct_predictions / num_examples
+
+
+def calc_loss_batch(input_batch, target_batch, model, device):
+input_batch, target_batch = input_batch.to(device), target_batch.to(device)
+logits = model(input_batch)[:, -1, :] # Logits of last output token
+loss = torch.nn.functional.cross_entropy(logits, target_batch)
+return loss
+```
+Note que para cada lote, estamos interessados apenas nos **logits do último token previsto**.
+
+## Código completo de classificação de fine-tune do GPT2
+
+Você pode encontrar todo o código para ajustar o GPT2 para ser um classificador de spam em [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/load-finetuned-model.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch06/01_main-chapter-code/load-finetuned-model.ipynb)
+
+## Referências
+
+- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)
diff --git a/src/AI/AI-llm-architecture/7.2.-fine-tuning-to-follow-instructions.md b/src/AI/AI-llm-architecture/7.2.-fine-tuning-to-follow-instructions.md
index 247fcb4c4..99adba892 100644
--- a/src/AI/AI-llm-architecture/7.2.-fine-tuning-to-follow-instructions.md
+++ b/src/AI/AI-llm-architecture/7.2.-fine-tuning-to-follow-instructions.md
@@ -5,7 +5,7 @@
## Conjunto de Dados
-Para ajustar finamente um LLM para seguir instruções, é necessário ter um conjunto de dados com instruções e respostas para ajustar o LLM. Existem diferentes formatos para treinar um LLM a seguir instruções, por exemplo:
+Para ajustar finamente um LLM para seguir instruções, é necessário ter um conjunto de dados com instruções e respostas para ajustar o LLM. Existem diferentes formatos para treinar um LLM para seguir instruções, por exemplo:
- O exemplo de estilo de prompt Apply Alpaca:
```csharp
@@ -59,7 +59,7 @@ Então, é necessário agrupar todas as entradas e saídas esperadas para o trei
- Preencher todas as amostras para o mesmo comprimento (geralmente o comprimento será tão grande quanto o comprimento do contexto usado para pré-treinar o LLM)
- Criar os tokens esperados deslocando 1 a entrada em uma função de colagem personalizada
- Substituir alguns tokens de preenchimento por -100 para excluí-los da perda de treinamento: Após o primeiro token `endoftext`, substituir todos os outros tokens `endoftext` por -100 (porque usar `cross_entropy(...,ignore_index=-100)` significa que ele ignorará alvos com -100)
-- \[Opcional] Mascarar usando -100 também todos os tokens pertencentes à pergunta para que o LLM aprenda apenas como gerar a resposta. No estilo Apply Alpaca, isso significará mascarar tudo até `### Response:`
+- \[Opcional] Mascarar usando -100 também todos os tokens pertencentes à pergunta para que o LLM aprenda apenas a gerar a resposta. No estilo Apply Alpaca, isso significará mascarar tudo até `### Response:`
Com isso criado, é hora de criar os carregadores de dados para cada conjunto de dados (treinamento, validação e teste).
@@ -80,16 +80,16 @@ Outros testes a serem realizados para verificar a qualidade das respostas:
1. **Measuring Massive Multitask Language Understanding (**[**MMLU**](https://arxiv.org/abs/2009.03300)**):** MMLU avalia o conhecimento e as habilidades de resolução de problemas de um modelo em 57 disciplinas, incluindo humanidades, ciências e mais. Ele usa perguntas de múltipla escolha para avaliar a compreensão em vários níveis de dificuldade, desde o elementar até o profissional avançado.
2. [**LMSYS Chatbot Arena**](https://arena.lmsys.org): Esta plataforma permite que os usuários comparem respostas de diferentes chatbots lado a lado. Os usuários inserem um prompt, e vários chatbots geram respostas que podem ser comparadas diretamente.
3. [**AlpacaEval**](https://github.com/tatsu-lab/alpaca_eval)**:** AlpacaEval é uma estrutura de avaliação automatizada onde um LLM avançado como o GPT-4 avalia as respostas de outros modelos a vários prompts.
-4. **General Language Understanding Evaluation (**[**GLUE**](https://gluebenchmark.com/)**):** GLUE é uma coleção de nove tarefas de compreensão de linguagem natural, incluindo análise de sentimentos, implicação textual e resposta a perguntas.
+4. **General Language Understanding Evaluation (**[**GLUE**](https://gluebenchmark.com/)**):** GLUE é uma coleção de nove tarefas de compreensão de linguagem natural, incluindo análise de sentimento, implicação textual e resposta a perguntas.
5. [**SuperGLUE**](https://super.gluebenchmark.com/)**:** Construindo sobre o GLUE, o SuperGLUE inclui tarefas mais desafiadoras projetadas para serem difíceis para os modelos atuais.
6. **Beyond the Imitation Game Benchmark (**[**BIG-bench**](https://github.com/google/BIG-bench)**):** BIG-bench é um benchmark em larga escala com mais de 200 tarefas que testam as habilidades de um modelo em áreas como raciocínio, tradução e resposta a perguntas.
7. **Holistic Evaluation of Language Models (**[**HELM**](https://crfm.stanford.edu/helm/lite/latest/)**):** HELM fornece uma avaliação abrangente em várias métricas, como precisão, robustez e justiça.
-8. [**OpenAI Evals**](https://github.com/openai/evals)**:** Uma estrutura de avaliação de código aberto da OpenAI que permite o teste de modelos de IA em tarefas personalizadas e padronizadas.
+8. [**OpenAI Evals**](https://github.com/openai/evals)**:** Uma estrutura de avaliação de código aberto da OpenAI que permite testar modelos de IA em tarefas personalizadas e padronizadas.
9. [**HumanEval**](https://github.com/openai/human-eval)**:** Uma coleção de problemas de programação usados para avaliar as habilidades de geração de código de modelos de linguagem.
10. **Stanford Question Answering Dataset (**[**SQuAD**](https://rajpurkar.github.io/SQuAD-explorer/)**):** SQuAD consiste em perguntas sobre artigos da Wikipedia, onde os modelos devem compreender o texto para responder com precisão.
11. [**TriviaQA**](https://nlp.cs.washington.edu/triviaqa/)**:** Um conjunto de dados em larga escala de perguntas e respostas de trivia, juntamente com documentos de evidência.
-e muitos, muitos mais
+e muitos outros
## Follow instructions fine-tuning code
diff --git a/src/AI/AI-llm-architecture/README.md b/src/AI/AI-llm-architecture/README.md
index 33f8c2edc..473d153f0 100644
--- a/src/AI/AI-llm-architecture/README.md
+++ b/src/AI/AI-llm-architecture/README.md
@@ -50,7 +50,7 @@ Você deve começar lendo este post para alguns conceitos básicos que você dev
4.-attention-mechanisms.md
{{#endref}}
-## 5. Arquitetura de LLM
+## 5. Arquitetura do LLM
> [!TIP]
> O objetivo desta quinta fase é muito simples: **Desenvolver a arquitetura do LLM completo**. Juntar tudo, aplicar todas as camadas e criar todas as funções para gerar texto ou transformar texto em IDs e vice-versa.
@@ -61,7 +61,7 @@ Você deve começar lendo este post para alguns conceitos básicos que você dev
5.-llm-architecture.md
{{#endref}}
-## 6. Pré-treinamento & Carregamento de modelos
+## 6. Pré-treinamento e Carregamento de Modelos
> [!TIP]
> O objetivo desta sexta fase é muito simples: **Treinar o modelo do zero**. Para isso, a arquitetura LLM anterior será usada com alguns loops sobre os conjuntos de dados usando as funções de perda e otimizador definidos para treinar todos os parâmetros do modelo.
@@ -70,7 +70,7 @@ Você deve começar lendo este post para alguns conceitos básicos que você dev
6.-pre-training-and-loading-models.md
{{#endref}}
-## 7.0. Melhorias de LoRA em ajuste fino
+## 7.0. Melhorias de LoRA em Fine-Tuning
> [!TIP]
> O uso de **LoRA reduz muito a computação** necessária para **ajustar** modelos já treinados.
@@ -79,19 +79,19 @@ Você deve começar lendo este post para alguns conceitos básicos que você dev
7.0.-lora-improvements-in-fine-tuning.md
{{#endref}}
-## 7.1. Ajuste Fino para Classificação
+## 7.1. Fine-Tuning para Classificação
> [!TIP]
-> O objetivo desta seção é mostrar como ajustar finamente um modelo já pré-treinado para que, em vez de gerar novo texto, o LLM selecione e forneça as **probabilidades do texto dado ser categorizado em cada uma das categorias dadas** (como se um texto é spam ou não).
+> O objetivo desta seção é mostrar como ajustar um modelo já pré-treinado para que, em vez de gerar novo texto, o LLM selecione e forneça as **probabilidades do texto dado ser categorizado em cada uma das categorias dadas** (como se um texto é spam ou não).
{{#ref}}
7.1.-fine-tuning-for-classification.md
{{#endref}}
-## 7.2. Ajuste Fino para seguir instruções
+## 7.2. Fine-Tuning para Seguir Instruções
> [!TIP]
-> O objetivo desta seção é mostrar como **ajustar finamente um modelo já pré-treinado para seguir instruções** em vez de apenas gerar texto, por exemplo, respondendo a tarefas como um chatbot.
+> O objetivo desta seção é mostrar como **ajustar um modelo já pré-treinado para seguir instruções** em vez de apenas gerar texto, por exemplo, respondendo a tarefas como um chatbot.
{{#ref}}
7.2.-fine-tuning-to-follow-instructions.md