5. LLM Mimarisi

Reading time: 16 minutes

LLM Mimarisi

tip

Bu beşinci aşamanın amacı çok basit: Tam LLM mimarisini geliştirmek. Her şeyi bir araya getirin, tüm katmanları uygulayın ve metin oluşturmak veya metni kimliklere ve geriye dönüştürmek için tüm işlevleri oluşturun.

Bu mimari, hem eğitim hem de eğitimden sonra metin tahmini için kullanılacaktır.

LLM mimarisi örneği https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb:

Yüksek seviyeli bir temsil aşağıda gözlemlenebilir:

https://camo.githubusercontent.com/6c8c392f72d5b9e86c94aeb9470beab435b888d24135926f1746eb88e0cc18fb/68747470733a2f2f73656261737469616e72617363686b612e636f6d2f696d616765732f4c4c4d732d66726f6d2d736372617463682d696d616765732f636830345f636f6d707265737365642f31332e776562703f31

  1. Girdi (Tokenize Edilmiş Metin): Süreç, sayısal temsillere dönüştürülen tokenize edilmiş metinle başlar.
  2. Token Gömme ve Pozisyon Gömme Katmanı: Tokenize edilmiş metin, kelime sırasını anlamak için kritik olan bir dizideki token'ların konumunu yakalayan bir token gömme katmanı ve bir pozisyon gömme katmanı aracılığıyla geçer.
  3. Transformer Blokları: Model, her biri birden fazla katmana sahip 12 transformer bloğu içerir. Bu bloklar aşağıdaki diziyi tekrarlar:
  • Masked Multi-Head Attention: Modelin girdi metninin farklı kısımlarına aynı anda odaklanmasını sağlar.
  • Katman Normalizasyonu: Eğitimi stabilize etmek ve geliştirmek için bir normalizasyon adımı.
  • İleri Besleme Katmanı: Dikkat katmanından gelen bilgileri işlemek ve bir sonraki token hakkında tahminlerde bulunmakla sorumludur.
  • Dropout Katmanları: Bu katmanlar, eğitim sırasında birimlerin rastgele düşürülmesiyle aşırı uyumu önler.
  1. Son Çıktı Katmanı: Model, 50,257 kelime dağarcığı boyutunu temsil eden 4x50,257 boyutunda bir tensör üretir. Bu tensördeki her bir satır, modelin dizideki bir sonraki kelimeyi tahmin etmek için kullandığı bir vektöre karşılık gelir.
  2. Amaç: Amaç, bu gömmeleri alıp tekrar metne dönüştürmektir. Özellikle, çıktının son satırı, bu diyagramda "ileri" olarak temsil edilen bir sonraki kelimeyi oluşturmak için kullanılır.

Kod temsili

python
import torch
import torch.nn as nn
import tiktoken

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 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 num_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.contiguous().view(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 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

GPT_CONFIG_124M = {
"vocab_size": 50257,    # Vocabulary size
"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
"qkv_bias": False       # Query-Key-Value bias
}

torch.manual_seed(123)
model = GPTModel(GPT_CONFIG_124M)
out = model(batch)
print("Input batch:\n", batch)
print("\nOutput shape:", out.shape)
print(out)

GELU Aktivasyon Fonksiyonu

python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
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))
))

Amaç ve İşlevsellik

  • GELU (Gaussian Error Linear Unit): Modelle doğrusal olmayanlık getiren bir aktivasyon fonksiyonu.
  • Düzgün Aktivasyon: Negatif girdileri sıfıra indiren ReLU'nun aksine, GELU girdileri çıktılara düzgün bir şekilde haritalar, negatif girdiler için küçük, sıfırdan farklı değerler sağlar.
  • Matematiksel Tanım:

tip

FeedForward katmanındaki doğrusal katmanlardan sonra bu fonksiyonun kullanılmasının amacı, doğrusal verileri doğrusal olmayan hale getirerek modelin karmaşık, doğrusal olmayan ilişkileri öğrenmesine izin vermektir.

