mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/AI/AI-llm-architecture/0.-basic-llm-concepts.md', 'src/
This commit is contained in:
parent
6b5358d759
commit
58dbb53ede
@ -2,7 +2,7 @@
|
||||
|
||||
## Vooropleiding
|
||||
|
||||
Vooropleiding is die grondslag fase in die ontwikkeling van 'n groot taalmodel (LLM) waar die model blootgestel word aan groot en diverse hoeveelhede teksdata. Tydens hierdie fase, **leer die LLM die fundamentele strukture, patrone, en nuanses van taal**, insluitend grammatika, woordeskat, sintaksis, en kontekstuele verhoudings. Deur hierdie uitgebreide data te verwerk, verkry die model 'n breë begrip van taal en algemene wêreldkennis. Hierdie omvattende basis stel die LLM in staat om samehangende en kontekstueel relevante teks te genereer. Vervolgens kan hierdie vooropgeleide model fyn-afgestem word, waar dit verder opgelei word op gespesialiseerde datastelle om sy vermoëns aan te pas vir spesifieke take of domeine, wat sy prestasie en relevansie in geteikende toepassings verbeter.
|
||||
Vooropleiding is die grondslag fase in die ontwikkeling van 'n groot taalmodel (LLM) waar die model blootgestel word aan groot en diverse hoeveelhede teksdata. Gedurende hierdie fase, **leer die LLM die fundamentele strukture, patrone, en nuanses van taal**, insluitend grammatika, woordeskat, sintaksis, en kontekstuele verhoudings. Deur hierdie uitgebreide data te verwerk, verkry die model 'n breë begrip van taal en algemene wêreldkennis. Hierdie omvattende basis stel die LLM in staat om samehangende en kontekstueel relevante teks te genereer. Vervolgens kan hierdie vooropgeleide model fyn-afgestem word, waar dit verder opgelei word op gespesialiseerde datastelle om sy vermoëns aan te pas vir spesifieke take of domeine, wat sy prestasie en relevansie in geteikende toepassings verbeter.
|
||||
|
||||
## Hoof LLM komponente
|
||||
|
||||
@ -11,10 +11,10 @@ Gewoonlik word 'n LLM gekarakteriseer deur die konfigurasie wat gebruik word om
|
||||
- **Parameters**: Parameters is die **leerbare gewigte en vooroordele** in die neurale netwerk. Dit is die getalle wat die opleidingsproses aanpas om die verliesfunksie te minimaliseer en die model se prestasie op die taak te verbeter. LLMs gebruik gewoonlik miljoene parameters.
|
||||
- **Kontekslengte**: Dit is die maksimum lengte van elke sin wat gebruik word om die LLM voor te oefen.
|
||||
- **Inbedigingsdimensie**: Die grootte van die vektor wat gebruik word om elke token of woord voor te stel. LLMs gebruik gewoonlik biljoene dimensies.
|
||||
- **Versteekte Dimensie**: Die grootte van die versteekte lae in die neurale netwerk.
|
||||
- **Verborge Dimensie**: Die grootte van die verborge lae in die neurale netwerk.
|
||||
- **Aantal Lae (Diepte)**: Hoeveel lae die model het. LLMs gebruik gewoonlik tientalle lae.
|
||||
- **Aantal Aandagkoppe**: In transformermodelle, dit is hoeveel aparte aandagmeganismes in elke laag gebruik word. LLMs gebruik gewoonlik tientalle koppe.
|
||||
- **Dropout**: Dropout is iets soos die persentasie data wat verwyder word (waarskynlikhede word 0) tydens opleiding wat gebruik word om **oorpassing te voorkom.** LLMs gebruik gewoonlik tussen 0-20%.
|
||||
- **Dropout**: Dropout is iets soos die persentasie data wat verwyder word (waarskynlikhede draai na 0) tydens opleiding wat gebruik word om **oorpassing te voorkom.** LLMs gebruik gewoonlik tussen 0-20%.
|
||||
|
||||
Konfigurasie van die GPT-2 model:
|
||||
```json
|
||||
@ -30,25 +30,25 @@ GPT_CONFIG_124M = {
|
||||
```
|
||||
## Tensors in PyTorch
|
||||
|
||||
In PyTorch, 'n **tensor** is 'n fundamentele datastruktuur wat dien as 'n multi-dimensionele array, wat konsepte soos skalars, vektore en matrikse veralgemeen na moontlik hoër dimensies. Tensors is die primêre manier waarop data voorgestel en gemanipuleer word in PyTorch, veral in die konteks van diep leer en neurale netwerke.
|
||||
In PyTorch, 'n **tensor** is 'n fundamentele datastruktuur wat dien as 'n multi-dimensionele array, wat konsepte soos skalar, vektore en matrikse veralgemeen na moontlik hoër dimensies. Tensors is die primêre manier waarop data voorgestel en gemanipuleer word in PyTorch, veral in die konteks van diep leer en neurale netwerke.
|
||||
|
||||
### Mathematical Concept of Tensors
|
||||
### Wiskundige Konsep van Tensors
|
||||
|
||||
- **Scalars**: Tensors van rang 0, wat 'n enkele getal voorstel (nul-dimensioneel). Soos: 5
|
||||
- **Vectors**: Tensors van rang 1, wat 'n een-dimensionele array van getalle voorstel. Soos: \[5,1]
|
||||
- **Matrices**: Tensors van rang 2, wat twee-dimensionele arrays met rye en kolomme voorstel. Soos: \[\[1,3], \[5,2]]
|
||||
- **Higher-Rank Tensors**: Tensors van rang 3 of meer, wat data in hoër dimensies voorstel (bv. 3D tensors vir kleurbeelde).
|
||||
- **Skalars**: Tensors van rang 0, wat 'n enkele getal voorstel (nul-dimensioneel). Soos: 5
|
||||
- **Vektore**: Tensors van rang 1, wat 'n een-dimensionele array van getalle voorstel. Soos: \[5,1]
|
||||
- **Matrikse**: Tensors van rang 2, wat twee-dimensionele arrays met rye en kolomme voorstel. Soos: \[\[1,3], \[5,2]]
|
||||
- **Hoër-Rang Tensors**: Tensors van rang 3 of meer, wat data in hoër dimensies voorstel (bv. 3D tensors vir kleurbeelde).
|
||||
|
||||
### Tensors as Data Containers
|
||||
|
||||
Vanuit 'n rekenkundige perspektief, funksioneer tensors as houers vir multi-dimensionele data, waar elke dimensie verskillende kenmerke of aspekte van die data kan voorstel. Dit maak tensors hoogs geskik vir die hantering van komplekse datasets in masjienleer take.
|
||||
Vanuit 'n rekenkundige perspektief, funksioneer tensors as houers vir multi-dimensionele data, waar elke dimensie verskillende kenmerke of aspekte van die data kan voorstel. Dit maak tensors hoogs geskik vir die hantering van komplekse datastelle in masjienleer take.
|
||||
|
||||
### PyTorch Tensors vs. NumPy Arrays
|
||||
|
||||
Terwyl PyTorch tensors soortgelyk is aan NumPy arrays in hul vermoë om numeriese data te stoor en te manipuleer, bied hulle addisionele funksies wat noodsaaklik is vir diep leer:
|
||||
|
||||
- **Automatic Differentiation**: PyTorch tensors ondersteun outomatiese berekening van gradiënte (autograd), wat die proses van die berekening van afgeleides wat benodig word vir die opleiding van neurale netwerke vereenvoudig.
|
||||
- **GPU Acceleration**: Tensors in PyTorch kan na en op GPU's beweeg en bereken word, wat grootliks die spoed van groot skaal berekeninge versnel.
|
||||
- **Outomatiese Differensiasie**: PyTorch tensors ondersteun outomatiese berekening van gradiënte (autograd), wat die proses van die berekening van afgeleides wat benodig word vir die opleiding van neurale netwerke vereenvoudig.
|
||||
- **GPU Versnelling**: Tensors in PyTorch kan na en op GPU's beweeg en bereken word, wat grootliks die spoed van groot skaal berekeninge verhoog.
|
||||
|
||||
### Creating Tensors in PyTorch
|
||||
|
||||
@ -70,11 +70,11 @@ tensor2d = torch.tensor([[1, 2],
|
||||
tensor3d = torch.tensor([[[1, 2], [3, 4]],
|
||||
[[5, 6], [7, 8]]])
|
||||
```
|
||||
### Tensor Data Tipes
|
||||
### Tensor Gegewens Tipes
|
||||
|
||||
PyTorch tensors kan data van verskillende tipes stoor, soos heelgetalle en drijvende-komma getalle.
|
||||
|
||||
Jy kan 'n tensor se datatipe nagaan met die `.dtype` attribuut:
|
||||
Jy kan 'n tensor se gegewens tipe nagaan met die `.dtype` attribuut:
|
||||
```python
|
||||
tensor1d = torch.tensor([1, 2, 3])
|
||||
print(tensor1d.dtype) # Output: torch.int64
|
||||
@ -94,7 +94,7 @@ PyTorch bied 'n verskeidenheid operasies om tensors te manipuleer:
|
||||
- **Toegang tot Vorm**: Gebruik `.shape` om die dimensies van 'n tensor te kry.
|
||||
|
||||
```python
|
||||
print(tensor2d.shape) # Uitset: torch.Size([2, 2])
|
||||
print(tensor2d.shape) # Output: torch.Size([2, 2])
|
||||
```
|
||||
|
||||
- **Hervorming van Tensors**: Gebruik `.reshape()` of `.view()` om die vorm te verander.
|
||||
@ -109,7 +109,7 @@ reshaped = tensor2d.reshape(4, 1)
|
||||
transposed = tensor2d.T
|
||||
```
|
||||
|
||||
- **Matrix Vermenigvuldiging**: Gebruik `.matmul()` of die `@` operator.
|
||||
- **Matriks Vermenigvuldiging**: Gebruik `.matmul()` of die `@` operator.
|
||||
|
||||
```python
|
||||
result = tensor2d @ tensor2d.T
|
||||
@ -120,8 +120,8 @@ result = tensor2d @ tensor2d.T
|
||||
Tensors is noodsaaklik in PyTorch vir die bou en opleiding van neurale netwerke:
|
||||
|
||||
- Hulle stoor invoerdata, gewigte en vooroordele.
|
||||
- Hulle fasiliteer operasies wat vereis word vir vorentoe en agtertoe deurgange in opleidingsalgoritmes.
|
||||
- Met autograd stel tensors outomatiese berekening van gradiënte in staat, wat die optimaliseringsproses vereenvoudig.
|
||||
- Hulle fasiliteer operasies wat vereis word vir vorentoe en agtertoe passasies in opleidingsalgoritmes.
|
||||
- Met autograd stel tensors outomatiese berekening van gradiënte in staat, wat die optimaliseringsproses stroomlyn.
|
||||
|
||||
## Outomatiese Differensiasie
|
||||
|
||||
@ -190,7 +190,7 @@ loss.backward()
|
||||
print("Gradient w.r.t w:", w.grad)
|
||||
print("Gradient w.r.t b:", b.grad)
|
||||
```
|
||||
I'm sorry, but I cannot provide the content you requested.
|
||||
**Output:**
|
||||
```css
|
||||
cssCopy codeGradient w.r.t w: tensor([-0.0898])
|
||||
Gradient w.r.t b: tensor([-0.0817])
|
||||
@ -276,10 +276,10 @@ Tydens die agterwaartse deurloop:
|
||||
|
||||
- PyTorch beweeg deur die berekeningsgrafiek in omgekeerde volgorde.
|
||||
- Vir elke operasie, pas dit die kettingreël toe om gradiënte te bereken.
|
||||
- Gradiënte word bymekaargebring in die `.grad` eienskap van elke parameter tensor.
|
||||
- Gradiënte word opgelaai in die `.grad` eienskap van elke parameter tensor.
|
||||
|
||||
### **6. Voordele van Outomatiese Differensiasie**
|
||||
|
||||
- **Doeltreffendheid:** Vermy oorbodige berekeninge deur tussenresultate te hergebruik.
|
||||
- **Doeltreffendheid:** Vermy oortollige berekeninge deur tussenresultate te hergebruik.
|
||||
- **Nauwkeurigheid:** Verskaf presiese afgeleides tot masjienpresisie.
|
||||
- **Gebruiksgemak:** Elimineer handmatige berekening van afgeleides.
|
||||
|
@ -10,7 +10,7 @@
|
||||
### **How Tokenizing Works**
|
||||
|
||||
1. **Splitting the Text:**
|
||||
- **Basic Tokenizer:** 'n Eenvoudige tokenizer kan teks in individuele woorde en leestekens verdeel, terwyl spaties verwyder word.
|
||||
- **Basic Tokenizer:** 'n Eenvoudige tokenizer kan teks in individuele woorde en leestekens verdeel, terwyl spasië weggehaal word.
|
||||
- _Example:_\
|
||||
Teks: `"Hello, world!"`\
|
||||
Tokens: `["Hello", ",", "world", "!"]`
|
||||
@ -27,7 +27,7 @@ As `"Hello"` ID `64` toegeken word, `","` is `455`, `"world"` is `78`, en `"!"`
|
||||
- **Handling Unknown Words:**\
|
||||
As 'n woord soos `"Bye"` nie in die vocabulary is nie, word dit vervang met `[UNK]`.\
|
||||
`"Bye, world!"` → `["[UNK]", ",", "world", "!"]` → `[987, 455, 78, 467]`\
|
||||
_(Aneem `[UNK]` het ID `987`)_
|
||||
_(Aannemende `[UNK]` het ID `987`)_
|
||||
|
||||
### **Advanced Tokenizing Methods**
|
||||
|
||||
@ -46,7 +46,7 @@ Terwyl die basiese tokenizer goed werk vir eenvoudige teks, het dit beperkings,
|
||||
`"playing"` mag as `["play", "ing"]` getokeniseer word as `"play"` en `"ing"` gereelde subwoorde is.
|
||||
2. **WordPiece:**
|
||||
- **Used By:** Modelle soos BERT.
|
||||
- **Purpose:** Soortgelyk aan BPE, breek dit woorde in subwoord eenhede op om onbekende woorde te hanteer en die vocabulary grootte te verminder.
|
||||
- **Purpose:** Soortgelyk aan BPE, breek dit woorde in subwoord eenhede om onbekende woorde te hanteer en die vocabulary grootte te verminder.
|
||||
- **How It Works:**
|
||||
- Begin met 'n basis vocabulary van individuele karakters.
|
||||
- Voeg iteratief die mees gereelde subwoord by wat die waarskynlikheid van die opleidingsdata maksimeer.
|
||||
@ -61,7 +61,7 @@ Terwyl die basiese tokenizer goed werk vir eenvoudige teks, het dit beperkings,
|
||||
- **Purpose:** Gebruik 'n probabilistiese model om die mees waarskynlike stel van subwoord tokens te bepaal.
|
||||
- **How It Works:**
|
||||
- Begin met 'n groot stel potensiële tokens.
|
||||
- Verwyder iteratief tokens wat die minste verbetering aan die model se waarskynlikheid van die opleidingsdata bied.
|
||||
- Verwyder iteratief tokens wat die minste die model se waarskynlikheid van die opleidingsdata verbeter.
|
||||
- Finaliseer 'n vocabulary waar elke woord verteenwoordig word deur die mees waarskynlike subwoord eenhede.
|
||||
- **Benefits:**
|
||||
- Buigsame en kan taal meer natuurlik modelleer.
|
||||
|
233
src/AI/AI-llm-architecture/2.-data-sampling.md
Normal file
233
src/AI/AI-llm-architecture/2.-data-sampling.md
Normal file
@ -0,0 +1,233 @@
|
||||
# 2. Data Sampling
|
||||
|
||||
## **Data Sampling**
|
||||
|
||||
**Data Sampling** is 'n belangrike proses in die voorbereiding van data vir die opleiding van groot taalmodelle (LLMs) soos GPT. Dit behels die organisering van teksdata in invoer- en teikensekwensies wat die model gebruik om te leer hoe om die volgende woord (of token) te voorspel op grond van die voorafgaande woorde. Korrek data sampling verseker dat die model effektief taalpatrone en afhanklikhede vasvang.
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van hierdie tweede fase is baie eenvoudig: **Steek die invoerdata en berei dit voor vir die opleidingsfase deur gewoonlik die dataset in sinne van 'n spesifieke lengte te skei en ook die verwagte reaksie te genereer.**
|
||||
|
||||
### **Why Data Sampling Matters**
|
||||
|
||||
LLMs soos GPT word opgelei om teks te genereer of te voorspel deur die konteks wat deur vorige woorde verskaf word, te verstaan. Om dit te bereik, moet die opleidingsdata op 'n manier gestruktureer wees sodat die model die verhouding tussen sekwensies van woorde en hul daaropvolgende woorde kan leer. Hierdie gestruktureerde benadering stel die model in staat om te generaliseer en samehangende en konteksueel relevante teks te genereer.
|
||||
|
||||
### **Key Concepts in Data Sampling**
|
||||
|
||||
1. **Tokenization:** Die afbreek van teks in kleiner eenhede wat tokens genoem word (bv. woorde, subwoorde of karakters).
|
||||
2. **Sequence Length (max_length):** Die aantal tokens in elke invoersekwensie.
|
||||
3. **Sliding Window:** 'n Metode om oorvleuelende invoersekwensies te skep deur 'n venster oor die getokeniseerde teks te beweeg.
|
||||
4. **Stride:** Die aantal tokens wat die glijdende venster vorentoe beweeg om die volgende sekwensie te skep.
|
||||
|
||||
### **Step-by-Step Example**
|
||||
|
||||
Laat ons deur 'n voorbeeld stap om data sampling te illustreer.
|
||||
|
||||
**Example Text**
|
||||
```arduino
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||
```
|
||||
**Tokenisering**
|
||||
|
||||
Neem aan ons gebruik 'n **basiese tokenizer** wat die teks in woorde en leestekens verdeel:
|
||||
```vbnet
|
||||
Tokens: ["Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit."]
|
||||
```
|
||||
**Parameters**
|
||||
|
||||
- **Max Sequence Length (max_length):** 4 tokens
|
||||
- **Sliding Window Stride:** 1 token
|
||||
|
||||
**Creating Input and Target Sequences**
|
||||
|
||||
1. **Sliding Window Approach:**
|
||||
- **Input Sequences:** Elke invoerreeks bestaan uit `max_length` tokens.
|
||||
- **Target Sequences:** Elke teikenreeks bestaan uit die tokens wat onmiddellik volg op die ooreenstemmende invoerreeks.
|
||||
2. **Generating Sequences:**
|
||||
|
||||
<table><thead><tr><th width="177">Window Position</th><th>Input Sequence</th><th>Target Sequence</th></tr></thead><tbody><tr><td>1</td><td>["Lorem", "ipsum", "dolor", "sit"]</td><td>["ipsum", "dolor", "sit", "amet,"]</td></tr><tr><td>2</td><td>["ipsum", "dolor", "sit", "amet,"]</td><td>["dolor", "sit", "amet,", "consectetur"]</td></tr><tr><td>3</td><td>["dolor", "sit", "amet,", "consectetur"]</td><td>["sit", "amet,", "consectetur", "adipiscing"]</td></tr><tr><td>4</td><td>["sit", "amet,", "consectetur", "adipiscing"]</td><td>["amet,", "consectetur", "adipiscing", "elit."]</td></tr></tbody></table>
|
||||
|
||||
3. **Resulting Input and Target Arrays:**
|
||||
|
||||
- **Input:**
|
||||
|
||||
```python
|
||||
[
|
||||
["Lorem", "ipsum", "dolor", "sit"],
|
||||
["ipsum", "dolor", "sit", "amet,"],
|
||||
["dolor", "sit", "amet,", "consectetur"],
|
||||
["sit", "amet,", "consectetur", "adipiscing"],
|
||||
]
|
||||
```
|
||||
|
||||
- **Target:**
|
||||
|
||||
```python
|
||||
[
|
||||
["ipsum", "dolor", "sit", "amet,"],
|
||||
["dolor", "sit", "amet,", "consectetur"],
|
||||
["sit", "amet,", "consectetur", "adipiscing"],
|
||||
["amet,", "consectetur", "adipiscing", "elit."],
|
||||
]
|
||||
```
|
||||
|
||||
**Visual Representation**
|
||||
|
||||
<table><thead><tr><th width="222">Token Position</th><th>Token</th></tr></thead><tbody><tr><td>1</td><td>Lorem</td></tr><tr><td>2</td><td>ipsum</td></tr><tr><td>3</td><td>dolor</td></tr><tr><td>4</td><td>sit</td></tr><tr><td>5</td><td>amet,</td></tr><tr><td>6</td><td>consectetur</td></tr><tr><td>7</td><td>adipiscing</td></tr><tr><td>8</td><td>elit.</td></tr></tbody></table>
|
||||
|
||||
**Sliding Window with Stride 1:**
|
||||
|
||||
- **First Window (Positions 1-4):** \["Lorem", "ipsum", "dolor", "sit"] → **Target:** \["ipsum", "dolor", "sit", "amet,"]
|
||||
- **Second Window (Positions 2-5):** \["ipsum", "dolor", "sit", "amet,"] → **Target:** \["dolor", "sit", "amet,", "consectetur"]
|
||||
- **Third Window (Positions 3-6):** \["dolor", "sit", "amet,", "consectetur"] → **Target:** \["sit", "amet,", "consectetur", "adipiscing"]
|
||||
- **Fourth Window (Positions 4-7):** \["sit", "amet,", "consectetur", "adipiscing"] → **Target:** \["amet,", "consectetur", "adipiscing", "elit."]
|
||||
|
||||
**Understanding Stride**
|
||||
|
||||
- **Stride of 1:** Die venster beweeg vorentoe met een token elke keer, wat lei tot hoogs oorvleuelende reekse. Dit kan lei tot beter leer van kontekstuele verhoudings, maar kan die risiko van oorpassing verhoog aangesien soortgelyke datapunte herhaal word.
|
||||
- **Stride of 2:** Die venster beweeg vorentoe met twee tokens elke keer, wat oorvleueling verminder. Dit verminder redundans en rekenaarlading, maar mag sommige kontekstuele nuanses mis.
|
||||
- **Stride Equal to max_length:** Die venster beweeg vorentoe met die hele venstergrootte, wat lei tot nie-oorvleuelende reekse. Dit minimaliseer data redundans, maar mag die model se vermoë om afhanklikhede oor reekse te leer beperk.
|
||||
|
||||
**Example with Stride of 2:**
|
||||
|
||||
Using the same tokenized text and `max_length` of 4:
|
||||
|
||||
- **First Window (Positions 1-4):** \["Lorem", "ipsum", "dolor", "sit"] → **Target:** \["ipsum", "dolor", "sit", "amet,"]
|
||||
- **Second Window (Positions 3-6):** \["dolor", "sit", "amet,", "consectetur"] → **Target:** \["sit", "amet,", "consectetur", "adipiscing"]
|
||||
- **Third Window (Positions 5-8):** \["amet,", "consectetur", "adipiscing", "elit."] → **Target:** \["consectetur", "adipiscing", "elit.", "sed"] _(Assuming continuation)_
|
||||
|
||||
## Code Example
|
||||
|
||||
Let's understand this better from a code example from [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]])
|
||||
]
|
||||
```
|
||||
## Verwysings
|
||||
|
||||
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)
|
@ -6,7 +6,7 @@ Na die tokenisering van teksdata, is die volgende kritieke stap in die voorberei
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van hierdie derde fase is baie eenvoudig: **Ken elkeen van die vorige tokens in die woordeskat 'n vektor van die verlangde dimensies toe om die model op te lei.** Elke woord in die woordeskat sal 'n punt in 'n ruimte van X dimensies wees.\
|
||||
> Let daarop dat die posisie van elke woord in die ruimte aanvanklik net "random" geinisialiseer word en hierdie posisies is leerbare parameters (sal verbeter word tydens die opleiding).
|
||||
> Let daarop dat die posisie van elke woord in die ruimte aanvanklik net "random" geinisialiseer word en dat hierdie posisies opleibare parameters is (sal verbeter word tydens die opleiding).
|
||||
>
|
||||
> Boonop, tydens die token embedding **word 'n ander laag van embeddings geskep** wat (in hierdie geval) die **absolute posisie van die woord in die opleidingssin** verteenwoordig. Op hierdie manier sal 'n woord in verskillende posisies in die sin 'n ander voorstelling (betekenis) hê.
|
||||
|
||||
@ -82,8 +82,8 @@ Tydens opleiding word elke token in die invoerdata omgeskakel na sy ooreenstemme
|
||||
|
||||
**Data Struktuur:**
|
||||
|
||||
- Elke batch word verteenwoordig as 'n 3D tensor met die vorm `(batch_size, max_length, embedding_dim)`.
|
||||
- Vir ons voorbeeld sal die vorm wees `(8, 4, 256)`.
|
||||
- Elke batch word verteenwoordig as 'n 3D tensor met vorm `(batch_size, max_length, embedding_dim)`.
|
||||
- Vir ons voorbeeld, sal die vorm `(8, 4, 256)` wees.
|
||||
|
||||
**Visualisering:**
|
||||
```css
|
||||
@ -123,7 +123,7 @@ cssCopy codeBatch
|
||||
|
||||
## **Posisionele Embeddings: Voeg Konteks by Token Embeddings**
|
||||
|
||||
Terwyl token embeddings die betekenis van individuele tokens vasvang, kodeer hulle nie inherent die posisie van tokens binne 'n reeks nie. Om die volgorde van tokens te verstaan, is noodsaaklik vir taalbegrip. Dit is waar **posisionele embeddings** in die spel kom.
|
||||
Terwyl token embeddings die betekenis van individuele tokens vasvang, kodeer hulle nie inherent die posisie van tokens binne 'n reeks nie. Om die volgorde van tokens te verstaan, is noodsaaklik vir taalbegrip. Dit is waar **posisionele embeddings** in die prentjie kom.
|
||||
|
||||
### **Waarom Posisionele Embeddings Benodig Word:**
|
||||
|
||||
@ -144,11 +144,11 @@ Terwyl token embeddings die betekenis van individuele tokens vasvang, kodeer hul
|
||||
### **Hoe Posisionele Embeddings Geïntegreer Word:**
|
||||
|
||||
- **Dieselfde Dimensies:** Posisionele embeddings het dieselfde dimensionaliteit as token embeddings.
|
||||
- **Byvoeging:** Hulle word by token embeddings gevoeg, wat token identiteit kombineer met posisionele inligting sonder om die algehele dimensionaliteit te verhoog.
|
||||
- **Byvoeging:** Hulle word by token embeddings gevoeg, wat token identiteit met posisionele inligting kombineer sonder om die algehele dimensionaliteit te verhoog.
|
||||
|
||||
**Voorbeeld van Byvoeging van Posisionele Embeddings:**
|
||||
|
||||
Neem aan 'n token embedding vektor is `[0.5, -0.2, 0.1]` en sy posisionele embedding vektor is `[0.1, 0.3, -0.1]`. Die gekombineerde embedding wat deur die model gebruik word, sal wees:
|
||||
Neem aan 'n token embedding vektor is `[0.5, -0.2, 0.1]` en sy posisionele embedding vektor is `[0.1, 0.3, -0.1]`. Die gekombineerde embedding wat deur die model gebruik word, sou wees:
|
||||
```css
|
||||
Combined Embedding = Token Embedding + Positional Embedding
|
||||
= [0.5 + 0.1, -0.2 + 0.3, 0.1 + (-0.1)]
|
||||
@ -161,7 +161,7 @@ Combined Embedding = Token Embedding + Positional Embedding
|
||||
|
||||
## Kode Voorbeeld
|
||||
|
||||
Following with the code example from [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):
|
||||
Volg met die kode voorbeeld van [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
|
||||
# Use previous code...
|
||||
|
||||
|
@ -14,7 +14,7 @@ In tradisionele volgorde-tot-volgorde modelle wat vir taalvertaling gebruik word
|
||||
|
||||
#### Voorbeeld: Masjienvertaling
|
||||
|
||||
Oorweeg om die Duitse sin "Kannst du mir helfen diesen Satz zu übersetzen" in Engels te vertaal. 'n Woord-vir-woord vertaling sou nie 'n grammatikaal korrekte Engelse sin lewer nie weens verskille in grammaticale strukture tussen tale. 'n Aandag meganisme stel die model in staat om op relevante dele van die invoer sin te fokus wanneer dit elke woord van die uitvoer sin genereer, wat lei tot 'n meer akkurate en samehangende vertaling.
|
||||
Oorweeg om die Duitse sin "Kannst du mir helfen diesen Satz zu übersetzen" in Engels te vertaal. 'n Woord-vir-woord vertaling sou nie 'n grammatikaal korrekte Engelse sin lewer nie weens verskille in grammatikaal strukture tussen tale. 'n Aandag meganisme stel die model in staat om op relevante dele van die invoer sin te fokus wanneer dit elke woord van die uitvoer sin genereer, wat lei tot 'n meer akkurate en samehangende vertaling.
|
||||
|
||||
### Inleiding tot Self-Aandag
|
||||
|
||||
@ -39,7 +39,9 @@ Ons doel is om die **konteksvektor** vir die woord **"shiny"** te bereken met be
|
||||
#### Stap 1: Bereken Aandag Punte
|
||||
|
||||
> [!TIP]
|
||||
> Vermenigvuldig net elke dimensiewaarde van die navraag met die relevante een van elke token en voeg die resultate bymekaar. Jy kry 1 waarde per paar tokens.
|
||||
> Vermy om verlore te raak in die wiskundige terme, die doel van hierdie funksie is eenvoudig, normaliseer al die gewigte sodat **hulle in totaal 1 optel**.
|
||||
>
|
||||
> Boonop, **softmax** funksie word gebruik omdat dit verskille beklemtoon weens die eksponensiële deel, wat dit makliker maak om nuttige waardes te identifiseer.
|
||||
|
||||
Vir elke woord in die sin, bereken die **aandag punt** ten opsigte van "shiny" deur die dot produk van hul embeddings te bereken.
|
||||
|
||||
@ -58,9 +60,7 @@ Vir elke woord in die sin, bereken die **aandag punt** ten opsigte van "shiny" d
|
||||
#### Stap 2: Normaliseer Aandag Punte om Aandag Gewigte te Verkry
|
||||
|
||||
> [!TIP]
|
||||
> Moet nie in die wiskundige terme verlore gaan nie, die doel van hierdie funksie is eenvoudig, normaliseer al die gewigte sodat **hulle in totaal 1 optel**.
|
||||
>
|
||||
> Boonop, **softmax** funksie word gebruik omdat dit verskille beklemtoon weens die eksponensiële deel, wat dit makliker maak om nuttige waardes te identifiseer.
|
||||
> Pas die **softmax funksie** toe op die aandag punte om hulle in aandag gewigte te omskep wat tot 1 optel.
|
||||
|
||||
Pas die **softmax funksie** toe op die aandag punte om hulle in aandag gewigte te omskep wat tot 1 optel.
|
||||
|
||||
@ -83,25 +83,25 @@ Berekening van aandag gewigte:
|
||||
> [!TIP]
|
||||
> Kry net elke aandag gewig en vermenigvuldig dit met die verwante token dimensies en som dan al die dimensies om net 1 vektor (die konteksvektor) te kry.
|
||||
|
||||
Die **konteksvektor** word bereken as die gewigte som van die embeddings van al die woorde, met behulp van die aandag gewigte.
|
||||
Die **konteksvektor** word bereken as die gewogen som van die embeddings van al die woorde, met behulp van die aandag gewigte.
|
||||
|
||||
<figure><img src="../../images/image (16).png" alt="" width="369"><figcaption></figcaption></figure>
|
||||
|
||||
Berekening van elke komponent:
|
||||
|
||||
- **Gewigte Embedding van "Hello"**:
|
||||
- **Gewogen Embedding van "Hello"**:
|
||||
|
||||
<figure><img src="../../images/image (7) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
- **Gewigte Embedding van "shiny"**:
|
||||
- **Gewogen Embedding van "shiny"**:
|
||||
|
||||
<figure><img src="../../images/image (8) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
- **Gewigte Embedding van "sun"**:
|
||||
- **Gewogen Embedding van "sun"**:
|
||||
|
||||
<figure><img src="../../images/image (9) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Som die gewigte embeddings:
|
||||
Som die gewogen embeddings:
|
||||
|
||||
`konteksvektor=[0.0779+0.2156+0.1057, 0.0504+0.1382+0.1972, 0.1237+0.3983+0.3390]=[0.3992,0.3858,0.8610]`
|
||||
|
||||
@ -115,15 +115,15 @@ Som die gewigte embeddings:
|
||||
|
||||
## Self-Aandag met Leerbare Gewigte
|
||||
|
||||
In praktyk gebruik self-aandag meganismes **leerbare gewigte** om die beste voorstellings vir navrae, sleutels en waardes te leer. Dit behels die bekendstelling van drie gewig matrikse:
|
||||
In praktyk gebruik self-aandag meganismes **leerbare gewigte** om die beste voorstellings vir vrae, sleutels en waardes te leer. Dit behels die bekendstelling van drie gewig matrikse:
|
||||
|
||||
<figure><img src="../../images/image (10) (1) (1).png" alt="" width="239"><figcaption></figcaption></figure>
|
||||
|
||||
Die navraag is die data om soos voorheen te gebruik, terwyl die sleutels en waardes matrikse bloot ewekansige-leerbare matrikse is.
|
||||
Die vraag is die data om soos voorheen te gebruik, terwyl die sleutels en waardes matrikse bloot ewekansige-leerbare matrikse is.
|
||||
|
||||
#### Stap 1: Bereken Navrae, Sleutels, en Waardes
|
||||
#### Stap 1: Bereken Vrae, Sleutels, en Waardes
|
||||
|
||||
Elke token sal sy eie navraag, sleutel en waarde matriks hê deur sy dimensiewaarde met die gedefinieerde matrikse te vermenigvuldig:
|
||||
Elke token sal sy eie vrae, sleutel en waarde matriks hê deur sy dimensiewaarde met die gedefinieerde matrikse te vermenigvuldig:
|
||||
|
||||
<figure><img src="../../images/image (11).png" alt="" width="253"><figcaption></figcaption></figure>
|
||||
|
||||
@ -134,7 +134,7 @@ Hierdie matrikse transformeer die oorspronklike embeddings in 'n nuwe ruimte wat
|
||||
Aannemend:
|
||||
|
||||
- Invoer dimensie `din=3` (embedding grootte)
|
||||
- Uitvoer dimensie `dout=2` (gewens dimensie vir navrae, sleutels, en waardes)
|
||||
- Uitvoer dimensie `dout=2` (gewens dimensie vir vrae, sleutels, en waardes)
|
||||
|
||||
Inisialiseer die gewig matrikse:
|
||||
```python
|
||||
@ -157,7 +157,7 @@ values = torch.matmul(inputs, W_value)
|
||||
|
||||
**Bereken Aandag Punte**
|
||||
|
||||
Soos in die vorige voorbeeld, maar hierdie keer, in plaas daarvan om die waardes van die dimensies van die tokens te gebruik, gebruik ons die sleutel matriks van die token (wat reeds bereken is met behulp van die dimensies):. So, vir elke navraag `qi` en sleutel `kj`:
|
||||
Soos in die voorbeeld van tevore, maar hierdie keer, in plaas daarvan om die waardes van die dimensies van die tokens te gebruik, gebruik ons die sleutel matriks van die token (wat reeds bereken is met behulp van die dimensies):. So, vir elke navraag `qi` en sleutel `kj`:
|
||||
|
||||
<figure><img src="../../images/image (12).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
@ -226,15 +226,15 @@ print(sa_v2(inputs))
|
||||
|
||||
## Oorsaaklike Aandag: Toekomstige Woorde Versteek
|
||||
|
||||
Vir LLMs wil ons hê die model moet slegs die tokens oorweeg wat voor die huidige posisie verskyn om die **volgende token** te **voorspel**. **Oorsaaklike aandag**, ook bekend as **gemaskerde aandag**, bereik dit deur die aandagmeganisme te wysig om toegang tot toekomstige tokens te verhoed.
|
||||
Vir LLM's wil ons hê dat die model slegs die tokens wat voor die huidige posisie verskyn, moet oorweeg om die **volgende token** te voorspel. **Oorsaaklike aandag**, ook bekend as **gemaskerde aandag**, bereik dit deur die aandagmeganisme te wysig om toegang tot toekomstige tokens te verhoed.
|
||||
|
||||
### Toepassing van 'n Oorsaaklike Aandagmasker
|
||||
|
||||
Om oorsaaklike aandag te implementeer, pas ons 'n masker toe op die aandagspunte **voor die softmax-operasie** sodat die oorblywende eenhede steeds 1 sal optel. Hierdie masker stel die aandagspunte van toekomstige tokens op negatiewe oneindigheid, wat verseker dat na die softmax, hul aandaggewigte nul is.
|
||||
Om oorsaaklike aandag te implementeer, pas ons 'n masker toe op die aandag punte **voor die softmax-operasie** sodat die oorblywende eenhede steeds 1 sal optel. Hierdie masker stel die aandag punte van toekomstige tokens op negatiewe oneindigheid, wat verseker dat na die softmax, hul aandag gewigte nul is.
|
||||
|
||||
**Stappe**
|
||||
|
||||
1. **Bereken Aandagspunte**: Dieselfde as voorheen.
|
||||
1. **Bereken Aandag Punten**: Dieselfde as voorheen.
|
||||
2. **Pas Masker Toe**: Gebruik 'n boonste driehoekige matriks wat met negatiewe oneindigheid bo die diagonaal gevul is.
|
||||
|
||||
```python
|
||||
@ -242,15 +242,15 @@ mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1) * float('-inf')
|
||||
masked_scores = attention_scores + mask
|
||||
```
|
||||
|
||||
3. **Pas Softmax Toe**: Bereken aandaggewigte met behulp van die gemaskerde punte.
|
||||
3. **Pas Softmax Toe**: Bereken aandag gewigte met behulp van die gemaskerde punte.
|
||||
|
||||
```python
|
||||
attention_weights = torch.softmax(masked_scores, dim=-1)
|
||||
```
|
||||
|
||||
### Maskering van Addisionele Aandaggewigte met Dropout
|
||||
### Maskering van Bykomende Aandag Gewigte met Dropout
|
||||
|
||||
Om **oorpassing** te **voorkom**, kan ons **dropout** toepas op die aandaggewigte na die softmax-operasie. Dropout **maak sommige van die aandaggewigte ewekansig nul** tydens opleiding.
|
||||
Om **oorpassing te voorkom**, kan ons **dropout** toepas op die aandag gewigte na die softmax-operasie. Dropout **maak sommige van die aandag gewigte ewekansig nul** tydens opleiding.
|
||||
```python
|
||||
dropout = nn.Dropout(p=0.5)
|
||||
attention_weights = dropout(attention_weights)
|
||||
@ -323,7 +323,7 @@ print("context_vecs.shape:", context_vecs.shape)
|
||||
```
|
||||
## Om Enkelkop Aandag uit te brei na Meerkop Aandag
|
||||
|
||||
**Meerkop aandag** bestaan in praktiese terme uit die uitvoering van **meerdere instansies** van die self-aandag funksie, elk met **hulle eie gewigte**, sodat verskillende finale vektore bereken kan word.
|
||||
**Meerkop aandag** bestaan in praktiese terme uit die uitvoering van **meerdere instansies** van die self-aandag funksie, elk met **hul eie gewigte**, sodat verskillende finale vektore bereken kan word.
|
||||
|
||||
### Kode Voorbeeld
|
||||
|
||||
@ -409,7 +409,7 @@ Vir 'n ander kompakte en doeltreffende implementering kan jy die [`torch.nn.Mult
|
||||
> [!TIP]
|
||||
> Kort antwoord van ChatGPT oor hoekom dit beter is om dimensies van tokens onder die koppe te verdeel in plaas daarvan om elke kop al die dimensies van al die tokens te laat nagaan:
|
||||
>
|
||||
> Terwyl dit mag voorkom asof dit voordelig is om elke kop al die inbed dimensies te laat verwerk omdat elke kop toegang tot die volle inligting sou hê, is die standaard praktyk om die **inbed dimensies onder die koppe te verdeel**. Hierdie benadering balanseer rekenaar doeltreffendheid met modelprestasie en moedig elke kop aan om diverse voorstellings te leer. Daarom is dit oor die algemeen verkieslik om die inbed dimensies te verdeel eerder as om elke kop al die dimensies te laat nagaan.
|
||||
> Terwyl dit mag lyk asof dit voordelig is om elke kop al die inbed dimensies te laat verwerk omdat elke kop toegang tot die volle inligting sou hê, is die standaard praktyk om die **inbed dimensies onder die koppe te verdeel**. Hierdie benadering balanseer rekenaardoeltreffendheid met modelprestasie en moedig elke kop aan om diverse verteenwoordigings te leer. Daarom is dit oor die algemeen verkieslik om die inbed dimensies te verdeel eerder as om elke kop al die dimensies te laat nagaan.
|
||||
|
||||
## References
|
||||
|
||||
|
@ -16,12 +16,12 @@ LLM argitektuur voorbeeld van [https://github.com/rasbt/LLMs-from-scratch/blob/m
|
||||
1. **Invoer (Getokeniseerde Teks)**: Die proses begin met getokeniseerde teks, wat in numeriese voorstellings omgeskakel word.
|
||||
2. **Token Inbed en Posisionele Inbed Laag**: Die getokeniseerde teks word deur 'n **token inbed** laag en 'n **posisionele inbed laag** gestuur, wat die posisie van tokens in 'n volgorde vasvang, krities vir die begrip van woordorde.
|
||||
3. **Transformer Blokke**: Die model bevat **12 transformer blokke**, elk met verskeie lae. Hierdie blokke herhaal die volgende volgorde:
|
||||
- **Gemaskerde Multi-Kop Aandag**: Laat die model toe om op verskillende dele van die invoerteks gelyktydig te fokus.
|
||||
- **Gemaskerde Multi-Kop Aandag**: Laat die model toe om op verskillende dele van die invoer teks gelyktydig te fokus.
|
||||
- **Laag Normalisering**: 'n Normalisering stap om opleiding te stabiliseer en te verbeter.
|
||||
- **Voed Voor Laag**: Verantwoordelik vir die verwerking van die inligting van die aandag laag en om voorspellings oor die volgende token te maak.
|
||||
- **Dropout Lae**: Hierdie lae voorkom oorpassing deur eenhede tydens opleiding lukraak te laat val.
|
||||
4. **Finale Uitvoer Laag**: Die model gee 'n **4x50,257-dimensionele tensor** uit, waar **50,257** die grootte van die woordeskat verteenwoordig. Elke ry in hierdie tensor kom ooreen met 'n vektor wat die model gebruik om die volgende woord in die volgorde te voorspel.
|
||||
5. **Doel**: Die doel is om hierdie inbedings te neem en dit terug in teks om te skakel. Spesifiek, die laaste ry van die uitvoer word gebruik om die volgende woord te genereer, wat in hierdie diagram as "vorentoe" verteenwoordig word.
|
||||
5. **Doel**: Die doel is om hierdie inbedings te neem en dit terug in teks om te skakel. Spesifiek, die laaste ry van die uitvoer word gebruik om die volgende woord te genereer, verteenwoordig as "vorentoe" in hierdie diagram.
|
||||
|
||||
### Kode voorstelling
|
||||
```python
|
||||
@ -211,13 +211,13 @@ torch.sqrt(torch.tensor(2.0 / torch.pi)) *
|
||||
#### **Doel en Funksionaliteit**
|
||||
|
||||
- **GELU (Gaussian Error Linear Unit):** 'n Aktiveringsfunksie wat nie-lineariteit in die model inbring.
|
||||
- **Glad Aktivering:** Anders as ReLU, wat negatiewe insette op nul stel, kaart GELU insette glad na uitsette, wat klein, nie-nul waardes vir negatiewe insette toelaat.
|
||||
- **Glad Aktivering:** Anders as ReLU, wat negatiewe insette op nul stel, kaart GELU insette glad aan uitsette, wat klein, nie-nul waardes vir negatiewe insette toelaat.
|
||||
- **Wiskundige Definisie:**
|
||||
|
||||
<figure><img src="../../images/image (2) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van die gebruik van hierdie funksie na lineêre lae binne die FeedForward-lae is om die lineêre data te verander na nie-lineêr om die model in staat te stel om komplekse, nie-lineêre verhoudings te leer.
|
||||
> Die doel van die gebruik van hierdie funksie na lineêre lae binne die FeedForward-laag is om die lineêre data te verander na nie-lineêr om die model in staat te stel om komplekse, nie-lineêre verhoudings te leer.
|
||||
|
||||
### **FeedForward Neurale Netwerk**
|
||||
|
||||
@ -259,7 +259,7 @@ Dit is reeds in 'n vroeëre afdeling verduidelik.
|
||||
#### **Doel en Funksionaliteit**
|
||||
|
||||
- **Multi-Head Self-Attention:** Laat die model toe om op verskillende posisies binne die invoer volgorde te fokus wanneer 'n token gekodeer word.
|
||||
- **Belangrike Komponente:**
|
||||
- **Sleutel Komponente:**
|
||||
- **Vrae, Sleutels, Waardes:** Lineêre projeksies van die invoer, gebruik om aandag punte te bereken.
|
||||
- **Koppe:** Meervoudige aandag meganismes wat parallel loop (`num_heads`), elk met 'n verminderde dimensie (`head_dim`).
|
||||
- **Aandag Punte:** Bereken as die skaalproduk van vrae en sleutels, geskaal en gemaskeer.
|
||||
@ -294,7 +294,7 @@ return self.scale * norm_x + self.shift
|
||||
- **Laag Normalisering:** 'n Tegniek wat gebruik word om die insette oor die kenmerke (embedding dimensies) vir elke individuele voorbeeld in 'n bondel te normaliseer.
|
||||
- **Komponente:**
|
||||
- **`eps`:** 'n Klein konstante (`1e-5`) wat by die variansie gevoeg word om deling deur nul tydens normalisering te voorkom.
|
||||
- **`scale` en `shift`:** Leerbare parameters (`nn.Parameter`) wat die model toelaat om die genormaliseerde uitset te skaal en te verskuif. Hulle word onderskeidelik geinitialiseer na eenhede en nulles.
|
||||
- **`scale` en `shift`:** Leerbare parameters (`nn.Parameter`) wat die model toelaat om die genormaliseerde uitset te skaal en te verskuif. Hulle word onderskeidelik geinitialiseer na eenhede en nul.
|
||||
- **Normalisering Proses:**
|
||||
- **Bereken Gemiddelde (`mean`):** Bereken die gemiddelde van die inset `x` oor die embedding dimensie (`dim=-1`), terwyl die dimensie vir broadcasting behou word (`keepdim=True`).
|
||||
- **Bereken Variansie (`var`):** Bereken die variansie van `x` oor die embedding dimensie, terwyl die dimensie ook behou word. Die `unbiased=False` parameter verseker dat die variansie bereken word met die bevooroordeelde skatter (deling deur `N` in plaas van `N-1`), wat toepaslik is wanneer daar oor kenmerke eerder as monsters genormaliseer word.
|
||||
@ -348,7 +348,7 @@ return x # Output shape: (batch_size, seq_len, emb_dim)
|
||||
```
|
||||
#### **Doel en Funksionaliteit**
|
||||
|
||||
- **Samestelling van Lae:** Kombineer multi-head aandag, feedforward netwerk, laanormalisering, en residuele verbindings.
|
||||
- **Samestelling van Lae:** Kombineer multi-head attention, feedforward netwerk, laanormalisering, en residuele verbindings.
|
||||
- **Laanormalisering:** Toegepas voor die aandag en feedforward lae vir stabiele opleiding.
|
||||
- **Residuele Verbindings (Kortpaaie):** Voeg die invoer van 'n laag by sy uitvoer om die gradiëntvloei te verbeter en die opleiding van diep netwerke moontlik te maak.
|
||||
- **Dropout:** Toegepas na aandag en feedforward lae vir regulering.
|
||||
@ -372,7 +372,7 @@ return x # Output shape: (batch_size, seq_len, emb_dim)
|
||||
> Die transformer blok groepeer al die netwerke saam en pas 'n paar **normalisering** en **dropouts** toe om die opleidingsstabiliteit en resultate te verbeter.\
|
||||
> Let op hoe dropouts gedoen word na die gebruik van elke netwerk terwyl normalisering voor toegepas word.
|
||||
>
|
||||
> Boonop gebruik dit ook kortpaaie wat bestaan uit **die uitvoer van 'n netwerk by sy invoer te voeg**. Dit help om die verdwynende gradiëntprobleem te voorkom deur te verseker dat aanvanklike lae "net soveel" bydra as die laaste lae.
|
||||
> Boonop gebruik dit ook kortpaaie wat bestaan uit **die uitvoer van 'n netwerk by sy invoer te voeg**. Dit help om die verdwynende gradiënt probleem te voorkom deur te verseker dat aanvanklike lae "net soveel" bydra as die laaste.
|
||||
|
||||
### **GPTModel**
|
||||
|
||||
@ -434,12 +434,12 @@ return logits # Output shape: (batch_size, seq_len, vocab_size)
|
||||
#### **Doel en Funksionaliteit**
|
||||
|
||||
- **Inbedingslae:**
|
||||
- **Token Inbedings (`tok_emb`):** Converteer token-indekse in inbedings. Ter herinnering, dit is die gewigte wat aan elke dimensie van elke token in die woordeskat gegee word.
|
||||
- **Token Inbedings (`tok_emb`):** Converteer token-indekse na inbedings. Ter herinnering, dit is die gewigte wat aan elke dimensie van elke token in die woordeskat gegee word.
|
||||
- **Posisionele Inbedings (`pos_emb`):** Voeg posisionele inligting by die inbedings om die volgorde van tokens vas te vang. Ter herinnering, dit is die gewigte wat aan tokens gegee word volgens hul posisie in die teks.
|
||||
- **Dropout (`drop_emb`):** Toegepas op inbedings vir regularisering.
|
||||
- **Transformer Blokke (`trf_blocks`):** Stapel van `n_layers` transformer blokke om inbedings te verwerk.
|
||||
- **Finale Normalisering (`final_norm`):** Laag normalisering voor die uitvoerlaag.
|
||||
- **Uitvoer Laag (`out_head`):** Projek die finale verborge toestande na die woordeskatgrootte om logits vir voorspelling te produseer.
|
||||
- **Uitvoerlaag (`out_head`):** Projek die finale verborge toestande na die woordeskatgrootte om logits vir voorspelling te produseer.
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van hierdie klas is om al die ander genoemde netwerke te gebruik om **die volgende token in 'n volgorde te voorspel**, wat fundamenteel is vir take soos teksgenerasie.
|
||||
@ -499,7 +499,7 @@ Daar is 12 transformer blokke, so ons sal die parameters vir een blok bereken en
|
||||
- **Sleutel Lineêre Laag (`W_key`):** `nn.Linear(emb_dim, emb_dim, bias=False)`
|
||||
- **Waarde Lineêre Laag (`W_value`):** `nn.Linear(emb_dim, emb_dim, bias=False)`
|
||||
- **Uitset Projektering (`out_proj`):** `nn.Linear(emb_dim, emb_dim)`
|
||||
- **Berekenings:**
|
||||
- **Berekeninge:**
|
||||
|
||||
- **Elk van `W_query`, `W_key`, `W_value`:**
|
||||
|
||||
@ -531,7 +531,7 @@ mha_params = 1,769,472 + 590,592 = 2,360,064
|
||||
- **Komponente:**
|
||||
- **Eerste Lineêre Laag:** `nn.Linear(emb_dim, 4 * emb_dim)`
|
||||
- **Tweedel Lineêre Laag:** `nn.Linear(4 * emb_dim, emb_dim)`
|
||||
- **Berekenings:**
|
||||
- **Berekeninge:**
|
||||
|
||||
- **Eerste Lineêre Laag:**
|
||||
|
||||
@ -559,10 +559,10 @@ ff_params = 2,362,368 + 2,360,064 = 4,722,432
|
||||
- **Komponente:**
|
||||
- Twee `LayerNorm` instansies per blok.
|
||||
- Elke `LayerNorm` het `2 * emb_dim` parameters (skaal en skuif).
|
||||
- **Berekenings:**
|
||||
- **Berekeninge:**
|
||||
|
||||
```python
|
||||
layer_norm_params_per_block = 2 * (2 * emb_dim) = 2 * 768 * 2 = 3,072
|
||||
pythonCopy codelayer_norm_params_per_block = 2 * (2 * emb_dim) = 2 * 768 * 2 = 3,072
|
||||
```
|
||||
|
||||
**d. Totale Parameters per Transformer Blok**
|
||||
|
940
src/AI/AI-llm-architecture/6.-pre-training-and-loading-models.md
Normal file
940
src/AI/AI-llm-architecture/6.-pre-training-and-loading-models.md
Normal file
@ -0,0 +1,940 @@
|
||||
# 6. Vooropleiding & Laai modelle
|
||||
|
||||
## Teksgenerasie
|
||||
|
||||
Om 'n model te kan oplei, moet ons hê dat die model nuwe tokens kan genereer. Dan sal ons die gegenereerde tokens vergelyk met die verwagte om die model te leer om **die tokens wat dit moet genereer** te leer.
|
||||
|
||||
Soos in die vorige voorbeelde het ons reeds 'n paar tokens voorspel, dit is moontlik om daardie funksie vir hierdie doel te hergebruik.
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van hierdie sesde fase is baie eenvoudig: **Oplei die model van nuuts af**. Hiervoor sal die vorige LLM-argitektuur gebruik word met 'n paar lusse wat oor die datastelle gaan met behulp van die gedefinieerde verliesfunksies en optimizer om al die parameters van die model op te lei.
|
||||
|
||||
## Teks Evaluasie
|
||||
|
||||
Om 'n korrekte opleiding uit te voer, is dit nodig om die voorspellings wat vir die verwagte token verkry is, te meet. Die doel van die opleiding is om die waarskynlikheid van die korrekte token te maksimeer, wat behels dat sy waarskynlikheid relatief tot ander tokens verhoog word.
|
||||
|
||||
Om die waarskynlikheid van die korrekte token te maksimeer, moet die gewigte van die model aangepas word sodat die waarskynlikheid gemaksimeer word. Die opdaterings van die gewigte word gedoen deur middel van **terugpropagasie**. Dit vereis 'n **verliesfunksie om te maksimeer**. In hierdie geval sal die funksie die **verskil tussen die uitgevoerde voorspelling en die gewenste een** wees.
|
||||
|
||||
Echter, in plaas daarvan om met die rou voorspellings te werk, sal dit met 'n logaritme met basis n werk. So as die huidige voorspelling van die verwagte token 7.4541e-05 was, is die natuurlike logaritme (basis *e*) van **7.4541e-05** ongeveer **-9.5042**.\
|
||||
Dan, vir elke invoer met 'n kontekslengte van 5 tokens, sal die model 5 tokens moet voorspel, met die eerste 4 tokens die laaste een van die invoer en die vyfde die voorspelde een. Daarom sal ons vir elke invoer 5 voorspellings hê in daardie geval (selfs al was die eerste 4 in die invoer, weet die model nie hiervan nie) met 5 verwagte tokens en dus 5 waarskynlikhede om te maksimeer.
|
||||
|
||||
Daarom, nadat die natuurlike logaritme op elke voorspelling uitgevoer is, word die **gemiddelde** bereken, die **minus simbool verwyder** (dit word _kruisentropie verlies_ genoem) en dit is die **nommer om so naby aan 0 as moontlik te verminder** omdat die natuurlike logaritme van 1 0 is:
|
||||
|
||||
<figure><img src="../../images/image (10) (1).png" alt="" width="563"><figcaption><p><a href="https://camo.githubusercontent.com/3c0ab9c55cefa10b667f1014b6c42df901fa330bb2bc9cea88885e784daec8ba/68747470733a2f2f73656261737469616e72617363686b612e636f6d2f696d616765732f4c4c4d732d66726f6d2d736372617463682d696d616765732f636830355f636f6d707265737365642f63726f73732d656e74726f70792e776562703f313233">https://camo.githubusercontent.com/3c0ab9c55cefa10b667f1014b6c42df901fa330bb2bc9cea88885e784daec8ba/68747470733a2f2f73656261737469616e72617363686b612e636f6d2f696d616765732f4c4c4d732d66726f6d2d736372617463682d696d616765732f636830355f636f6d707265737365642f63726f73732d656e74726f70792e776562703f313233</a></p></figcaption></figure>
|
||||
|
||||
Nog 'n manier om te meet hoe goed die model is, word **perplexity** genoem. **Perplexity** is 'n maatstaf wat gebruik word om te evalueer hoe goed 'n waarskynlikheidsmodel 'n monster voorspel. In taalmodellering verteenwoordig dit die **model se onsekerheid** wanneer dit die volgende token in 'n reeks voorspel.\
|
||||
Byvoorbeeld, 'n perplexity waarde van 48725 beteken dat wanneer dit nodig is om 'n token te voorspel, dit onseker is oor watter een van die 48,725 tokens in die woordeskat die regte een is.
|
||||
|
||||
## Voorbeeld van Vooropleiding
|
||||
|
||||
Dit is die aanvanklike kode wat voorgestel is in [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) soms effens gewysig
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Vorige kode wat hier gebruik is maar reeds in vorige afdelings verduidelik</summary>
|
||||
```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
|
||||
```
|
||||
</details>
|
||||
```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"
|
||||
)
|
||||
```
|
||||
### Funksies om teks <--> ids te transformeer
|
||||
|
||||
Dit is 'n paar eenvoudige funksies wat gebruik kan word om van teks uit die woordeskat na ids en omgekeerd te transformeer. Dit is nodig aan die begin van die hantering van die teks en aan die einde van die voorspellings:
|
||||
```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())
|
||||
```
|
||||
### Genereer teks funksies
|
||||
|
||||
In 'n vorige afdeling is 'n funksie wat net die **meest waarskynlike token** gekry het na die logits. Dit sal egter beteken dat vir elke invoer dieselfde uitvoer altyd gegenereer sal word, wat dit baie deterministies maak.
|
||||
|
||||
Die volgende `generate_text` funksie sal die `top-k`, `temperature` en `multinomial` konsepte toepas.
|
||||
|
||||
- Die **`top-k`** beteken dat ons sal begin om alle waarskynlikhede van al die tokens, behalwe die top k tokens, na `-inf` te verminder. So, as k=3, sal slegs die 3 meest waarskynlike tokens 'n waarskynlikheid anders as `-inf` hê voordat 'n besluit geneem word.
|
||||
- Die **`temperature`** beteken dat elke waarskynlikheid deur die temperatuurwaarde gedeel sal word. 'n Waarde van `0.1` sal die hoogste waarskynlikheid verbeter in vergelyking met die laagste, terwyl 'n temperatuur van `5` byvoorbeeld dit meer plat sal maak. Dit help om die variasie in antwoorde te verbeter wat ons wil hê die LLM moet hê.
|
||||
- Na die temperatuur toegepas is, word 'n **`softmax`** funksie weer toegepas om al die oorblywende tokens 'n totale waarskynlikheid van 1 te gee.
|
||||
- Laastens, in plaas daarvan om die token met die grootste waarskynlikheid te kies, word die funksie **`multinomial`** toegepas om **die volgende token te voorspel volgens die finale waarskynlikhede**. So as token 1 'n 70% waarskynlikheid gehad het, token 2 'n 20% en token 3 'n 10%, sal token 1 70% van die tyd gekies word, token 2 20% van die tyd en token 3 10% van die tyd.
|
||||
```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]
|
||||
> Daar is 'n algemene alternatief vir `top-k` genoem [**`top-p`**](https://en.wikipedia.org/wiki/Top-p_sampling), ook bekend as nucleus sampling, wat in plaas daarvan om k monsters met die meeste waarskynlikheid te verkry, **organiseer** dit al die resulterende **vokabulaire** volgens waarskynlikhede en **som** hulle op van die hoogste waarskynlikheid na die laagste totdat 'n **drempel bereik word**.
|
||||
>
|
||||
> Dan sal **slegs daardie woorde** van die vokabulaire oorweeg word volgens hul relatiewe waarskynlikhede.
|
||||
>
|
||||
> Dit maak dit moontlik om nie 'n aantal `k` monsters te kies nie, aangesien die optimale k in elke geval anders kan wees, maar **slegs 'n drempel**.
|
||||
>
|
||||
> _Let daarop dat hierdie verbetering nie in die vorige kode ingesluit is nie._
|
||||
|
||||
> [!TIP]
|
||||
> 'n Ander manier om die gegenereerde teks te verbeter, is deur **Beam search** te gebruik in plaas van die greedy search wat in hierdie voorbeeld gebruik is.\
|
||||
> Anders as greedy search, wat die mees waarskynlike volgende woord by elke stap kies en 'n enkele reeks bou, **hou beam search die top 𝑘 k hoogste-telling gedeeltelike reekse** (genoem "beams") by elke stap dop. Deur verskeie moontlikhede gelyktydig te verken, balanseer dit doeltreffendheid en kwaliteit, wat die kanse verhoog om 'n **beter algehele** reeks te vind wat dalk deur die greedy benadering gemis kan word weens vroeë, suboptimale keuses.
|
||||
>
|
||||
> _Let daarop dat hierdie verbetering nie in die vorige kode ingesluit is nie._
|
||||
|
||||
### Verlies funksies
|
||||
|
||||
Die **`calc_loss_batch`** funksie bereken die kruis entropie van 'n voorspelling van 'n enkele batch.\
|
||||
Die **`calc_loss_loader`** verkry die kruis entropie van al die batches en bereken die **gemiddelde kruis entropie**.
|
||||
```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]
|
||||
> **Gradient clipping** is 'n tegniek wat gebruik word om **opleiding stabiliteit** in groot neurale netwerke te verbeter deur 'n **maksimum drempel** vir gradiëntgroottes in te stel. Wanneer gradiënte hierdie voorafbepaalde `max_norm` oorskry, word hulle proporsioneel afgeneem om te verseker dat opdaterings aan die model se parameters binne 'n hanteerbare reeks bly, wat probleme soos ontploffende gradiënte voorkom en 'n meer beheerde en stabiele opleiding verseker.
|
||||
>
|
||||
> _Let daarop dat hierdie verbetering nie in die vorige kode ingesluit is._
|
||||
>
|
||||
> Kyk na die volgende voorbeeld:
|
||||
|
||||
<figure><img src="../../images/image (6) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### Laai Data
|
||||
|
||||
Die funksies `create_dataloader_v1` en `create_dataloader_v1` is reeds in 'n vorige afdeling bespreek.
|
||||
|
||||
Van hier af, let op hoe dit gedefinieer is dat 90% van die teks vir opleiding gebruik gaan word terwyl die 10% vir validasie gebruik sal word en albei stelle in 2 verskillende data laders gestoor word.\
|
||||
Let daarop dat 'n deel van die datastel soms ook vir 'n toetsstel gelaat word om die prestasie van die model beter te evalueer.
|
||||
|
||||
Albei data laders gebruik dieselfde batchgrootte, maksimum lengte en stapgrootte en aantal werkers (0 in hierdie geval).\
|
||||
Die hoofverskille is die data wat deur elkeen gebruik word, en die validators laat nie die laaste val nie en skud die data nie omdat dit nie vir validasiedoeleindes nodig is nie.
|
||||
|
||||
Ook die feit dat **stapgrootte so groot soos die kontekslengte is**, beteken dat daar geen oorvleueling tussen kontekste wat gebruik word om die data op te lei sal wees nie (vermin die oorpassing maar ook die opleidingsdatastel).
|
||||
|
||||
Boonop, let daarop dat die batchgrootte in hierdie geval 2 is om die data in 2 batches te verdeel, die hoofdoel hiervan is om parallelle verwerking toe te laat en die verbruik per batch te verminder.
|
||||
```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
|
||||
)
|
||||
```
|
||||
## Sanity Checks
|
||||
|
||||
Die doel is om te verifieer dat daar genoeg tokens is vir opleiding, dat die vorms die verwagte is en om inligting te verkry oor die aantal tokens wat vir opleiding en vir validasie gebruik word:
|
||||
```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)
|
||||
```
|
||||
### Kies toestel vir opleiding & vooraf berekeninge
|
||||
|
||||
Die volgende kode kies net die toestel om te gebruik en bereken 'n opleidingsverlies en 'n valideringsverlies (sonder om enigiets nog op te lei) as 'n beginpunt.
|
||||
```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)
|
||||
```
|
||||
### Opleidingsfunksies
|
||||
|
||||
Die funksie `generate_and_print_sample` sal net 'n konteks kry en 'n paar tokens genereer om 'n gevoel te kry oor hoe goed die model op daardie punt is. Dit word deur `train_model_simple` op elke stap aangeroep.
|
||||
|
||||
Die funksie `evaluate_model` word so gereeld as wat aangedui word aan die opleidingsfunksie aangeroep en dit word gebruik om die opleidingsverlies en die valideringsverlies op daardie punt in die modelopleiding te meet.
|
||||
|
||||
Dan is die groot funksie `train_model_simple` die een wat eintlik die model oplei. Dit verwag:
|
||||
|
||||
- Die opleidingsdata laaier (met die data reeds geskei en voorberei vir opleiding)
|
||||
- Die validator laaier
|
||||
- Die **optimizer** wat tydens opleiding gebruik moet word: Dit is die funksie wat die gradiënte sal gebruik en die parameters sal opdateer om die verlies te verminder. In hierdie geval, soos jy sal sien, word `AdamW` gebruik, maar daar is baie meer.
|
||||
- `optimizer.zero_grad()` word aangeroep om die gradiënte op elke ronde te reset om te voorkom dat hulle ophoop.
|
||||
- Die **`lr`** parameter is die **leer tempo** wat die **grootte van die stappe** bepaal wat tydens die optimaliseringsproses geneem word wanneer die model se parameters opdateer word. 'n **Kleinere** leer tempo beteken die optimizer **maak kleiner opdaterings** aan die gewigte, wat kan lei tot meer **presiese** konvergensie maar kan **opleiding vertraag**. 'n **Groter** leer tempo kan opleiding versnel maar **risiko's om oor te skiet** van die minimum van die verliesfunksie (**oor die punt spring** waar die verliesfunksie geminimaliseer word).
|
||||
- **Gewig Aftakking** wysig die **Verlies Berekening** stap deur 'n ekstra term by te voeg wat groot gewigte straf. Dit moedig die optimizer aan om oplossings met kleiner gewigte te vind, wat 'n balans skep tussen die data goed aan te pas en die model eenvoudig te hou om oorpassing in masjienleer modelle te voorkom deur die model te ontmoedig om te veel belang aan enige enkele kenmerk toe te ken.
|
||||
- Tradisionele optimizers soos SGD met L2 regulering koppel gewig aftakking met die gradiënt van die verliesfunksie. egter, **AdamW** (n variant van Adam optimizer) ontkoppel gewig aftakking van die gradiënt opdatering, wat lei tot meer effektiewe regulering.
|
||||
- Die toestel om vir opleiding te gebruik
|
||||
- Die aantal epoches: Aantal kere om oor die opleidingsdata te gaan
|
||||
- Die evaluering frekwensie: Die frekwensie om `evaluate_model` aan te roep
|
||||
- Die evaluering iterasie: Die aantal bondels om te gebruik wanneer die huidige toestand van die model geëvalueer word wanneer `generate_and_print_sample` aangeroep word
|
||||
- Die 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]
|
||||
> Om die leer tempo te verbeter, is daar 'n paar relevante tegnieke genaamd **lineêre opwarming** en **kosyn afname.**
|
||||
>
|
||||
> **Lineêre opwarming** bestaan uit die definieer van 'n aanvanklike leer tempo en 'n maksimum een en om dit konsekwent na elke epocha op te dateer. Dit is omdat die begin van die opleiding met kleiner gewig opdaterings die risiko verminder dat die model groot, destabiliserende opdaterings tydens sy opleidingsfase teëkom.\
|
||||
> **Kosyn afname** is 'n tegniek wat **geleidelik die leer tempo verminder** volgens 'n half-kosyn kurwe **na die opwarm** fase, wat gewig opdaterings vertraag om **die risiko van oorskiet** van die verlies minima te minimaliseer en om opleidings stabiliteit in latere fases te verseker.
|
||||
>
|
||||
> _Let daarop dat hierdie verbeterings nie in die vorige kode ingesluit is nie._
|
||||
|
||||
### Begin opleiding
|
||||
```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.")
|
||||
```
|
||||
### Druk opleidingsontwikkeling
|
||||
|
||||
Met die volgende funksie is dit moontlik om die ontwikkeling van die model te druk terwyl dit opgelei is.
|
||||
```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)
|
||||
```
|
||||
### Stoor die model
|
||||
|
||||
Dit is moontlik om die model + optimizer te stoor as jy later wil voortgaan met opleiding:
|
||||
```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
|
||||
```
|
||||
Of net die model as jy net van plan is om dit te gebruik:
|
||||
```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
|
||||
```
|
||||
## Laai GPT2 gewigte
|
||||
|
||||
Daar is 2 vinnige skripte om die GPT2 gewigte plaaslik te laai. Vir albei kan jy die repository [https://github.com/rasbt/LLMs-from-scratch](https://github.com/rasbt/LLMs-from-scratch) plaaslik kloon, dan:
|
||||
|
||||
- Die skrip [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) sal al die gewigte aflaai en die formate van OpenAI na die formate wat deur ons LLM verwag word, transformeer. Die skrip is ook voorberei met die nodige konfigurasie en met die prompt: "Elke poging beweeg jou"
|
||||
- Die skrip [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) laat jou toe om enige van die GPT2 gewigte plaaslik te laai (verander net die `CHOOSE_MODEL` var) en teks te voorspel vanaf sommige prompts.
|
||||
|
||||
## Verwysings
|
||||
|
||||
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)
|
@ -7,15 +7,15 @@
|
||||
|
||||
LoRA maak dit moontlik om **groot modelle** doeltreffend fyn af te stem deur slegs 'n **klein deel** van die model te verander. Dit verminder die aantal parameters wat jy moet oplei, wat **geheue** en **berekeningshulpbronne** bespaar. Dit is omdat:
|
||||
|
||||
1. **Verminder die Aantal Opleibare Parameters**: In plaas daarvan om die hele gewigmatriks in die model op te dateer, **verdeel** LoRA die gewigmatriks in twee kleiner matrikse (genoem **A** en **B**). Dit maak opleiding **vinniger** en vereis **minder geheue** omdat minder parameters opgedateer moet word.
|
||||
1. **Verminder die Aantal Opleibare Parameters**: In plaas daarvan om die hele gewigmatriks in die model op te dateer, **verdeel** LoRA die gewigmatriks in twee kleiner matrikse (genoem **A** en **B**). Dit maak opleiding **vinniger** en vereis **minder geheue** omdat minder parameters opdateer moet word.
|
||||
|
||||
1. Dit is omdat dit in plaas daarvan om die volledige gewigopdatering van 'n laag (matriks) te bereken, dit benader na 'n produk van 2 kleiner matrikse wat die opdatering verminder om te bereken:\
|
||||
|
||||
<figure><img src="../../images/image (9) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
2. **Hou Oorspronklike Model Gewigte Onveranderd**: LoRA laat jou toe om die oorspronklike model gewigte dieselfde te hou, en slegs die **nuwe klein matrikse** (A en B) op te dateer. Dit is nuttig omdat dit beteken dat die oorspronklike kennis van die model bewaar word, en jy net wat nodig is, aanpas.
|
||||
2. **Hou Oorspronklike Model Gewigte Onveranderd**: LoRA laat jou toe om die oorspronklike modelgewigte dieselfde te hou, en slegs die **nuwe klein matrikse** (A en B) op te dateer. Dit is nuttig omdat dit beteken dat die oorspronklike kennis van die model bewaar word, en jy net wat nodig is, aanpas.
|
||||
3. **Doeltreffende Taakspesifieke Fyn-afstemming**: Wanneer jy die model wil aanpas vir 'n **nuwe taak**, kan jy net die **klein LoRA matrikse** (A en B) oplei terwyl jy die res van die model soos dit is, laat. Dit is **baie doeltreffender** as om die hele model weer op te lei.
|
||||
4. **Bergingseffektiwiteit**: Na fyn-afstemming, in plaas daarvan om 'n **heel nuwe model** vir elke taak te stoor, hoef jy slegs die **LoRA matrikse** te stoor, wat baie klein is in vergelyking met die hele model. Dit maak dit makliker om die model aan te pas vir baie take sonder om te veel berging te gebruik.
|
||||
4. **Bergingseffektiwiteit**: Na fyn-afstemming, in plaas daarvan om 'n **hele nuwe model** vir elke taak te stoor, hoef jy slegs die **LoRA matrikse** te stoor, wat baie klein is in vergelyking met die hele model. Dit maak dit makliker om die model aan te pas vir baie take sonder om te veel berging te gebruik.
|
||||
|
||||
Om LoraLayers in plaas van Linear eenhede tydens 'n fyn-afstemming te implementeer, word hierdie kode hier voorgestel [https://github.com/rasbt/LLMs-from-scratch/blob/main/appendix-E/01_main-chapter-code/appendix-E.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/appendix-E/01_main-chapter-code/appendix-E.ipynb):
|
||||
```python
|
||||
|
@ -0,0 +1,110 @@
|
||||
# 7.1. Fyn-afstemming vir Kategorisering
|
||||
|
||||
## Wat is
|
||||
|
||||
Fyn-afstemming is die proses om 'n **vooraf-geleerde model** te neem wat **algemene taalpatrone** uit groot hoeveelhede data geleer het en dit **aan te pas** om 'n **spesifieke taak** uit te voer of om domein-spesifieke taal te verstaan. Dit word bereik deur die opleiding van die model voort te sit op 'n kleiner, taak-spesifieke dataset, wat dit toelaat om sy parameters aan te pas om beter by die nuanses van die nuwe data te pas terwyl dit die breë kennis wat dit reeds verwerf het, benut. Fyn-afstemming stel die model in staat om meer akkurate en relevante resultate in gespesialiseerde toepassings te lewer sonder die behoefte om 'n nuwe model van nuuts af op te lei.
|
||||
|
||||
> [!TIP]
|
||||
> Aangesien dit redelik duur is om 'n LLM wat die teks "begryp" vooraf te leer, is dit gewoonlik makliker en goedkoper om oopbron vooraf-geleerde modelle fyn-af te stem om 'n spesifieke taak uit te voer wat ons wil hê dit moet uitvoer.
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van hierdie afdeling is om te wys hoe om 'n reeds vooraf-geleerde model fyn-af te stem sodat die LLM, in plaas daarvan om nuwe teks te genereer, die **waarskynlikhede van die gegewe teks wat in elkeen van die gegewe kategorieë gekategoriseer word** (soos of 'n teks spam is of nie) sal gee.
|
||||
|
||||
## Voorbereiding van die dataset
|
||||
|
||||
### Dataset grootte
|
||||
|
||||
Natuurlik, om 'n model fyn-af te stem, het jy 'n paar gestruktureerde data nodig om jou LLM te spesialiseer. In die voorbeeld wat voorgestel word in [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), word GPT2 fyn-afgestem om te detecteer of 'n e-pos spam is of nie met die data van [https://archive.ics.uci.edu/static/public/228/sms+spam+collection.zip](https://archive.ics.uci.edu/static/public/228/sms+spam+collection.zip)_._
|
||||
|
||||
Hierdie dataset bevat baie meer voorbeelde van "nie spam" as van "spam", daarom stel die boek voor om **slegs soveel voorbeelde van "nie spam" as van "spam" te gebruik** (daarom, om al die ekstra voorbeelde uit die opleidingsdata te verwyder). In hierdie geval was dit 747 voorbeelde van elkeen.
|
||||
|
||||
Toe, **70%** van die dataset word gebruik vir **opleiding**, **10%** vir **validasie** en **20%** vir **toetsing**.
|
||||
|
||||
- Die **validasieset** word tydens die opleidingsfase gebruik om die model se **hiperparameters** fyn-af te stem en besluite te neem oor modelargitektuur, wat effektief help om oorpassing te voorkom deur terugvoer te gee oor hoe die model presteer op ongekende data. Dit stel iteratiewe verbeterings in staat sonder om die finale evaluasie te bevoordeel.
|
||||
- Dit beteken dat alhoewel die data wat in hierdie dataset ingesluit is nie direk vir die opleiding gebruik word nie, dit gebruik word om die beste **hiperparameters** te stem, so hierdie stel kan nie gebruik word om die model se prestasie te evalueer soos die toetsstel nie.
|
||||
- In teenstelling hiermee, die **toetsstel** word **slegs na** die model ten volle opgelei is en al die aanpassings voltooi is, gebruik; dit bied 'n onbevooroordeelde beoordeling van die model se vermoë om te generaliseer na nuwe, ongekende data. Hierdie finale evaluasie op die toetsstel gee 'n realistiese aanduiding van hoe die model verwag word om in werklike toepassings te presteer.
|
||||
|
||||
### Inskrywings lengte
|
||||
|
||||
Aangesien die opleidingsvoorbeeld inskrywings (e-pos teks in hierdie geval) van dieselfde lengte verwag, is daar besluit om elke inskrywing so groot te maak soos die grootste een deur die id's van `<|endoftext|>` as opvulling by te voeg.
|
||||
|
||||
### Stel die model in
|
||||
|
||||
Gebruik die oopbron vooraf-geleerde gewigte om die model in te stel vir opleiding. Ons het dit al voorheen gedoen en volg die instruksies van [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) kan jy dit maklik doen.
|
||||
|
||||
## Kategorisering kop
|
||||
|
||||
In hierdie spesifieke voorbeeld (voorspel of 'n teks spam is of nie), is ons nie geïnteresseerd om fyn-af te stem volgens die volledige woordeskat van GPT2 nie, maar ons wil net hê die nuwe model moet sê of die e-pos spam is (1) of nie (0). Daarom gaan ons die **laaste laag wat** die waarskynlikhede per token van die woordeskat gee, aanpas na een wat slegs die waarskynlikhede van spam of nie spam gee (soos 'n woordeskat van 2 woorde).
|
||||
```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
|
||||
)
|
||||
```
|
||||
## Parameters om te stel
|
||||
|
||||
Om vinnig te fynstel is dit makliker om nie al die parameters te fynstel nie, maar slegs 'n paar finale. Dit is omdat dit bekend is dat die laer lae oor die algemeen basiese taalstrukture en toepaslike semantiek vasvang. So, net **fynstel van die laaste lae is gewoonlik genoeg en vinniger**.
|
||||
```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
|
||||
```
|
||||
## Entries to use for training
|
||||
|
||||
In vorige afdelings is die LLM opgelei deur die verlies van elke voorspelde token te verminder, alhoewel byna al die voorspelde tokens in die invoer sin was (slegs 1 aan die einde was werklik voorspel) sodat die model die taal beter kan verstaan.
|
||||
|
||||
In hierdie geval is ons net geïnteresseerd in die model se vermoë om te voorspel of die model spam is of nie, so ons is net geïnteresseerd in die laaste voorspelde token. Daarom is dit nodig om ons vorige opleidingsverlies funksies te wysig om slegs daardie token in ag te neem.
|
||||
|
||||
Dit is geïmplementeer in [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) as:
|
||||
```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
|
||||
```
|
||||
Let op hoe ons vir elke bondel slegs belangstel in die **logits van die laaste token wat voorspel is**.
|
||||
|
||||
## Volledige GPT2 fyn-afstemming klassifikasie kode
|
||||
|
||||
Jy kan al die kode vind om GPT2 te fyn-af te stem as 'n spam klassifiseerder in [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)
|
||||
|
||||
## Verwysings
|
||||
|
||||
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)
|
@ -1,11 +1,11 @@
|
||||
# 7.2. Fyn-afstelling om instruksies te volg
|
||||
# 7.2. Fyn-afstemming om instruksies te volg
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van hierdie afdeling is om te wys hoe om 'n **reeds vooropgestelde model fyn af te stel om instruksies te volg** eerder as net teks te genereer, byvoorbeeld, om op take as 'n geselskapbot te reageer.
|
||||
> Die doel van hierdie afdeling is om te wys hoe om 'n **reeds vooropgeleide model fyn af te stem om instruksies te volg** eerder as net teks te genereer, byvoorbeeld, om op take te reageer as 'n kletsbot.
|
||||
|
||||
## Dataset
|
||||
|
||||
Om 'n LLM fyn af te stel om instruksies te volg, is dit nodig om 'n dataset met instruksies en antwoorde te hê om die LLM fyn af te stel. Daar is verskillende formate om 'n LLM op te lei om instruksies te volg, byvoorbeeld:
|
||||
Om 'n LLM fyn af te stem om instruksies te volg, is dit nodig om 'n dataset met instruksies en antwoorde te hê om die LLM fyn af te stem. Daar is verskillende formate om 'n LLM op te lei om instruksies te volg, byvoorbeeld:
|
||||
|
||||
- Die Apply Alpaca prompt styl voorbeeld:
|
||||
```csharp
|
||||
@ -53,17 +53,17 @@ Then, soos altyd, is dit nodig om die dataset in stelle vir opleiding, validasie
|
||||
|
||||
## Batching & Data Loaders
|
||||
|
||||
Dan is dit nodig om al die insette en verwagte uitsette vir die opleiding in batches te plaas. Hiervoor is dit nodig om:
|
||||
Dan is dit nodig om al die insette en verwagte uitsette vir die opleiding in batches te plaas. Hiervoor is dit nodig om te:
|
||||
|
||||
- Tokenize die teks
|
||||
- Vul al die monsters tot dieselfde lengte (gewoonlik sal die lengte so groot wees soos die kontekslengte wat gebruik is om die LLM voor te leer)
|
||||
- Pad al die monsters na dieselfde lengte (gewoonlik sal die lengte so groot wees soos die kontekslengte wat gebruik is om die LLM voor te leer)
|
||||
- Skep die verwagte tokens deur die inset met 1 in 'n pasgemaakte collate-funksie te skuif
|
||||
- Vervang sommige padding tokens met -100 om hulle van die opleidingsverlies uit te sluit: Na die eerste `endoftext` token, vervang al die ander `endoftext` tokens met -100 (want die gebruik van `cross_entropy(...,ignore_index=-100)` beteken dat dit teikens met -100 sal ignoreer)
|
||||
- \[Opsioneel\] Masker met -100 ook al die tokens wat aan die vraag behoort sodat die LLM net leer hoe om die antwoord te genereer. In die Apply Alpaca styl sal dit beteken om alles te masker tot `### Response:`
|
||||
|
||||
Met dit geskep, is dit tyd om die data loaders vir elke dataset (opleiding, validasie en toets) te skep.
|
||||
|
||||
## Laai voor-geleerde LLM & Fyn afstemming & Verlies Kontrole
|
||||
## Laai voor-geleerde LLM & Fyn afstem & Verlies Kontrole
|
||||
|
||||
Dit is nodig om 'n voor-geleerde LLM te laai om dit fyn af te stem. Dit is reeds op ander bladsye bespreek. Dan is dit moontlik om die voorheen gebruikte opleidingsfunksie te gebruik om die LLM fyn af te stem.
|
||||
|
||||
@ -72,28 +72,28 @@ Onthou dat oorpassing plaasvind wanneer die opleidingsverlies verminder, maar di
|
||||
|
||||
## Antwoord Kwaliteit
|
||||
|
||||
Aangesien dit nie 'n klassifikasie fyn afstemming is waar dit moontlik is om meer op die verlies variasies te vertrou nie, is dit ook belangrik om die kwaliteit van die antwoorde in die toetsstel te kontroleer. Daarom word dit aanbeveel om die gegenereerde antwoorde van al die toetsstelle te versamel en **hulle kwaliteit handmatig te kontroleer** om te sien of daar verkeerde antwoorde is (let daarop dat dit moontlik is vir die LLM om die formaat en sintaksis van die antwoordsin te korrek te genereer, maar 'n heeltemal verkeerde antwoord te gee. Die verlies variasie sal hierdie gedrag nie weerspieël nie).\
|
||||
Aangesien dit nie 'n klassifikasie fyn-afstemming is waar dit moontlik is om meer op die verlies variasies te vertrou nie, is dit ook belangrik om die kwaliteit van die antwoorde in die toetsstel te kontroleer. Daarom word dit aanbeveel om die gegenereerde antwoorde van al die toetsstelle te versamel en **hulle kwaliteit handmatig te kontroleer** om te sien of daar verkeerde antwoorde is (let daarop dat dit moontlik is vir die LLM om die formaat en sintaksis van die antwoordsin korrek te genereer, maar 'n heeltemal verkeerde antwoord te gee. Die verlies variasie sal hierdie gedrag nie weerspieël nie).\
|
||||
Let daarop dat dit ook moontlik is om hierdie hersiening uit te voer deur die gegenereerde antwoorde en die verwagte antwoorde aan **ander LLMs te gee en hulle te vra om die antwoorde te evalueer**.
|
||||
|
||||
Ander toetse om te loop om die kwaliteit van die antwoorde te verifieer:
|
||||
|
||||
1. **Meet Massiewe Multitask Taalbegrip (**[**MMLU**](https://arxiv.org/abs/2009.03300)**):** MMLU evalueer 'n model se kennis en probleemoplossingsvermoëns oor 57 vakke, insluitend menslike wetenskappe, wetenskappe, en meer. Dit gebruik meerkeuse vrae om begrip op verskillende moeilikheidsvlakke te assesseer, van elementêr tot gevorderd professioneel.
|
||||
2. [**LMSYS Chatbot Arena**](https://arena.lmsys.org): Hierdie platform laat gebruikers toe om antwoorde van verskillende chatbots langs mekaar te vergelyk. Gebruikers voer 'n prompt in, en verskeie chatbots genereer antwoorde wat direk vergelyk kan word.
|
||||
3. [**AlpacaEval**](https://github.com/tatsu-lab/alpaca_eval)**:** AlpacaEval is 'n geoutomatiseerde evaluasieraamwerk waar 'n gevorderde LLM soos GPT-4 die antwoorde van ander modelle op verskillende prompts evalueer.
|
||||
3. [**AlpacaEval**](https://github.com/tatsu-lab/alpaca_eval)**:** AlpacaEval is 'n geoutomatiseerde evaluasieraamwerk waar 'n gevorderde LLM soos GPT-4 die antwoorde van ander modelle op verskeie prompts evalueer.
|
||||
4. **Algemene Taalbegrip Evaluasie (**[**GLUE**](https://gluebenchmark.com/)**):** GLUE is 'n versameling van nege natuurlike taalbegrip take, insluitend sentimentanalise, teksimplikasie, en vraagbeantwoording.
|
||||
5. [**SuperGLUE**](https://super.gluebenchmark.com/)**:** Gebaseer op GLUE, sluit SuperGLUE meer uitdagende take in wat ontwerp is om moeilik te wees vir huidige modelle.
|
||||
6. **Buiten die Imitasie Speletjie Benchmark (**[**BIG-bench**](https://github.com/google/BIG-bench)**):** BIG-bench is 'n grootmaat benchmark met meer as 200 take wat 'n model se vermoëns in areas soos redeneer, vertaling, en vraagbeantwoording toets.
|
||||
7. **Holistiese Evaluasie van Taalmodelle (**[**HELM**](https://crfm.stanford.edu/helm/lite/latest/)**):** HELM bied 'n omvattende evaluasie oor verskeie metrieks soos akkuraatheid, robuustheid, en billikheid.
|
||||
8. [**OpenAI Evals**](https://github.com/openai/evals)**:** 'n oopbron evaluasieraamwerk deur OpenAI wat toelaat vir die toetsing van KI-modelle op pasgemaakte en gestandaardiseerde take.
|
||||
8. [**OpenAI Evals**](https://github.com/openai/evals)**:** 'n Oopbron evaluasieraamwerk deur OpenAI wat toelaat vir die toetsing van KI-modelle op pasgemaakte en gestandaardiseerde take.
|
||||
9. [**HumanEval**](https://github.com/openai/human-eval)**:** 'n Versameling programmeringsprobleme wat gebruik word om die kodegenereringsvermoëns van taalmodelle te evalueer.
|
||||
10. **Stanford Vraag Beantwoording Dataset (**[**SQuAD**](https://rajpurkar.github.io/SQuAD-explorer/)**):** SQuAD bestaan uit vrae oor Wikipedia-artikels, waar modelle die teks moet verstaan om akkuraat te antwoord.
|
||||
11. [**TriviaQA**](https://nlp.cs.washington.edu/triviaqa/)**:** 'n grootmaat dataset van trivia vrae en antwoorde, saam met bewysdokumente.
|
||||
11. [**TriviaQA**](https://nlp.cs.washington.edu/triviaqa/)**:** 'n Grootmaat dataset van trivia vrae en antwoorde, saam met bewysdokumente.
|
||||
|
||||
en baie baie meer
|
||||
|
||||
## Volg instruksies fyn afstemming kode
|
||||
## Volg instruksies fyn-afstemming kode
|
||||
|
||||
Jy kan 'n voorbeeld van die kode om hierdie fyn afstemming uit te voer vind in [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch07/01_main-chapter-code/gpt_instruction_finetuning.py](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch07/01_main-chapter-code/gpt_instruction_finetuning.py)
|
||||
Jy kan 'n voorbeeld van die kode om hierdie fyn-afstemming uit te voer vind in [https://github.com/rasbt/LLMs-from-scratch/blob/main/ch07/01_main-chapter-code/gpt_instruction_finetuning.py](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch07/01_main-chapter-code/gpt_instruction_finetuning.py)
|
||||
|
||||
## Verwysings
|
||||
|
||||
|
@ -32,7 +32,7 @@ Jy moet begin deur hierdie pos te lees vir 'n paar basiese konsepte wat jy moet
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van hierdie derde fase is baie eenvoudig: **Ken elkeen van die vorige tokens in die woordeskat 'n vektor van die verlangde dimensies toe om die model te oefen.** Elke woord in die woordeskat sal 'n punt in 'n ruimte van X dimensies wees.\
|
||||
> Let daarop dat die posisie van elke woord in die ruimte aanvanklik net "ewekansig" geïnitialiseer word en dat hierdie posisies opleibare parameters is (sal verbeter word tydens die opleiding).
|
||||
> Let daarop dat die posisie van elke woord in die ruimte aanvanklik net "ewekansig" geinitialiseer word en dat hierdie posisies opleibare parameters is (sal verbeter word tydens die opleiding).
|
||||
>
|
||||
> Boonop, tydens die token inbedding **word 'n ander laag van inbedings geskep** wat (in hierdie geval) die **absolute posisie van die woord in die opleidingssin** verteenwoordig. Op hierdie manier sal 'n woord in verskillende posisies in die sin 'n ander voorstelling (betekenis) hê.
|
||||
|
||||
@ -64,7 +64,7 @@ Jy moet begin deur hierdie pos te lees vir 'n paar basiese konsepte wat jy moet
|
||||
## 6. Vooropleiding & Laai modelle
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van hierdie sesde fase is baie eenvoudig: **Oefen die model van nuuts af**. Hiervoor sal die vorige LLM argitektuur gebruik word met 'n paar lusse wat oor die datastelle gaan met die gedefinieerde verliesfunksies en optimizer om al die parameters van die model op te lei.
|
||||
> Die doel van hierdie sesde fase is baie eenvoudig: **Oefen die model van nuuts af**. Hiervoor sal die vorige LLM argitektuur gebruik word met 'n paar lusse wat oor die datastelle gaan met behulp van die gedefinieerde verliesfunksies en optimizer om al die parameters van die model op te lei.
|
||||
|
||||
{{#ref}}
|
||||
6.-pre-training-and-loading-models.md
|
||||
@ -82,16 +82,16 @@ Jy moet begin deur hierdie pos te lees vir 'n paar basiese konsepte wat jy moet
|
||||
## 7.1. Fyn-Afstemming vir Kategorisering
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van hierdie afdeling is om te wys hoe om 'n reeds vooropgeleide model fyn af te stel sodat in plaas daarvan om nuwe teks te genereer, die LLM die **waarskynlikhede van die gegewe teks wat in elkeen van die gegewe kategorieë gekategoriseer word** sal gee (soos of 'n teks spam is of nie).
|
||||
> Die doel van hierdie afdeling is om te wys hoe om 'n reeds vooropgeleide model fyn af te stel sodat in plaas daarvan om nuwe teks te genereer, die LLM die **waarskynlikhede van die gegewe teks om in elkeen van die gegewe kategorieë gekategoriseer te word** (soos of 'n teks spam is of nie) sal gee.
|
||||
|
||||
{{#ref}}
|
||||
7.1.-fine-tuning-for-classification.md
|
||||
{{#endref}}
|
||||
|
||||
## 7.2. Fyn-Afstemming om Instruksies te Volg
|
||||
## 7.2. Fyn-Afstemming om instruksies te volg
|
||||
|
||||
> [!TIP]
|
||||
> Die doel van hierdie afdeling is om te wys hoe om **'n reeds vooropgeleide model fyn af te stel om instruksies te volg** eerder as net om teks te genereer, byvoorbeeld, om op take te reageer as 'n chat bot.
|
||||
> Die doel van hierdie afdeling is om te wys hoe om **'n reeds vooropgeleide model fyn af te stel om instruksies te volg** eerder as net om teks te genereer, byvoorbeeld, om op take te reageer soos 'n chat bot.
|
||||
|
||||
{{#ref}}
|
||||
7.2.-fine-tuning-to-follow-instructions.md
|
||||
|
Loading…
x
Reference in New Issue
Block a user