إنتقل إلى المحتوى الرئيسي

الفصل 3: الترميز الموضعي والتضمينات

مقدمة​

على عكس الشبكات العصبية المتكررة (RNNs) التي تعالج التسلسلات رمزًا برمز، تعالج المحولات جميع المواقع في وقت واحد. هذا يخلق تحديًا أساسيًا: كيف يعرف النموذج ترتيب الرموز؟ يشرح هذا الفصل الترميز الموضعي وعملية تضمين الرموز الأوسع.

مشكلة الموضع​

لماذا الموضع مهم​

اعتبر هذه الجمل:

  • "الكلب عض الرجل"
  • "الرجل عض الكلب"

نفس الرموز، معاني مختلفة تمامًا. الموضع ضروري لفهم اللغة.

المحولات وثبات التبديل​

بدون معلومات موضعية، يكون الانتباه الذاتي ثابت التبديل:

SelfAttention(π(X))=π(SelfAttention(X))\text{SelfAttention}(\pi(X)) = \pi(\text{SelfAttention}(X))

حيث π\pi هو أي تبديل. يعامل النموذج "الكلب عض الرجل" و"الرجل عض الكلب" بشكل متطابق!

# توضيح: درجات الانتباه مستقلة عن الموضع
import torch

def attention_scores(Q, K):
return torch.softmax(Q @ K.T, dim=-1)

# التسلسل الأصلي
x1 = torch.tensor([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]])

# التسلسل المبدل (تبديل المواضع 0 و 1)
x2 = torch.tensor([[0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])

# نفس نمط الانتباه (فقط مبدل)!
print(attention_scores(x1, x1))
print(attention_scores(x2, x2))

تضمينات الرموز​

من الرموز إلى المتجهات​

قبل إضافة المعلومات الموضعية، نقوم أولاً بتحويل الرموز إلى متجهات كثيفة:

E=Embedding(token_ids)E = \text{Embedding}(\text{token\_ids})

التنفيذ​

import torch
import torch.nn as nn

class TokenEmbedding(nn.Module):
"""
تحويل معرفات الرموز إلى متجهات كثيفة.
"""
def __init__(self, vocab_size, d_model):
super().__init__()
self.embedding = nn.Embedding(vocab_size, d_model)
self.d_model = d_model

def forward(self, token_ids):
"""
token_ids: (batch, seq_len) من معرفات الرموز الصحيحة
Returns: (batch, seq_len, d_model) التضمينات
"""
# مقياس التضمينات (من الورقة الأصلية للمحول)
return self.embedding(token_ids) * (self.d_model ** 0.5)

# مثال
vocab_size = 50000
d_model = 512
embedding_layer = TokenEmbedding(vocab_size, d_model)

tokens = torch.tensor([[101, 7592, 2088, 102]]) # [CLS] Hello World [SEP]
embeddings = embedding_layer(tokens)
print(f"Shape: {embeddings.shape}") # (1, 4, 512)

الترميز الموضعي الجيبي​

النهج الأصلي​

استخدمت الورقة الأصلية للمحول دوال جيبية ثابتة:

PE(pos,2i)=sin⁡(pos100002i/dmodel)PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right) PE(pos,2i+1)=cos⁡(pos100002i/dmodel)PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right)

حيث:

  • pospos = الموضع في التسلسل
  • ii = مؤشر البعد
  • dmodeld_{model} = بعد النموذج

الفكرة​

كل بعد يتذبذب بتردد مختلف:

  • الأبعاد المنخفضة: تذبذب بطيء (التقاط المواضع بعيدة المدى)
  • الأبعاد العالية: تذبذب سريع (التقاط المواضع الدقيقة)

التنفيذ​

import numpy as np