FeedForward Sinir Ağı

Şekillerin matrislerin şekillerini daha iyi anlamak için yorum olarak eklendi:

python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
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):
# x shape: (batch_size, seq_len, emb_dim)

x = self.layers[0](x)# x shape: (batch_size, seq_len, 4 * emb_dim)
x = self.layers[1](x) # x shape remains: (batch_size, seq_len, 4 * emb_dim)
x = self.layers[2](x) # x shape: (batch_size, seq_len, emb_dim)
return x  # Output shape: (batch_size, seq_len, emb_dim)

Amaç ve İşlevsellik

  • Pozisyon Bazlı FeedForward Ağı: Her pozisyona ayrı ve benzer şekilde iki katmanlı tam bağlantılı bir ağ uygular.
  • Katman Detayları:
  • İlk Lineer Katman: Boyutları emb_dim'den 4 * emb_dim'ye genişletir.
  • GELU Aktivasyonu: Doğrusal olmayanlık uygular.
  • İkinci Lineer Katman: Boyutları tekrar emb_dim'ye düşürür.

tip

Gördüğünüz gibi, Feed Forward ağı 3 katman kullanır. İlk katman, boyutları 4 ile çarpacak lineer bir katmandır (model içinde eğitilecek parametreler). Ardından, daha zengin temsilleri yakalamak için tüm bu boyutlarda doğrusal olmayan varyasyonlar uygulamak üzere GELU fonksiyonu kullanılır ve nihayetinde orijinal boyutlara geri dönmek için başka bir lineer katman kullanılır.

Çoklu Başlı Dikkat Mekanizması

Bu daha önceki bir bölümde açıklandı.

Amaç ve İşlevsellik

  • Çoklu Başlı Kendine Dikkat: Modelin bir token'ı kodlarken girdi dizisi içindeki farklı pozisyonlara odaklanmasına olanak tanır.
  • Ana Bileşenler:
  • Sorgular, Anahtarlar, Değerler: Girdinin lineer projeksiyonları, dikkat puanlarını hesaplamak için kullanılır.
  • Başlar: Paralel çalışan birden fazla dikkat mekanizması (num_heads), her biri azaltılmış bir boyutla (head_dim).
  • Dikkat Puanları: Sorgular ve anahtarların noktasal çarpımı olarak hesaplanır, ölçeklendirilir ve maske uygulanır.
  • Maskeleme: Gelecek token'lara dikkat edilmesini önlemek için nedensel bir maske uygulanır (GPT gibi otoregresif modeller için önemlidir).
  • Dikkat Ağırlıkları: Maskelenmiş ve ölçeklendirilmiş dikkat puanlarının softmax'ı.
  • Bağlam Vektörü: Dikkat ağırlıklarına göre değerlerin ağırlıklı toplamı.
  • Çıktı Projeksiyonu: Tüm başların çıktısını birleştiren lineer katman.

tip

Bu ağın amacı, aynı bağlamdaki token'lar arasındaki ilişkileri bulmaktır. Ayrıca, aşırı uyum sağlamayı önlemek için token'lar farklı başlara bölünmüştür, ancak her başta bulunan nihai ilişkiler bu ağın sonunda birleştirilir.

Ayrıca, eğitim sırasında nedensel bir maske uygulanır, böylece belirli bir token'a bakarken sonraki token'lar dikkate alınmaz ve aşırı uyumu önlemek için bazı dropout uygulanır.

Katman Normalizasyon

python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class LayerNorm(nn.Module):
def __init__(self, emb_dim):
super().__init__()
self.eps = 1e-5 # Prevent division by zero during normalization.
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

