تخطَّ إلى المحتوى

🤖 شرح الذكاء الاصطناعي وتعلّم الآلة

الانتشار الأمامي (Forward Propagation)

الدرس 29 من 50· ⏱ 6 دقائق قراءة· 🗓 آخر تحديث: ١٩ سبتمبر ٢٠٢٦

بعد أن تعرّفت العصبون ودوال التنشيط، الخطوة التالية هي رؤية كيف تنتشر البيانات فعليًا عبر الشبكة من المدخل إلى المخرَج. هذه الصفحة تتعمّد عدم استخدام PyTorch — لتفهم ما يحدث قبل أن يغطّيه الإطار.

ما هو الانتشار الأمامي؟

Forward propagation = مسار البيانات من المدخل، عبر الطبقات المخفية، حتى المخرَج. عكس الانتشار العكسي الذي يُحدَّث فيه الأوزان.

كل طبقة تطبّق نفس العملية:

z = W @ a_prev + b     # تحويل خطّي
a = activation(z)      # دالة تنشيط

حيث a_prev هو مخرَج الطبقة السابقة (أو المدخل x نفسه في الطبقة الأولى).

بنية الشبكة في هذا الدرس

Input (2) → Hidden (3, ReLU) → Output (1, Sigmoid) → ŷ
  • مدخل: ميزتان (x₁, x₂).
  • طبقة مخفية واحدة بثلاث خلايا، تنشيط ReLU.
  • مخرَج: خلية واحدة مع Sigmoid (احتمال ينتمي للصنف 1).

الأوزان والانحيازات (مُعرّفة يدويًا للتوضيح)

في الواقع، الأوزان تبدأ عشوائية وتُحدَّث بالتدرّج. هنا نُعيّنها يدويًا لنتمكّن من الحساب.

import numpy as np

# الأوزان والانحيازات
W1 = np.array([
    [ 0.5, -0.3],   # أوزان الخلية المخفية h₁
    [-0.2,  0.8],   # أوزان الخلية المخفية h₂
    [ 0.4,  0.1],   # أوزان الخلية المخفية h₃
])
b1 = np.array([0.1, -0.2, 0.0])

W2 = np.array([[0.6, 0.5, -0.4]])   # شكل (1, 3) — خلية مخرَج واحدة
b2 = np.array([0.05])

الأشكال مهمة:

المصفوفةالشكلالمعنى
x(2,)ميزتان
W1(3, 2)3 خلايا مخفية × 2 مدخلات
b1(3,)انحياز لكل خلية مخفية
W2(1, 3)خلية مخرَج واحدة × 3 مدخلات
b2(1,)انحياز المخرَج

قاعدة شكل المصفوفات: إذا كانت a_prev شكله (n,) و W شكله (m, n)، فالنتيجة W @ a_prev شكله (m,). هذا الترتيب يسمّى shape convention: الأوزان تُكتب بالشكل (out, in).

الطبقة الأولى (المخفية)

الحساب اليدوي

المُعطى: x = [2.0, 3.0].

الخلية المخفية h₁:

z₁ = (0.5 × 2.0) + (-0.3 × 3.0) + 0.1
   = 1.0 + (-0.9) + 0.1
   = 0.2

الخلية المخفية h₂:

z₂ = (-0.2 × 2.0) + (0.8 × 3.0) + (-0.2)
   = -0.4 + 2.4 + (-0.2)
   = 1.8

الخلية المخفية h₃:

z₃ = (0.4 × 2.0) + (0.1 × 3.0) + 0.0
   = 0.8 + 0.3
   = 1.1

الحساب بمصفوفات

x = np.array([2.0, 3.0])

z1 = W1 @ x + b1
print("z1 =", z1)   # [0.2, 1.8, 1.1]

تطبيق ReLU (هنا كلها موجبة، فلا شيء يُقصّ):

a1 = np.maximum(0, z1)
print("a1 =", a1)   # [0.2, 1.8, 1.1]

ملاحظة: مع مدخلات سالبة كافية لقيم ما قبل التنشيط، ستُرجع ReLU صفرًا. هنا كل zᵢ موجب فلا يحدث قصّ.

الطبقة الثانية (المخرَج)

الحساب اليدوي

z_out = (0.6 × 0.2) + (0.5 × 1.8) + (-0.4 × 1.1) + 0.05
      = 0.12 + 0.90 + (-0.44) + 0.05
      = 0.63

الحساب بمصفوفات

z2 = W2 @ a1 + b2
print("z2 =", z2)   # [0.63]

