דף זה תורגם על ידי Cloud Translation API.
Switch to English

סיווג תמונות

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub הורד מחברת

מדריך זה מראה כיצד לסווג תמונות של פרחים. זה יוצר מסווג תמונות באמצעות מודל keras.Sequential , וטוען נתונים באמצעות preprocessing.image_dataset_from_directory . תוכלו לרכוש ניסיון מעשי עם המושגים הבאים:

  • טעינה יעילה של מערך נתונים מהדיסק.
  • זיהוי התאמת יתר ויישום טכניקות למיתון, כולל הגדלת נתונים ונשירה.

מדריך זה עוקב אחר זרימת עבודה בסיסית של למידת מכונה:

  1. לבחון ולהבין נתונים
  2. בנה צינור קלט
  3. בנה את המודל
  4. תאמן את המודל
  5. בדוק את המודל
  6. שפר את המודל וחזור על התהליך
pip install -q tf-nightly
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the '/tmpfs/src/tf_docs_env/bin/python -m pip install --upgrade pip' command.

ייבא את 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

הורד וחקור את מערך הנתונים

הדרכה זו משתמשת במערך של כ -3,700 תמונות פרחים. מערך הנתונים מכיל 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 [==============================] - 1s 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]))

png

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

png

וכמה צבעונים:

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

png

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

png

טען באמצעות keras.preprocessing

בואו לטעון את התמונות האלה מהדיסק באמצעות כלי השירות המועיל image_dataset_from_directory . זה ייקח אותך מספריית תמונות בדיסק ל- 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 = 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")

png

תוכלו לאמן מודל באמצעות מערכי הנתונים הללו על ידי model.fit לרגע ל- 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 (הממד האחרון 180x180x3 לערוצי צבע RGB). label_batch הוא טנסור של הצורה (32,) , אלה תוויות תואמות ל- 32 התמונות.

אתה יכול להתקשר ל- .numpy() image_batch labels_batch ו- labels_batch כדי להמיר אותם ל- numpy.ndarray .

הגדר את מערך הנתונים לביצועים

בואו נדאג להשתמש באיסוף מקדים שנאגר על מנת שנוכל להניב נתונים מהדיסק מבלי ש- I / O יהפוך לחסימה. אלה שתי שיטות חשובות שעליך להשתמש בהן בעת ​​טעינת נתונים.

Dataset.cache() שומר את התמונות בזיכרון לאחר 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 על ידי פונקציית הפעלת 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)
])

הידר את המודל

להדרכה זו בחר losses.SparseCategoricalCrossentropy optimizers.Adam אופטימיזציה של losses.SparseCategoricalCrossentropy והפסדים. 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 [==============================] - 28s 295ms/step - loss: 1.5722 - accuracy: 0.3664 - val_loss: 1.0810 - val_accuracy: 0.5477
Epoch 2/10
92/92 [==============================] - 27s 289ms/step - loss: 1.0409 - accuracy: 0.5835 - val_loss: 1.0014 - val_accuracy: 0.5954
Epoch 3/10
92/92 [==============================] - 26s 287ms/step - loss: 0.8835 - accuracy: 0.6517 - val_loss: 0.9068 - val_accuracy: 0.6281
Epoch 4/10
92/92 [==============================] - 27s 298ms/step - loss: 0.7079 - accuracy: 0.7252 - val_loss: 0.9219 - val_accuracy: 0.6471
Epoch 5/10
92/92 [==============================] - 27s 290ms/step - loss: 0.5099 - accuracy: 0.8101 - val_loss: 0.9138 - val_accuracy: 0.6526
Epoch 6/10
92/92 [==============================] - 27s 292ms/step - loss: 0.3452 - accuracy: 0.8870 - val_loss: 0.9905 - val_accuracy: 0.6703
Epoch 7/10
92/92 [==============================] - 27s 295ms/step - loss: 0.1839 - accuracy: 0.9490 - val_loss: 1.2020 - val_accuracy: 0.6322
Epoch 8/10
92/92 [==============================] - 27s 289ms/step - loss: 0.1096 - accuracy: 0.9673 - val_loss: 1.4612 - val_accuracy: 0.6308
Epoch 9/10
92/92 [==============================] - 27s 289ms/step - loss: 0.0611 - accuracy: 0.9846 - val_loss: 1.6569 - val_accuracy: 0.6403
Epoch 10/10
92/92 [==============================] - 26s 286ms/step - loss: 0.0679 - accuracy: 0.9799 - val_loss: 1.8237 - val_accuracy: 0.6199

דמיין את תוצאות האימונים

צור מגרשים של אובדן ודיוק בערכות האימון והאימות.

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()

png

כפי שניתן לראות מהעלילות, דיוק האימונים ודיוק האימות אינם מופעלים בהפרש גדול והמודל השיג רק דיוק של כ 60% בערכת האימות.

בואו נבדוק מה השתבש וננסה להגדיל את הביצועים הכוללים של הדגם.

התאמת יתר

בעלילות לעיל, דיוק האימונים גדל באופן ליניארי לאורך זמן, ואילו דיוק האימות עומד בסביבות 60% בתהליך האימון. כמו כן, בולט ההבדל ברמת הדיוק בין אימון לדיוק אימות - סימן להתאמת יתר .

