Logo

بناء شبكة CNN لتصنيف الصور في مجموعة بيانات CIFAR-10

10 دقائق قراءة
شرائح الدرس
1 / 14

بناء شبكة عصبية التفافية (CNN) لمجموعة بيانات CIFAR-10

الطبقات الالتفافية، شبكة عصبية التفافية أولى، وتحسينها بزيادة البيانات (Data Augmentation)

بناء شبكة CNN لتصنيف الصور في مجموعة بيانات CIFAR-10

سنعمل مع مجموعة بيانات CIFAR-10. هذه مجموعة بيانات معروفة لتصنيف الصور، تتكوّن من 60000 صورة ملوّنة بحجم 32×32 موزَّعة على 10 فئات، بواقع 6000 صورة لكل فئة. توجد 50000 صورة تدريب و10000 صورة اختبار.

الفئات العشر هي:

  1. طائرة (airplane)
  2. سيارة (automobile)
  3. طائر (bird)
  4. قطة (cat)
  5. غزال (deer)
  6. كلب (dog)
  7. ضفدع (frog)
  8. حصان (horse)
  9. سفينة (ship)
  10. شاحنة (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)
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ conv2d (Conv2D)                 │ (None, 16, 16, 32)     │         2,432 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation (Activation)         │ (None, 16, 16, 32)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_1 (Conv2D)               │ (None, 6, 6, 32)       │        25,632 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d (MaxPooling2D)    │ (None, 3, 3, 32)       │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout (Dropout)               │ (None, 3, 3, 32)       │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ flatten (Flatten)               │ (None, 288)            │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense)                   │ (None, 512)            │       147,968 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_1 (Activation)       │ (None, 512)            │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_1 (Dropout)             │ (None, 512)            │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 10)             │         5,130 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_2 (Activation)       │ (None, 10)             │             0 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 181,162 (707.66 KB)
 Trainable params: 181,162 (707.66 KB)
 Non-trainable params: 0 (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
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 13s 6ms/step - accuracy: 0.3136 - loss: 1.8675 - val_accuracy: 0.4506 - val_loss: 1.5152
Epoch 2/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.4739 - loss: 1.4538 - val_accuracy: 0.5385 - val_loss: 1.2852
Epoch 3/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 5s 3ms/step - accuracy: 0.5191 - loss: 1.3548 - val_accuracy: 0.5606 - val_loss: 1.2484
Epoch 4/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.5435 - loss: 1.3027 - val_accuracy: 0.5620 - val_loss: 1.2574
Epoch 5/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.5592 - loss: 1.2648 - val_accuracy: 0.5616 - val_loss: 1.2314
Epoch 6/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.5674 - loss: 1.2424 - val_accuracy: 0.5452 - val_loss: 1.3458
Epoch 7/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 10s 4ms/step - accuracy: 0.5718 - loss: 1.2406 - val_accuracy: 0.5917 - val_loss: 1.1967
Epoch 8/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 5s 4ms/step - accuracy: 0.5748 - loss: 1.2402 - val_accuracy: 0.6114 - val_loss: 1.1200
Epoch 9/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.5711 - loss: 1.2464 - val_accuracy: 0.5790 - val_loss: 1.2071
Epoch 10/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 10s 4ms/step - accuracy: 0.5820 - loss: 1.2403 - val_accuracy: 0.4886 - val_loss: 1.5045
Epoch 11/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.5753 - loss: 1.2391 - val_accuracy: 0.6053 - val_loss: 1.1649
Epoch 12/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 10s 4ms/step - accuracy: 0.5779 - loss: 1.2530 - val_accuracy: 0.5396 - val_loss: 1.3305
Epoch 13/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.5763 - loss: 1.2622 - val_accuracy: 0.5561 - val_loss: 1.2769
Epoch 14/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 6s 4ms/step - accuracy: 0.5788 - loss: 1.2512 - val_accuracy: 0.5679 - val_loss: 1.2698
Epoch 15/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 6s 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 مناسبة)

  1. ابنِ نموذجًا أكثر تعقيدًا بالنمط التالي:
  • Conv -> Conv -> MaxPool -> Conv -> Conv -> MaxPool -> (Flatten) -> Dense -> التصنيف النهائي

  • استخدم خطوة (Stride) بقيمة 1 لجميع الطبقات الالتفافية.

  1. كم عدد المعاملات التي يمتلكها نموذجك؟ كيف تُقارَن بالنموذج السابق؟

  2. درّبه لـ5 دورات تدريبية (Epochs). ماذا تلاحظ حول زمن التدريب وأرقام الخسارة والدقة (على كل من مجموعتَي التدريب والتحقق)؟

  3. جرّب بُنى وأزمنة تشغيل مختلفة، وانظر إلى أي مدى يمكن أن يصل نموذجك في الدقة.

