ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

تصنيف الصور

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تنزيل دفتر الملاحظات

يوضح هذا البرنامج التعليمي كيفية تصنيف صور الزهور. يقوم بإنشاء مصنف صورة باستخدام نموذج keras.Sequential ، ويحمل البيانات باستخدام preprocessing.image_dataset_from_directory . سوف تكتسب خبرة عملية مع المفاهيم التالية:

  • تحميل مجموعة بيانات من القرص بكفاءة.
  • تحديد التجهيز الزائد وتطبيق تقنيات للتخفيف منها ، بما في ذلك زيادة البيانات والتسرب.

يتبع هذا البرنامج التعليمي سير عمل تعلم الآلة الأساسي:

  1. فحص البيانات وفهمها
  2. بناء خط أنابيب الإدخال
  3. قم ببناء النموذج
  4. درب النموذج
  5. اختبر النموذج
  6. تحسين النموذج وتكرار العملية
pip install -q tf-nightly

استيراد TensorFlow والمكتبات الأخرى

 import matplotlib.pyplot as plt
import numpy as np
import os
import PIL
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
 

تنزيل واستكشاف مجموعة البيانات

يستخدم هذا البرنامج التعليمي مجموعة بيانات تحتوي على حوالي 3700 صورة من الزهور. تحتوي مجموعة البيانات على 5 أدلة فرعية ، واحدة لكل فئة:

 flower_photo/
  daisy/
  dandelion/
  roses/
  sunflowers/
  tulips/
 
 import pathlib
dataset_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz"
data_dir = tf.keras.utils.get_file('flower_photos', origin=dataset_url, untar=True)
data_dir = pathlib.Path(data_dir)
 
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
228818944/228813984 [==============================] - 4s 0us/step

بعد التنزيل ، يجب أن يكون لديك الآن نسخة من مجموعة البيانات المتاحة. يوجد 3،670 صورة كاملة:

 image_count = len(list(data_dir.glob('*/*.jpg')))
print(image_count)
 
3670

إليك بعض الورود:

 roses = list(data_dir.glob('roses/*'))
PIL.Image.open(str(roses[0]))
 

بي إن جي

 PIL.Image.open(str(roses[1]))
 

بي إن جي

وبعض الزنبق:

 tulips = list(data_dir.glob('tulips/*'))
PIL.Image.open(str(tulips[0]))
 

بي إن جي

 PIL.Image.open(str(tulips[1]))
 

بي إن جي

تحميل باستخدام keras.preprocessing

لنقم بتحميل هذه الصور من القرص باستخدام أداة image_dataset_from_directory المفيدة. سينقلك هذا من دليل الصور الموجودة على القرص إلى مجموعة بيانات tf.data.Dataset في tf.data.Dataset فقط من التعليمات البرمجية. إذا كنت ترغب في ذلك ، يمكنك أيضًا كتابة رمز تحميل البيانات الخاص بك من البداية عن طريق زيارة البرنامج التعليمي الخاص بتحميل الصور .

إنشاء مجموعة بيانات

حدد بعض معلمات اللودر:

 batch_size = 32
img_height = 180
img_width = 180
 

من الممارسة السليمة استخدام تقسيم التحقق عند تطوير النموذج الخاص بك. سنستخدم 80٪ من الصور للتدريب و 20٪ للتحقق.

 train_ds = tf.keras.preprocessing.image_dataset_from_directory(
  data_dir,
  validation_split=0.2,
  subset="training",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size)
 
Found 3670 files belonging to 5 classes.
Using 2936 files for training.

 val_ds = tf.keras.preprocessing.image_dataset_from_directory(
  data_dir,
  validation_split=0.2,
  subset="validation",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size)
 
Found 3670 files belonging to 5 classes.
Using 734 files for validation.

يمكنك العثور على أسماء class_names سمة class_names في مجموعات البيانات هذه. تتوافق هذه مع أسماء الدليل حسب الترتيب الأبجدي.

 class_names = train_ds.class_names
print(class_names)
 
['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']