def get_sinusoidal_encoding(max_len, d_model):
"""
توليد الترميزات الموضعية الجيبية.

Args:
max_len: الطول الأقصى للتسلسل
d_model: بعد النموذج

Returns:
مصفوفة الترميز الموضعي (max_len, d_model)
"""
position = np.arange(max_len)[:, np.newaxis]
div_term = np.exp(np.arange(0, d_model, 2) * -(np.log(10000.0) / d_model))

pe = np.zeros((max_len, d_model))
pe[:, 0::2] = np.sin(position * div_term) # الفهارس الزوجية
pe[:, 1::2] = np.cos(position * div_term) # الفهارس الفردية

return pe

# التصور
import matplotlib.pyplot as plt

pe = get_sinusoidal_encoding(100, 512)
plt.figure(figsize=(12, 4))
plt.imshow(pe.T, aspect='auto', cmap='RdBu')
plt.xlabel('Position')
plt.ylabel('Dimension')
plt.colorbar(label='Value')
plt.title('Sinusoidal Positional Encoding')
plt.show()

خصائص الترميز الجيبي​

  1. فريد: لكل موضع ترميز فريد
  2. محدود: القيم دائمًا في [-1, 1]
  3. المواضع النسبية: العلاقة الخطية تمكن من تعلم المواضع النسبية
  4. التعميم: يمكن التعميم على تسلسلات أطول مما شوهد في التدريب

تنفيذ PyTorch​

class SinusoidalPositionalEncoding(nn.Module):
"""
الترميز الموضعي الجيبي من 'Attention Is All You Need'.
"""
def __init__(self, d_model, max_len=5000, dropout=0.1):
super().__init__()
self.dropout = nn.Dropout(p=dropout)

# إنشاء مصفوفة الترميز الموضعي
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(
torch.arange(0, d_model, 2).float() * (-np.log(10000.0) / d_model)
)

pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0) # إضافة بعد الدفعة

# تسجيل كذاكرة مؤقتة (ليس معلمة، ولكن محفوظة مع النموذج)
self.register_buffer('pe', pe)

def forward(self, x):
"""
x: (batch, seq_len, d_model)
"""
x = x + self.pe[:, :x.size(1)]
return self.dropout(x)

التضمينات الموضعية المتعلمة​

نهج بديل​

تستخدم العديد من نماذج اللغة الكبيرة الحديثة (LLMs) تضمينات موضعية متعلمة:

class LearnedPositionalEmbedding(nn.Module):
"""
التضمينات الموضعية المتعلمة (مثل المستخدمة في GPT-2).
"""
def __init__(self, max_len, d_model):
super().__init__()
self.position_embedding = nn.Embedding(max_len, d_model)

def forward(self, x):
"""
x: (batch, seq_len, d_model)
"""
positions = torch.arange(x.size(1), device=x.device)
return x + self.position_embedding(positions)

المقارنة​

الجانبجيبيمتعلم
المعلماتلا يوجدmax_len × d_model
التعميمأفضلمحدود بـ max_len
الأداءقابل للمقارنةقابل للمقارنة
المرونةثابتيتكيف مع البيانات

تضمين الموضع الدوار (RoPE)​

ابتكار حديث​

RoPE، المستخدم في LLaMA وMistral والعديد من النماذج الحديثة، يرمز الموضع من خلال الدوران:

fq(xm,m)=xmeimθf_q(x_m, m) = x_m e^{im\theta}

الفوائد الرئيسية​

  1. المواضع النسبية: يلتقط بشكل طبيعي معلومات الموضع النسبي
  2. التعميم: معالجة أفضل للتسلسلات الأطول
  3. الكفاءة: يطبق مباشرة على Q، K بدون معلمات إضافية

تنفيذ مبسط​

def rotary_embedding(x, seq_len, d_model):
"""
تنفيذ RoPE مبسط.
x: (batch, seq_len, d_model)
"""
# توليد زوايا الدوران
position = torch.arange(seq_len).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) * -(np.log(10000.0) / d_model))
angles = position * div_term

