0. Podstawowe pojęcia LLM

Reading time: 8 minutes

Pretraining

Pretraining to podstawowa faza w rozwijaniu dużego modelu językowego (LLM), w której model jest narażony na ogromne i różnorodne ilości danych tekstowych. W tym etapie LLM uczy się fundamentalnych struktur, wzorców i niuansów języka, w tym gramatyki, słownictwa, składni i relacji kontekstowych. Przetwarzając te obszerne dane, model nabywa szerokie zrozumienie języka i ogólnej wiedzy o świecie. Ta kompleksowa baza umożliwia LLM generowanie spójnego i kontekstowo odpowiedniego tekstu. Następnie ten wstępnie wytrenowany model może przejść do fine-tuningu, gdzie jest dalej trenowany na wyspecjalizowanych zbiorach danych, aby dostosować swoje możliwości do konkretnych zadań lub dziedzin, poprawiając swoją wydajność i trafność w docelowych aplikacjach.

Główne komponenty LLM

Zazwyczaj LLM charakteryzuje się konfiguracją używaną do jego trenowania. Oto wspólne komponenty podczas trenowania LLM:

  • Parametry: Parametry to uczone wagi i biasy w sieci neuronowej. To są liczby, które proces treningowy dostosowuje, aby zminimalizować funkcję straty i poprawić wydajność modelu w zadaniu. LLM zazwyczaj używa milionów parametrów.
  • Długość kontekstu: To maksymalna długość każdego zdania używanego do wstępnego trenowania LLM.
  • Wymiar osadzenia: Rozmiar wektora używanego do reprezentacji każdego tokena lub słowa. LLM zazwyczaj używa miliardów wymiarów.
  • Wymiar ukryty: Rozmiar warstw ukrytych w sieci neuronowej.
  • Liczba warstw (głębokość): Ile warstw ma model. LLM zazwyczaj używa dziesiątek warstw.
  • Liczba głów uwagi: W modelach transformatorowych, to ile oddzielnych mechanizmów uwagi jest używanych w każdej warstwie. LLM zazwyczaj używa dziesiątek głów.
  • Dropout: Dropout to coś w rodzaju procentu danych, które są usuwane (prawdopodobieństwa stają się 0) podczas treningu, używanego do zapobiegania przeuczeniu. LLM zazwyczaj używa od 0 do 20%.

Konfiguracja modelu GPT-2:

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
}

Tenzory w PyTorch

W PyTorch, tenzor to podstawowa struktura danych, która służy jako wielowymiarowa tablica, uogólniając pojęcia takie jak skalar, wektor i macierz do potencjalnie wyższych wymiarów. Tenzory są głównym sposobem reprezentacji i manipulacji danymi w PyTorch, szczególnie w kontekście uczenia głębokiego i sieci neuronowych.

Matematyczna koncepcja tensorów

  • Skalary: Tenzory rangi 0, reprezentujące pojedynczą liczbę (zero-wymiarowe). Na przykład: 5
  • Wektory: Tenzory rangi 1, reprezentujące jednowymiarową tablicę liczb. Na przykład: [5,1]
  • Macierze: Tenzory rangi 2, reprezentujące dwuwymiarowe tablice z wierszami i kolumnami. Na przykład: [[1,3], [5,2]]
  • Tenzory wyższej rangi: Tenzory rangi 3 lub wyższej, reprezentujące dane w wyższych wymiarach (np. 3D tenzory dla obrazów kolorowych).

Tenzory jako pojemniki na dane

Z perspektywy obliczeniowej, tenzory działają jako pojemniki na dane wielowymiarowe, gdzie każdy wymiar może reprezentować różne cechy lub aspekty danych. To sprawia, że tenzory są bardzo odpowiednie do obsługi złożonych zbiorów danych w zadaniach uczenia maszynowego.

Tenzory PyTorch vs. tablice NumPy

Chociaż tenzory PyTorch są podobne do tablic NumPy w swojej zdolności do przechowywania i manipulacji danymi numerycznymi, oferują dodatkowe funkcjonalności kluczowe dla uczenia głębokiego:

  • Automatyczna różniczkowanie: Tenzory PyTorch wspierają automatyczne obliczanie gradientów (autograd), co upraszcza proces obliczania pochodnych wymaganych do trenowania sieci neuronowych.
  • Przyspieszenie GPU: Tenzory w PyTorch mogą być przenoszone i obliczane na GPU, co znacznie przyspiesza obliczenia na dużą skalę.

Tworzenie tensorów w PyTorch

Możesz tworzyć tenzory za pomocą funkcji torch.tensor:

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]]])

Typy danych tensorów

Tensory PyTorch mogą przechowywać dane różnych typów, takich jak liczby całkowite i liczby zmiennoprzecinkowe.

Możesz sprawdzić typ danych tensora, używając atrybutu .dtype:

