Batch Normalization — التعلم العميق
Batch Normalization
المشكلة
أثناء التدريب تتغير أوزان كل طبقة، فيتغير توزيع مدخلات الطبقة التالية باستمرار: كأنك تتعلم إصابة هدف يتحرك. والقيم قد تكبر أو تصغر جداً عبر الطبقات العميقة فيصعب التدريب.
الحل
لكل دفعة، Batch Normalization يقيّس مخرجات الطبقة لكل ميزة:
x̂ = (x − متوسط الدفعة) / √(تباين الدفعة + ε)
y = γ · x̂ + β ← γ و β معاملان يتعلمهما النموذجγ و β يسمحان للشبكة باستعادة أي مقياس تحتاجه — حتى إلغاء التقييس إن كان مفيداً.
import numpy as np
rng = np.random.default_rng(0)
batch = rng.normal(loc=50, scale=12, size=(32, 3)) # مخرجات طبقة بمقياس كبير
mean, var = batch.mean(axis=0), batch.var(axis=0)
normalized = (batch - mean) / np.sqrt(var + 1e-5)
print("قبل: المتوسط", mean.round(1), "الانحراف", np.sqrt(var).round(1))
print("بعد: المتوسط", normalized.mean(axis=0).round(3), "الانحراف", normalized.std(axis=0).round(3))التدريب مقابل التنبؤ
- أثناء التدريب: إحصائيات الدفعة الحالية، مع تحديث متوسط متحرك للإحصائيات.
- أثناء التنبؤ: المتوسط المتحرك المحفوظ (قد تكون "الدفعة" صورة واحدة!).
Keras يبدّل تلقائياً. لكن لهذا السبب: نموذج يحتوي BatchNorm ويتصرف بشكل مختلف بين fit و predict — أمر طبيعي.
الفوائد
- تدريب أسرع وأثبت، ويتحمل معدلات تعلم أعلى.
- أقل حساسية لتهيئة الأوزان.
- أثر تنظيمي خفيف (ضوضاء إحصائيات الدفعة).
الموضع
الشائع: Dense/Conv ← BatchNormalization ← Activation. ولأن BN يطرح المتوسط، يمكن إلغاء انحياز الطبقة السابقة (use_bias=False). عملياً الترتيب بعد التنشيط يعمل أيضاً؛ المهم الاتساق.
متى لا يناسب؟
- دفعات صغيرة جداً (2–4): الإحصائيات غير موثوقة.
- RNN و Transformers: نستخدم Layer Normalization بدلاً منه — يقيّس عبر ميزات العينة الواحدة لا عبر الدفعة، فلا يعتمد على حجمها.
- عند نقل التعلم ب BatchNorm مجمّد: اترك طبقاته في وضع التنبؤ (training=False) أثناء الضبط الدقيق.
تمرين
في المثال الكامل درّب النموذجين (مع BN وبدونه) بمعدل تعلم 0.01 بدل الافتراضي وقارن الدقة بعد 3 epochs. أيهما تحمّل المعدل الأعلى؟
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# ===== بدون Batch Normalization =====
model_without_bn = keras.Sequential([
layers.Dense(256, activation='relu', input_shape=(784,)),
layers.Dense(128, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(10, activation='softmax')
])
# ===== مع Batch Normalization =====
model_with_bn = keras.Sequential([
layers.Dense(256, input_shape=(784,)),
layers.BatchNormalization(),
layers.Activation('relu'),
layers.Dense(128),
layers.BatchNormalization(),
layers.Activation('relu'),
layers.Dense(64),
layers.BatchNormalization(),
layers.Activation('relu'),
layers.Dense(10, activation='softmax')
])
# ===== في CNN =====
cnn_model = keras.Sequential([
layers.Conv2D(32, (3, 3), input_shape=(28, 28, 1)),
layers.BatchNormalization(),
layers.Activation('relu'),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, (3, 3)),
layers.BatchNormalization(),
layers.Activation('relu'),
layers.MaxPooling2D((2, 2)),
layers.Flatten(),
layers.Dense(64),
layers.BatchNormalization(),
layers.Activation('relu'),
layers.Dense(10, activation='softmax')
])
# ===== مقارنة =====
# تحميل MNIST
(X_train, y_train), (X_test, y_test) = keras.datasets.mnist.load_data()
X_train = X_train.reshape(-1, 784) / 255.0
X_test = X_test.reshape(-1, 784) / 255.0
for name, model in [('Without BN', model_without_bn), ('With BN', model_with_bn)]:
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
history = model.fit(X_train, y_train, epochs=5, validation_split=0.1, verbose=0)
test_acc = model.evaluate(X_test, y_test, verbose=0)[1]
print(f"{name}: Test Accuracy = {test_acc:.2%}")اختبر فهمك