تصور البيانات

فيما يلي أول 9 صور من مجموعة بيانات التدريب.

 import matplotlib.pyplot as plt

plt.figure(figsize=(10, 10))
for images, labels in train_ds.take(1):
  for i in range(9):
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(images[i].numpy().astype("uint8"))
    plt.title(class_names[labels[i]])
    plt.axis("off")
 

بي إن جي

ستقوم بتدريب نموذج باستخدام مجموعات البيانات هذه عن طريق تمريرها إلى model.fit في لحظة. إذا أردت ، يمكنك أيضًا التكرار يدويًا عبر مجموعة البيانات واسترداد مجموعات الصور:

 for image_batch, labels_batch in train_ds:
  print(image_batch.shape)
  print(labels_batch.shape)
  break
 
(32, 180, 180, 3)
(32,)

image_batch هو موتر الشكل (32, 180, 180, 3) . هذه مجموعة من 32 صورة من الشكل 180x180x3 (يشير البعد الأخير إلى قنوات الألوان RGB). إن label_batch عبارة عن موتر الشكل (32,) ، فهذه تسميات مطابقة للصور الـ 32.

يمكنك استدعاء .numpy() على image_batch و labels_batch لتحويلها إلى numpy.ndarray .

تكوين مجموعة البيانات للأداء

دعونا نتأكد من استخدام الجلب المسبق المخزن حتى نتمكن من إنتاج البيانات من القرص دون أن يصبح الإدخال / الإخراج حظرًا. هذان أسلوبان مهمان يجب عليك استخدامهما عند تحميل البيانات.

Dataset.cache() يحافظ على الصور في الذاكرة بعد تحميلها من القرص خلال الحقبة الأولى. سيضمن ذلك عدم تحول مجموعة البيانات إلى عنق الزجاجة أثناء تدريب النموذج الخاص بك. إذا كانت مجموعة البيانات كبيرة جدًا بحيث لا تتناسب مع الذاكرة ، يمكنك أيضًا استخدام هذه الطريقة لإنشاء ذاكرة تخزين مؤقت على القرص.

Dataset.prefetch() المعالجة المسبقة للبيانات وتنفيذ النموذج أثناء التدريب.

يمكن للقراء المهتمين معرفة المزيد حول كلتا الطريقتين ، وكذلك كيفية تخزين البيانات على القرص في ذاكرة التخزين المؤقت في دليل أداء البيانات .

 AUTOTUNE = tf.data.experimental.AUTOTUNE

train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
 

توحيد البيانات

تقع قيم قناة RGB في النطاق [0, 255] . هذه ليست مثالية للشبكة العصبية. بشكل عام يجب أن تسعى لجعل قيم الإدخال الخاصة بك صغيرة. هنا ، سنقوم بتوحيد القيم لتكون في [0, 1] باستخدام طبقة إعادة القياس.

 normalization_layer = layers.experimental.preprocessing.Rescaling(1./255)
 

هناك طريقتان لاستخدام هذه الطبقة. يمكنك تطبيقه على مجموعة البيانات عن طريق الاتصال بالخريطة:

 normalized_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_ds))
first_image = image_batch[0]
# Notice the pixels values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image)) 
 
0.0 1.0

أو يمكنك تضمين الطبقة داخل تعريف النموذج الخاص بك ، والذي يمكن أن يبسط النشر. سنستخدم النهج الثاني هنا.

قم بإنشاء النموذج

يتكون النموذج من ثلاث كتل الالتفاف مع طبقة تجمع الأقصى في كل منها. هناك طبقة متصلة بالكامل مع 128 وحدة فوقها يتم تنشيطها بواسطة وظيفة تنشيط relu . لم يتم ضبط هذا النموذج للحصول على دقة عالية ، والهدف من هذا البرنامج التعليمي هو إظهار نهج قياسي.

 num_classes = 5