Amaç ve İşlevsellik

  • Katman Normalizasyonu: Bir partideki her bireysel örnek için özellikler (gömme boyutları) boyunca girdileri normalleştirmek için kullanılan bir teknik.
  • Bileşenler:
  • eps: Normalizasyon sırasında sıfıra bölmeyi önlemek için varyansa eklenen küçük bir sabit (1e-5).
  • scale ve shift: Normalleştirilmiş çıktıyı ölçeklendirmek ve kaydırmak için modelin kullanabileceği öğrenilebilir parametreler (nn.Parameter). Sırasıyla birler ve sıfırlar ile başlatılır.
  • Normalizasyon Süreci:
  • Ortalama Hesaplama (mean): Gömme boyutu boyunca giriş x'in ortalamasını hesaplar (dim=-1), yayılma için boyutu korur (keepdim=True).
  • Varyans Hesaplama (var): Gömme boyutu boyunca x'in varyansını hesaplar, boyutu da korur. unbiased=False parametresi, varyansın yanlı tahminci kullanılarak hesaplanmasını sağlar (örnek sayısı N yerine N-1 ile bölünerek), bu da örnekler yerine özellikler üzerinde normalleştirme yaparken uygundur.
  • Normalleştirme (norm_x): x'ten ortalamayı çıkarır ve varyansın karekökü artı eps ile böler.
  • Ölçekleme ve Kaydırma: Normalleştirilmiş çıktıya öğrenilebilir scale ve shift parametrelerini uygular.

tip

Amaç, aynı token'ın tüm boyutları boyunca 0 ortalama ve 1 varyans sağlamaktır. Bunun amacı, derin sinir ağlarının eğitimini stabilize etmek için iç değişken kaymasını azaltmaktır; bu, eğitim sırasında parametrelerin güncellenmesi nedeniyle ağ aktivasyonlarının dağılımındaki değişimi ifade eder.

Transformer Bloğu

Matrislerin şekillerini daha iyi anlamak için yorum olarak şekiller eklenmiştir:

python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04

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):
# x shape: (batch_size, seq_len, emb_dim)

# Shortcut connection for attention block
shortcut = x  # shape: (batch_size, seq_len, emb_dim)
x = self.norm1(x)  # shape remains (batch_size, seq_len, emb_dim)
x = self.att(x)    # shape: (batch_size, seq_len, emb_dim)
x = self.drop_shortcut(x)  # shape remains (batch_size, seq_len, emb_dim)
x = x + shortcut   # shape: (batch_size, seq_len, emb_dim)

# Shortcut connection for feedforward block
shortcut = x       # shape: (batch_size, seq_len, emb_dim)
x = self.norm2(x)  # shape remains (batch_size, seq_len, emb_dim)
x = self.ff(x)     # shape: (batch_size, seq_len, emb_dim)
x = self.drop_shortcut(x)  # shape remains (batch_size, seq_len, emb_dim)
x = x + shortcut   # shape: (batch_size, seq_len, emb_dim)

return x  # Output shape: (batch_size, seq_len, emb_dim)

Amaç ve İşlevsellik

  • Katmanların Bileşimi: Çok başlı dikkat, ileri besleme ağı, katman normalizasyonu ve artımlı bağlantıları birleştirir.
  • Katman Normalizasyonu: Dikkat ve ileri besleme katmanlarından önce uygulanır, böylece eğitim istikrarlı olur.
  • Artımlı Bağlantılar (Kısa Yollar): Bir katmanın girişini çıkışına ekleyerek gradyan akışını iyileştirir ve derin ağların eğitimini mümkün kılar.
  • Dropout: Düzenleme için dikkat ve ileri besleme katmanlarından sonra uygulanır.

Adım Adım İşlevsellik

  1. İlk Artımlı Yol (Kendi Dikkati):
  • Giriş (shortcut): Artımlı bağlantı için orijinal girişi kaydedin.
  • Katman Normu (norm1): Girişi normalleştir.
  • Çok Başlı Dikkat (att): Kendi dikkatini uygula.
  • Dropout (drop_shortcut): Düzenleme için dropout uygula.
  • Artımlı Ekle (x + shortcut): Orijinal girişle birleştir.
  1. İkinci Artımlı Yol (İleri Besleme):
  • Giriş (shortcut): Bir sonraki artımlı bağlantı için güncellenmiş girişi kaydedin.
  • Katman Normu (norm2): Girişi normalleştir.
  • İleri Besleme Ağı (ff): İleri besleme dönüşümünü uygula.
  • Dropout (drop_shortcut): Dropout uygula.
  • Artımlı Ekle (x + shortcut): İlk artımlı yoldan gelen girişle birleştir.

