0. Basic LLM Concepts

Vortraining

Vortraining ist die grundlegende Phase bei der Entwicklung eines großen Sprachmodells (LLM), in der das Modell einer Vielzahl von Textdaten ausgesetzt wird. Während 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ärenten und kontextuell relevanten Text 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 wurde. 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 die Anzahl der separaten Aufmerksamkeitsmechanismen, die 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:

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 Möglichkeit, wie Daten in PyTorch dargestellt und manipuliert werden, insbesondere im Kontext von Deep Learning und neuronalen Netzwerken.

Mathematisches Konzept von 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 verschiedene 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, numerische Daten zu speichern und zu manipulieren, ähnlich sind, 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:

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:

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 .to()-Methode:

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.

print(tensor2d.shape)  # Ausgabe: torch.Size([2, 2])
  • Umformen von Tensors: Verwenden Sie .reshape() oder .view(), um die Form zu ändern.

reshaped = tensor2d.reshape(4, 1)
  • Transponieren von Tensors: Verwenden Sie .T, um einen 2D-Tensor zu transponieren.

transposed = tensor2d.T
  • Matrixmultiplikation: Verwenden Sie .matmul() oder den @-Operator.

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

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

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.

  1. Beispiel

Betrachten wir eine einfache Funktion:

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

5. Numerische Berechnung

Implementierung der automatischen Differenzierung in PyTorch

Jetzt sehen wir, wie PyTorch diesen Prozess automatisiert.

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:

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 Bias).

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

4. PyTorch-Implementierung

PyTorch vereinfacht diesen Prozess mit seiner Autograd-Engine.

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.

Last updated