model = Sequential([
  layers.experimental.preprocessing.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
  layers.Conv2D(16, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(32, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(64, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Flatten(),
  layers.Dense(128, activation='relu'),
  layers.Dense(num_classes)
])
 

اجمع النموذج

في هذا البرنامج التعليمي ، اختر optimizers.Adam محسّن losses.SparseCategoricalCrossentropy والخسائر. وظيفة الخسارة losses.SparseCategoricalCrossentropy . لعرض دقة التدريب والتحقق من الصحة لكل فترة تدريب ، قم بتمرير وسيطة metrics .

 model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])
 

ملخص نموذج

عرض جميع طبقات الشبكة باستخدام طريقة summary النموذج:

 model.summary()
 
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
rescaling_1 (Rescaling)      (None, 180, 180, 3)       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 180, 180, 16)      448       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 90, 90, 16)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 90, 90, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 45, 45, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 45, 45, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 22, 22, 64)        0         
_________________________________________________________________
flatten (Flatten)            (None, 30976)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               3965056   
_________________________________________________________________
dense_1 (Dense)              (None, 5)                 645       
=================================================================
Total params: 3,989,285
Trainable params: 3,989,285
Non-trainable params: 0
_________________________________________________________________

درب النموذج

 epochs=10
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
 
Epoch 1/10
92/92 [==============================] - 4s 47ms/step - loss: 1.9328 - accuracy: 0.2896 - val_loss: 1.1022 - val_accuracy: 0.5245
Epoch 2/10
92/92 [==============================] - 1s 10ms/step - loss: 1.0441 - accuracy: 0.5761 - val_loss: 1.0057 - val_accuracy: 0.5913
Epoch 3/10
92/92 [==============================] - 1s 10ms/step - loss: 0.8640 - accuracy: 0.6763 - val_loss: 0.8951 - val_accuracy: 0.6499
Epoch 4/10
92/92 [==============================] - 1s 10ms/step - loss: 0.7106 - accuracy: 0.7472 - val_loss: 0.8992 - val_accuracy: 0.6621
Epoch 5/10
92/92 [==============================] - 1s 10ms/step - loss: 0.4817 - accuracy: 0.8285 - val_loss: 0.8997 - val_accuracy: 0.6662
Epoch 6/10
92/92 [==============================] - 1s 10ms/step - loss: 0.3131 - accuracy: 0.8903 - val_loss: 1.0014 - val_accuracy: 0.6567
Epoch 7/10
92/92 [==============================] - 1s 10ms/step - loss: 0.1782 - accuracy: 0.9436 - val_loss: 1.2140 - val_accuracy: 0.6431
Epoch 8/10
92/92 [==============================] - 1s 10ms/step - loss: 0.1024 - accuracy: 0.9703 - val_loss: 1.5144 - val_accuracy: 0.6240
Epoch 9/10
92/92 [==============================] - 1s 10ms/step - loss: 0.0736 - accuracy: 0.9815 - val_loss: 1.7651 - val_accuracy: 0.5926
Epoch 10/10
92/92 [==============================] - 1s 10ms/step - loss: 0.0761 - accuracy: 0.9775 - val_loss: 2.0429 - val_accuracy: 0.5967

تصور نتائج التدريب

إنشاء مؤامرات الخسارة والدقة في مجموعات التدريب والتحقق.

 acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss=history.history['loss']
val_loss=history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
 

بي إن جي

كما ترى من المؤامرات ، فإن دقة التدريب ودقة التحقق معطلة بهامش كبير وقد حقق النموذج دقة حوالي 60٪ فقط في مجموعة التحقق.

دعونا نلقي نظرة على الخطأ الذي حدث ونحاول زيادة الأداء العام للنموذج.

المبالغة

في المخططات أعلاه ، تزداد دقة التدريب بشكل خطي بمرور الوقت ، بينما تتوقف دقة التحقق حول 60٪ في عملية التدريب. أيضا ، الفرق في الدقة بين التدريب ودقة التحقق ملحوظ - علامة على الإفراط في التجهيز .