tip

Transformer bloğu tüm ağları bir araya getirir ve eğitim istikrarını ve sonuçlarını iyileştirmek için bazı normalizasyon ve dropout uygular.
Dropout'ların her ağın kullanımından sonra yapıldığını, normalizasyonun ise öncesinde uygulandığını not edin.

Ayrıca, bir ağın çıkışını girişi ile eklemeyi içeren kısa yolları da kullanır. Bu, başlangıç katmanlarının son katmanlar kadar "çok" katkıda bulunmasını sağlayarak kaybolan gradyan sorununu önlemeye yardımcı olur.

GPTModel

Şekillerin matrislerin şekillerini daha iyi anlamak için yorum olarak eklendi:

python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class GPTModel(nn.Module):
def __init__(self, cfg):
super().__init__()
self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])
# shape: (vocab_size, emb_dim)

self.pos_emb = nn.Embedding(cfg["context_length"], cfg["emb_dim"])
# shape: (context_length, emb_dim)

self.drop_emb = nn.Dropout(cfg["drop_rate"])

self.trf_blocks = nn.Sequential(
*[TransformerBlock(cfg) for _ in range(cfg["n_layers"])]
)
# Stack of TransformerBlocks

self.final_norm = LayerNorm(cfg["emb_dim"])
self.out_head = nn.Linear(cfg["emb_dim"], cfg["vocab_size"], bias=False)
# shape: (emb_dim, vocab_size)

def forward(self, in_idx):
# in_idx shape: (batch_size, seq_len)
batch_size, seq_len = in_idx.shape

# Token embeddings
tok_embeds = self.tok_emb(in_idx)
# shape: (batch_size, seq_len, emb_dim)

# Positional embeddings
pos_indices = torch.arange(seq_len, device=in_idx.device)
# shape: (seq_len,)
pos_embeds = self.pos_emb(pos_indices)
# shape: (seq_len, emb_dim)

# Add token and positional embeddings
x = tok_embeds + pos_embeds  # Broadcasting over batch dimension
# x shape: (batch_size, seq_len, emb_dim)

x = self.drop_emb(x)  # Dropout applied
# x shape remains: (batch_size, seq_len, emb_dim)

x = self.trf_blocks(x)  # Pass through Transformer blocks
# x shape remains: (batch_size, seq_len, emb_dim)

x = self.final_norm(x)  # Final LayerNorm
# x shape remains: (batch_size, seq_len, emb_dim)

logits = self.out_head(x)  # Project to vocabulary size
# logits shape: (batch_size, seq_len, vocab_size)

return logits  # Output shape: (batch_size, seq_len, vocab_size)

Amaç ve İşlevsellik

  • Gömme Katmanları:
  • Token Gömme (tok_emb): Token indekslerini gömmelere dönüştürür. Hatırlatma olarak, bunlar kelime dağarcığındaki her tokenin her boyutuna verilen ağırlıklardır.
  • Pozisyonel Gömme (pos_emb): Gömmelere pozisyonel bilgi ekleyerek tokenlerin sırasını yakalar. Hatırlatma olarak, bunlar metindeki pozisyonuna göre tokenlere verilen ağırlıklardır.
  • Dropout (drop_emb): Gömmelere düzenleme için uygulanır.
  • Transformer Blokları (trf_blocks): Gömmeleri işlemek için n_layers transformer bloğunun yığını.
  • Son Normalizasyon (final_norm): Çıktı katmanından önce katman normalizasyonu.
  • Çıktı Katmanı (out_head): Son gizli durumları kelime dağarcığı boyutuna projekte ederek tahmin için logitleri üretir.

tip

