بعد أن فهمت الشبكات العصبية — الأساسيات، تأتي هذه الصفحة لتشرح لماذا نضع دالة غير خطّية بعد كل ضرب مصفوفات. الإجابة المختصرة: بدونها، الشبكة كلها تصبح معادلة خطّية واحدة مهما زاد عدد الطبقات.
المشكلة: لماذا لا تكفي الطبقة الخطّية؟
الانحدار الخطي الذي درسته في الانحدار الخطي هو تحويل خطّي:
y = W · x + b
لو وضعنا طبقة خطّية أخرى فوقه:
h = W₁ · x + b₁
y = W₂ · h + b₂
عوّض h في y:
y = W₂ · (W₁ · x + b₁) + b₂
y = (W₂ · W₁) · x + (W₂ · b₁ + b₂)
النتيجة: تحويل خطّي واحد مكافئ تمامًا للشبكة المكوّنة من طبقتين. إضافة المزيد من الطبقات الخطّية لن يزيد القدرة التعبيرية — النتيجة دائمًا معادلة خطّية.
هذا يعني: شبكة من 100 طبقة خطّية = شبكة من طبقة خطّية واحدة، في القدرة على تمثيل الدوال.
الحل: دالة تنشيط غير خطّية
نُدخل بين كل ضرب مصفوفات وأخرى دالة غير خطّية:
h = activation(W₁ · x + b₁)
y = W₂ · h + b₂
activation هنا ليست ضربًا في ثابت ولا جمعًا — هي دالة تغيّر العلاقة. أشهرها: ReLU، Sigmoid، Tanh، Softmax.
ReLU — Rectified Linear Unit
أبسط دالة وأكثرها استخدامًا في الطبقات المخفية.
ReLU(z) = max(0, z)
- إذا
z > 0→ أعدzكما هي. - إذا
z ≤ 0→ أعد0.
import numpy as np
def relu(z):
return np.maximum(0, z)
print(relu(2.0)) # 2.0
print(relu(-1.5)) # 0.0
print(relu(0.0)) # 0.0
منحنى ReLU:
y
↑ /
| /
| /
| /
| /
|______/________→ z
0
لماذا ReLU شائعة؟
- رخيصة حسابيًا: مقارنة وإرجاع صفر أو z.
- خفّفت مشكلة تلاشي التدرّج (vanishing gradient) التي كانت تعطّل Sigmoid في الشبكات العميقة.
- عمليًا تعطي نتائج ممتازة في طبقات المخفية لمعظم المهام.
عيب ReLU
dying ReLU: إذا أصبح z سالبًا باستمرار لعصبون معيّن، يصبح مخرَجه دائمًا صفرًا، ومُشتقّ ReLU يساوي صفرًا في المنطقة السالبة — فالعصبون يتوقّف عن التعلّم. يُعالَج عادةً بـ Leaky ReLU التي تسمح بميل خفيف في المنطقة السالبة.
Sigmoid — الاحتمال الثنائي
σ(z) = 1 / (1 + e^(-z))
المخرَج دائمًا بين 0 و 1، لذا تُستخدم لتمثيل احتمال.
def sigmoid(z):
return 1.0 / (1.0 + np.exp(-z))
print(sigmoid(0.0)) # 0.5
print(sigmoid(2.0)) # ≈ 0.8808
print(sigmoid(-2.0)) # ≈ 0.1192
print(sigmoid(10.0)) # ≈ 0.99995 (شببع)
print(sigmoid(-10.0)) # ≈ 0.000045
منحنى Sigmoid:
y
1.0 ┤ ___________
│ /
0.5 ┤──/
│/
0.0 ┤/
└───────────────→ z
-∞ 0 +∞
متى نستخدم Sigmoid؟
في طبقة المخرَج حين يكون لديك تصنيف ثنائي (احتمال ينتمي للصنف 1). مثلًا: هل الرسالة spam أم لا؟
عيب Sigmoid في الطبقات المخفية
- في الأطراف (z كبير جدًا أو صغير جدًا)، المُشتقّ يقترب من الصفر → التدرّج يتلاشى، التعلّم يتباطأ.
Tanh — hyperbolic tangent
tanh(z) = (e^z - e^(-z)) / (e^z + e^(-z))
المخرَج بين -1 و 1، ومتماثل حول الصفر.
def tanh(z):
return np.tanh(z)
print(tanh(0.0)) # 0.0
print(tanh(2.0)) # ≈ 0.9640
print(tanh(-2.0)) # ≈ -0.9640
منحنى tanh:
y
1 ┤ ___________
│ /
0 ┤──/
│/
-1 ┤/
└───────────────→ z
-∞ 0 +∞
مقارنة Sigmoid و Tanh
| الميزة | Sigmoid | Tanh |
|---|---|---|
| المدى | 0 إلى 1 | -1 إلى 1 |
| متماثل حول الصفر؟ | لا | نعم |
| الاستخدام الشائع | مخرَج ثنائي | بعض الطبقات المخفية (RNN تاريخيًا) |
في الطبقات المخفية الحديثة، ReLU تتفوّق عمليًا على الاثنين في معظم الحالات.
Softmax — توزيع احتمالات متعدد الأصناف
softmax(zᵢ) = e^(zᵢ) / Σⱼ e^(zⱼ)
تأخذ متجهًا (logits) وتُرجع توزيع احتمالات مجموعه 1.
def softmax(z):
# shift for numerical stability
z_shifted = z - np.max(z)
exps = np.exp(z_shifted)
return exps / np.sum(exps)
logits = np.array([2.0, 1.0, 0.1])
probs = softmax(logits)
print(probs)
print(f"المجموع: {probs.sum():.6f}")
النتيجة المتوقّعة:
[0.6590, 0.2424, 0.0986]
المجموع: 1.000000
متى نستخدم Softmax؟
دائمًا في طبقة المخرَج حين يكون لديك تصنيف متعدد الأصناف (احتمال لكل صنف، مجموعها 1). مثلًا: تصنيف صورة إلى قطة/كلب/عصفور.
ملاحظة: في PyTorch،
nn.CrossEntropyLossيدمجLogSoftmax + NLLLoss، فلا تطبّق Softmax يدويًا قبله. سنتناول ذلك في Loss و Optimizer في PyTorch.
ملخّص بصري
| الدالة | المدى | الاستخدام الشائع |
|---|---|---|
| ReLU | [0, +∞) | الطبقات المخفية (الافتراضي) |
| Sigmoid | (0, 1) | مخرَج تصنيف ثنائي |
| Tanh | (-1, 1) | بعض الحالات (RNN تاريخيًا) |
| Softmax | (0, 1)، مجموع 1 | مخرَج تصنيف متعدد الأصناف |
أيّ دالة أختار؟ (لا توجد قاعدة مطلقة)
كقاعدة عامة:
- الطبقات المخفية: ابدأ بـ ReLU. سريعة ومستقرّة.
- مخرَج ثنائي (احتمال): Sigmoid.
- مخرَج متعدد الأصناف: Softmax.
- مخرَج انحدار (رقم): بدون دالة تنشيط (هوية).
لكن هذه ليست قواعد مطلقة:
- في بعض مهام الرؤية، GELU أو SiLU تتفوّق على ReLU.
- في بعض مهام الـ RNN، Tanh أفضل من ReLU.
- في المخرَج الثنائي،
BCEWithLogitsLossأفضل منSigmoid + BCELoss(لأسباب عدديّة)، فتترك السينيوم خارج الرسم البياني. سنتناول ذلك في Loss Functions.
افهم المبدأ، ثم اختر حسب السياق والنتائج التجريبية.
اختبار سريع: هل الشبكة فعلًا غير خطّية؟
بدون دالة تنشيط:
import numpy as np
x = np.array([-2.0, 1.0])
# 3 طبقات خطّية متتالية (نفس الأوزان التي استخدمناها في الدرس السابق)
W1 = np.array([[0.5, 0.3], [0.2, 0.7]])
W2 = np.array([[0.4, 0.1], [0.6, 0.9]])
W3 = np.array([[0.3, 0.5], [0.8, 0.2]])
# بطبقات خطّية فقط — لا تنشيط
y_linear = W3 @ (W2 @ (W1 @ x))
y_linear_2 = (W3 @ W2 @ W1) @ x # نفس النتيجة: تركيب خطّي
print("بالطبقات:", y_linear)
print("بمصفوفة واحدة:", y_linear_2)
print("متطابقان؟", np.allclose(y_linear, y_linear_2))
النتيجة: المتجهان متطابقان — 3 طبقات خطّية = طبقة واحدة في القدرة.
مع ReLU بين الطبقات:
def relu(z):
return np.maximum(0, z)
# pre-activation للطبقة الأولى = [-0.7, 0.3] → relu = [0, 0.3]
h1 = relu(W1 @ x)
# pre-activation للطبقة الثانية = [0.03, 0.27] → كلها موجبة
h2 = relu(W2 @ h1)
y_relu = W3 @ h2
print("مع ReLU:", y_relu)
النتيجة الفعلية (تحقّق منها بنفس الكود):
بالطبقات: [-0.15 -0.23]
بمصفوفة واحدة: [-0.15 -0.23] ← متطابقان
مع ReLU: [0.144 0.078] ← مختلف تمامًا
هذا هو الفرق الجوهري: الشبكة مع ReLU ليست مكافئة لتحويل خطّي واحد. non-linearity تصنع قدرة تعبيرية جديدة.
أخطاء شائعة
- "Sigmoid أفضل لأنها تعطي احتمال": في الطبقات المخفية لا — استخدم ReLU. Sigmoid في المخرَج فقط.
- "أطبّق Sigmoid ثم Binary Cross Entropy": في PyTorch، استخدم
BCEWithLogitsLossواترك logits خام (أكثر استقرارًا عدديًا). - "أطبّق Softmax ثم CrossEntropyLoss": في PyTorch،
CrossEntropyLossيتوقّع logits خام، فهو يطبّق Softmax داخليًا. - "ReLU سيئة لأنها تقتل النصف السالب": ReLU خيار ممتاز في الطبقة المخفية. dying ReLU مشكلة حقيقية لكن تُعالَج بـ Leaky ReLU أو تهيئة جيدة.
الخطوات التالية
- الانتشار الأمامي — حساب
W @ x + b → activation → ...يدويًا. - الشبكات العصبية — الأساسيات — تعريف العصبون والطبقات.
- دوال الخسارة — ما الذي تقيسه الشبكة بعد المخرَج.
- Optimizers و Regularization — كيف تُحدَّث الأوزان.
🔍 ستلتقي ReLU و Sigmoid و Softmax داخل PyTorch في Loss و Optimizer في PyTorch وفي مشروع مشروع: مصنّف صور.