عندما يكون هناك عدد قليل من أمثلة التدريب ، يتعلم النموذج أحيانًا من الضوضاء أو التفاصيل غير المرغوب فيها من أمثلة التدريب - إلى حد أنه يؤثر سلبًا على أداء النموذج في الأمثلة الجديدة. تُعرف هذه الظاهرة بإفراط. هذا يعني أن النموذج سيواجه وقتًا صعبًا في التعميم على مجموعة بيانات جديدة.

هناك طرق عديدة لمحاربة الإفراط في عملية التدريب. في هذا البرنامج التعليمي ، ستستخدم زيادة البيانات وإضافة Dropout إلى نموذجك.

زيادة البيانات

يحدث الإفراط في التجهيز بشكل عام عندما يكون هناك عدد قليل من أمثلة التدريب. تعتمد زيادة البيانات على نهج توليد بيانات تدريب إضافية من الأمثلة الموجودة لديك عن طريق زيادة ثم تحويلات عشوائية تعطي صورًا قابلة للتصديق. هذا يساعد على عرض النموذج لمزيد من جوانب البيانات والتعميم بشكل أفضل.

سننفذ زيادة البيانات باستخدام طبقات المعالجة الأولية Keras التجريبية. يمكن تضمينها داخل النموذج الخاص بك مثل الطبقات الأخرى ، وتشغيلها على وحدة معالجة الرسومات.

 data_augmentation = keras.Sequential(
  [
    layers.experimental.preprocessing.RandomFlip("horizontal", 
                                                 input_shape=(img_height, 
                                                              img_width,
                                                              3)),
    layers.experimental.preprocessing.RandomRotation(0.1),
    layers.experimental.preprocessing.RandomZoom(0.1),
  ]
)
 

دعونا نتصور كيف تبدو بعض الأمثلة المعززة من خلال تطبيق زيادة البيانات على نفس الصورة عدة مرات:

 plt.figure(figsize=(10, 10))
for images, _ in train_ds.take(1):
  for i in range(9):
    augmented_images = data_augmentation(images)
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(augmented_images[0].numpy().astype("uint8"))
    plt.axis("off")
 

بي إن جي

سنستخدم زيادة البيانات لتدريب نموذج في لحظة.

أوقع

أسلوب آخر لتقليل التجهيز الزائد هو إدخال Dropout في الشبكة ، وهو شكل من أشكال التنظيم .

عندما تقوم بتطبيق Dropout على طبقة ، فإنها تسقط بشكل عشوائي (عن طريق ضبط التنشيط على صفر) عدد وحدات الإخراج من الطبقة أثناء عملية التدريب. يأخذ Dropout عددًا كسريًا كقيمة الإدخال الخاصة به ، في شكل مثل 0.1 ، 0.2 ، 0.4 ، إلخ. وهذا يعني استبعاد 10٪ أو 20٪ أو 40٪ من وحدات الإخراج بشكل عشوائي من الطبقة المطبقة.

لنقم بإنشاء شبكة عصبية جديدة باستخدام layers.Dropout ، ثم قم بتدريبها باستخدام الصور المعززة.

 model = Sequential([
  data_augmentation,
  layers.experimental.preprocessing.Rescaling(1./255),
  layers.Conv2D(16, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(32, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(64, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Dropout(0.2),
  layers.Flatten(),
  layers.Dense(128, activation='relu'),
  layers.Dense(num_classes)
])
 

تجميع وتدريب النموذج

 model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])
 
 model.summary()
 
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
sequential_1 (Sequential)    (None, 180, 180, 3)       0         
_________________________________________________________________
rescaling_2 (Rescaling)      (None, 180, 180, 3)       0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 180, 180, 16)      448       
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 90, 90, 16)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 90, 90, 32)        4640      
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 45, 45, 32)        0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 45, 45, 64)        18496     
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 22, 22, 64)        0         
_________________________________________________________________
dropout (Dropout)            (None, 22, 22, 64)        0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 30976)             0         
_________________________________________________________________
dense_2 (Dense)              (None, 128)               3965056   
_________________________________________________________________
dense_3 (Dense)              (None, 5)                 645       
=================================================================
Total params: 3,989,285
Trainable params: 3,989,285
Non-trainable params: 0
_________________________________________________________________

 epochs = 15
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
 