Bu sınıfın amacı, bir dizideki bir sonraki tokeni tahmin etmek için diğer bahsedilen tüm ağları kullanmaktır; bu, metin üretimi gibi görevler için temeldir.

Belirtilen kadar transformer bloğu kullanacağını ve her transformer bloğunun bir çok başlı dikkat ağı, bir ileri besleme ağı ve birkaç normalizasyon kullandığını not edin. Yani 12 transformer bloğu kullanılıyorsa, bunu 12 ile çarpın.

Ayrıca, çıktıdan önce bir normalizasyon katmanı eklenir ve sonuçları uygun boyutlarla almak için sonunda bir son doğrusal katman uygulanır. Her son vektörün kullanılan kelime dağarcığının boyutuna sahip olduğunu not edin. Bu, kelime dağarcığındaki her olası token için bir olasılık elde etmeye çalıştığı içindir.

Eğitilecek Parametre Sayısı

GPT yapısı tanımlandığında, eğitilecek parametre sayısını bulmak mümkündür:

python
GPT_CONFIG_124M = {
"vocab_size": 50257,    # Vocabulary size
"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
"qkv_bias": False       # Query-Key-Value bias
}

model = GPTModel(GPT_CONFIG_124M)
total_params = sum(p.numel() for p in model.parameters())
print(f"Total number of parameters: {total_params:,}")
# Total number of parameters: 163,009,536

Adım Adım Hesaplama

1. Gömme Katmanları: Token Gömme ve Konum Gömme

  • Katman: nn.Embedding(vocab_size, emb_dim)
  • Parametreler: vocab_size * emb_dim
python
token_embedding_params = 50257 * 768 = 38,597,376
  • Katman: nn.Embedding(context_length, emb_dim)
  • Parametreler: context_length * emb_dim
python
position_embedding_params = 1024 * 768 = 786,432

Toplam Gömme Parametreleri

python
embedding_params = token_embedding_params + position_embedding_params
embedding_params = 38,597,376 + 786,432 = 39,383,808

2. Transformer Blokları

12 transformer bloğu vardır, bu yüzden bir bloğun parametrelerini hesaplayacağız ve ardından 12 ile çarpacağız.

Her Transformer Bloğu için Parametreler

a. Çoklu Başlı Dikkat

  • Bileşenler:

  • Sorgu Lineer Katmanı (W_query): nn.Linear(emb_dim, emb_dim, bias=False)

  • Anahtar Lineer Katmanı (W_key): nn.Linear(emb_dim, emb_dim, bias=False)

  • Değer Lineer Katmanı (W_value): nn.Linear(emb_dim, emb_dim, bias=False)

  • Çıktı Projeksiyonu (out_proj): nn.Linear(emb_dim, emb_dim)

  • Hesaplamalar:

  • W_query, W_key, W_value için her biri:

python
qkv_params = emb_dim * emb_dim = 768 * 768 = 589,824

Üç böyle katman olduğu için:

python
total_qkv_params = 3 * qkv_params = 3 * 589,824 = 1,769,472
  • Çıktı Projeksiyonu (out_proj):
python
out_proj_params = (emb_dim * emb_dim) + emb_dim = (768 * 768) + 768 = 589,824 + 768 = 590,592
  • Toplam Çoklu Başlı Dikkat Parametreleri:
python
mha_params = total_qkv_params + out_proj_params
mha_params = 1,769,472 + 590,592 = 2,360,064

b. İleri Besleme Ağı

  • Bileşenler:

  • İlk Lineer Katman: nn.Linear(emb_dim, 4 * emb_dim)

  • İkinci Lineer Katman: nn.Linear(4 * emb_dim, emb_dim)

  • Hesaplamalar:

  • İlk Lineer Katman:

python
ff_first_layer_params = (emb_dim * 4 * emb_dim) + (4 * emb_dim)
ff_first_layer_params = (768 * 3072) + 3072 = 2,359,296 + 3,072 = 2,362,368
  • İkinci Lineer Katman:
