AI: الشبكات العصبية
آخر تحديث: 2026-08-26
قد يبدو "التعلم العميق" غامضًا ومعقدًا، لكن لبنته الأساسية -العصبون- ببساطة هو مجموع مرجح يليه تحويل خطي غير متسلسل. بدًا من المُدرِك (perceptron)، يرشدك هذا الفصل خطوة بخطوة عبر الشبكات متعددة الطبقات، ودالات التنشيط، وانتشار الأمام، وانتشار الخلف، ويُريك كيفية تنفيذ نموذج MLP من الصفر باستخدام بايثون البحت الذي يمكنه حل مشكلة XOR.
١. ما ستتعلمه
- البيرسيبترون (Perceptron): أبسط وحدة في الشبكات العصبية
- بنية الحدس (الحدس) للشبكات العصبية متعددة الطبقات (MLPs)
- دور واختيار دوال التفعيل (Sigmoid / ReLU / Tanh)
- الحدس الجوهري وراء التانتشار الأمامي (Forward Propagation) والانتشار العكسي (Backpropagation)
- التعلم العميق = مجموعة مكدسة من الشبكات العصبية متعددة الطبقات
2. القصة: "الدماغ" المُختبئ خلف 10 أسطر من الكود
(1) نقطة الألم: ما هو التعلم العميق بالضبط؟
كان بوب قد سمع أن "التعلم العميق" يمكنه التعرف على الوجوه، وترجمة اللغات، وكتابة المقالات، لكنه لم يكن لديه أدنى فكرة عن ماهية الشبكة العصبية. حاول البحث عنها في ويكيبيديا، لكن الشاشة المليئة بالتفاضلات الجزئية وقاعدة السلسلة جعلته يُغلق الصفحة فورًا. "ألا يمكن لأحد أن يشرح لي بأبسط المصطلحات ما تفعله 'الخلية العصبية' بالفعل؟"
(2) كود أليس المكون من 10 أسطر
ابتسمت أليس وفتحت بايثون وكتبت خلية عصبية بسيطة (perceptron) في 10 أسطر من الكود - تأخذ رقمين كمدخل وتطبع إما 0 أو 1:
# A single perceptron: AND gate
def perceptron(x1, x2, w1=1, w2=1, b=-1.5):
return 1 if w1 * x1 + w2 * x2 + b > 0 else 0
for x1, x2 in [(0,0),(0,1),(1,0),(1,1)]:
print(f"AND({x1},{x2}) = {perceptron(x1, x2)}")
AND(0,0) = 0
AND(0,1) = 0
AND(1,0) = 0
AND(1,1) = 1
أدرك بوب: "أليست هذه مجرد عملية تصويت مُرجّح؟ كلا المُدخلين مضبوطان على 1، وإذا تجاوز المجموع المُرجّح العتبة، يكون المُخرج 1 - إذن الشبكة العصبية هي مجرد سلسلة من هذه الوحدات القرار البسيطة المتصلة معًا!"
(3) الفوائد: من وحدات بسيطة إلى شبكات قوية
أومأت أليس برأسها. "صحيح. الخلية العصبية البسيطة (البِرسِبترون) الواحدة لا تستطيع أداء سوى التصنيف الخطي، لكن عن طريق تكديس عشرات أو مئات الخلايا العصبية البسيطة في شبكة متعددة الطبقات، يمكننا مُطابقة دوال معقدة للغاية - وهنا يبدأ التعلم العميق."
3. التحسين—الشبكة العصبية الأبسط
(1) التعريف الرياضي للحساسية (Perceptron)
الحساسية، التي اقترحها عالم النفس روزنبلات في عام 1958، هي واحدة من أقدم نماذج الخلايا العصبية الاصطناعية. تستقبل مدخلات متعددة $x_1, x_2, \ldots, x_n$، لكل منها وزن $w_i$، بالإضافة إلى انحياز $b$، وأخيرًا تُخرج 0 أو 1 عبر دالة خطوة:
$$y = \text{step}\left(\sum_{i=1}^{n} w_i x_i + b\right)$$
على وجه الخصوص، $\text{step}(z) = 1$ إذا كان $z > 0$، و $0$ خلاف ذلك.
حدس: تخيّل أنك تقرر الخروج للركض—درجة الحرارة، جودة الهواء، إن كان لديك وقت، ومزاجك—كل عامل له "وزن"، وإذا تجاوز المجموع "عتبة الكسل" الخاصة بك، فإنك تخرج.
(2) المعنى الهندسي للحساسية
ترسم الحساسية خطًا مستقيمًا $w_1 x_1 + w_2 x_2 + b = 0$ في الفضاء ثنائي الأبعاد، حيث تُخرج 1 على جانب من الخط و 0 على الجانب الآخر. يمكنها حل مشكلات خطية التفرقة بشكل مثالي مثل AND و OR، لكنها لا تستطيع حل مشكلة XOR—لأن فئتي النقاط في مشكلة XOR لا يمكن فصلهما بخط مستقيم واحد.
▶ مثال: تنفيذ بوابة AND باستخدام الحساسية بلغة Python البحتة (الصعوبة: ⭐)
# Perceptron AND gate with training
def step(z):
return 1 if z > 0 else 0
def train_perceptron(X, y, lr=0.1, epochs=10):
n_features = len(X[0])
weights = [0.0] * n_features
bias = 0.0
for epoch in range(epochs):
total_error = 0
for xi, yi in zip(X, y):
z = sum(w * x for w, x in zip(weights, xi)) + bias
pred = step(z)
error = yi - pred
total_error += abs(error)
for j in range(n_features):
weights[j] += lr * error * xi[j]
bias += lr * error
if total_error == 0:
break
return weights, bias
X = [[0,0],[0,1],[1,0],[1,1]]
y_and = [0, 0, 0, 1]
w, b = train_perceptron(X, y_and)
print(f"Weights: {w}, Bias: {b}")
for xi in X:
print(f"AND{xi} = {step(sum(ww*xx for ww,xx in zip(w,xi)) + b)}")
Weights: [0.2, 0.1], Bias: -0.2
AND[0, 0] = 0
AND[0, 1] = 0
AND[1, 0] = 0
AND[1, 1] = 1
4. بنية الشبكة العصبية متعددة الطبقات (MLP)
(1) لماذا نحتاج إلى طبقات متعددة؟
الشبكة العصبية ذات الطبقة الواحدة (Perceptron) تستطيع رسم خطوط مستقيمة فقط ولا تستطيع حل مشكلة XOR. ولكن، إذا تم تغذية مخرجات شبكتين عصبيتين (Perceptron) إلى شبكة عصبية ثالثة، يصبح من الممكن الجمع بين خطيين مستقيمين لتشكيل حدود غير خطية. هذه هي الفكرة الأساسية وراء الشبكة العصبية متعددة الطبقات (MLP).
(2) البنية ثلاثية الطبقات للشبكة العصبية متعددة الطبقات (MLP)
| الطبقة | الوظيفة | مثال |
|---|---|---|
| طبقة الإدخال | تستقبل السمات الأصلية | 2 عقدة (x₁، x₂) |
| الطبقة المخفية | تستخرج سمات وسيطة | 4 عقد (سمات تم تعلمها) |
| طبقة الإخراج | تقدم التنبؤ النهائي | 1 عقدة (0 أو 1) |
كل عقدة في كل طبقة متصلة بجميع العقد في الطبقة السابقة (متصلة بالكامل)، وتتمثل قوة هذه الروابط بالـ الأوزان.
(3) عملية الانتشار الأمامي في الشبكات العصبية
graph LR
X1["x₁"] --> H1["h₁ (sigmoid)"]
X1 --> H2["h₂ (sigmoid)"]
X2["x₂"] --> H1
X2 --> H2
H1 --> Y["y (output)"]
H2 --> Y
style X1 fill:#e1f5fe
style X2 fill:#e1f5fe
style H1 fill:#fff9c4
style H2 fill:#fff9c4
style Y fill:#c8e6c9
حدس: طبقة الإدخال مثل العينين، والطبقات المخفية مثل مناطق المعالجة في الدماغ، وطبقة الإخراج مثل الفم الذي ينطق الإجابة. كل طبقة تقوم بعملية "مجموع مرجّح + تحويل خطي".
(4) مقارنة: الشبكة العصبية البسيطة (Perceptron) مقابل الشبكة العصبية متعددة الطبقات (MLP) مقابل الشبكات العصبية العميقة
| البعد | الشبكة العصبية البسيطة (Perceptron) | الشبكة العصبية متعددة الطبقات (MLP) | الشبكة العصبية العميقة |
|---|---|---|---|
| عدد الطبقات | طبقة واحدة (بدون طبقات مخفية) | 2-3 طبقات | ≥ 4 طبقات (بما في ذلك طبقات مخفية متعددة) |
| القدرات | التصنيف الخطي | التصنيف/الانحدار غير الخطي | استخراج السمات المعقدة تلقائيًا |
| التدريب | قواعد الشبكة العصبية البسيطة | الانتشار العكسي | الانتشار العكسي + تقنيات التحسين |
| التطبيقات النموذجية | بوابات المنطق البسيطة | التصنيف/الانحدار على نطاق صغير | التعرف على الصور، معالجة اللغة الطبيعية، التوليد |
| عدد المعلمات | عشرات | مئات إلى آلاف | عشرات المليارات إلى المليارات |
| النماذج الممثلة | روزنبلات بيرسيبترون | شبكة MLP ذات طبقتين | CNN، Transformer |
5. دوال التنشيط—إضافة عدمية الخطية للشبكة
(1) لماذا نحتاج إلى دوال التنشيط؟
بدون دوال التنشيط، بغض النظر عن عدد الطبقات، تكون الشبكة في جوهرها تحولاً خطيًا—تركيبة التحولات الخطية المتعددة تبقى خطية. تُدخل دوال التنشيط عدمية الخطية، مما يتيح للشبكة مواءمة المنحنيات والحدود المعقدة.
تشبيه: التحولات الخطية مثل "التمديد والتدوير"؛ بغض النظر عن عدد مرات تمديد الأشياء أو تدويرها، يظل النتيجة خطًا مستقيمًا أو سطحًا. دوال التنشيط مثل "الطي"—اطوِ ورقة مرة واحدة، وتتغير المساحة.
(2) مقارنة بين دوال التنشيط الشائعة
| الدالة | الصيغة | نطاق الإخراج | المزايا | العيوب |
|---|---|---|---|---|
| سيجمويد | $\sigma(z)=\frac{1}{1+e^{-z}}$ | (0, 1) | يمكن اعتبار الخرج كاحتمال | تلاشي التدرجات، مركز غير صفري |
| تانج | $\tanh(z)=\frac{e^z-e^{-z}}{e^z+e^{-z}}$ | (-1, 1) | مركزه صفري | التدرج يتلاشى |
| ريلو | $\max(0, z)$ | [0, +∞) | حساب سريع، يخفف من مشكلة تلاشي التدرج | موت العصبون (التدرج هو 0 في المنطقة السالبة) |
| ليك ريلو | $\max(0.01z, z)$ | (-∞, +∞) | يعالج موت العصبون | يتطلب ضبط فرط المعاملات |
▶ مثال: مقارنة قيم الخرج لدوال تنشيط مختلفة (الصعوبة: ⭐)
# Compare activation functions
import numpy as np
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def tanh(z):
return np.tanh(z)
def relu(z):
return np.maximum(0, z)
def leaky_relu(z, alpha=0.01):
return np.where(z > 0, z, alpha * z)
z_values = np.array([-5, -2, -1, 0, 1, 2, 5])
print(f"{'z':>5} | {'Sigmoid':>8} | {'Tanh':>8} | {'ReLU':>8} | {'LeakyReLU':>10}")
print("-" * 55)
for z in z_values:
s = sigmoid(z)
t = tanh(z)
r = float(relu(np.array([z]))[0])
lr = float(leaky_relu(np.array([z]))[0])
print(f"{z:5.1f} | {s:8.4f} | {t:8.4f} | {r:8.4f} | {lr:10.4f}")
z | Sigmoid | Tanh | ReLU | LeakyReLU
-------------------------------------------------------
-5.0 | 0.0067 | -0.9999 | 0.0000 | -0.0500
-2.0 | 0.1192 | -0.9640 | 0.0000 | -0.0200
-1.0 | 0.2689 | -0.7616 | 0.0000 | -0.0100
0.0 | 0.5000 | 0.0000 | 0.0000 | 0.0000
1.0 | 0.7311 | 0.7616 | 1.0000 | 1.0000
2.0 | 0.8808 | 0.9640 | 2.0000 | 2.0000
5.0 | 0.9933 | 0.9999 | 5.0000 | 5.0000
6. الانتشار الأمامي والانتشار الخلفي
(1) الانتشار الأمامي: من المدخلات إلى المخرجات
الانتشار الأمامي هو العملية التي تنتقل فيها البيانات من طبقة الإدخال عبر كل طبقة حتى تصل إلى طبقة الإخراج:
- حساب مدخلات الطبقة المخفية: $z^{(1)} = W^{(1)} \cdot x + b^{(1)}$
- تطبيق دالة التنشيط: $h = \sigma(z^{(1)})$
- حساب مدخلات طبقة الإخراج: $z^{(2)} = W^{(2)} \cdot h + b^{(2)}$
- توقع الإخراج: $\hat{y} = \sigma(z^{(2)})$
(2) دالة الخسارة: قياس الفرق بين التوقع والقيمة الحقيقية
المتوسط الحسابي لمربعات الأخطاء (MSE) يُستخدم عادةً كدالة خسارة:
الخسارة هي مربع الفرق بين القيمة الفعلية والقيمة المتوقعة.
كلما كانت الخسارة أصغر، كان التوقع أكثر دقة. الهدف من التدريب هو إيجاد الأوزان التي تُقلل الخسارة.
(3) الانتشار الخلفي: من الخطأ إلى تحديثات الأوزان
الفكرة الأساسية للانتشار الخلفي: بدءًا من الخطأ في طبقة الإخراج، حساب "مساهمة" كل وزن في الخطأ عن طريق الانتشار الخلفي عبر الشبكة طبقة بطبقة، ثم ضبط الأوزان.
تشبيه حدسي: مصنع لديه مشكلة في منتجاته—
- أولاً، تحقق من عدد الأخطاء التي وقعت في الخطوة الأخيرة (طبقة الإخراج)
- ثم اسأل عن عدد الأخطاء التي سببتها العملية السابقة (الطبقة المخفية)
- تتبع الخطأ طبقة بطبقة حتى تصل إلى المواد الخام (طبقة الإدخال)
- يجب على كل عملية ضبط إجراءاتها التشغيلية بناءً على الأخطاء التي ساهمت فيها
رياضيًا، هذه هي قاعدة السلسلة: $\frac{\partial L}{\partial w} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z} \cdot \frac{\partial z}{\partial w}$
(4) جدول المفاهيم الأساسية في الانتشار الخلفي
| المفهوم | المعنى | الحدس |
|---|---|---|
| المنحدر (المشتقة) | المشتقة الجزئية للخسارة بالنسبة للأوزان | في أي اتجاه وبأي مقدار يجب ضبط الأوزان |
| معدل التعلم | حجم الخطوة لكل تكرار | كبير جدًا → يتجاوز الحد الأمثل؛ صغير جدًا → التدريب بطيء جدًا |
| قاعدة السلسلة | تمرير المنحدر طبقة بطبقة | تتبع "من المسؤول" طبقة بطبقة من الإخراج إلى الإدخال |
| تحديث الأوزان | $w \leftarrow w - \eta \cdot \frac{\partial L}{\partial w}$ | اتخاذ خطوة في الاتجاه المعاكس للمنحدر |
| تلاشي المنحدرات | المنحدرات تتناقص مع كل طبقة وتقترب من 0 | الطبقات الأولى تتعلم بالكاد شيئًا |
| انفجار المنحدرات | المنحدرات تتصاعد طبقة بطبقة | تحديثات الأوزان شديدة العدوانية، مما يسبب عدم استقرار النموذج |
(5) حدسية لسطوح الخسارة
تخيل أنك تقف على جبل، وهدفك هو الوصول إلى قاع الوادي (أقل خسارة). مع كل خطوة، تسير على المنحدر الأكثر حدة—هذا هو الهبوط التدريجي (المنحدر). معدل التعلم يحدد حجم خطواتك:
- خطوة طويلة جدًا: قد تنتهي بعبور قاع الوادي والهبوط على المنحدر المقابل
- خطوة قصيرة جدًا: تكون قد مشيت لفترة طويلة ولا تزال في منتصف الجبل.
- خطوة مثالية: الوصول إلى قاع الوادي بثبات وبسرعة.
7. جرب بسرعة نموذج MLP باستخدام sklearn
▶ مثال: تصنيف باستخدام MLPClassifier من sklearn (الصعوبة: ⭐)
# MLPClassifier on XOR problem
from sklearn.neural_network import MLPClassifier
import numpy as np
X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([0, 1, 1, 0])
mlp = MLPClassifier(hidden_layer_sizes=(4,), activation='relu',
max_iter=2000, random_state=42)
mlp.fit(X, y)
print("XOR Predictions:")
for xi in X:
print(f" {xi} -> {mlp.predict([xi])[0]}")
print(f"Accuracy: {mlp.score(X, y):.2f}")
XOR Predictions:
[0 0] -> 0
[0 1] -> 1
[1 0] -> 1
[1 1] -> 0
Accuracy: 1.00
MLPClassifier من sklearn تلقائياً مع جميع التفاصيل، بما في ذلك الانتشار الأمامي، والانتشار العكسي، وتهيئة الأوزان. بمجرد فهمك للمبادئ الأساسية، يُعد استخدام المكتبة هو النهج الأكثر كفاءة.
▶ مثال: تصور التغيرات في حدود القرار (الصعوبة: ⭐⭐)
# Visualize MLP decision boundary on XOR
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neural_network import MLPClassifier
X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([0, 1, 1, 0])
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
hidden_configs = [(2,), (4,), (8,)]
for ax, config in zip(axes, hidden_configs):
mlp = MLPClassifier(hidden_layer_sizes=config, activation='relu',
max_iter=3000, random_state=42)
mlp.fit(X, y)
xx, yy = np.meshgrid(np.linspace(-0.5, 1.5, 100),
np.linspace(-0.5, 1.5, 100))
Z = mlp.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=0.3, cmap='coolwarm')
ax.scatter(X[:,0], X[:,1], c=y, cmap='coolwarm', edgecolors='k', s=100)
ax.set_title(f"Hidden: {config}, Acc: {mlp.score(X,y):.2f}")
ax.set_xlabel("x1")
ax.set_ylabel("x2")
plt.tight_layout()
plt.savefig("mlp_decision_boundary.png", dpi=100)
plt.show()
المخرجات: (شغّل المثال لرؤية المخرجات الفعلية، أو راجع ملاحظة المخرجات المتوقعة في تعليقات الكود أعلاه.)
▶ مثال: كيف يؤثر عدد العقد في الطبقة المخفية على الأداء (الصعوبة: ⭐⭐)
# Effect of hidden layer size on training loss
import numpy as np
from sklearn.neural_network import MLPClassifier
import matplotlib.pyplot as plt
X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([0, 1, 1, 0])
fig, ax = plt.subplots(figsize=(8, 5))
for n_hidden in [2, 4, 8, 16]:
mlp = MLPClassifier(hidden_layer_sizes=(n_hidden,), activation='relu',
max_iter=3000, random_state=42, solver='lbfgs')
mlp.fit(X, y)
ax.plot(range(len(mlp.loss_curve_)), mlp.loss_curve_,
label=f"Hidden={n_hidden}")
ax.set_xlabel("Iteration")
ax.set_ylabel("Loss")
ax.set_title("Training Loss vs Hidden Layer Size")
ax.legend()
ax.set_yscale('log')
plt.savefig("hidden_size_loss.png", dpi=100)
plt.show()
المخرجات: (شغّل المثال لرؤية المخرجات الفعلية، أو راجع ملاحظة المخرجات المتوقعة في تعليقات الكود أعلاه.)
8. التعلم العميق = الشبكات العصبية متعددة الطبقات
(1) المقارنة بين التعلم الآلي التقليدي والتعلم العميق
| البُعد | التعلم الآلي التقليدي | التعلم العميق |
|---|---|---|
| هندسة الميزات | يتطلب تصميم وتحديد الميزات يدويًا | يتعلم الميزات تلقائيًا من البيانات |
| متطلبات حجم البيانات | يكفي كمية صغيرة من البيانات | عادةً يتطلب كمية كبيرة من البيانات |
| موارد الحوسبة | وحدة المعالجة المركزية (CPU) فقط | عادةً يتطلب وحدة معالجة الرسومات (GPU) |
| قابلية تفسير النموذج | مرتفعة (أشجار القرار، النماذج الخطية) | منخفضة (صندوق أسود) |
| الحد الأقصى للأداء | يعتمد على جودة الميزات | يتناسب مع حجم البيانات والنموذج |
| الخوارزميات التمثيلية | SVM، الغابة العشوائية، XGBoost | CNN، RNN، Transformer |
| السيناريوهات المطبقة | البيانات المنظمة، العينات الصغيرة | البيانات غير المنظمة مثل الصور والنصوص والصوت |
(2) ماذا يعني "عميق"؟
يشير مصطلح "العمق" إلى عدد الطبقات المخفية. من المتفق عليه عمومًا أن:
- شبكة ضحلة: طبقة مخفية واحدة (MLP)
- شبكات عصبية عميقة: طبقتان مخفيتان أو أكثر
- شبكات فائقة العمق: عشرات أو حتى مئات الطبقات (ResNet-152 تحتوي على 152 طبقة)
كلما زاد عدد الطبقات، أصبحت الميزات التي يمكن للشبكة استخراجها أكثر تجريدًا—قد تتعلم الطبقة الأولى الحواف، والطبقة الثانية قد تتعلم القوام، والطبقة الثالثة قد تتعلم المكونات؛ ومع التقدم أعمق في الشبكة، تصبح الميزات أكثر "دلالة" بشكل متزايد.
(3) المزيد من العمق ليس بالضرورة أفضل
شبكة أعمق لا تعني بالضرورة أداء أفضل. عدد كبير جدًا من الطبقات يمكن أن يؤدي إلى:
- تلاشي/انفجار التدرج: يتناقص أو يتضخم الإشارة أثناء مرورها عبر الطبقات المتتالية
- الملاءمة الزائدة: عدد كبير جدًا من المعلمات، مما يسبب للنموذج حفظ بيانات التدريب بدلاً من تعلم الأنماط الأساسية
- تكلفة التدريب: المزيد من المعلمات = وقت تدريب أطول + قوة حوسبة أكبر
يجب اختيار عمق النموذج بناءً على تعقيد المهمة وكمية البيانات— تمامًا كبناء ناطحة سحاب: الأطول ليس دائمًا الأفضل؛ يجب مراعاة الأساس والميزانية.
9. تمرين عملي شامل: تنفيذ مصنف MLP من الصفر
▶ مثال: تنفيذ MLP من الصفر لحل مشكلة XOR (الصعوبة: ⭐⭐⭐)
سنقوم أدناه بتنفيذ MLP كامل باستخدام بايثون البحت (بدون أي أطر عمل)، بما في ذلك الانتشار الأمامي، والانتشار العكسي، وحلقة التدريب، بهدف حل مشكلة XOR.
# MLP from scratch: solve XOR with pure Python + NumPy
import numpy as np
# --- Activation functions and their derivatives ---
def sigmoid(z):
return 1 / (1 + np.exp(-np.clip(z, -500, 500)))
def sigmoid_deriv(a):
return a * (1 - a)
# --- MLP class ---
class MLP:
def __init__(self, layer_sizes):
# layer_sizes e.g. [2, 4, 1] => 2-input, 4-hidden, 1-output
self.weights = []
self.biases = []
for i in range(len(layer_sizes) - 1):
w = np.random.randn(layer_sizes[i], layer_sizes[i+1]) * 0.5
b = np.zeros((1, layer_sizes[i+1]))
self.weights.append(w)
self.biases.append(b)
def forward(self, X):
self.activations = [X]
for i in range(len(self.weights)):
z = self.activations[-1] @ self.weights[i] + self.biases[i]
a = sigmoid(z)
self.activations.append(a)
return self.activations[-1]
def backward(self, y, lr=0.5):
m = y.shape[0]
deltas = [None] * len(self.weights)
# Output layer delta
output = self.activations[-1]
deltas[-1] = (output - y) * sigmoid_deriv(output)
# Hidden layer deltas (backprop)
for i in range(len(self.weights) - 2, -1, -1):
deltas[i] = (deltas[i+1] @ self.weights[i+1].T) * sigmoid_deriv(self.activations[i+1])
# Update weights and biases
for i in range(len(self.weights)):
self.weights[i] -= lr * (self.activations[i].T @ deltas[i]) / m
self.biases[i] -= lr * np.mean(deltas[i], axis=0, keepdims=True)
def train(self, X, y, epochs=5000, lr=0.5):
self.losses = []
for epoch in range(epochs):
pred = self.forward(X)
loss = np.mean((y - pred) ** 2) / 2
self.losses.append(loss)
self.backward(y, lr)
if epoch % 1000 == 0:
print(f"Epoch {epoch:4d} | Loss: {loss:.6f}")
def predict(self, X):
return (self.forward(X) > 0.5).astype(int)
# --- Train on XOR ---
np.random.seed(42)
X = np.array([[0,0],[0,1],[1,0],[1,1]], dtype=float)
y = np.array([[0],[1],[1],[0]], dtype=float)
mlp = MLP([2, 4, 1])
mlp.train(X, y, epochs=5000, lr=1.0)
print("\nFinal Predictions:")
preds = mlp.predict(X)
for i in range(4):
print(f" XOR{X[i].astype(int).tolist()} = {preds[i,0]} (target: {y[i,0].astype(int)})")
Epoch 0 | Loss: 0.132415
Epoch 1000 | Loss: 0.004726
Epoch 2000 | Loss: 0.002517
Epoch 3000 | Loss: 0.001751
Epoch 4000 | Loss: 0.001330
Final Predictions:
XOR[0, 0] = 0 (target: 0)
XOR[0, 1] = 1 (target: 1)
XOR[1, 0] = 1 (target: 1)
XOR[1, 1] = 0 (target: 0)
❓ أسئلة شائعة
📖 ملخص
- المُدرِك (Perceptron): مجموع مرجّح + دالة خطوة، أبسط مصنّف خطي
- الشبكة العصبية متعددة الطبقات (MLP): المُدرِك متعدد الطبقات؛ الطبقات المخفية توفر قدرات غير خطية
- دالة التفعيل (Activation Function): تُدخل عدم الخطية؛ ReLU هي الخيار الافتراضي الحديث
- الانتشار الأمامي (Forward Propagation): تتم معالجة البيانات طبقة بطبقة من طبقة الإدخال إلى طبقة الإخراج
- الانتشار العكسي (Backpropagation): يتم انتشار الخطأ إلى الخلف طبقة بطبقة من طبقة الإخراج لتحديث الأوزان
- التعلم العميق (Deep Learning): الشبكات العصبية متعددة الطبقات: التعلم التلقائي للميزات متعددة المستويات
- الانحدار التدرّجي (Gradient Descent): النزول على طول أشد منحدر لسطح الخسارة للعثور على الأوزان المثلى
المفهوم الأساسي: العصبون = مجموع مرجّح + تحويل غير خطي. الشبكة العصبية = العديد من العصبونات مكدّسة معًا. التعلم العميق = شبكة عصبية ذات طبقات كثيرة. من مُدرِك بـ 10 أسطر من الشيفرة إلى GPT، جميعها تتبع في الأساس نفس النموذج.
📝 تمارين
- الأساسيات (⭐): قم بتنفيذ خلية ادراك (perceptron) لبوابة OR. جدول الحقيقة للبوابة OR هو: (0,0) → 0، (0,1) → 1، (1,0) → 1، (1,1) → 1. قم بتعديل التسميات في كود تدريب الخلية العصبية من هذا الفصل، ثم قم بتدريب النموذج والتحقق من النتائج.
# Starter code: OR gate perceptron
X = [[0,0],[0,1],[1,0],[1,1]]
y_or = [0, 1, 1, 1]
# TODO: train and test
- متقدم (⭐⭐): استخدم MLPClassifier من sklearn لإجراء التصنيف على مجموعة بيانات Iris، وضبط المعلمات، وتسجيل الدقة للتكوينات المختلفة:
# Starter code: MLP on Iris
from sklearn.datasets import load_iris
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
iris.data, iris.target, test_size=0.3, random_state=42)
# TODO: try different hidden_layer_sizes, activation, learning_rate
# Record accuracy for each config
جرب على الأقل 3 تكوينات مختلفة لـ hidden_layer_sizes (مثل (10,), (50,50), (100,)) و 2 دوال تنشيط، ولاحظ أي تكوين يعمل بشكل أفضل.
- تحدي (⭐⭐⭐): بناءً على تنفيذ MLP باستخدام Python النقي في هذا الفصل، قم برسم منحنى الخسارة للـ MLP الذي تم تنفيذه يدويًا:
# After training MLP from Section 9, plot the loss curve
import matplotlib.pyplot as plt
# mlp.losses is already recorded during training
plt.figure(figsize=(8, 5))
plt.plot(range(len(mlp.losses)), mlp.losses, linewidth=1)
plt.xlabel("العصر")
plt.ylabel("الخسارة (المتوسط التربيعي للخطأ)")
plt.title("منحنى خسارة تدريب MLP (XOR)")
plt.yscale('log')
plt.grid(True, alpha=0.3)
plt.savefig("mlp_loss_curve.png", dpi=100)
plt.show()
التمديدات المتقدمة: ① قارن منحنى الخسارة لمعدلات تعلم مختلفة (0.1، 0.5، 1.0، 2.0) على نفس الرسم البياني؛ ② جرب أحجام طبقات مخفية مختلفة (2، 4، 8) لمعرفة كيف تؤثر على معدل التقارب؛ ③ لاحظ ما إذا كانت الخسارة تتذبذب أو حتى تتباعد عندما يكون معدل التعلم مرتفعًا جدًا.