معاينة مختبر آمنة
Intro Keras
هذي معاينة منقّحة للقراءة فقط؛ ما فيه أي شيء يشتغل داخل الصفحة.
قراءة فقط
معاينة الدفتر
Intro Keras
## أبسط مقدمة للشبكات العصبية باستخدام Keras
> هالدفتر جزء من منهج الذكاء الاصطناعي للمبتدئين. زوروا صفحة المنهج عشان تلقون مواد التعلّم كاملة.
### أطر الشبكات العصبية
فيه عدة أطر نقدر نستخدمها لتدريب الشبكات العصبية. وإذا تبون تبدأون بسرعة من دون ما تدخلون بتفاصيل كثيرة عن آلية العمل الداخلية، فـ [Keras](https://keras.io/) خيار مناسب. هالشرح المختصر بيعطيكم نقطة بداية، وإذا ودكم تفهمون التفاصيل بشكل أعمق، شوفوا دفتر [مقدمة إلى TensorFlow وKeras](/learn/ai-for-beginners/neural-network-frameworks/labs/lesson-05-intro-keras-tf).
### تجهيز البيئة
تستخدم هالنسخة Keras 3 مع خلفية TensorFlow المثبّتة في ملف تشغيل الدفتر. Keras واجهة متعددة الخلفيات، لذلك ما نثبّت حزمًا من داخل الدفتر ولا نفترض إن كل تثبيت Keras تابع لنسخة TensorFlow بعينها.
import tensorflow as tf
from tensorflow import keras
import numpy as np
from sklearn.datasets import make_classification
import matplotlib.pyplot as plt
print(f'Tensorflow version = {tf.__version__}')
print(f'Keras version = {keras.__version__}')## المفهوم الأساسي: الموتر
**الموتر** مصفوفة متعددة الأبعاد، ويفيدنا بتمثيل أنواع مختلفة من البيانات:
* 400x400 - صورة بالأبيض والأسود
* 400x400x3 - صورة ملوّنة
* 16x400x400x3 - دفعة مصغّرة من 16 صورة ملوّنة
* 25x400x400x3 - ثانية وحدة من فيديو بمعدل 25 إطارًا بالثانية
* 8x25x400x400x3 - دفعة مصغّرة من 8 مقاطع، مدة كل واحد منها 1 ثانية
الموترات تعطينا طريقة مريحة نمثل فيها بيانات الإدخال والإخراج، وكذلك الأوزان داخل الشبكة العصبية.
## مسألة تجريبية
خلونا ناخذ مسألة تصنيف ثنائي. مثال مناسب عليها: التصنيف بين ورم خبيث وورم حميد بناءً على حجمه وعمره. نبدأ بتوليد بيانات تجريبية:
np.random.seed(0) # pick the seed for reproducibility - change it to explore the effects of random variations
n = 100
X, Y = make_classification(n_samples = n, n_features=2,
n_redundant=0, n_informative=2, flip_y=0.05,class_sep=1.5)
X = X.astype(np.float32)
Y = Y.astype(np.int32)
split = [ 70*n//100 ]
train_x, test_x = np.split(X, split)
train_labels, test_labels = np.split(Y, split)def plot_dataset(features, labels, W=None, b=None):
# prepare the plot
fig, ax = plt.subplots(1, 1)
ax.set_xlabel('$x_i[0]$ -- (feature 1)')
ax.set_ylabel('$x_i[1]$ -- (feature 2)')
colors = ['r' if l else 'b' for l in labels]
ax.scatter(features[:, 0], features[:, 1], marker='o', c=colors, s=100, alpha = 0.5)
if W is not None:
min_x = min(features[:,0])
max_x = max(features[:,1])
min_y = min(features[:,1])*(1-.1)
max_y = max(features[:,1])*(1+.1)
cx = np.array([min_x,max_x],dtype=np.float32)
cy = (0.5-W[0]*cx-b)/W[1]
ax.plot(cx,cy,'g')
ax.set_ylim(min_y,max_y)
fig.show()plot_dataset(train_x, train_labels)## تطبيع البيانات
قبل التدريب، المعتاد إننا نجيب سمات الإدخال إلى المجال القياسي [0,1] (أو [-1,1]). بنشرح السبب بالتفصيل لاحقًا في الدورة، لكن الفكرة باختصار إننا ما نبي القيم اللي تمر داخل الشبكة تكبر أو تصغر زيادة. عادةً نخلي القيم في مجال صغير قريب من 0؛ لذلك نهيّئ الأوزان بأرقام عشوائية صغيرة ونحافظ على الإشارات في المجال نفسه.
لما نطبّع البيانات، نطرح أصغر قيمة ثم نقسم على المدى. نحسب أصغر قيمة والمدى من بيانات التدريب، وبعدها نطبّع مجموعة البيانات المخصصة للاختبار أو بيانات التحقق باستخدام القيم نفسها المأخوذة من بيانات التدريب. السبب إننا في الاستخدام الحقيقي نعرف مجموعة التدريب فقط، وما نعرف مسبقًا كل قيمة جديدة بتطلبون من الشبكة تتنبأ بها. أحيانًا تطلع قيمة جديدة برا المجال [0,1]، وهذا مب مشكلة جوهرية.
train_x_norm = (train_x-np.min(train_x,axis=0)) / (np.max(train_x,axis=0)-np.min(train_x,axis=0))
test_x_norm = (test_x-np.min(train_x,axis=0)) / (np.max(train_x,axis=0)-np.min(train_x,axis=0))## تدريب شبكة بطبقة واحدة (Perceptron)
في حالات كثيرة تكون الشبكة العصبية عبارة عن سلسلة طبقات. نقدر نعرّفها في Keras باستخدام نموذج `Sequential` بالطريقة هذي:
model = keras.models.Sequential()
model.add(keras.Input(shape=(2,)))
model.add(keras.layers.Dense(1))
model.add(keras.layers.Activation(keras.activations.sigmoid))
model.summary()هنا ننشئ النموذج أول، ثم نضيف له الطبقات:
* طبقة `Input` الأولى—وهي من ناحية دقيقة مب طبقة فعلية—تحدد حجم مدخلات الشبكة
* طبقة `Dense` هي Perceptron الفعلي، وفيها الأوزان القابلة للتدريب
* وأخيرًا، نحط طبقة بدالة تنشيط *sigmoid* من خلال `Activation` عشان تحوّل ناتج الشبكة إلى المجال 0-1 وتخليه احتمالًا
وللاختصار، نقدر نحدد حجم الإدخال ودالة التنشيط مباشرة داخل طبقة `Dense`:
model = keras.models.Sequential()
model.add(keras.layers.Dense(1,input_shape=(2,),activation='sigmoid'))
model.summary()قبل ما ندرّب النموذج، نحتاج **نهيّئه للتدريب** باستدعاء compile، ومعنى هذا إننا نحدد:
* **دالة الخسارة** اللي تبيّن طريقة حساب الخسارة. بما إن عندنا مسألة تصنيف بفئتين، بنستخدم *خسارة الانتروبيا المتقاطعة الثنائية*.
* **المحسّن** اللي بنستخدمه. أبسط خيار هو `sgd` لتنفيذ *الانحدار المتدرج العشوائي*، وفيه محسّنات أوسع مثل `adam`.
* **المقاييس** اللي بنقيس بها نجاح التدريب. وبما إن المهمة هي التصنيف، فمقياس `Accuracy`—أو اختصاره `acc`—مناسب.
نقدر نحدد الخسارة والمقاييس والمحسّن كسلاسل نصية، أو نمرر كائنات من إطار Keras. في مثالنا نحتاج نحدد المعامل `learning_rate` عشان نضبط سرعة تعلّم النموذج، ولذلك نمرر الاسم الكامل لمحسّن SGD في Keras.
model.compile(optimizer=keras.optimizers.SGD(learning_rate=0.2),loss='binary_crossentropy',metrics=['acc'])بعد تجميع النموذج، نبدأ التدريب الفعلي باستدعاء الدالة `fit`. أهم المعاملات هي:
* `x` و`y` يحددون بيانات التدريب: السمات والتسميات بالترتيب
* إذا بغينا نجري تحققًا مع كل حقبة، نحدد المعامل `validation_data` على شكل زوج من السمات والتسميات
* `epochs` يحدد عدد الحقب
* إذا بغينا التدريب يصير بدفعات مصغّرة، نحدد المعامل `batch_size`. وتقدرون تقسّمون البيانات إلى دفعات يدويًا قبل تمريرها إلى `x`/`y`/`validation_data`؛ وقتها ما تحتاجون `batch_size`
model.fit(x=train_x_norm,y=train_labels,validation_data=(test_x_norm,test_labels),epochs=10,batch_size=1)جرّبوا تغيّرون معاملات التدريب وشوفوا وش يتغير:
* إذا خليتوا `batch_size` كبير زيادة—أو ما حددتوه أصلًا—قد يصير التدريب أقل استقرارًا؛ لأن أحجام الدفعات الصغيرة مع البيانات منخفضة الأبعاد تعطي اتجاه تدرج أدق لكل حالة
* إذا كان `learning_rate` عالي زيادة، قد يسبب فرط التكيّف أو نتائج أقل استقرارًا. وإذا كان منخفض زيادة، بنحتاج حقب أكثر عشان نوصل للنتيجة
> انتبهوا إنكم تقدرون تستدعون الدالة `fit` أكثر من مرة ورا بعض لمواصلة تدريب الشبكة. وإذا بغيتوا تبدأون التدريب من الصفر، أعيدوا تشغيل الخلية اللي تعرّف النموذج.
عشان نتأكد إن التدريب اشتغل، خلونا نرسم الخط اللي يفصل بين الفئتين. معادلة خط الفصل هي $W\times x + b = 0.5$
plot_dataset(train_x,train_labels,model.layers[0].weights[0],model.layers[0].weights[1])## رسم منحنيات التدريب
الدالة `fit` ترجع كائن `history`، ونستخدمه عشان نتابع الخسارة والمقاييس في كل حقبة. في المثال تحت بنعيد التدريب من البداية بمعدل تعلّم صغير، وبنشوف كيف تتغير الخسارة والدقة.
> **ملاحظة:** هنا نستخدم صياغة مختلفة شوي لتعريف نموذج `Sequential`. بدل ما نستدعي `add` ونضيف الطبقات وحدة وحدة، نقدر نعطي النموذج قائمة الطبقات من لحظة إنشائه. هالصياغة أقصر، وقد تناسبكم أكثر.
model = keras.models.Sequential([
keras.layers.Dense(1,input_shape=(2,),activation='sigmoid')])
model.compile(optimizer=keras.optimizers.SGD(learning_rate=0.05),loss='binary_crossentropy',metrics=['acc'])
hist = model.fit(x=train_x_norm,y=train_labels,validation_data=(test_x_norm,test_labels),epochs=10,batch_size=1)plt.plot(hist.history['acc'])
plt.plot(hist.history['val_acc'])## التصنيف متعدد الفئات
إذا بغينا نحل مسألة تصنيف فيها أكثر من فئة، بيكون للشبكة أكثر من مخرج—بعدد الفئات $C$. كل مخرج يحتوي احتمال الفئة المقابلة له.
> تقدرون بعد تستخدمون شبكة بمخرجين للتصنيف الثنائي بالطريقة نفسها، وهذا بالضبط اللي بنوضحه الحين.
لما نتوقع من الشبكة تطلع مجموعة احتمالات $p_1,\dots, p_C$، نحتاج مجموعها يكون 1. عشان نضمن هذا، نستخدم `softmax` كدالة التنشيط الأخيرة في آخر طبقة. تستقبل **Softmax** متجهًا وتتأكد إن كل عناصره تتحول إلى احتمالات.
وبما إن خرج الشبكة متجه أبعاده $C$، نحتاج التسميات تكون بالشكل نفسه. نحقق هذا باستخدام **ترميز one-hot**، بحيث يتحول رقم الفئة $i$ إلى متجه أصفار، وفي الموضع $i$ تكون القيمة 1.
ولمقارنة توزيع الاحتمالات اللي تطلعه الشبكة العصبية بالتسمية المتوقعة بترميز one-hot، نستخدم **دالة خسارة الانتروبيا المتقاطعة**. الدالة تستقبل توزيعين احتماليين وترجع قيمة تبيّن مقدار اختلافهم.
إذًا، للتصنيف متعدد الفئات وعددها $C$ نحتاج:
* تكون في آخر طبقة من الشبكة عصبونات بعدد $C$
* تكون دالة التنشيط الأخيرة **softmax**
* تكون دالة الخسارة **خسارة الانتروبيا المتقاطعة**
* نحوّل التسميات إلى **ترميز one-hot**، سواءً باستخدام `numpy` أو أداة Keras المسماة `to_categorical`
model = keras.models.Sequential([
keras.layers.Dense(5,input_shape=(2,),activation='relu'),
keras.layers.Dense(2,activation='softmax')
])
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.01),
loss="categorical_crossentropy",
metrics=["accuracy"],
)
# Two ways to convert to one-hot encoding
train_labels_onehot = keras.utils.to_categorical(train_labels)
test_labels_onehot = np.eye(2)[test_labels]
hist = model.fit(x=train_x_norm,y=train_labels_onehot,
validation_data=[test_x_norm,test_labels_onehot],batch_size=1,epochs=10)### الانتروبيا المتقاطعة الفئوية المتناثرة
غالبًا نمثل التسميات في التصنيف متعدد الفئات بأرقام الفئات. يدعم Keras نوعًا ثانيًا من دالة الخسارة اسمه **الانتروبيا المتقاطعة الفئوية المتناثرة**، ويتوقع أرقام الفئات كأعداد صحيحة بدل متجهات one-hot. باستخدام هالدالة نقدر نبسّط كود التدريب:
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.01),
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
model.fit(x=train_x_norm,y=train_labels,validation_data=[test_x_norm,test_labels],batch_size=1,epochs=10)## التصنيف متعدد التسميات
أحيانًا يكون العنصر تابعًا لفئتين بنفس الوقت. مثلًا، نفترض إننا نبي نطوّر مصنّفًا للقطط والكلاب في الصورة، ونبي بعد نتعامل مع صورة فيها قط وكلب مع بعض.
في التصنيف متعدد التسميات، بدل متجه one-hot اللي فيه قيمة وحدة، يكون عندنا متجه قيمته 1 في كل موضع يقابل فئة موجودة في عينة الإدخال. لذلك مخرجات الشبكة ما تكون احتمالات مطبّعة على مستوى كل الفئات مجتمعة، بل احتمال مستقل لكل فئة؛ وهذا يناسب دالة التنشيط **sigmoid**. ونقدر نستمر باستخدام الانتروبيا المتقاطعة كدالة الخسارة.
> **ملاحظة:** هالطريقة قريبة جدًا من استخدام **شبكات عصبية مختلفة** للتصنيف الثنائي لكل فئة؛ لكن الجزء الأول من الشبكة—إلى طبقة التصنيف الأخيرة—مشترك بين كل الفئات.
## ملخص دوال الخسارة في التصنيف
شفنا إن التصنيف الثنائي ومتعدد الفئات ومتعدد التسميات يختلف بحسب دالة الخسارة ودالة التنشيط في آخر طبقة من الشبكة. بالبداية قد تتداخل عليكم، فهذي قواعد مختصرة تذكّروها:
* إذا كان للشبكة مخرج واحد (**تصنيف ثنائي**) نستخدم دالة التنشيط **sigmoid**، أما في **التصنيف متعدد الفئات** فنستخدم **softmax**
* إذا مثّلنا فئة الخرج بترميز one-hot، تكون دالة الخسارة **خسارة الانتروبيا المتقاطعة** (categorical cross-entropy). وإذا كان الخرج رقم الفئة، نستخدم **الانتروبيا المتقاطعة الفئوية المتناثرة**. وفي **التصنيف الثنائي** نستخدم **الانتروبيا المتقاطعة الثنائية**، وهي نفسها **log loss**
* **التصنيف متعدد التسميات** يعني إن العنصر قد ينتمي لعدة فئات بنفس الوقت. هنا نرمّز التسميات بترميز one-hot ونستخدم **sigmoid** كدالة التنشيط، عشان يكون احتمال كل فئة بين 0 و1
| نوع التصنيف | صيغة التسمية | دالة التنشيط | الخسارة |
|---------------|-----------------------|-----------------|----------|
| ثنائي | احتمال الفئة 1 | sigmoid | binary crossentropy |
| ثنائي | ترميز one-hot (مخرجان 2) | softmax | categorical crossentropy |
| متعدد الفئات | ترميز one-hot | softmax | categorical crossentropy |
| متعدد الفئات | رقم الفئة | softmax | sparse categorical crossentropy |
| متعدد التسميات | ترميز one-hot | sigmoid | categorical crossentropy |
**المهمة**:
استخدموا Keras لتدريب مصنّف لأرقام MNIST المكتوبة باليد:
* انتبهوا إن Keras فيها مجموعات بيانات قياسية، ومنها MNIST. عشان تستخدمون MNIST من Keras ما تحتاجون إلا سطرين تقريبًا من الكود—والتفاصيل [هنا](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/mnist)
* جرّبوا أكثر من إعداد للشبكة، مع تغيير عدد الطبقات والعصبونات ودوال التنشيط
وش أفضل دقة قدرتوا توصلون لها؟
## الخلاصة
* Keras 3 واجهة عالية المستوى لبناء النماذج وتدريبها على أكثر من خلفية.
* استخدموا الواجهة الوظيفية أو تخصيص Model للمعماريات غير الخطية وحلقات التدريب الخاصة.
* قارنوا TensorFlow وPyTorch حسب متطلبات المهمة والتشغيل، مب حسب ادعاء ثابت عن الأفضلية.
حذفنا المخرجات وعدّادات التشغيل والودجات والمحتوى النشط وقت الاستيراد. شغّل الدفاتر بس في بيئة خارجية تثق فيها.
سجّل تطبيقك
التسجيل اختياري، يفيدك تتذكر وش طبّقت، ولا يمنع إكمال الدورة.