Используйте сверточные нейронные сети (CNN) со сложными изображениями

1. Прежде чем начать

В этой кодовой лаборатории вы будете использовать свертки для классификации изображений лошадей и людей. В этой лабораторной работе вы будете использовать TensorFlow для создания CNN, обученной распознавать изображения лошадей и людей и классифицировать их.

Предпосылки

Если вы никогда раньше не создавали свертки с помощью TensorFlow, вам может потребоваться выполнить лабораторную работу по сборке сверток и объединению кода, где мы представляем свертки и объединение, а также Построение сверточных нейронных сетей (CNN) для улучшения компьютерного зрения , где мы обсуждаем, как создавать компьютеры. эффективнее распознавать изображения.

Что вы узнаете

  • Как научить компьютеры распознавать элементы на изображении, на которых объект не ясен

Что вы будете строить

  • Сверточная нейронная сеть, которая может различать изображения лошадей и изображения людей.

Что вам понадобится

Вы можете найти код остальной кодлабы , работающей в Colab .

Вам также потребуется установленный TensorFlow и библиотеки, которые вы установили в предыдущей лаборатории кода.

2. Начало работы: сбор данных

Вы сделаете это, создав классификатор лошадей или людей, который сообщит вам, содержит ли данное изображение лошадь или человека, где сеть обучена распознавать функции, которые определяют, кто есть кто. Вам нужно будет выполнить некоторую обработку данных, прежде чем вы сможете тренироваться.

Сначала загрузите данные:

!wget --no-check-certificate https://storage.googleapis.com/laurencemoroney-blog.appspot.com/horse-or-human.zip  -O /tmp/horse-or-human.zip

Следующий код Python будет использовать библиотеку ОС для использования библиотек операционной системы, предоставляя вам доступ к файловой системе и библиотеке zip-файлов, что позволяет распаковывать данные.

import os
import zipfile
 
local_zip = '/tmp/horse-or-human.zip'
zip_ref = zipfile.ZipFile(local_zip, 'r')
zip_ref.extractall('/tmp/horse-or-human')
zip_ref.close()

Содержимое zip-файла извлекается в базовый каталог /tmp/horse-or-human , который содержит подкаталоги Horses и Human.

Короче говоря, обучающая выборка — это данные, которые используются для того, чтобы сообщить модели нейронной сети, что «вот так выглядит лошадь» и «вот как выглядит человек».

3. Используйте ImageGenerator для маркировки и подготовки данных

Вы явно не помечаете изображения как лошадей или людей.

Позже вы увидите, что используется нечто, называемое ImageDataGenerator . Он читает изображения из подкаталогов и автоматически помечает их по имени этого подкаталога. Например, у вас есть учебный каталог, содержащий каталог лошадей и каталог людей. ImageDataGenerator соответствующим образом пометит изображения, сократив этап кодирования.

Определите каждый из этих каталогов.

# Directory with our training horse pictures
train_horse_dir = os.path.join('/tmp/horse-or-human/horses')
 
# Directory with our training human pictures
train_human_dir = os.path.join('/tmp/horse-or-human/humans')

Теперь посмотрите, как выглядят имена файлов в каталогах тренировок лошадей и людей:

train_horse_names = os.listdir(train_horse_dir)
print(train_horse_names[:10])
train_human_names = os.listdir(train_human_dir)
print(train_human_names[:10])

Найдите общее количество изображений лошадей и людей в каталогах:

print('total training horse images:', len(os.listdir(train_horse_dir)))
print('total training human images:', len(os.listdir(train_human_dir)))

4. Изучите данные

Взгляните на несколько фотографий, чтобы лучше понять, как они выглядят.

Сначала настройте параметры matplot :

%matplotlib inline
 
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
 
# Parameters for our graph; we'll output images in a 4x4 configuration
nrows = 4
ncols = 4
 
# Index for iterating over images
pic_index = 0

Теперь покажите набор из восьми изображений лошадей и восьми изображений людей. Вы можете перезапустить ячейку, чтобы каждый раз видеть новую партию.

# Set up matplotlib fig, and size it to fit 4x4 pics
fig = plt.gcf()
fig.set_size_inches(ncols * 4, nrows * 4)
 
pic_index += 8
next_horse_pix = [os.path.join(train_horse_dir, fname) 
                for fname in train_horse_names[pic_index-8:pic_index]]