python
tensor1d = torch.tensor([1, 2, 3])
print(tensor1d.dtype)  # Output: torch.int64
  • Tensory utworzone z liczb całkowitych Pythona mają typ torch.int64.
  • Tensory utworzone z liczb zmiennoprzecinkowych Pythona mają typ torch.float32.

Aby zmienić typ danych tensora, użyj metody .to():

python
float_tensor = tensor1d.to(torch.float32)
print(float_tensor.dtype)  # Output: torch.float32

Common Tensor Operations

PyTorch provides a variety of operations to manipulate tensors:

  • Accessing Shape: Use .shape to get the dimensions of a tensor.
python
print(tensor2d.shape)  # Output: torch.Size([2, 2])
  • Reshaping Tensors: Use .reshape() or .view() to change the shape.
python
reshaped = tensor2d.reshape(4, 1)
  • Transposing Tensors: Use .T to transpose a 2D tensor.
python
transposed = tensor2d.T
  • Matrix Multiplication: Use .matmul() or the @ operator.
python
result = tensor2d @ tensor2d.T

Importance in Deep Learning

Tensors are essential in PyTorch for building and training neural networks:

  • They store input data, weights, and biases.
  • They facilitate operations required for forward and backward passes in training algorithms.
  • With autograd, tensors enable automatic computation of gradients, streamlining the optimization process.

Automatic Differentiation

Automatic differentiation (AD) is a computational technique used to evaluate the derivatives (gradients) of functions efficiently and accurately. In the context of neural networks, AD enables the calculation of gradients required for optimization algorithms like gradient descent. PyTorch provides an automatic differentiation engine called autograd that simplifies this process.

Mathematical Explanation of Automatic Differentiation

1. The Chain Rule

At the heart of automatic differentiation is the chain rule from calculus. The chain rule states that if you have a composition of functions, the derivative of the composite function is the product of the derivatives of the composed functions.

Mathematically, if y=f(u) and u=g(x), then the derivative of y with respect to x is:

2. Computational Graph

In AD, computations are represented as nodes in a computational graph, where each node corresponds to an operation or a variable. By traversing this graph, we can compute derivatives efficiently.

  1. Example

Let's consider a simple function:

Where:

  • σ(z) is the sigmoid function.
  • y=1.0 is the target label.
  • L is the loss.

We want to compute the gradient of the loss L with respect to the weight w and bias b.

4. Computing Gradients Manually

5. Numerical Calculation

Implementing Automatic Differentiation in PyTorch

Now, let's see how PyTorch automates this process.

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)

Wyjście:

css
cssCopy codeGradient w.r.t w: tensor([-0.0898])
Gradient w.r.t b: tensor([-0.0817])

Backpropagation w większych sieciach neuronowych

1. Rozszerzenie do sieci wielowarstwowych

W większych sieciach neuronowych z wieloma warstwami proces obliczania gradientów staje się bardziej złożony z powodu zwiększonej liczby parametrów i operacji. Jednak podstawowe zasady pozostają takie same:

  • Forward Pass: Oblicz wyjście sieci, przekazując dane wejściowe przez każdą warstwę.
  • Compute Loss: Oceń funkcję straty, używając wyjścia sieci i docelowych etykiet.
  • Backward Pass (Backpropagation): Oblicz gradienty straty względem każdego parametru w sieci, stosując regułę łańcuchową rekurencyjnie od warstwy wyjściowej do warstwy wejściowej.

2. Algorytm Backpropagation

  • Krok 1: Zainicjalizuj parametry sieci (wagi i biasy).
  • Krok 2: Dla każdego przykładu treningowego wykonaj forward pass, aby obliczyć wyjścia.
  • Krok 3: Oblicz stratę.
  • Krok 4: Oblicz gradienty straty względem każdego parametru, stosując regułę łańcuchową.
  • Krok 5: Zaktualizuj parametry, używając algorytmu optymalizacji (np. gradient descent).

3. Reprezentacja matematyczna

Rozważ prostą sieć neuronową z jedną ukrytą warstwą:

4. Implementacja w PyTorch

PyTorch upraszcza ten proces dzięki swojemu silnikowi autograd.

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}")

W tym kodzie:

  • Forward Pass: Oblicza wyjścia sieci.
  • Backward Pass: loss.backward() oblicza gradienty straty względem wszystkich parametrów.
  • Parameter Update: optimizer.step() aktualizuje parametry na podstawie obliczonych gradientów.

5. Zrozumienie Backward Pass

Podczas backward pass:

  • PyTorch przeszukuje graf obliczeniowy w odwrotnej kolejności.
  • Dla każdej operacji stosuje regułę łańcuchową do obliczenia gradientów.
  • Gradienty są gromadzone w atrybucie .grad każdego tensora parametru.

6. Zalety automatycznej różniczkowania

  • Efektywność: Unika zbędnych obliczeń poprzez ponowne wykorzystanie wyników pośrednich.
  • Dokładność: Zapewnia dokładne pochodne do precyzji maszyny.
  • Łatwość użycia: Eliminuje ręczne obliczanie pochodnych.