احفظ التاريخ! يعود مؤتمر Google I / O من 18 إلى 20 مايو. سجل الآن
ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

تصنيف الصورة

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

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

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

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

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

استيراد 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 [==============================] - 2s 0us/step

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

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.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 0.9407408

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

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

يتكون النموذج من ثلاث كتل التفاف مع طبقة تجمع قصوى في كل منها. هناك طبقة متصلة بالكامل مع 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 وظيفة الخسارة. لعرض دقة التدريب والتحقق من الصحة لكل فترة تدريب ، قم بتمرير وسيطة metrics .

02942 تغذية ب 0

ملخص نموذج

اعرض جميع طبقات الشبكة باستخدام طريقة 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 [==============================] - 5s 27ms/step - loss: 1.7103 - accuracy: 0.2977 - val_loss: 1.2054 - val_accuracy: 0.4986
Epoch 2/10
92/92 [==============================] - 1s 11ms/step - loss: 1.0959 - accuracy: 0.5544 - val_loss: 1.0688 - val_accuracy: 0.5763
Epoch 3/10
92/92 [==============================] - 1s 11ms/step - loss: 0.9331 - accuracy: 0.6329 - val_loss: 0.9369 - val_accuracy: 0.6267
Epoch 4/10
92/92 [==============================] - 1s 11ms/step - loss: 0.8064 - accuracy: 0.6951 - val_loss: 0.9546 - val_accuracy: 0.6335
Epoch 5/10
92/92 [==============================] - 1s 11ms/step - loss: 0.5643 - accuracy: 0.7933 - val_loss: 0.9191 - val_accuracy: 0.6444
Epoch 6/10
92/92 [==============================] - 1s 11ms/step - loss: 0.3910 - accuracy: 0.8717 - val_loss: 1.2451 - val_accuracy: 0.5954
Epoch 7/10
92/92 [==============================] - 1s 11ms/step - loss: 0.2849 - accuracy: 0.9094 - val_loss: 1.2454 - val_accuracy: 0.6335
Epoch 8/10
92/92 [==============================] - 1s 11ms/step - loss: 0.1562 - accuracy: 0.9567 - val_loss: 1.2909 - val_accuracy: 0.6322
Epoch 9/10
92/92 [==============================] - 1s 11ms/step - loss: 0.1154 - accuracy: 0.9622 - val_loss: 1.4424 - val_accuracy: 0.6322
Epoch 10/10
92/92 [==============================] - 1s 11ms/step - loss: 0.0649 - accuracy: 0.9866 - val_loss: 1.6675 - val_accuracy: 0.6308

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

إنشاء قطع من الضياع والدقة على التدريب ومجموعات التحقق من الصحة.

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 ٪ تقريبًا في عملية التدريب. كما أن الفرق في الدقة بين التدريب ودقة التحقق من الصحة ملحوظ - علامة على فرط التجهيز .

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

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

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

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

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

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 على طبقة ، فإنها تسقط عشوائيًا (عن طريق ضبط التنشيط على الصفر) عددًا من وحدات الإخراج من الطبقة أثناء عملية التدريب. يأخذ التسرب رقمًا كسريًا كقيمة إدخال ، في شكل مثل 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 13ms/step - loss: 1.5399 - accuracy: 0.3400 - val_loss: 1.2366 - val_accuracy: 0.4986
Epoch 2/15
92/92 [==============================] - 1s 11ms/step - loss: 1.0382 - accuracy: 0.5900 - val_loss: 0.9774 - val_accuracy: 0.6090
Epoch 3/15
92/92 [==============================] - 1s 11ms/step - loss: 0.9348 - accuracy: 0.6472 - val_loss: 0.9294 - val_accuracy: 0.6362
Epoch 4/15
92/92 [==============================] - 1s 11ms/step - loss: 0.8891 - accuracy: 0.6486 - val_loss: 0.9018 - val_accuracy: 0.6621
Epoch 5/15
92/92 [==============================] - 1s 11ms/step - loss: 0.8445 - accuracy: 0.6727 - val_loss: 0.9248 - val_accuracy: 0.6485
Epoch 6/15
92/92 [==============================] - 1s 11ms/step - loss: 0.8139 - accuracy: 0.6912 - val_loss: 0.8845 - val_accuracy: 0.6485
Epoch 7/15
92/92 [==============================] - 1s 11ms/step - loss: 0.7541 - accuracy: 0.7093 - val_loss: 0.7796 - val_accuracy: 0.6921
Epoch 8/15
92/92 [==============================] - 1s 11ms/step - loss: 0.7094 - accuracy: 0.7369 - val_loss: 0.7921 - val_accuracy: 0.6757
Epoch 9/15
92/92 [==============================] - 1s 11ms/step - loss: 0.6755 - accuracy: 0.7444 - val_loss: 0.8087 - val_accuracy: 0.6880
Epoch 10/15
92/92 [==============================] - 1s 11ms/step - loss: 0.6329 - accuracy: 0.7627 - val_loss: 0.7770 - val_accuracy: 0.7016
Epoch 11/15
92/92 [==============================] - 1s 11ms/step - loss: 0.6456 - accuracy: 0.7634 - val_loss: 0.7409 - val_accuracy: 0.7180
Epoch 12/15
92/92 [==============================] - 1s 11ms/step - loss: 0.5807 - accuracy: 0.7889 - val_loss: 0.7696 - val_accuracy: 0.7098
Epoch 13/15
92/92 [==============================] - 1s 11ms/step - loss: 0.5716 - accuracy: 0.7772 - val_loss: 0.7634 - val_accuracy: 0.6989
Epoch 14/15
92/92 [==============================] - 1s 11ms/step - loss: 0.5665 - accuracy: 0.7859 - val_loss: 0.7610 - val_accuracy: 0.7112
Epoch 15/15
92/92 [==============================] - 1s 11ms/step - loss: 0.5303 - accuracy: 0.7959 - val_loss: 0.7306 - val_accuracy: 0.7371

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

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

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 97.38 percent confidence.