next_human_pix = [os.path.join(train_human_dir, fname) 
                for fname in train_human_names[pic_index-8:pic_index]]
 
for i, img_path in enumerate(next_horse_pix+next_human_pix):
  # Set up subplot; subplot indices start at 1
  sp = plt.subplot(nrows, ncols, i + 1)
  sp.axis('Off') # Don't show axes (or gridlines)
 
  img = mpimg.imread(img_path)
  plt.imshow(img)
 
plt.show()
 

Вот несколько примеров изображений, показывающих лошадей и людей в разных позах и ориентациях:

6b6ebbc6e694ccd2.png

5. Определите модель

Начните определять модель.

Начните с импорта TensorFlow:

import tensorflow as tf

Затем добавьте сверточные слои и сгладьте окончательный результат, чтобы передать его плотно связанным слоям. Наконец, добавьте плотно связанные слои.

Обратите внимание, что, поскольку вы столкнулись с проблемой классификации двух классов (проблема бинарной классификации ), вы закончите свою сеть с сигмовидной активацией , так что выход вашей сети будет единственным скаляром между 0 и 1, кодирующим вероятность того, что текущее изображение относится к классу 1 (в отличие от класса 0).

model = tf.keras.models.Sequential([
    # Note the input shape is the desired size of the image 300x300 with 3 bytes color
    # This is the first convolution
    tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(300, 300, 3)),
    tf.keras.layers.MaxPooling2D(2, 2),
    # The second convolution
    tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    # The third convolution
    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    # The fourth convolution
    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    # The fifth convolution
    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    # Flatten the results to feed into a DNN
    tf.keras.layers.Flatten(),
    # 512 neuron hidden layer
    tf.keras.layers.Dense(512, activation='relu'),
    # Only 1 output neuron. It will contain a value from 0-1 where 0 for 1 class ('horses') and 1 for the other ('humans')
    tf.keras.layers.Dense(1, activation='sigmoid')
])

Вызов метода model.summary() выводит сводку сети.

model.summary()

Вы можете увидеть результаты здесь:

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 298, 298, 16)      448       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 149, 149, 16)      0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 147, 147, 32)      4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 73, 73, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 71, 71, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 35, 35, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 33, 33, 64)        36928     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 16, 16, 64)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 14, 14, 64)        36928     
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 7, 7, 64)          0         
_________________________________________________________________
flatten (Flatten)            (None, 3136)              0         
_________________________________________________________________
dense (Dense)                (None, 512)               1606144   
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 513       
=================================================================
Total params: 1,704,097
Trainable params: 1,704,097
Non-trainable params: 0

Столбец выходной формы показывает, как меняется размер вашей карты объектов на каждом последующем слое. Слои свертки немного уменьшают размер карт объектов из-за заполнения, и каждый объединяющий слой уменьшает размеры вдвое.

6. Скомпилируйте модель

Затем настройте спецификации для обучения модели. Обучите свою модель с помощью потери binary_crossentropy , потому что это проблема бинарной классификации, а ваша окончательная активация — это сигмоид. (Дополнительную информацию о метриках потерь см. в разделе « Спуск в машинное обучение».) Используйте оптимизатор rmsprop со скоростью обучения 0,001. Во время обучения следите за точностью классификации.

from tensorflow.keras.optimizers import RMSprop
 
model.compile(loss='binary_crossentropy',
              optimizer=RMSprop(lr=0.001),
              metrics=['acc'])

7. Обучите модель из генераторов

Настройте генераторы данных, которые считывают изображения из ваших исходных папок, преобразовывают их в тензоры float32 и передают их (с их метками) в вашу сеть.

У вас будет один генератор для обучающих изображений и один для проверочных изображений. Ваши генераторы будут давать пакеты изображений размером 300x300 и их метки (бинарные).

Как вы, возможно, уже знаете, данные, которые поступают в нейронные сети, обычно должны быть каким-то образом нормализованы, чтобы сделать их более поддающимися обработке сетью. (Необычно передавать необработанные пиксели в CNN.) В вашем случае вы предварительно обработаете свои изображения, нормализовав значения пикселей, чтобы они находились в диапазоне [0, 1] (изначально все значения находятся в диапазоне [0, 255] ).

