28 KiB
5. LLM Mimarisi
LLM Mimarisi
{% hint style="success" %} 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. {% endhint %}
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:
- Girdi (Tokenize Edilmiş Metin): Süreç, sayısal temsillere dönüştürülen tokenize edilmiş metinle başlar.
- Token Gömme ve Pozisyon Gömme Katmanı: Tokenize edilmiş metin, bir token gömme katmanından ve bir pozisyon gömme katmanından geçirilir; bu, kelime sırasını anlamak için kritik olan bir dizideki token'ların konumunu yakalar.
- 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ı.
- İleri Besleme 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.
- Son Çıktı Katmanı: Model, 50,257 boyutlu 4x50,257'lik bir tensör çıktısı verir; burada 50,257 kelime dağarcığının boyutunu temsil eder. Bu tensördeki her bir satır, modelin dizideki bir sonraki kelimeyi tahmin etmek için kullandığı bir vektöre karşılık gelir.
- 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 ekleyen 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 eşler ve negatif girdiler için küçük, sıfırdan farklı değerler sağlar.
- Matematiksel Tanım:
{% hint style="info" %} 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. {% endhint %}
FeedForward Sinir Ağı
Şekillerin matrislerin şekillerini daha iyi anlamak için yorum olarak eklendi:
# 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
'den4 * emb_dim
'ye genişletir. - GELU Aktivasyonu: Doğrusal olmayanlık uygular.
- İkinci Lineer Katman: Boyutları tekrar
emb_dim
'ye düşürür.
{% hint style="info" %} 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, tüm bu boyutlarda 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. {% endhint %}
Ç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ç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ştirmek için lineer katman.
{% hint style="info" %} 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 bölünmüştür, böylece aşırı uyum önlenir.
Ayrıca, eğitim sırasında belirli bir token'a yönelik 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. {% endhint %}
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: Her bir örneğin özellikler (gömme boyutları) arasında 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
veshift
: Normalleştirilmiş çıktıyı ölçeklendirmek ve kaydırmak için modelin öğrenebileceği 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 boyuncax
'in varyansını hesaplar, boyutu da korur.unbiased=False
parametresi, varyansın yanlı tahminci kullanılarak hesaplanmasını sağlar (örnek sayısıN
yerineN-1
ile bölünerek), bu da ö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
veshift
parametrelerini uygular.
{% hint style="info" %} 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çsel kovaryat 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. {% endhint %}
Transformer 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
- İ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ı Ekle (
x + shortcut
): Orijinal girişle birleştirin.
- İ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ı Ekle (
x + shortcut
): İlk artımlı yoldan gelen girişle birleştirin.
{% hint style="info" %}
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 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 "katkıda bulunmasını" sağlayarak kaybolan gradyan sorununu önlemeye yardımcı olur. {% endhint %}
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. - Pozisyon Gömme (
pos_emb
): Gömülere pozisyon bilgisi 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 için uygulanır. - Transformer Blokları (
trf_blocks
): Gömüleri işlemek içinn_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.
{% hint style="info" %} 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 ç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 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. {% endhint %}
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.
Transformer Bloğu Başına 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
'nin 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 başına iki
LayerNorm
örneği. - Her
LayerNorm
'un2 * 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. Transformer Bloğu Başına 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
Generate Text
Bir ö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.
Code from https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb:
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]))