5. LLM Architecture

LLM Mimarisi

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 ID'lere 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:

  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ından ve bir pozisyon gömme katmanından geçirilir.

  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ına olanak tanır.

  • Katman Normalizasyonu: Eğitimi stabilize etmek ve geliştirmek için bir normalizasyon adımı.

  • Feed Forward Katmanı: Dikkat katmanından gelen bilgileri işlemek ve bir sonraki token hakkında tahminler yapmakla 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

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

# 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 negatif girdiler için küçük, sıfırdan farklı değerler alarak girdileri çıktılara düzgün bir şekilde eşler.

  • Matematiksel Tanım:

Bu fonksiyonun FeedForward katmanındaki doğrusal katmanlardan sonra 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ğı

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

# 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ğ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.

Gördüğünüz gibi, Feed Forward ağı 3 katman kullanır. İlk katman, boyutları 4 ile çarpacak bir lineer katmandır ve bu, model içinde eğitilecek lineer ağırlıklar (parametreler) kullanır. Ardından, bu boyutların hepsinde daha zengin temsilleri yakalamak için 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: Dikkat puanlarını hesaplamak için kullanılan girdi lineer projeksiyonları.

  • 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 nokta çarpımı olarak hesaplanır, ölçeklenir 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çeklenmiş 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ştirmek için lineer katman.

Bu ağın amacı, aynı bağlamdaki token'lar arasındaki ilişkileri bulmaktır. Ayrıca, son ilişkilerin her başta bulunmasını sağlamak için token'lar farklı başlara ayrılır, böylece aşırı uyum önlenir.

Ayrıca, eğitim sırasında belirli bir token'a ilişkin ilişkileri incelerken daha sonraki token'ların dikkate alınmaması için bir nedensel maske uygulanır ve aşırı uyumu önlemek için bazı dropout uygulanır.

Katman Normalizasyon

# 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 girişleri 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.

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.

Dönüştürücü Bloğu

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

# 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 kararlılığı sağlanır.

  • 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ştirin.

  • Çok Başlı Dikkat (att): Kendi dikkatinizi uygulayın.

  • Dropout (drop_shortcut): Düzenleme için dropout uygulayın.

  • Artımlı Ekleme (x + shortcut): Orijinal girişle birleştirin.

  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ştirin.

  • İleri Besleme Ağı (ff): İleri besleme dönüşümünü uygulayın.

  • Dropout (drop_shortcut): Dropout uygulayın.

  • Artımlı Ekleme (x + shortcut): İlk artımlı yoldan gelen girişle birleştirin.

Transformer bloğu tüm ağları bir araya getirir ve eğitim kararlılığını ve sonuçlarını iyileştirmek için bazı normalizasyon ve dropout uygulamaları yapar. 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 "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:

# 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ömülere 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ömülere 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ömülere düzenleme uygulamak için kullanılır.

  • Transformer Blokları (trf_blocks): Gömüleri 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.

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.

Ne kadar belirtilen kadar transformer bloğu kullanacağını ve her transformer bloğunun bir çoklu başlık 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 lineer katman uygulanır. Her son vektörün kullanılan kelime dağarcığı 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:

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 & Konum Gömme

  • Katman: nn.Embedding(vocab_size, emb_dim)

  • Parametreler: vocab_size * emb_dim

token_embedding_params = 50257 * 768 = 38,597,376
  • Katman: nn.Embedding(context_length, emb_dim)

  • Parametreler: context_length * emb_dim

position_embedding_params = 1024 * 768 = 786,432

Toplam Gömme Parametreleri

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:

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

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

total_qkv_params = 3 * qkv_params = 3 * 589,824 = 1,769,472
  • Çıktı Projeksiyonu (out_proj):

out_proj_params = (emb_dim * emb_dim) + emb_dim = (768 * 768) + 768 = 589,824 + 768 = 590,592
  • Toplam Çoklu Başlı Dikkat Parametreleri:

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:

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:

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:

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 blok için iki LayerNorm örneği.

  • Her LayerNorm'un 2 * emb_dim parametresi vardır (ölçek ve kaydırma).

  • Hesaplamalar:

layer_norm_params_per_block = 2 * (2 * emb_dim) = 2 * 768 * 2 = 3,072

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

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

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ırma)

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

pythonCopy codeoutput_projection_params = 768 * 50257 = 38,597,376

4. Tüm Parametreleri Özetleme

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 Üret

Önceki gibi bir sonraki token'ı tahmin eden bir modele sahip olduğunuzda, çıktının son token değerlerini almak yeterlidir (çünkü bunlar tahmin edilen token'ın değerleri olacaktır), bu da sözlükteki her bir 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 adresinden kod:

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

Last updated