بناء شبكة CNN لتصنيف الصور في مجموعة بيانات CIFAR-10
بناء شبكة CNN لتصنيف الصور في مجموعة بيانات CIFAR-10
سنعمل مع مجموعة بيانات CIFAR-10. هذه مجموعة بيانات معروفة لتصنيف الصور، تتكوّن من 60000 صورة ملوّنة بحجم 32×32 موزَّعة على 10 فئات، بواقع 6000 صورة لكل فئة. توجد 50000 صورة تدريب و10000 صورة اختبار.
الفئات العشر هي:
- طائرة (airplane)
- سيارة (automobile)
- طائر (bird)
- قطة (cat)
- غزال (deer)
- كلب (dog)
- ضفدع (frog)
- حصان (horse)
- سفينة (ship)
- شاحنة (truck)
لمزيد من التفاصيل حول CIFAR-10 راجع: https://www.cs.toronto.edu/~kriz/cifar.html
لمجموعة من نتائج الأداء المنشورة على CIFAR-10، راجع: http://rodrigob.github.io/are_we_there_yet/build/classification_datasets_results.html
بناء الشبكات العصبية الالتفافية (Convolutional Neural Nets)
في هذا التمرين سنبني وندرّب أول شبكة عصبية التفافية (CNN) لنا. في الجزء الأول، نستعرض الطبقات المختلفة وكيفية إعدادها. وفي الجزء الثاني، ستبني نموذجك الخاص، وتدرّبه، وتقارن الأداء.
import keras
from keras.datasets import cifar10
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
import matplotlib.pyplot as plt
%matplotlib inline# The data, shuffled and split between train and test sets:
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')x_train shape: (50000, 32, 32, 3)
50000 train samples
10000 test samples## Each image is a 32 x 32 x 3 numpy array
x_train[444].shape(32, 32, 3)## Let's look at one of the images
print(y_train[444])
plt.imshow(x_train[444]);[9]num_classes = 10
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)# now instead of classes described by an integer between 0-9 we have a vector with a 1 in the (Pythonic) 9th position
y_train[444]array([0., 0., 0., 0., 0., 0., 0., 0., 0., 1.])# As before, let's make everything float and scale
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255طبقات Keras للشبكات الالتفافية (CNNs)
-
سبق أن بنينا شبكات عصبية باستخدام طبقات Dense وActivation وDropout بشكل أساسي.
-
هنا سنشرح كيفية استخدام بعض الطبقات الخاصة بالشبكات الالتفافية التي توفّرها Keras
Conv2D
keras.layers.convolutional.Conv2D(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None, **kwargs)شرح بعض المعاملات:
filters: عدد المرشِّحات (Filters) المستخدَمة لكل موضع. بمعنى آخر، عمق المُخرج.kernel_size: زوج (x,y) يحدّد ارتفاع وعرض النواة (Kernel) المستخدَمةstrides: زوج (x,y) يحدّد الخطوة (Stride) في كل بُعد. القيمة الافتراضية هي(1,1)input_shape: مطلوب فقط للطبقة الأولى
لاحظ أن حجم المُخرج يُحدَّد بناءً على kernel_size وstrides
MaxPooling2D
keras.layers.pooling.MaxPooling2D(pool_size=(2, 2), strides=None, padding='valid', data_format=None)
pool_size: حجم (x,y) للشبكة التي سيُطبَّق عليها التجميع (Pooling)strides: يُفترَض أنها تساويpool_sizeما لم تُحدَّد خلاف ذلك
Flatten
تحوّل مُدخلها إلى متجه أحادي البُعد (لكل عيّنة). تُستخدَم عادةً عند الانتقال بين الطبقات الالتفافية والطبقات المتصلة بالكامل (Fully connected).
أول شبكة عصبية التفافية (CNN)
سنبني أدناه أول شبكة عصبية التفافية لنا. لأغراض العرض التوضيحي (حتى تتدرّب بسرعة) فهي ليست عميقة جدًا ولديها عدد قليل نسبيًا من المعاملات. نستخدم خطوة (Stride) بقيمة 2 في أول طبقتين التفافيتين، ما يقلّل بسرعة من أبعاد المُخرج. بعد طبقة MaxPooling، نُسطِّح (Flatten) البيانات، ثم نمتلك طبقة واحدة متصلة بالكامل قبل طبقة التصنيف النهائية.
# Let's build a CNN using Keras' Sequential capabilities
model_1 = Sequential()
## 5x5 convolution with 2x2 stride and 32 filters
model_1.add(Conv2D(32, kernel_size = (5, 5), strides = (2,2), padding='same',
input_shape=x_train.shape[1:]))
model_1.add(Activation('relu'))
## Another 5x5 convolution with 2x2 stride and 32 filters
model_1.add(Conv2D(32, (5, 5), strides = (2,2), activation="relu")) # another way to define activation
## 2x2 max pooling reduces to 3 x 3 x 32
model_1.add(MaxPooling2D(pool_size=(2, 2)))
model_1.add(Dropout(0.25))
## Flatten turns 3x3x32 into 288x1
model_1.add(Flatten())
model_1.add(Dense(512))
model_1.add(Activation('relu'))
model_1.add(Dropout(0.5))
model_1.add(Dense(num_classes))
model_1.add(Activation('softmax'))
model_1.summary()/usr/local/lib/python3.11/dist-packages/keras/src/layers/convolutional/base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
super().__init__(activity_regularizer=activity_regularizer, **kwargs)
[1mModel: "sequential"[0m
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃[1m [0m[1mLayer (type) [0m[1m [0m┃[1m [0m[1mOutput Shape [0m[1m [0m┃[1m [0m[1m Param #[0m[1m [0m┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ conv2d ([38;5;33mConv2D[0m) │ ([38;5;45mNone[0m, [38;5;34m16[0m, [38;5;34m16[0m, [38;5;34m32[0m) │ [38;5;34m2,432[0m │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation ([38;5;33mActivation[0m) │ ([38;5;45mNone[0m, [38;5;34m16[0m, [38;5;34m16[0m, [38;5;34m32[0m) │ [38;5;34m0[0m │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_1 ([38;5;33mConv2D[0m) │ ([38;5;45mNone[0m, [38;5;34m6[0m, [38;5;34m6[0m, [38;5;34m32[0m) │ [38;5;34m25,632[0m │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d ([38;5;33mMaxPooling2D[0m) │ ([38;5;45mNone[0m, [38;5;34m3[0m, [38;5;34m3[0m, [38;5;34m32[0m) │ [38;5;34m0[0m │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout ([38;5;33mDropout[0m) │ ([38;5;45mNone[0m, [38;5;34m3[0m, [38;5;34m3[0m, [38;5;34m32[0m) │ [38;5;34m0[0m │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ flatten ([38;5;33mFlatten[0m) │ ([38;5;45mNone[0m, [38;5;34m288[0m) │ [38;5;34m0[0m │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense ([38;5;33mDense[0m) │ ([38;5;45mNone[0m, [38;5;34m512[0m) │ [38;5;34m147,968[0m │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_1 ([38;5;33mActivation[0m) │ ([38;5;45mNone[0m, [38;5;34m512[0m) │ [38;5;34m0[0m │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_1 ([38;5;33mDropout[0m) │ ([38;5;45mNone[0m, [38;5;34m512[0m) │ [38;5;34m0[0m │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 ([38;5;33mDense[0m) │ ([38;5;45mNone[0m, [38;5;34m10[0m) │ [38;5;34m5,130[0m │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_2 ([38;5;33mActivation[0m) │ ([38;5;45mNone[0m, [38;5;34m10[0m) │ [38;5;34m0[0m │
└─────────────────────────────────┴────────────────────────┴───────────────┘
[1m Total params: [0m[38;5;34m181,162[0m (707.66 KB)
[1m Trainable params: [0m[38;5;34m181,162[0m (707.66 KB)
[1m Non-trainable params: [0m[38;5;34m0[0m (0.00 B)لا يزال لدينا 181 ألف معامل، رغم أن هذا نموذج "صغير".
batch_size = 32
# initiate RMSprop optimizer
opt = keras.optimizers.RMSprop(learning_rate=0.0005)
# Let's train the model using RMSprop
model_1.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
model_1.fit(x_train, y_train,
batch_size=batch_size,
epochs=15,
validation_data=(x_test, y_test),
shuffle=True)Epoch 1/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m13s[0m 6ms/step - accuracy: 0.3136 - loss: 1.8675 - val_accuracy: 0.4506 - val_loss: 1.5152
Epoch 2/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m6s[0m 4ms/step - accuracy: 0.4739 - loss: 1.4538 - val_accuracy: 0.5385 - val_loss: 1.2852
Epoch 3/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 3ms/step - accuracy: 0.5191 - loss: 1.3548 - val_accuracy: 0.5606 - val_loss: 1.2484
Epoch 4/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m6s[0m 4ms/step - accuracy: 0.5435 - loss: 1.3027 - val_accuracy: 0.5620 - val_loss: 1.2574
Epoch 5/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m6s[0m 4ms/step - accuracy: 0.5592 - loss: 1.2648 - val_accuracy: 0.5616 - val_loss: 1.2314
Epoch 6/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m6s[0m 4ms/step - accuracy: 0.5674 - loss: 1.2424 - val_accuracy: 0.5452 - val_loss: 1.3458
Epoch 7/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m10s[0m 4ms/step - accuracy: 0.5718 - loss: 1.2406 - val_accuracy: 0.5917 - val_loss: 1.1967
Epoch 8/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 4ms/step - accuracy: 0.5748 - loss: 1.2402 - val_accuracy: 0.6114 - val_loss: 1.1200
Epoch 9/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m6s[0m 4ms/step - accuracy: 0.5711 - loss: 1.2464 - val_accuracy: 0.5790 - val_loss: 1.2071
Epoch 10/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m10s[0m 4ms/step - accuracy: 0.5820 - loss: 1.2403 - val_accuracy: 0.4886 - val_loss: 1.5045
Epoch 11/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m6s[0m 4ms/step - accuracy: 0.5753 - loss: 1.2391 - val_accuracy: 0.6053 - val_loss: 1.1649
Epoch 12/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m10s[0m 4ms/step - accuracy: 0.5779 - loss: 1.2530 - val_accuracy: 0.5396 - val_loss: 1.3305
Epoch 13/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m6s[0m 4ms/step - accuracy: 0.5763 - loss: 1.2622 - val_accuracy: 0.5561 - val_loss: 1.2769
Epoch 14/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m6s[0m 4ms/step - accuracy: 0.5788 - loss: 1.2512 - val_accuracy: 0.5679 - val_loss: 1.2698
Epoch 15/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m6s[0m 4ms/step - accuracy: 0.5731 - loss: 1.2575 - val_accuracy: 0.5863 - val_loss: 1.2251
<keras.src.callbacks.history.History at 0x7b0b01452b50>تمرين
كان النموذج السابق يمتلك البنية التالية:
Conv -> Conv -> MaxPool -> (Flatten) -> Dense -> التصنيف النهائي
(مع دوال تفعيل وطبقات إسقاط - Dropout مناسبة)
- ابنِ نموذجًا أكثر تعقيدًا بالنمط التالي:
-
Conv -> Conv -> MaxPool -> Conv -> Conv -> MaxPool -> (Flatten) -> Dense -> التصنيف النهائي
-
استخدم خطوة (Stride) بقيمة 1 لجميع الطبقات الالتفافية.
-
كم عدد المعاملات التي يمتلكها نموذجك؟ كيف تُقارَن بالنموذج السابق؟
-
درّبه لـ5 دورات تدريبية (Epochs). ماذا تلاحظ حول زمن التدريب وأرقام الخسارة والدقة (على كل من مجموعتَي التدريب والتحقق)؟
-
جرّب بُنى وأزمنة تشغيل مختلفة، وانظر إلى أي مدى يمكن أن يصل نموذجك في الدقة.
# Please provide your solution here
# Create model_2 as mentioned in the exerciseتصنيف CIFAR-10 مع زيادة البيانات (Data Augmentation)
تُستخدَم زيادة البيانات (Data augmentation) لتوسيع مجموعة بيانات التدريب اصطناعيًا من خلال إنشاء نسخ معدَّلة من الصور الموجودة.
تساعد هذه التعديلات نموذجك على أن يصبح أكثر متانة، ويعمّم بشكل أفضل، ويتجنّب الإفراط في الملاءمة (Overfitting).
بعد ذلك، سنعود إلى CIFAR-10 والشبكات التي بنيناها سابقًا. سنستخدم زيادة بيانات آنية (Real-time) لمحاولة تحسين نتائجنا.
عندما تنتهي من مراجعة الدفتر (Notebook)، جرّب معاملات زيادة بيانات مختلفة وانظر إن كانت تساعد (أو تضرّ!) بأداء المصنِّف الخاص بك.
from tensorflow.keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
rotation_range=20, # randomly rotate images in the range (degrees, 0 to 180)
width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.1, # randomly shift images vertically (fraction of total height)
horizontal_flip=False, # randomly flip images
shear_range=0.1, # Shear intensity (shear angle in counter-clockwise direction)
zoom_range=0.1, # Zoom in/out on images
fill_mode='nearest' # Strategy to fill in new pixels
)# Let's build a CNN using Keras' Sequential capabilities
model_1 = Sequential()
## 5x5 convolution with 2x2 stride and 32 filters
model_1.add(Conv2D(32, kernel_size = (5, 5), strides = (2,2), padding='same',
input_shape=x_train.shape[1:]))
model_1.add(Activation('relu'))
## Another 5x5 convolution with 2x2 stride and 32 filters
model_1.add(Conv2D(32, (5, 5), strides = (2,2), activation="relu")) # another way to define activation
## 2x2 max pooling reduces to 3 x 3 x 32
model_1.add(MaxPooling2D(pool_size=(2, 2)))
#model_1.add(Dropout(0.25))
## Flatten turns 3x3x32 into 288x1
model_1.add(Flatten())
model_1.add(Dense(512))
model_1.add(Activation('relu'))
#model_1.add(Dropout(0.5))
model_1.add(Dense(num_classes))
model_1.add(Activation('softmax'))
batch_size = 32
# Let's train the model using RMSprop
model_1.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
# Fit the model on the batches generated by datagen.flow().
model_1.fit(
datagen.flow(x_train, y_train, batch_size=batch_size),
epochs=15,
validation_data=(x_test, y_test)
)Epoch 1/15
/usr/local/lib/python3.11/dist-packages/keras/src/trainers/data_adapters/py_dataset_adapter.py:121: UserWarning: Your `PyDataset` class should call `super().__init__(**kwargs)` in its constructor. `**kwargs` can include `workers`, `use_multiprocessing`, `max_queue_size`. Do not pass these arguments to `fit()`, as they will be ignored.
self._warn_if_super_not_called()
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m33s[0m 20ms/step - accuracy: 0.3195 - loss: 1.8580 - val_accuracy: 0.5205 - val_loss: 1.3476
Epoch 2/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m30s[0m 19ms/step - accuracy: 0.4840 - loss: 1.4376 - val_accuracy: 0.5217 - val_loss: 1.3634
Epoch 3/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m30s[0m 19ms/step - accuracy: 0.5274 - loss: 1.3324 - val_accuracy: 0.5750 - val_loss: 1.1765
Epoch 4/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m41s[0m 19ms/step - accuracy: 0.5594 - loss: 1.2471 - val_accuracy: 0.6083 - val_loss: 1.1206
Epoch 5/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m30s[0m 19ms/step - accuracy: 0.5745 - loss: 1.2118 - val_accuracy: 0.6015 - val_loss: 1.1356
Epoch 6/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m29s[0m 19ms/step - accuracy: 0.5858 - loss: 1.1726 - val_accuracy: 0.6154 - val_loss: 1.1121
Epoch 7/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m42s[0m 19ms/step - accuracy: 0.6007 - loss: 1.1575 - val_accuracy: 0.6197 - val_loss: 1.0995
Epoch 8/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m33s[0m 21ms/step - accuracy: 0.6035 - loss: 1.1363 - val_accuracy: 0.6273 - val_loss: 1.0985
Epoch 9/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m38s[0m 19ms/step - accuracy: 0.6074 - loss: 1.1399 - val_accuracy: 0.6604 - val_loss: 0.9989
Epoch 10/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m31s[0m 20ms/step - accuracy: 0.6181 - loss: 1.1066 - val_accuracy: 0.6279 - val_loss: 1.1005
Epoch 11/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m30s[0m 19ms/step - accuracy: 0.6132 - loss: 1.1133 - val_accuracy: 0.6527 - val_loss: 1.0322
Epoch 12/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m31s[0m 20ms/step - accuracy: 0.6201 - loss: 1.1032 - val_accuracy: 0.6485 - val_loss: 1.0506
Epoch 13/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m30s[0m 19ms/step - accuracy: 0.6232 - loss: 1.1023 - val_accuracy: 0.6452 - val_loss: 1.0841
Epoch 14/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m31s[0m 20ms/step - accuracy: 0.6258 - loss: 1.0899 - val_accuracy: 0.6548 - val_loss: 1.0765
Epoch 15/15
[1m1563/1563[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m29s[0m 19ms/step - accuracy: 0.6221 - loss: 1.1078 - val_accuracy: 0.6285 - val_loss: 1.0848
<keras.src.callbacks.history.History at 0x7b0b01450390>كيف يُقارَن الأداء مع التدريب غير المُعزَّز بزيادة البيانات؟
تمرين
دورك الآن
- جرّب أعلاه إعدادات مختلفة لمعاملات زيادة البيانات. هل يمكنك جعل النموذج يؤدي أداءً أفضل؟ هل يمكنك جعله يؤدي أداءً أسوأ؟