python
ff_second_layer_params = (4 * emb_dim * emb_dim) + emb_dim
ff_second_layer_params = (3072 * 768) + 768 = 2,359,296 + 768 = 2,360,064
  • Toplam İleri Besleme Parametreleri:
python
ff_params = ff_first_layer_params + ff_second_layer_params
ff_params = 2,362,368 + 2,360,064 = 4,722,432

c. Katman Normalizasyonları

  • Bileşenler:
  • Her blokta iki LayerNorm örneği.
  • Her LayerNorm'un 2 * emb_dim parametresi vardır (ölçek ve kaydırma).
  • Hesaplamalar:
python
layer_norm_params_per_block = 2 * (2 * emb_dim) = 2 * 768 * 2 = 3,072

d. Her Transformer Bloğu için Toplam Parametreler

python
pythonCopy codeparams_per_block = mha_params + ff_params + layer_norm_params_per_block
params_per_block = 2,360,064 + 4,722,432 + 3,072 = 7,085,568

Tüm Dönüştürücü Blokları için Toplam Parametreler

python
pythonCopy codetotal_transformer_blocks_params = params_per_block * n_layers
total_transformer_blocks_params = 7,085,568 * 12 = 85,026,816

3. Son Katmanlar

a. Son Katman Normalizasyonu

  • Parametreler: 2 * emb_dim (ölçek ve kaydır)
python
pythonCopy codefinal_layer_norm_params = 2 * 768 = 1,536

b. Çıktı Projeksiyon Katmanı (out_head)

  • Katman: nn.Linear(emb_dim, vocab_size, bias=False)
  • Parametreler: emb_dim * vocab_size
python
pythonCopy codeoutput_projection_params = 768 * 50257 = 38,597,376

4. Tüm Parametreleri Özetleme

python
pythonCopy codetotal_params = (
embedding_params +
total_transformer_blocks_params +
final_layer_norm_params +
output_projection_params
)
total_params = (
39,383,808 +
85,026,816 +
1,536 +
38,597,376
)
total_params = 163,009,536

Metin Üretimi

Bir önceki gibi bir sonraki token'ı tahmin eden bir modele sahip olmak, çıktının son token değerlerini almak için gereklidir (çünkü bunlar tahmin edilen token'ın değerleri olacaktır), bu da sözlükteki her giriş için bir değer olacak ve ardından softmax fonksiyonunu kullanarak boyutları 1'e toplam olan olasılıklara normalize etmek ve ardından en büyük girişin indeksini almak, bu da sözlükteki kelimenin indeksi olacaktır.

https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb kodu:

python
def generate_text_simple(model, idx, max_new_tokens, context_size):
# idx is (batch, n_tokens) array of indices in the current context
for _ in range(max_new_tokens):

# Crop current context if it exceeds the supported context size
# E.g., if LLM supports only 5 tokens, and the context size is 10
# then only the last 5 tokens are used as context
idx_cond = idx[:, -context_size:]

# Get the predictions
with torch.no_grad():
logits = model(idx_cond)

# Focus only on the last time step
# (batch, n_tokens, vocab_size) becomes (batch, vocab_size)
logits = logits[:, -1, :]

# Apply softmax to get probabilities
probas = torch.softmax(logits, dim=-1)  # (batch, vocab_size)

# Get the idx of the vocab entry with the highest probability value
idx_next = torch.argmax(probas, dim=-1, keepdim=True)  # (batch, 1)

# Append sampled index to the running sequence
idx = torch.cat((idx, idx_next), dim=1)  # (batch, n_tokens+1)

return idx


start_context = "Hello, I am"

encoded = tokenizer.encode(start_context)
print("encoded:", encoded)

encoded_tensor = torch.tensor(encoded).unsqueeze(0)
print("encoded_tensor.shape:", encoded_tensor.shape)

model.eval() # disable dropout

out = generate_text_simple(
model=model,
idx=encoded_tensor,
max_new_tokens=6,
context_size=GPT_CONFIG_124M["context_length"]
)

print("Output:", out)
print("Output length:", len(out[0]))

Referanslar