הצג באתר TensorFlow.org | הפעל בגוגל קולאב | צפה במקור ב-GitHub | הורד מחברת |
מדריך זה מראה כיצד לסווג תמונות של פרחים. הוא יוצר מסיווג תמונה באמצעות מודל tf.keras.Sequential
, וטוען נתונים באמצעות tf.keras.utils.image_dataset_from_directory
. אתה תרכוש ניסיון מעשי עם המושגים הבאים:
- טעינה יעילה של מערך נתונים מחוץ לדיסק.
- זיהוי התאמת יתר ויישום טכניקות להפחתה, כולל הגדלת נתונים ונשירה.
מדריך זה עוקב אחר זרימת עבודה בסיסית של למידת מכונה:
- לבחון ולהבין נתונים
- בניית צינור קלט
- בנה את הדגם
- אימון הדגם
- בדוק את הדגם
- שפר את המודל וחזור על התהליך
ייבוא 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 תמונות של פרחים. מערך הנתונים מכיל חמש ספריות משנה, אחת לכל מחלקה:
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)
לאחר ההורדה, כעת אמור להיות לך עותק של מערך הנתונים זמין. יש 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
בוא נטען את התמונות האלה מהדיסק באמצעות כלי השירות המועיל tf.keras.utils.image_dataset_from_directory
. זה יעביר אותך מספריית תמונות בדיסק ל- tf.data.Dataset
בכמה שורות קוד בלבד. אם תרצה, תוכל גם לכתוב את קוד טעינת הנתונים שלך מאפס על ידי ביקור במדריך הטעינה ועיבוד מקדים של תמונות .
צור מערך נתונים
הגדר כמה פרמטרים עבור המטען:
batch_size = 32
img_height = 180
img_width = 180
מומלץ להשתמש בפיצול אימות בעת פיתוח המודל שלך. בואו נשתמש ב-80% מהתמונות לאימון, ו-20% לאימות.
train_ds = tf.keras.utils.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.utils.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']
דמיינו את הנתונים
להלן תשע התמונות הראשונות מתוך מערך ההדרכה:
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
image_batch ו- labels_batch
כדי להמיר אותם ל- numpy.ndarray
.
הגדר את מערך הנתונים לביצועים
בוא נוודא להשתמש באחזור מראש מאוחסן כדי שתוכל להפיק נתונים מהדיסק מבלי שהקלט/פלט ייחסם. אלו הן שתי שיטות חשובות שבהן עליך להשתמש בעת טעינת נתונים:
-
Dataset.cache
שומר את התמונות בזיכרון לאחר טעינתן מהדיסק במהלך התקופה הראשונה. זה יבטיח שמערך הנתונים לא יהפוך לצוואר בקבוק בזמן אימון המודל שלך. אם מערך הנתונים שלך גדול מכדי להתאים לזיכרון, אתה יכול גם להשתמש בשיטה זו כדי ליצור מטמון בעל ביצועים בדיסק. -
Dataset.prefetch
חופף לעיבוד מקדים של נתונים וביצוע מודלים תוך כדי אימון.
קוראים מעוניינים יכולים ללמוד עוד על שתי השיטות, כמו גם כיצד לשמר נתונים בדיסק בסעיף 'אחזור מראש ' של מדריך ביצועים טובים יותר עם tf.data API .
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]
באמצעות tf.keras.layers.Rescaling
:
normalization_layer = layers.Rescaling(1./255)
ישנן שתי דרכים להשתמש בשכבה זו. אתה יכול להחיל אותו על מערך הנתונים על ידי קריאה Dataset.map
:
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 pixel values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image))
0.0 1.0
לחלופין, אתה יכול לכלול את השכבה בתוך הגדרת המודל שלך, מה שיכול לפשט את הפריסה. בואו נשתמש בגישה השנייה כאן.
צור את הדגם
המודל Sequential מורכב משלושה בלוקים של קונבולציה ( tf.keras.layers.Conv2D
) עם שכבת בריכה מקסימלית ( tf.keras.layers.MaxPooling2D
) בכל אחד מהם. יש שכבה מחוברת לחלוטין ( tf.keras.layers.Dense
) עם 128 יחידות מעליה שמופעלת על ידי פונקציית הפעלה של ReLU ( 'relu'
). מודל זה לא כוונן לרמת דיוק גבוהה - המטרה של הדרכה זו היא להראות גישה סטנדרטית.
num_classes = len(class_names)
model = Sequential([
layers.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)
])
הרכיב את המודל
עבור הדרכה זו, בחר את פונקציית האובדן tf.keras.optimizers.Adam
ו- tf.keras.losses.SparseCategoricalCrossentropy
. כדי להציג את דיוק ההדרכה והאימות עבור כל עידן אימון, העבר את ארגומנט metrics
אל Model.compile
.
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
תקציר הדגם
הצג את כל שכבות הרשת בשיטת Model.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 (MaxPooling (None, 45, 45, 32) 0 2D) conv2d_2 (Conv2D) (None, 45, 45, 64) 18496 max_pooling2d_2 (MaxPooling (None, 22, 22, 64) 0 2D) 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 [==============================] - 3s 16ms/step - loss: 1.2769 - accuracy: 0.4489 - val_loss: 1.0457 - val_accuracy: 0.5804 Epoch 2/10 92/92 [==============================] - 1s 11ms/step - loss: 0.9386 - accuracy: 0.6328 - val_loss: 0.9665 - val_accuracy: 0.6158 Epoch 3/10 92/92 [==============================] - 1s 11ms/step - loss: 0.7390 - accuracy: 0.7200 - val_loss: 0.8768 - val_accuracy: 0.6540 Epoch 4/10 92/92 [==============================] - 1s 11ms/step - loss: 0.5649 - accuracy: 0.7963 - val_loss: 0.9258 - val_accuracy: 0.6540 Epoch 5/10 92/92 [==============================] - 1s 11ms/step - loss: 0.3662 - accuracy: 0.8733 - val_loss: 1.1734 - val_accuracy: 0.6267 Epoch 6/10 92/92 [==============================] - 1s 11ms/step - loss: 0.2169 - accuracy: 0.9343 - val_loss: 1.3728 - val_accuracy: 0.6499 Epoch 7/10 92/92 [==============================] - 1s 11ms/step - loss: 0.1191 - accuracy: 0.9629 - val_loss: 1.3791 - val_accuracy: 0.6471 Epoch 8/10 92/92 [==============================] - 1s 11ms/step - loss: 0.0497 - accuracy: 0.9871 - val_loss: 1.8002 - val_accuracy: 0.6390 Epoch 9/10 92/92 [==============================] - 1s 11ms/step - loss: 0.0372 - accuracy: 0.9922 - val_loss: 1.8545 - val_accuracy: 0.6390 Epoch 10/10 92/92 [==============================] - 1s 11ms/step - loss: 0.0715 - accuracy: 0.9813 - val_loss: 2.0656 - val_accuracy: 0.6049
דמיין את תוצאות האימון
צור עלילות של אובדן ודיוק על ערכות ההדרכה והאימות:
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 הבאות: tf.keras.layers.RandomFlip
, tf.keras.layers.RandomRotation
ו- tf.keras.layers.RandomZoom
. אלה יכולים להיכלל בתוך הדגם שלך כמו שכבות אחרות, ולהפעיל על ה-GPU.
data_augmentation = keras.Sequential(
[
layers.RandomFlip("horizontal",
input_shape=(img_height,
img_width,
3)),
layers.RandomRotation(0.1),
layers.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")
אתה תשתמש בהגדלת נתונים כדי להכשיר מודל בעוד רגע.
נשר
טכניקה נוספת להפחתת התאמת יתר היא הכנסת הסדרת נשירה לרשת.
כאשר אתה מחיל נשירה על שכבה, היא נושרת באופן אקראי (על ידי הגדרת ההפעלה לאפס) מספר יחידות פלט מהשכבה במהלך תהליך האימון. נשירה לוקחת מספר חלקי כערך הקלט שלו, בצורה כמו 0.1, 0.2, 0.4 וכו'. משמעות הדבר היא נשירה של 10%, 20% או 40% מיחידות הפלט באופן אקראי מהשכבה המיושמת.
בואו ניצור רשת עצבית חדשה עם tf.keras.layers.Dropout
לפני אימון באמצעות התמונות המוגדלות:
model = Sequential([
data_augmentation,
layers.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 (MaxPooling (None, 90, 90, 16) 0 2D) conv2d_4 (Conv2D) (None, 90, 90, 32) 4640 max_pooling2d_4 (MaxPooling (None, 45, 45, 32) 0 2D) conv2d_5 (Conv2D) (None, 45, 45, 64) 18496 max_pooling2d_5 (MaxPooling (None, 22, 22, 64) 0 2D) 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 14ms/step - loss: 1.3840 - accuracy: 0.3999 - val_loss: 1.0967 - val_accuracy: 0.5518 Epoch 2/15 92/92 [==============================] - 1s 12ms/step - loss: 1.1152 - accuracy: 0.5395 - val_loss: 1.1123 - val_accuracy: 0.5545 Epoch 3/15 92/92 [==============================] - 1s 12ms/step - loss: 1.0049 - accuracy: 0.6052 - val_loss: 0.9544 - val_accuracy: 0.6253 Epoch 4/15 92/92 [==============================] - 1s 12ms/step - loss: 0.9452 - accuracy: 0.6257 - val_loss: 0.9681 - val_accuracy: 0.6213 Epoch 5/15 92/92 [==============================] - 1s 12ms/step - loss: 0.8804 - accuracy: 0.6591 - val_loss: 0.8450 - val_accuracy: 0.6798 Epoch 6/15 92/92 [==============================] - 1s 12ms/step - loss: 0.8001 - accuracy: 0.6945 - val_loss: 0.8715 - val_accuracy: 0.6594 Epoch 7/15 92/92 [==============================] - 1s 12ms/step - loss: 0.7736 - accuracy: 0.6965 - val_loss: 0.8059 - val_accuracy: 0.6935 Epoch 8/15 92/92 [==============================] - 1s 12ms/step - loss: 0.7477 - accuracy: 0.7078 - val_loss: 0.8292 - val_accuracy: 0.6812 Epoch 9/15 92/92 [==============================] - 1s 12ms/step - loss: 0.7053 - accuracy: 0.7251 - val_loss: 0.7743 - val_accuracy: 0.6989 Epoch 10/15 92/92 [==============================] - 1s 12ms/step - loss: 0.6884 - accuracy: 0.7340 - val_loss: 0.7867 - val_accuracy: 0.6907 Epoch 11/15 92/92 [==============================] - 1s 12ms/step - loss: 0.6536 - accuracy: 0.7469 - val_loss: 0.7732 - val_accuracy: 0.6785 Epoch 12/15 92/92 [==============================] - 1s 12ms/step - loss: 0.6456 - accuracy: 0.7500 - val_loss: 0.7801 - val_accuracy: 0.6907 Epoch 13/15 92/92 [==============================] - 1s 12ms/step - loss: 0.5941 - accuracy: 0.7735 - val_loss: 0.7185 - val_accuracy: 0.7330 Epoch 14/15 92/92 [==============================] - 1s 12ms/step - loss: 0.5824 - accuracy: 0.7735 - val_loss: 0.7282 - val_accuracy: 0.7357 Epoch 15/15 92/92 [==============================] - 1s 12ms/step - loss: 0.5771 - accuracy: 0.7851 - val_loss: 0.7308 - val_accuracy: 0.7343
דמיין את תוצאות האימון
לאחר החלת הגדלת נתונים ו- tf.keras.layers.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 = tf.keras.utils.load_img(
sunflower_path, target_size=(img_height, img_width)
)
img_array = tf.keras.utils.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 131072/117948 [=================================] - 0s 0us/step This image most likely belongs to sunflowers with a 89.13 percent confidence.