В Keras это можно сделать с помощью класса keras.preprocessing.image.ImageDataGenerator с использованием параметра масштабирования. Этот класс ImageDataGenerator позволяет создавать генераторы расширенных пакетов изображений (и их меток) через .flow(данные, метки) или .flow_from_directory(каталог). Затем эти генераторы можно использовать с методами модели Keras, которые принимают генераторы данных в качестве входных данных: fit_generator , predict_generator evaluate_generator

from tensorflow.keras.preprocessing.image import ImageDataGenerator
 
# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
 
# Flow training images in batches of 128 using train_datagen generator
train_generator = train_datagen.flow_from_directory(
        '/tmp/horse-or-human/',  # This is the source directory for training images
        target_size=(300, 300),  # All images will be resized to 150x150
        batch_size=128,
        # Since we use binary_crossentropy loss, we need binary labels
        class_mode='binary')

8. Тренируйтесь

Тренируйтесь 15 эпох. (Это может занять несколько минут.)

history = model.fit(
      train_generator,
      steps_per_epoch=8,  
      epochs=15,
      verbose=1)

Обратите внимание на значения за эпоху.

Потери и точность являются отличным показателем прогресса в обучении. Он делает предположение о классификации обучающих данных, а затем сопоставляет их с известной меткой и вычисляет результат. Точность – это доля правильных догадок.

Epoch 1/15
9/9 [==============================] - 9s 1s/step - loss: 0.8662 - acc: 0.5151
Epoch 2/15
9/9 [==============================] - 8s 927ms/step - loss: 0.7212 - acc: 0.5969
Epoch 3/15
9/9 [==============================] - 8s 921ms/step - loss: 0.6612 - acc: 0.6592
Epoch 4/15
9/9 [==============================] - 8s 925ms/step - loss: 0.3135 - acc: 0.8481
Epoch 5/15
9/9 [==============================] - 8s 919ms/step - loss: 0.4640 - acc: 0.8530
Epoch 6/15
9/9 [==============================] - 8s 896ms/step - loss: 0.2306 - acc: 0.9231
Epoch 7/15
9/9 [==============================] - 8s 915ms/step - loss: 0.1464 - acc: 0.9396
Epoch 8/15
9/9 [==============================] - 8s 935ms/step - loss: 0.2663 - acc: 0.8919
Epoch 9/15
9/9 [==============================] - 8s 883ms/step - loss: 0.0772 - acc: 0.9698
Epoch 10/15
9/9 [==============================] - 9s 951ms/step - loss: 0.0403 - acc: 0.9805
Epoch 11/15
9/9 [==============================] - 8s 891ms/step - loss: 0.2618 - acc: 0.9075
Epoch 12/15
9/9 [==============================] - 8s 902ms/step - loss: 0.0434 - acc: 0.9873
Epoch 13/15
9/9 [==============================] - 8s 904ms/step - loss: 0.0187 - acc: 0.9932
Epoch 14/15
9/9 [==============================] - 9s 951ms/step - loss: 0.0974 - acc: 0.9649
Epoch 15/15
9/9 [==============================] - 8s 877ms/step - loss: 0.2859 - acc: 0.9338

9. Протестируйте модель

Теперь на самом деле запустите прогноз, используя модель. Код позволит вам выбрать один или несколько файлов из вашей файловой системы. Затем он загрузит их и прогонит через модель, дав понять, является ли объект лошадью или человеком.

Вы можете загрузить изображения из Интернета в свою файловую систему, чтобы попробовать их! Обратите внимание, что вы можете увидеть, что сеть делает много ошибок, несмотря на то, что точность обучения выше 99%.

Это связано с тем, что называется переоснащением , что означает, что нейронная сеть обучается на очень ограниченных данных (всего около 500 изображений каждого класса). Таким образом, он очень хорошо распознает изображения, похожие на те, что есть в тренировочном наборе, но может сильно ошибаться при распознавании изображений, которых нет в тренировочном наборе.

Это данные, доказывающие, что чем больше данных вы тренируете, тем лучше будет ваша конечная сеть!

Есть много методов, которые можно использовать для улучшения вашего обучения, несмотря на ограниченные данные, в том числе то, что называется увеличением изображения, но это выходит за рамки этой лаборатории кода.

import numpy as np
from google.colab import files
from keras.preprocessing import image
 
uploaded = files.upload()
 
