Посмотреть на TensorFlow.org | Запустить в Google Colab | Посмотреть исходный код на 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
Загрузите и изучите набор данных
В этом руководстве используется набор данных из примерно 3700 фотографий цветов. Набор данных содержит пять подкаталогов, по одному на класс:
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)
После загрузки у вас должна быть доступна копия набора данных. Всего 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
Давайте загрузим эти образы с диска с помощью полезной утилиты 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
и labels_batch
, чтобы преобразовать их в numpy.ndarray
.
Настройте набор данных для производительности
Обязательно используйте предварительную выборку с буферизацией, чтобы вы могли получать данные с диска, не блокируя ввод-вывод. Вот два важных метода, которые вы должны использовать при загрузке данных:
-
Dataset.cache
хранит изображения в памяти после их загрузки с диска в течение первой эпохи. Это гарантирует, что набор данных не станет узким местом при обучении вашей модели. Если ваш набор данных слишком велик, чтобы поместиться в память, вы также можете использовать этот метод для создания производительного кэша на диске. -
Dataset.prefetch
перекрывает предварительную обработку данных и выполнение модели во время обучения.
Заинтересованные читатели могут узнать больше об обоих методах, а также о том, как кэшировать данные на диск, в разделе Предварительная выборка руководства Повышение производительности с помощью API tf.data .
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
Или вы можете включить слой в определение вашей модели, что может упростить развертывание. Здесь воспользуемся вторым подходом.
Создайте модель
Последовательная модель состоит из трех блоков свертки ( 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
. Они могут быть включены в вашу модель, как и другие слои, и работать на графическом процессоре.
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")
Вы будете использовать увеличение данных для обучения модели через мгновение.
Выбывать
Еще один способ уменьшить переобучение — ввести в сеть регуляризацию отсева .
Когда вы применяете выпадение к слою, оно случайным образом выбрасывает (путем установки активации на ноль) количество выходных единиц из слоя в процессе обучения. Dropout принимает дробное число в качестве входного значения, например, 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.