mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
286 lines
13 KiB
Markdown
286 lines
13 KiB
Markdown
# 0. Grundlegende LLM-Konzepte
|
||
|
||
## Vortraining
|
||
|
||
Vortraining ist die grundlegende Phase bei der Entwicklung eines großen Sprachmodells (LLM), in der das Modell einer Vielzahl und Vielfalt von Textdaten ausgesetzt wird. In dieser Phase **lernt das LLM die grundlegenden Strukturen, Muster und Nuancen der Sprache**, einschließlich Grammatik, Wortschatz, Syntax und kontextueller Beziehungen. Durch die Verarbeitung dieser umfangreichen Daten erwirbt das Modell ein breites Verständnis der Sprache und des allgemeinen Weltwissens. Diese umfassende Basis ermöglicht es dem LLM, kohärente und kontextuell relevante Texte zu generieren. Anschließend kann dieses vortrainierte Modell einer Feinabstimmung unterzogen werden, bei der es weiter auf spezialisierten Datensätzen trainiert wird, um seine Fähigkeiten für spezifische Aufgaben oder Bereiche anzupassen und seine Leistung und Relevanz in gezielten Anwendungen zu verbessern.
|
||
|
||
## Hauptkomponenten von LLM
|
||
|
||
Ein LLM wird normalerweise durch die Konfiguration charakterisiert, die zu seiner Ausbildung verwendet wird. Dies sind die gängigen Komponenten beim Training eines LLM:
|
||
|
||
- **Parameter**: Parameter sind die **lernbaren Gewichte und Verzerrungen** im neuronalen Netzwerk. Dies sind die Zahlen, die der Trainingsprozess anpasst, um die Verlustfunktion zu minimieren und die Leistung des Modells bei der Aufgabe zu verbessern. LLMs verwenden normalerweise Millionen von Parametern.
|
||
- **Kontextlänge**: Dies ist die maximale Länge jedes Satzes, der zum Vortraining des LLM verwendet wird.
|
||
- **Einbettungsdimension**: Die Größe des Vektors, der verwendet wird, um jedes Token oder Wort darzustellen. LLMs verwenden normalerweise Milliarden von Dimensionen.
|
||
- **Verborgene Dimension**: Die Größe der verborgenen Schichten im neuronalen Netzwerk.
|
||
- **Anzahl der Schichten (Tiefe)**: Wie viele Schichten das Modell hat. LLMs verwenden normalerweise Dutzende von Schichten.
|
||
- **Anzahl der Aufmerksamkeitsköpfe**: In Transformermodellen ist dies, wie viele separate Aufmerksamkeitsmechanismen in jeder Schicht verwendet werden. LLMs verwenden normalerweise Dutzende von Köpfen.
|
||
- **Dropout**: Dropout ist etwas wie der Prozentsatz der Daten, der entfernt wird (Wahrscheinlichkeiten werden auf 0 gesetzt) während des Trainings, um **Überanpassung zu verhindern.** LLMs verwenden normalerweise zwischen 0-20%.
|
||
|
||
Konfiguration des GPT-2-Modells:
|
||
```json
|
||
GPT_CONFIG_124M = {
|
||
"vocab_size": 50257, // Vocabulary size of the BPE tokenizer
|
||
"context_length": 1024, // Context length
|
||
"emb_dim": 768, // Embedding dimension
|
||
"n_heads": 12, // Number of attention heads
|
||
"n_layers": 12, // Number of layers
|
||
"drop_rate": 0.1, // Dropout rate: 10%
|
||
"qkv_bias": False // Query-Key-Value bias
|
||
}
|
||
```
|
||
## Tensors in PyTorch
|
||
|
||
In PyTorch ist ein **Tensor** eine grundlegende Datenstruktur, die als mehrdimensionales Array dient und Konzepte wie Skalare, Vektoren und Matrizen auf potenziell höhere Dimensionen verallgemeinert. Tensoren sind die primäre Art und Weise, wie Daten in PyTorch dargestellt und manipuliert werden, insbesondere im Kontext von Deep Learning und neuronalen Netzwerken.
|
||
|
||
### Mathematisches Konzept der Tensoren
|
||
|
||
- **Skalare**: Tensoren der Rang 0, die eine einzelne Zahl darstellen (nulldimensional). Wie: 5
|
||
- **Vektoren**: Tensoren der Rang 1, die ein eindimensionales Array von Zahlen darstellen. Wie: \[5,1]
|
||
- **Matrizen**: Tensoren der Rang 2, die zweidimensionale Arrays mit Zeilen und Spalten darstellen. Wie: \[\[1,3], \[5,2]]
|
||
- **Tensoren höheren Rangs**: Tensoren der Rang 3 oder mehr, die Daten in höheren Dimensionen darstellen (z.B. 3D-Tensoren für Farbbilder).
|
||
|
||
### Tensoren als Datencontainer
|
||
|
||
Aus computationaler Sicht fungieren Tensoren als Container für mehrdimensionale Daten, wobei jede Dimension unterschiedliche Merkmale oder Aspekte der Daten darstellen kann. Dies macht Tensoren besonders geeignet für die Verarbeitung komplexer Datensätze in Machine Learning-Aufgaben.
|
||
|
||
### PyTorch-Tensoren vs. NumPy-Arrays
|
||
|
||
Während PyTorch-Tensoren NumPy-Arrays in ihrer Fähigkeit ähneln, numerische Daten zu speichern und zu manipulieren, bieten sie zusätzliche Funktionalitäten, die für Deep Learning entscheidend sind:
|
||
|
||
- **Automatische Differenzierung**: PyTorch-Tensoren unterstützen die automatische Berechnung von Gradienten (autograd), was den Prozess der Berechnung von Ableitungen vereinfacht, die für das Training neuronaler Netzwerke erforderlich sind.
|
||
- **GPU-Beschleunigung**: Tensoren in PyTorch können auf GPUs verschoben und dort berechnet werden, was großangelegte Berechnungen erheblich beschleunigt.
|
||
|
||
### Erstellen von Tensoren in PyTorch
|
||
|
||
Sie können Tensoren mit der Funktion `torch.tensor` erstellen:
|
||
```python
|
||
pythonCopy codeimport torch
|
||
|
||
# Scalar (0D tensor)
|
||
tensor0d = torch.tensor(1)
|
||
|
||
# Vector (1D tensor)
|
||
tensor1d = torch.tensor([1, 2, 3])
|
||
|
||
# Matrix (2D tensor)
|
||
tensor2d = torch.tensor([[1, 2],
|
||
[3, 4]])
|
||
|
||
# 3D Tensor
|
||
tensor3d = torch.tensor([[[1, 2], [3, 4]],
|
||
[[5, 6], [7, 8]]])
|
||
```
|
||
### Tensor-Datentypen
|
||
|
||
PyTorch-Tensoren können Daten verschiedener Typen speichern, wie z.B. Ganzzahlen und Fließkommazahlen.
|
||
|
||
Sie können den Datentyp eines Tensors mit dem Attribut `.dtype` überprüfen:
|
||
```python
|
||
tensor1d = torch.tensor([1, 2, 3])
|
||
print(tensor1d.dtype) # Output: torch.int64
|
||
```
|
||
- Tensors, die aus Python-Ganzzahlen erstellt werden, sind vom Typ `torch.int64`.
|
||
- Tensors, die aus Python-Fließkommazahlen erstellt werden, sind vom Typ `torch.float32`.
|
||
|
||
Um den Datentyp eines Tensors zu ändern, verwenden Sie die Methode `.to()`:
|
||
```python
|
||
float_tensor = tensor1d.to(torch.float32)
|
||
print(float_tensor.dtype) # Output: torch.float32
|
||
```
|
||
### Häufige Tensoroperationen
|
||
|
||
PyTorch bietet eine Vielzahl von Operationen zur Manipulation von Tensors:
|
||
|
||
- **Zugriff auf die Form**: Verwenden Sie `.shape`, um die Dimensionen eines Tensors zu erhalten.
|
||
|
||
```python
|
||
print(tensor2d.shape) # Ausgabe: torch.Size([2, 2])
|
||
```
|
||
|
||
- **Umformen von Tensors**: Verwenden Sie `.reshape()` oder `.view()`, um die Form zu ändern.
|
||
|
||
```python
|
||
reshaped = tensor2d.reshape(4, 1)
|
||
```
|
||
|
||
- **Transponieren von Tensors**: Verwenden Sie `.T`, um einen 2D-Tensor zu transponieren.
|
||
|
||
```python
|
||
transposed = tensor2d.T
|
||
```
|
||
|
||
- **Matrixmultiplikation**: Verwenden Sie `.matmul()` oder den `@`-Operator.
|
||
|
||
```python
|
||
result = tensor2d @ tensor2d.T
|
||
```
|
||
|
||
### Bedeutung im Deep Learning
|
||
|
||
Tensors sind in PyTorch unerlässlich für den Aufbau und das Training von neuronalen Netzwerken:
|
||
|
||
- Sie speichern Eingabedaten, Gewichte und Bias.
|
||
- Sie erleichtern die für Vorwärts- und Rückwärtsdurchläufe in Trainingsalgorithmen erforderlichen Operationen.
|
||
- Mit Autograd ermöglichen Tensors die automatische Berechnung von Gradienten, was den Optimierungsprozess vereinfacht.
|
||
|
||
## Automatische Differenzierung
|
||
|
||
Automatische Differenzierung (AD) ist eine rechnerische Technik, die verwendet wird, um **die Ableitungen (Gradienten)** von Funktionen effizient und genau zu bewerten. Im Kontext von neuronalen Netzwerken ermöglicht AD die Berechnung der für **Optimierungsalgorithmen wie den Gradientenabstieg** erforderlichen Gradienten. PyTorch bietet eine automatische Differenzierungs-Engine namens **autograd**, die diesen Prozess vereinfacht.
|
||
|
||
### Mathematische Erklärung der automatischen Differenzierung
|
||
|
||
**1. Die Kettenregel**
|
||
|
||
Im Kern der automatischen Differenzierung steht die **Kettenregel** aus der Analysis. Die Kettenregel besagt, dass, wenn Sie eine Zusammensetzung von Funktionen haben, die Ableitung der zusammengesetzten Funktion das Produkt der Ableitungen der zusammengesetzten Funktionen ist.
|
||
|
||
Mathematisch, wenn `y=f(u)` und `u=g(x)`, dann ist die Ableitung von `y` bezüglich `x`:
|
||
|
||
<figure><img src="../../images/image (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
**2. Rechengraph**
|
||
|
||
In AD werden Berechnungen als Knoten in einem **Rechengraphen** dargestellt, wobei jeder Knoten einer Operation oder einer Variablen entspricht. Durch das Durchlaufen dieses Graphen können wir Ableitungen effizient berechnen.
|
||
|
||
3. Beispiel
|
||
|
||
Betrachten wir eine einfache Funktion:
|
||
|
||
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
Wo:
|
||
|
||
- `σ(z)` die Sigmoidfunktion ist.
|
||
- `y=1.0` das Ziel-Label ist.
|
||
- `L` der Verlust ist.
|
||
|
||
Wir möchten den Gradienten des Verlusts `L` bezüglich des Gewichts `w` und des Bias `b` berechnen.
|
||
|
||
**4. Manuelle Berechnung der Gradienten**
|
||
|
||
<figure><img src="../../images/image (2) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
**5. Numerische Berechnung**
|
||
|
||
<figure><img src="../../images/image (3) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
### Implementierung der automatischen Differenzierung in PyTorch
|
||
|
||
Jetzt sehen wir, wie PyTorch diesen Prozess automatisiert.
|
||
```python
|
||
pythonCopy codeimport torch
|
||
import torch.nn.functional as F
|
||
|
||
# Define input and target
|
||
x = torch.tensor([1.1])
|
||
y = torch.tensor([1.0])
|
||
|
||
# Initialize weights with requires_grad=True to track computations
|
||
w = torch.tensor([2.2], requires_grad=True)
|
||
b = torch.tensor([0.0], requires_grad=True)
|
||
|
||
# Forward pass
|
||
z = x * w + b
|
||
a = torch.sigmoid(z)
|
||
loss = F.binary_cross_entropy(a, y)
|
||
|
||
# Backward pass
|
||
loss.backward()
|
||
|
||
# Gradients
|
||
print("Gradient w.r.t w:", w.grad)
|
||
print("Gradient w.r.t b:", b.grad)
|
||
```
|
||
**Ausgabe:**
|
||
```css
|
||
cssCopy codeGradient w.r.t w: tensor([-0.0898])
|
||
Gradient w.r.t b: tensor([-0.0817])
|
||
```
|
||
## Backpropagation in Größeren Neuronalen Netzwerken
|
||
|
||
### **1. Erweiterung auf Mehrschichtige Netzwerke**
|
||
|
||
In größeren neuronalen Netzwerken mit mehreren Schichten wird der Prozess der Berechnung von Gradienten aufgrund der erhöhten Anzahl von Parametern und Operationen komplexer. Die grundlegenden Prinzipien bleiben jedoch gleich:
|
||
|
||
- **Forward Pass:** Berechne die Ausgabe des Netzwerks, indem du Eingaben durch jede Schicht leitest.
|
||
- **Compute Loss:** Bewerte die Verlustfunktion unter Verwendung der Ausgabe des Netzwerks und der Zielbeschriftungen.
|
||
- **Backward Pass (Backpropagation):** Berechne die Gradienten des Verlusts in Bezug auf jeden Parameter im Netzwerk, indem du die Kettenregel rekursiv von der Ausgabeschicht zurück zur Eingabeschicht anwendest.
|
||
|
||
### **2. Backpropagation-Algorithmus**
|
||
|
||
- **Schritt 1:** Initialisiere die Netzwerkparameter (Gewichte und Biases).
|
||
- **Schritt 2:** Führe für jedes Trainingsbeispiel einen Forward Pass durch, um die Ausgaben zu berechnen.
|
||
- **Schritt 3:** Berechne den Verlust.
|
||
- **Schritt 4:** Berechne die Gradienten des Verlusts in Bezug auf jeden Parameter unter Verwendung der Kettenregel.
|
||
- **Schritt 5:** Aktualisiere die Parameter mit einem Optimierungsalgorithmus (z. B. Gradient Descent).
|
||
|
||
### **3. Mathematische Darstellung**
|
||
|
||
Betrachte ein einfaches neuronales Netzwerk mit einer versteckten Schicht:
|
||
|
||
<figure><img src="../../images/image (5) (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
### **4. PyTorch-Implementierung**
|
||
|
||
PyTorch vereinfacht diesen Prozess mit seiner Autograd-Engine.
|
||
```python
|
||
import torch
|
||
import torch.nn as nn
|
||
import torch.optim as optim
|
||
|
||
# Define a simple neural network
|
||
class SimpleNet(nn.Module):
|
||
def __init__(self):
|
||
super(SimpleNet, self).__init__()
|
||
self.fc1 = nn.Linear(10, 5) # Input layer to hidden layer
|
||
self.relu = nn.ReLU()
|
||
self.fc2 = nn.Linear(5, 1) # Hidden layer to output layer
|
||
self.sigmoid = nn.Sigmoid()
|
||
|
||
def forward(self, x):
|
||
h = self.relu(self.fc1(x))
|
||
y_hat = self.sigmoid(self.fc2(h))
|
||
return y_hat
|
||
|
||
# Instantiate the network
|
||
net = SimpleNet()
|
||
|
||
# Define loss function and optimizer
|
||
criterion = nn.BCELoss()
|
||
optimizer = optim.SGD(net.parameters(), lr=0.01)
|
||
|
||
# Sample data
|
||
inputs = torch.randn(1, 10)
|
||
labels = torch.tensor([1.0])
|
||
|
||
# Training loop
|
||
optimizer.zero_grad() # Clear gradients
|
||
outputs = net(inputs) # Forward pass
|
||
loss = criterion(outputs, labels) # Compute loss
|
||
loss.backward() # Backward pass (compute gradients)
|
||
optimizer.step() # Update parameters
|
||
|
||
# Accessing gradients
|
||
for name, param in net.named_parameters():
|
||
if param.requires_grad:
|
||
print(f"Gradient of {name}: {param.grad}")
|
||
```
|
||
In diesem Code:
|
||
|
||
- **Forward Pass:** Berechnet die Ausgaben des Netzwerks.
|
||
- **Backward Pass:** `loss.backward()` berechnet die Gradienten des Verlusts in Bezug auf alle Parameter.
|
||
- **Parameter Update:** `optimizer.step()` aktualisiert die Parameter basierend auf den berechneten Gradienten.
|
||
|
||
### **5. Verständnis des Backward Pass**
|
||
|
||
Während des Backward Pass:
|
||
|
||
- PyTorch durchläuft den Berechnungsgraphen in umgekehrter Reihenfolge.
|
||
- Für jede Operation wird die Kettenregel angewendet, um Gradienten zu berechnen.
|
||
- Gradienten werden im `.grad` Attribut jedes Parameter-Tensors akkumuliert.
|
||
|
||
### **6. Vorteile der automatischen Differenzierung**
|
||
|
||
- **Effizienz:** Vermeidet redundante Berechnungen, indem Zwischenresultate wiederverwendet werden.
|
||
- **Genauigkeit:** Bietet exakte Ableitungen bis zur Maschinenpräzision.
|
||
- **Benutzerfreundlichkeit:** Beseitigt die manuelle Berechnung von Ableitungen.
|