כשיש מספר קטן של דוגמאות אימונים, המודל לומד לפעמים מרעשים או פרטים לא רצויים מדוגמאות אימונים - במידה שהוא משפיע לרעה על ביצועי המודל על דוגמאות חדשות. תופעה זו ידועה בשם התאמת יתר. המשמעות היא שהמודל יתקשה להכליל את מערך הנתונים החדש.

ישנן דרכים רבות להילחם בהתאמת יתר בתהליך האימון. במדריך זה, תשתמש בהגדלת נתונים ותוסיף נשירה למודל שלך.

הגדלת נתונים

התאמת יתר מתרחשת בדרך כלל כשיש מספר קטן של דוגמאות אימונים. הגדלת נתונים נוקטת את הגישה של יצירת נתוני אימון נוספים מהדוגמאות הקיימות שלך על ידי הגדלה ואז שימוש בתמורות אקראיות שמניבות תמונות אמינות למראה. זה עוזר לחשוף את המודל להיבטים נוספים של הנתונים ולהכליל טוב יותר.

אנו ניישם הגדלת נתונים באמצעות שכבות ניסוי Keras ניסיוניות. אלה יכולים להיכלל בתוך המודל שלך כמו שכבות אחרות ולהפעיל אותם ב- GPU.

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")

png

נשתמש בהגדלת נתונים כדי להכשיר מודל תוך רגע.

נשר

טכניקה נוספת להפחתת התאמת יתר היא הצגת Dropout לרשת, סוג של רגולציה .

כאשר אתה מחיל נשירה על שכבה הוא נשמט באופן אקראי (על ידי הגדרת ההפעלה לאפס) מספר יחידות פלט מהשכבה במהלך תהליך האימון. נשירה לוקחת מספר חלקי כערך הקלט שלו, בצורה כמו 0.1, 0.2, 0.4 וכו '. משמעות הדבר היא נשירה של 10%, 20% או 40% מיחידות הפלט באופן אקראי מהשכבה שהוחלה.

בואו ניצור רשת עצבית חדשה באמצעות layers.Dropout , ואז layers.Dropout אותה באמצעות תמונות 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 [==============================] - 30s 324ms/step - loss: 1.5318 - accuracy: 0.2934 - val_loss: 1.1438 - val_accuracy: 0.5245
Epoch 2/15
92/92 [==============================] - 30s 322ms/step - loss: 1.0707 - accuracy: 0.5490 - val_loss: 1.0091 - val_accuracy: 0.6104
Epoch 3/15
92/92 [==============================] - 30s 331ms/step - loss: 0.9404 - accuracy: 0.6322 - val_loss: 0.9887 - val_accuracy: 0.6281
Epoch 4/15
92/92 [==============================] - 30s 327ms/step - loss: 0.8895 - accuracy: 0.6538 - val_loss: 0.8148 - val_accuracy: 0.6717
Epoch 5/15
92/92 [==============================] - 31s 337ms/step - loss: 0.8084 - accuracy: 0.6749 - val_loss: 0.9402 - val_accuracy: 0.6649
Epoch 6/15
92/92 [==============================] - 30s 327ms/step - loss: 0.7779 - accuracy: 0.7044 - val_loss: 1.0416 - val_accuracy: 0.6253
Epoch 7/15
92/92 [==============================] - 30s 324ms/step - loss: 0.7517 - accuracy: 0.7207 - val_loss: 0.8159 - val_accuracy: 0.6948
Epoch 8/15
92/92 [==============================] - 30s 326ms/step - loss: 0.7096 - accuracy: 0.7305 - val_loss: 0.8161 - val_accuracy: 0.7016
Epoch 9/15
92/92 [==============================] - 30s 329ms/step - loss: 0.7199 - accuracy: 0.7166 - val_loss: 0.7851 - val_accuracy: 0.7030
Epoch 10/15
92/92 [==============================] - 30s 327ms/step - loss: 0.6464 - accuracy: 0.7562 - val_loss: 0.7606 - val_accuracy: 0.7180
Epoch 11/15
92/92 [==============================] - 29s 319ms/step - loss: 0.6099 - accuracy: 0.7694 - val_loss: 0.8871 - val_accuracy: 0.6866
Epoch 12/15
92/92 [==============================] - 30s 325ms/step - loss: 0.6185 - accuracy: 0.7629 - val_loss: 0.7661 - val_accuracy: 0.7016
Epoch 13/15
92/92 [==============================] - 30s 324ms/step - loss: 0.5980 - accuracy: 0.7751 - val_loss: 0.7051 - val_accuracy: 0.7262
Epoch 14/15
92/92 [==============================] - 30s 322ms/step - loss: 0.5628 - accuracy: 0.7928 - val_loss: 0.8075 - val_accuracy: 0.6921
Epoch 15/15
92/92 [==============================] - 30s 322ms/step - loss: 0.5702 - accuracy: 0.7811 - val_loss: 0.8650 - val_accuracy: 0.6935

דמיין את תוצאות האימונים

לאחר החלת הגדלת נתונים ונשירה, יש פחות התאמה מבעבר, ודיוק האימונים והאימות מתואמים יותר.

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()

png

חיזוי נתונים חדשים

לבסוף, בואו נשתמש במודל שלנו כדי לסווג תמונה שלא נכללה בערכות ההדרכה או האימות.

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