# تطبيق الدوران على أزواج الأبعاد
x_rotated = x.clone()
x_rotated[..., 0::2] = x[..., 0::2] * torch.cos(angles) - x[..., 1::2] * torch.sin(angles)
x_rotated[..., 1::2] = x[..., 0::2] * torch.sin(angles) + x[..., 1::2] * torch.cos(angles)

return x_rotated

ALiBi: الانتباه مع التحيزات الخطية​

نهج آخر​

ALiBi (الانتباه مع التحيزات الخطية) يضيف عقوبة خطية بناءً على المسافة:

softmax(qi⋅kj−m⋅∣i−j∣)\text{softmax}(q_i \cdot k_j - m \cdot |i - j|)

حيث mm هو ميل خاص بالرأس.

الفوائد​

  1. لا توجد معلمات إضافية
  2. تعميم ممتاز للطول
  3. تنفيذ بسيط

طبقة التضمين الكاملة​

جمع كل شيء معًا​

class TransformerEmbedding(nn.Module):
"""
طبقة التضمين الكاملة: رمز + موضع.
"""
def __init__(self, vocab_size, d_model, max_len=5000,
pos_encoding='sinusoidal', dropout=0.1):
super().__init__()

self.token_embedding = nn.Embedding(vocab_size, d_model)
self.d_model = d_model
self.dropout = nn.Dropout(dropout)

if pos_encoding == 'sinusoidal':
self.pos_encoding = SinusoidalPositionalEncoding(d_model, max_len)
elif pos_encoding == 'learned':
self.pos_encoding = LearnedPositionalEmbedding(max_len, d_model)
else:
raise ValueError(f"Unknown pos_encoding: {pos_encoding}")

def forward(self, token_ids):
"""
token_ids: (batch, seq_len)
Returns: (batch, seq_len, d_model)
"""
# تضمينات الرموز (مقاسة)
x = self.token_embedding(token_ids) * (self.d_model ** 0.5)

# إضافة الترميز الموضعي
x = self.pos_encoding(x)

return self.dropout(x)

# الاستخدام
embedding = TransformerEmbedding(
vocab_size=50000,
d_model=512,
max_len=2048,
pos_encoding='sinusoidal'
)

tokens = torch.randint(0, 50000, (32, 100)) # دفعة من 32، طول التسلسل 100
embedded = embedding(tokens)
print(f"Output shape: {embedded.shape}") # (32, 100, 512)

الصورة الكاملة​

إليك كيف تتدفق الرموز عبر التضمينات إلى الانتباه:

معرفات الرموز: [101, 7592, 2088, 102]
↓
تضمين الرموز: البحث في جدول التضمين
↓
[0.23, -0.45, ...] [0.89, 0.12, ...] [0.45, -0.23, ...] [0.67, 0.34, ...]
↓
إضافة الترميز الموضعي
↓
[0.23+PE_0, -0.45+PE_0, ...] [0.89+PE_1, 0.12+PE_1, ...] ...
↓
الانتباه متعدد الرؤوس
↓
الإخراج مع السياق الواعي للموضع

النقاط الرئيسية​

ملخص
  1. معلومات الموضع ضرورية - المحولات ثابتة التبديل بخلاف ذلك
  2. تضمينات الرموز تحول الرموز المنفصلة إلى متجهات مستمرة
  3. الترميز الجيبي يستخدم أنماط تردد ثابتة (المحول الأصلي)
  4. التضمينات المتعلمة تتكيف مع البيانات ولكن لها طول أقصى ثابت
  5. RoPE يرمز المواضع النسبية من خلال الدوران (النماذج الحديثة)
  6. ALiBi يستخدم تحيزات الانتباه القائمة على المسافة

نظرة مستقبلية​

الآن بعد أن فهمنا البنية الكاملة للمحول - الانتباه، الانتباه متعدد الرؤوس، والترميز الموضعي - نحن مستعدون للانتقال إلى الوحدة 3، حيث سنستكشف كيفية تدريب هذه النماذج الضخمة.