الوحدة 2 — الواجهة التتابعية: أوّل نموذج في أسطر قليلة
أرَت الوحدة الأولى اللبنات على مستواها المنخفض. وتُغلّف Keras هذه اللبنات في واجهة تسع فيها الشبكة الكاملة عشرة أسطر. والواجهة التتابعية أبسط الواجهات الثلاث، وتُغطّي وحدها نصف الحاجات الفعلية.
رصّ من الطبقات، مدخل واحد ومخرج واحد
يُكدِّس Sequential الطبقات بالترتيب. مخرج كلّ طبقة يصير مدخل التي تليها، دون استثناء.
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
keras.Input(shape=(28, 28)),
layers.Flatten(),
layers.Dense(128, activation="relu"),
layers.Dropout(0.2),
layers.Dense(10, activation="softmax"),
])
اتُّخذت في هذا المقطع أربعة قرارات دفعةً واحدة. أُعلن شكل المدخل صراحةً، فيبني Keras الأوزان فورًا بدل انتظار أوّل دفعة بيانات. تُسطِّح Flatten الصورة إلى شعاع من 784 قيمة. الطبقة المخفيّة ذات 128 عصبونًا بتنشيط ReLU تُطبّق درس الوحدة 2 من الدورة 07. ومخرج softmax على عشرة عصبونات يقابل عشرة أصناف متنافية.
بدون keras.Input يبقى النموذج غير مبنيّ: أوزانه لم توجد بعد، ويفشل model.summary()، ويظلّ عدد المعاملات مجهولًا. ينتظر Keras أوّل دفعة ليستنبط الأبعاد. هذا يعمل، لكنّنا نخسر الفحص الفوري للأشكال، وهو أفضل لحظة لاكتشاف خطأ معماري.
التصريف اختيار لثلاثة أشياء
النموذج المبنيّ لا يعرف بعدُ كيف يتعلّم. تُلحِق به compile مُحسِّنًا وخسارة ومقاييس.
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=1e-3),
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
اختيار الخسارة ليس حرًّا: بل يتبع صيغة العلامات وتنشيط المخرج.
| العلامات | المخرج | الخسارة |
|---|---|---|
| أعداد صحيحة، صنف واحد لكلّ مشاهدة | softmax | sparse_categorical_crossentropy |
| أشعّة ذات واحد وحيد | softmax | categorical_crossentropy |
| 0 أو 1 | sigmoid بعصبون واحد | binary_crossentropy |
| عدّة علامات في آنٍ | sigmoid لكلّ عصبون | binary_crossentropy |
| قيمة مستمرّة | خطّي | mse أو huber |
الخلط بين السطرين الأوّلَين هو الخطأ الأشيع عند المبتدئين. لا يرفع استثناءً: بل يُنتج خسارةً لا تنزل، أو تنزل بصورة سخيفة. أمّا لاحقة sparse_ فتعني «العلامات أعداد صحيحة»، لا أكثر من ذلك.
الخسارة هي ما يُقلّله المُحسِّن؛ ولا بدّ أن تكون قابلة للاشتقاق. أمّا المقياس فوظيفته إخبارك، ويجوز ألّا يكون قابلًا للاشتقاق — كالدقّة مثلًا. مقياس في مكان loss يرفع خطأ؛ وخسارة في مكان metrics تعمل لكنّها لا تُعلِّم شيئًا.
قراءة الملخّص قبل التدريب
يستحقّ model.summary() نظرة متأنّية قبل كلّ تدريب.
Layer (type) Output Shape Param #
=====================================================
flatten (Flatten) (None, 784) 0
dense (Dense) (None, 128) 100480
dropout (Dropout) (None, 128) 0
dense_1 (Dense) (None, 10) 1290
=====================================================
Total params: 101,770
يُفرَض عليك فحصان. أوّلًا الأشكال: كلّ Output Shape ينبغي أن يوافق قصدك، ولا بدّ من وجود None في مقدّمة كلّ سطر. ثمّ عدد المعاملات الذي يُعاد حسابه ذهنيًا: طبقة Dense من 784 مدخلًا إلى 128 مخرجًا لها معاملًا، أوزان زائد انحياز. أيّ فارق مع حسابك يشير إلى طبقة سيّئة التحجيم.
لاحظ أنّ Flatten وDropout لا تحملان أيّ معامل: هما تحويلان لا شيء فيهما يُتعلَّم.
التدريب والمتابعة
history = model.fit(
x_train, y_train,
epochs=20,
batch_size=32,
validation_split=0.2,
verbose=2,
)
يحجز validation_split=0.2 آخر 20٪ من البيانات دون خلطها. فإن كان جدولك مرتّبًا حسب الصنف لن تحوي هذه المصادقة إلّا جزءًا من الأصناف، وتصير النتائج غير قابلة للتفسير. اخلط البيانات قبل ذلك، أو قدّم validation_data صراحةً.
يحتفظ كائن history بالقيم لكلّ حقبة، وهو ما يُرسَم لقراءة منحنيات التعلّم التي دُرست في الوحدة 9 من الدورة 07:
import matplotlib.pyplot as plt
plt.plot(history.history["loss"], label="التدريب")
plt.plot(history.history["val_loss"], label="المصادقة")
plt.legend()
التقييم والتوقّع، ولا ينبغي الخلط بينهما
loss, accuracy = model.evaluate(x_test, y_test)
probabilities = model.predict(x_test)
classes = probabilities.argmax(axis=1)
يحتاج evaluate إلى العلامات و يُعيد النقاط. بينما لا يحتاجها predict ويُعيد مخارج الشبكة الخام — وهي هنا احتمالات، لا أصناف. ونسيان argmax مصدر كلاسيكي لنتائج غير مفهومة.
تفصيل خفيّ لكنّه مهمّ: Dropout فعّال أثناء fit ومعطَّل أثناء evaluate وpredict. ويُدير Keras هذا التبديل تلقائيًا. ولهذا قد تبدو خسارة التدريب المعروضة أعلى من خسارة المصادقة في أوّل الحقب، دون أن يكون في ذلك شيء غير طبيعيّ.
متى تعجز الواجهة التتابعية
تفترض الواجهة التتابعية سلسلة خطّية. وتصير عاجزةً حال ابتعاد المعمارية عن هذا المخطّط:
- مدخلان مختلفان في طبيعتهما، صورة ونصّ مثلًا؛
- مخرجان، كتصنيف وانحدار في آنٍ واحد؛
- تفرّع يلتفّ حول طبقات، وهو بعينه تعريف الاتّصال المتبقّي؛
- طبقة تُطبَّق مرّتين على الأوزان نفسها، كما في المعماريات السياميّة.
هذه الحالات الأربع ليست غريبة: بل تُغطّي معظم المعماريات الحديثة. وهذا ما يُبرّر وجود الوحدة التالية.