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:** + +
Posição da JanelaSequência de EntradaSequência de Alvo
1["Lorem", "ipsum", "dolor", "sit"]["ipsum", "dolor", "sit", "amet,"]
2["ipsum", "dolor", "sit", "amet,"]["dolor", "sit", "amet,", "consectetur"]
3["dolor", "sit", "amet,", "consectetur"]["sit", "amet,", "consectetur", "adipiscing"]
4["sit", "amet,", "consectetur", "adipiscing"]["amet,", "consectetur", "adipiscing", "elit."]
+ +3. **Arrays de Entrada e Alvo Resultantes:** + +- **Entrada:** + +```python +[ +["Lorem", "ipsum", "dolor", "sit"], +["ipsum", "dolor", "sit", "amet,"], +["dolor", "sit", "amet,", "consectetur"], +["sit", "amet,", "consectetur", "adipiscing"], +] +``` + +- **Alvo:** + +```python +[ +["ipsum", "dolor", "sit", "amet,"], +["dolor", "sit", "amet,", "consectetur"], +["sit", "amet,", "consectetur", "adipiscing"], +["amet,", "consectetur", "adipiscing", "elit."], +] +``` + +**Representação Visual** + +
Posição do TokenToken
1Lorem
2ipsum
3dolor
4sit
5amet,
6consectetur
7adipiscing
8elit.
+ +**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: + +

https://camo.githubusercontent.com/3c0ab9c55cefa10b667f1014b6c42df901fa330bb2bc9cea88885e784daec8ba/68747470733a2f2f73656261737469616e72617363686b612e636f6d2f696d616765732f4c4c4d732d66726f6d2d736372617463682d696d616765732f636830355f636f6d707265737365642f63726f73732d656e74726f70792e776562703f313233

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