Epoch 1/15
92/92 [==============================] - 2s 20ms/step - loss: 1.4842 - accuracy: 0.3279 - val_loss: 1.0863 - val_accuracy: 0.5640
Epoch 2/15
92/92 [==============================] - 1s 12ms/step - loss: 1.1215 - accuracy: 0.5284 - val_loss: 1.0374 - val_accuracy: 0.6022
Epoch 3/15
92/92 [==============================] - 1s 12ms/step - loss: 0.9680 - accuracy: 0.6117 - val_loss: 0.9200 - val_accuracy: 0.6485
Epoch 4/15
92/92 [==============================] - 1s 12ms/step - loss: 0.8538 - accuracy: 0.6753 - val_loss: 0.9206 - val_accuracy: 0.6417
Epoch 5/15
92/92 [==============================] - 1s 12ms/step - loss: 0.7744 - accuracy: 0.6977 - val_loss: 0.8169 - val_accuracy: 0.6839
Epoch 6/15
92/92 [==============================] - 1s 13ms/step - loss: 0.7758 - accuracy: 0.7093 - val_loss: 0.7743 - val_accuracy: 0.6880
Epoch 7/15
92/92 [==============================] - 1s 13ms/step - loss: 0.6805 - accuracy: 0.7481 - val_loss: 0.8598 - val_accuracy: 0.6540
Epoch 8/15
92/92 [==============================] - 1s 13ms/step - loss: 0.7132 - accuracy: 0.7278 - val_loss: 0.7177 - val_accuracy: 0.7207
Epoch 9/15
92/92 [==============================] - 1s 13ms/step - loss: 0.6634 - accuracy: 0.7580 - val_loss: 0.7152 - val_accuracy: 0.7166
Epoch 10/15
92/92 [==============================] - 1s 13ms/step - loss: 0.6562 - accuracy: 0.7538 - val_loss: 0.7251 - val_accuracy: 0.7248
Epoch 11/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5798 - accuracy: 0.7840 - val_loss: 0.7016 - val_accuracy: 0.7357
Epoch 12/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5635 - accuracy: 0.7913 - val_loss: 0.7755 - val_accuracy: 0.7248
Epoch 13/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5361 - accuracy: 0.7982 - val_loss: 0.7575 - val_accuracy: 0.7153
Epoch 14/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5420 - accuracy: 0.8022 - val_loss: 0.7375 - val_accuracy: 0.7302
Epoch 15/15
92/92 [==============================] - 1s 12ms/step - loss: 0.5132 - accuracy: 0.8120 - val_loss: 0.7561 - val_accuracy: 0.7289

تصور نتائج التدريب

بعد تطبيق زيادة البيانات و Dropout ، يكون هناك تجهيز مفرط أقل من ذي قبل ، ودقة التدريب والتحقق أقرب إلى التوافق.

 acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
 

بي إن جي

التنبؤ بالبيانات الجديدة

أخيرًا ، لنستخدم نموذجنا لتصنيف صورة لم يتم تضمينها في مجموعات التدريب أو التحقق.

 sunflower_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/592px-Red_sunflower.jpg"
sunflower_path = tf.keras.utils.get_file('Red_sunflower', origin=sunflower_url)

img = keras.preprocessing.image.load_img(
    sunflower_path, target_size=(img_height, img_width)
)
img_array = keras.preprocessing.image.img_to_array(img)
img_array = tf.expand_dims(img_array, 0) # Create a batch

predictions = model.predict(img_array)
score = tf.nn.softmax(predictions[0])

print(
    "This image most likely belongs to {} with a {:.2f} percent confidence."
    .format(class_names[np.argmax(score)], 100 * np.max(score))
)
 
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/592px-Red_sunflower.jpg
122880/117948 [===============================] - 0s 0us/step
This image most likely belongs to sunflowers with a 89.39 percent confidence.