for fn in uploaded.keys():
 
  # predicting images
  path = '/content/' + fn
  img = image.load_img(path, target_size=(300, 300))
  x = image.img_to_array(img)
  x = np.expand_dims(x, axis=0)
 
  images = np.vstack([x])
  classes = model.predict(images, batch_size=10)
  print(classes[0])
  if classes[0]>0.5:
    print(fn + " is a human")
  else:
    print(fn + " is a horse")

Например, скажите, что вы хотите протестировать это изображение:

9e07a57ff3be7a82.jpeg

Вот что выдает колаб:

77b678e70b00862a.png

Несмотря на то, что это мультяшная графика, она все же правильно классифицируется.

Следующее изображение также правильно классифицирует:

c9213173d9f3d83c.jpeg

f2844da737a1a2f2.png

Попробуйте свои собственные изображения и исследуйте!

10. Визуализируйте промежуточные представления

Чтобы понять, какие функции изучила ваша CNN, забавно визуализировать, как трансформируется ввод, когда он проходит через CNN.

Выберите случайное изображение из тренировочного набора, затем сгенерируйте фигуру, где каждая строка является результатом слоя, а каждое изображение в строке является определенным фильтром на этой выходной карте объектов. Повторно запустите эту ячейку, чтобы сгенерировать промежуточные представления для различных обучающих изображений.

import numpy as np
import random
from tensorflow.keras.preprocessing.image import img_to_array, load_img
 
# Let's define a new Model that will take an image as input, and will output
# intermediate representations for all layers in the previous model after
# the first.
successive_outputs = [layer.output for layer in model.layers[1:]]
#visualization_model = Model(img_input, successive_outputs)
visualization_model = tf.keras.models.Model(inputs = model.input, outputs = successive_outputs)
# Let's prepare a random input image from the training set.
horse_img_files = [os.path.join(train_horse_dir, f) for f in train_horse_names]
human_img_files = [os.path.join(train_human_dir, f) for f in train_human_names]
img_path = random.choice(horse_img_files + human_img_files)
 
img = load_img(img_path, target_size=(300, 300))  # this is a PIL image
x = img_to_array(img)  # Numpy array with shape (150, 150, 3)
x = x.reshape((1,) + x.shape)  # Numpy array with shape (1, 150, 150, 3)
 
# Rescale by 1/255
x /= 255
 
# Let's run our image through our network, thus obtaining all
# intermediate representations for this image.
successive_feature_maps = visualization_model.predict(x)
 
# These are the names of the layers, so can have them as part of our plot
layer_names = [layer.name for layer in model.layers]
 
# Now let's display our representations
for layer_name, feature_map in zip(layer_names, successive_feature_maps):
  if len(feature_map.shape) == 4:
    # Just do this for the conv / maxpool layers, not the fully-connected layers
    n_features = feature_map.shape[-1]  # number of features in feature map
    # The feature map has shape (1, size, size, n_features)
    size = feature_map.shape[1]
    # We will tile our images in this matrix
    display_grid = np.zeros((size, size * n_features))
    for i in range(n_features):
      # Postprocess the feature to make it visually palatable
      x = feature_map[0, :, :, i]
      x -= x.mean()
      if x.std()>0:
        x /= x.std()
      x *= 64
      x += 128
      x = np.clip(x, 0, 255).astype('uint8')
      # We'll tile each filter into this big horizontal grid
      display_grid[:, i * size : (i + 1) * size] = x
    # Display the grid
    scale = 20. / n_features
    plt.figure(figsize=(scale * n_features, scale))
    plt.title(layer_name)
    plt.grid(False)
    plt.imshow(display_grid, aspect='auto', cmap='viridis')

Вот примеры результатов:

e078d1bc9662c93f.png

Как видите, вы переходите от необработанных пикселей изображения ко все более абстрактным и компактным представлениям. Представления ниже по течению начинают выделять то, на что сеть обращает внимание, и они показывают все меньше и меньше функций, которые «активируются». Большинство установлено на ноль. Это называется разреженностью . Разреженность представления является ключевой особенностью глубокого обучения.

Эти представления несут все меньше информации об исходных пикселях изображения, но все больше уточняют информацию о классе изображения. Вы можете думать о CNN (или о глубокой сети в целом) как о конвейере дистилляции информации.

11. Поздравления

Вы научились использовать CNN для улучшения сложных изображений. Чтобы узнать, как еще больше улучшить модели компьютерного зрения, перейдите к разделу Использование сверточных нейронных сетей (CNN) с большими наборами данных, чтобы избежать переобучения .