تحويل لاحتمال عبر Sigmoid

ŷ = σ(0.63) = 1 / (1 + e^(-0.63)) ≈ 0.6525
a2 = 1.0 / (1.0 + np.exp(-z2))
print("a2 =", a2)   # ≈ [0.6525]

الكود الكامل للانتشار الأمامي

import numpy as np

# المدخل
x = np.array([2.0, 3.0])

# أوزان الطبقة المخفية
W1 = np.array([
    [ 0.5, -0.3],
    [-0.2,  0.8],
    [ 0.4,  0.1],
])
b1 = np.array([0.1, -0.2, 0.0])

# أوزان طبقة المخرَج
W2 = np.array([[0.6, 0.5, -0.4]])
b2 = np.array([0.05])

def relu(z):
    return np.maximum(0, z)

def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

# === الطبقة المخفية ===
z1 = W1 @ x + b1
a1 = relu(z1)

# === طبقة المخرَج ===
z2 = W2 @ a1 + b2
y_hat = sigmoid(z2)

print("z1 =", z1)        # [0.2, 1.8, 1.1]
print("a1 =", a1)        # [0.2, 1.8, 1.1]
print("z2 =", z2)        # [0.63]
print("ŷ =", y_hat)      # ≈ [0.6525]

ماذا عن دفعات (batches)؟

في الواقع لا نُمرّر عيّنة واحدة، بل دفعة (batch) لتجنّب ضوضاء التدرّج.

X shape: (batch_size, n_features)
# 4 عيّنات في وقت واحد
X = np.array([
    [2.0, 3.0],
    [1.0, 0.5],
    [-1.0, 2.0],
    [0.5, -0.5],
])

# الأوزان تبقى كما هي
z1_batch = X @ W1.T + b1   # لاحظ التبديل
a1_batch = relu(z1_batch)
z2_batch = a1_batch @ W2.T + b2
y_hat_batch = sigmoid(z2_batch)

print("شكل z1:", z1_batch.shape)   # (4, 3)
print("شكل y_hat:", y_hat_batch.shape)  # (4, 1)
print("احتمالات:\n", y_hat_batch)

ملاحظة الأشكال: مع X بالشكل (batch, features)، يصبح W بالشكل (out, in) كما اعتدنا، فيُحسب X @ W.T (تبديل W). المخرَج بالشكل (batch, out).

الشكل العام (Pseudocode)

أي شبكة عصبية تتكوّن من تكرار نفس العملية:

a⁰ = x                              # المدخل
for layer in [1, 2, ..., L]:
    zˡ = Wˡ · a^(l-1) + bˡ          # تحويل خطّي
    aˡ = activationˡ(zˡ)            # تنشيط
ŷ = a^L                             # المخرَج

ملخّص البُنى

الرمزالمعنى
x أو a⁰المدخل
أوزان الطبقة l (شكل (out_l, in_l))
انحياز الطبقة l (شكل (out_l,))
pre-activation (قبل التنشيط)
activation (مخرَج الطبقة بعد التنشيط)
ŷ أو a^Lتنبّؤ الشبكة

أخطاء شائعة

  • قلب شكل W: إذا كان W بالشكل (in, out)، يجب ضربه بترتيب مختلف. ثبّت قاعدة: W بالشكل (out, in)، فالعملية دائمًا input @ W.T + b.
  • نسيان التبديل للأوزان عند استخدام دفعات: X @ W بدل X @ W.T ينتج شكلًا خاطئًا.
  • عدم تطابق الأشكال بين الطبقات: W1 ينتج a1 بطول 3، فيجب أن يكون W2 بالشكل (out, 3). القلم والورق قبل الكود.
  • افتراض أن ReLU ستُغيّر دائمًا النتيجة: إذا كانت كل z موجبة، ReLU لا تُغيّر شيئًا. في هذه الحالة الشبكة تعمل كأنها خطّية في هذه المنطقة.

الخطوات التالية

🔍 في PyTorch، كل ما سبق يحدث داخل model(x). سترى ذلك في PyTorch: Model و nn.Module.

شرح الانتشار الأمامي (Forward Propagation) — الذكاء الاصطناعي وتعلّم الآلة بالعربي
الانتشار الأمامي (Forward Propagation)الذكاء الاصطناعي وتعلّم الآلة بالعربي · The Code Fix

📚 لمزيد من التعمّق في الذكاء الاصطناعي وتعلّم الآلة، راجِع توثيق scikit-learn الرسمي.

هل كان هذا الدرس مفيدًا؟