# 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()
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 33s 20ms/step - accuracy: 0.3195 - loss: 1.8580 - val_accuracy: 0.5205 - val_loss: 1.3476
Epoch 2/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 30s 19ms/step - accuracy: 0.4840 - loss: 1.4376 - val_accuracy: 0.5217 - val_loss: 1.3634
Epoch 3/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 30s 19ms/step - accuracy: 0.5274 - loss: 1.3324 - val_accuracy: 0.5750 - val_loss: 1.1765
Epoch 4/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 41s 19ms/step - accuracy: 0.5594 - loss: 1.2471 - val_accuracy: 0.6083 - val_loss: 1.1206
Epoch 5/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 30s 19ms/step - accuracy: 0.5745 - loss: 1.2118 - val_accuracy: 0.6015 - val_loss: 1.1356
Epoch 6/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 29s 19ms/step - accuracy: 0.5858 - loss: 1.1726 - val_accuracy: 0.6154 - val_loss: 1.1121
Epoch 7/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 42s 19ms/step - accuracy: 0.6007 - loss: 1.1575 - val_accuracy: 0.6197 - val_loss: 1.0995
Epoch 8/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 33s 21ms/step - accuracy: 0.6035 - loss: 1.1363 - val_accuracy: 0.6273 - val_loss: 1.0985
Epoch 9/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 38s 19ms/step - accuracy: 0.6074 - loss: 1.1399 - val_accuracy: 0.6604 - val_loss: 0.9989
Epoch 10/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 31s 20ms/step - accuracy: 0.6181 - loss: 1.1066 - val_accuracy: 0.6279 - val_loss: 1.1005
Epoch 11/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 30s 19ms/step - accuracy: 0.6132 - loss: 1.1133 - val_accuracy: 0.6527 - val_loss: 1.0322
Epoch 12/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 31s 20ms/step - accuracy: 0.6201 - loss: 1.1032 - val_accuracy: 0.6485 - val_loss: 1.0506
Epoch 13/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 30s 19ms/step - accuracy: 0.6232 - loss: 1.1023 - val_accuracy: 0.6452 - val_loss: 1.0841
Epoch 14/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 31s 20ms/step - accuracy: 0.6258 - loss: 1.0899 - val_accuracy: 0.6548 - val_loss: 1.0765
Epoch 15/15
1563/1563 ━━━━━━━━━━━━━━━━━━━━ 29s 19ms/step - accuracy: 0.6221 - loss: 1.1078 - val_accuracy: 0.6285 - val_loss: 1.0848
<keras.src.callbacks.history.History at 0x7b0b01450390>

كيف يُقارَن الأداء مع التدريب غير المُعزَّز بزيادة البيانات؟

تمرين

دورك الآن

  1. جرّب أعلاه إعدادات مختلفة لمعاملات زيادة البيانات. هل يمكنك جعل النموذج يؤدي أداءً أفضل؟ هل يمكنك جعله يؤدي أداءً أسوأ؟