Проект: Классификация пород собак¶

-Шаурья Синха

Постановка задачи¶

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

Ожидаемые результаты:

Наборы данных и входные данные¶

Мы будем использовать 2 набора данных, которые были предоставлены Audacity и могут быть найдены непосредственно в рабочей области:

  • Загрузите набор данных о собаках. Разархивируйте папку и поместите ее в домашнюю директорию этого проекта по адресу /dog_images.
  • Всего 8351 изображение собак и 133 различных породы собак.
  • Загрузите набор данных человека. Разархивируйте папку и поместите ее в домашний каталог по адресу /lfw.
  • Всего 13 233 изображения человека.

Мы будем использовать эти 2 набора данных для постановки задачи.

Решение¶

В этом проекте мы собираемся использовать Tensorflow для создания нашего классификатора. Tensorflow — это библиотека машинного обучения с открытым исходным кодом для Python, основанная на Torch и используемая для таких приложений, как обработка естественного языка. Он в основном разработан исследовательской группой Facebook по искусственному интеллекту, и на нем построено программное обеспечение вероятностного языка программирования Pyro от Uber.

  • Мы будем использовать CNN и Transfer Learning для реализации классификатора, подробности о них обоих можно найти выше.

Эталонная модель:¶

  • Мы будем использовать модель CNN, созданную с нуля, в качестве нашей эталонной модели, которая предоставит нам пространство для совершенствования и проверит эффективность трансферного обучения в гораздо более четкой науке.

Показатели оценки:¶

  • Мы будем использовать Test Loss и Test Accuracy модели в наборе данных Test Dog Images как для эталона модели, так и для ResNet50, чтобы сравнить их.
  • Проигрыш – результат плохого предсказания. Потеря — это число, указывающее, насколько плохим был прогноз модели на одном примере. Таким образом, потеря теста - это потеря в наборе тестовых данных.
  • Точность — это количество правильно предсказанных точек данных из всех точек данных, поэтому точность теста — это точность модели в тестовом наборе данных.

Дизайн проекта:¶

Процесс, которому необходимо следовать:

  1. Найдите правильный набор данных — набор данных о людях и набор данных о собаках, указанные выше.
  2. Импортируйте набор данных и предварительно обработайте его (при необходимости) — увеличение данных, обрезка, дополнение и т. д.
  3. Обнаружение людей
  4. Обнаружение собак
  5. Создайте собственную модель CNN с нуля, чтобы классифицировать породы собак.
  6. Создайте модель CNN, используя Transfer Learning для классификации пород собак — ResNet50 / VGG16 / и т. д.
  7. Протестируйте обе модели

Проект:¶

Импорт наборов данных:¶

  • Загрузите набор данных о собаках. Разархивируйте папку и поместите ее в домашнюю директорию этого проекта по адресу /dog_images.
  • Загрузите набор данных человека. Разархивируйте папку и поместите ее в домашний каталог по адресу /lfw.
from sklearn.datasets import load_files       
from keras.utils import np_utils
import numpy as np
from glob import glob
from IPython.core.display import Image, display
# define function to load train, test, and validation datasets
def load_dataset(path):
    data = load_files(path)
    dog_files = np.array(data['filenames'])
    dog_targets = np_utils.to_categorical(np.array(data['target']), 133)
    return dog_files, dog_targets
# load train, test, and validation datasets
train_files, train_targets = load_dataset('../../../data/dog_images/train')
valid_files, valid_targets = load_dataset('../../../data/dog_images/valid')
test_files, test_targets = load_dataset('../../../data/dog_images/test')
# load list of dog names
dog_names = [item[20:-1] for item in sorted(glob("../../../data/dog_images/train/*/"))]
# print statistics about the dataset
print('There are %d total dog categories.' % len(dog_names))
print('There are %s total dog images.\n' % len(np.hstack([train_files, valid_files, test_files])))
print('There are %d training dog images.' % len(train_files))
print('There are %d validation dog images.' % len(valid_files))
print('There are %d test dog images.'% len(test_files))

Шаг 1. Обнаружение людей¶

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

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

import cv2                
import matplotlib.pyplot as plt                        
%matplotlib inline
# extract pre-trained face detector
face_cascade = cv2.CascadeClassifier('haarcascades/haarcascade_frontalface_alt.xml')
# load color (BGR) image
img = cv2.imread(human_files[3])
# convert BGR image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# find faces in image
faces = face_cascade.detectMultiScale(gray)
# print number of faces detected in the image
print('Number of faces detected:', len(faces))
# get bounding box for each detected face
for (x,y,w,h) in faces:
    # add bounding box to color image
    cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    
# convert BGR image to RGB for plotting
cv_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# display the image, along with bounding box
plt.imshow(cv_rgb)
plt.show()

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

Шаг 2. Обнаружение собак¶

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

Получение предварительно обученной модели Resnet-50¶

Ячейка кода ниже загружает модель Resnet-50 вместе с весами, которые были обучены в ImageNet, очень большом и очень популярном наборе данных, используемом для классификации изображений и других задач зрения. ImageNet содержит более 10 миллионов URL-адресов, каждая из которых ведет к изображению, содержащему объект из одной из 1000 категорий. При наличии изображения эта предварительно обученная модель Resnet-50 возвращает прогноз (полученный из 1000 возможных категорий в ImageNet) для объекта, содержащегося в изображении.

from keras.applications.resnet50 import ResNet50
# define ResNet50 model
ResNet50_model = ResNet50(weights='imagenet')

(РЕАЛИЗАЦИЯ) Создание прогнозов с помощью предварительно обученной модели¶

При использовании TensorFlow в качестве бэкэнда для CNN Keras требуется 4D-массив (который мы также будем называть 4D-тензором) в качестве входных данных с формой

(nb_samples, строки, столбцы, каналы),

где nb_samples соответствует общему количеству изображений (или выборок), а строки, столбцы и каналы соответствуют количеству строк, столбцов и каналов для каждого изображения соответственно.

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

224×224

пикселей. Затем изображение преобразуется в массив, размер которого затем изменяется до четырехмерного тензора. В данном случае, поскольку мы работаем с цветными изображениями, каждое изображение имеет три канала. Аналогично, поскольку мы обрабатываем одно изображение (или образец), возвращаемый тензор всегда будет иметь вид

(1,224,224,3).

Функция paths_to_tensor принимает в качестве входных данных массив строковых путей изображения NumPy и возвращает четырехмерный тензор формы

(nb_samples, 224, 224, 3).

from keras.preprocessing import image                  
from tqdm import tqdm
def path_to_tensor(img_path):
    # loads RGB image as PIL.Image.Image type
    img = image.load_img(img_path, target_size=(224, 224))
    # convert PIL.Image.Image type to 3D tensor with shape (224, 224, 3)
    x = image.img_to_array(img)
    # convert 3D tensor to 4D tensor with shape (1, 224, 224, 3) and return 4D tensor
    return np.expand_dims(x, axis=0)
def paths_to_tensor(img_paths):
    list_of_tensors = [path_to_tensor(img_path) for img_path in tqdm(img_paths)]
    return np.vstack(list_of_tensors)

Подготовка 4D-тензора к ResNet-50 и любой другой предварительно обученной модели в Keras требует дополнительной обработки. Сначала изображение RGB преобразуется в BGR путем переупорядочения каналов. Все предварительно обученные модели имеют дополнительный шаг нормализации, который заключается в том, что средний пиксель (выраженный в RGB как

[103.939,116.779,123.68]

и рассчитывается из всех пикселей во всех изображениях в ImageNet) необходимо вычесть из каждого пикселя в каждом изображении. Это реализовано в импортированной функции preprocess_input. Если вам интересно, вы можете посмотреть код preprocess_input здесь.

Теперь, когда у нас есть способ отформатировать наше изображение для передачи в ResNet-50, мы готовы использовать модель для извлечения прогнозов. Это достигается с помощью метода прогнозирования, который возвращает массив,

i-я запись — это предсказанная моделью вероятность того, что изображение принадлежит

i-я категория ImageNet. Это реализовано в функции ResNet50_predict_labels ниже.

from keras.applications.resnet50 import preprocess_input, decode_predictions
def ResNet50_predict_labels(img_path):
    # returns prediction vector for image located at img_path
    img = preprocess_input(path_to_tensor(img_path))
    return np.argmax(ResNet50_model.predict(img))

Написать детектор собак

Шаг 3. Создайте CNN для классификации пород собак (с нуля)¶

Теперь, когда у нас есть функции для обнаружения людей и собак на изображениях, нам нужен способ предсказывать породу по изображениям. На этом этапе мы создадим CNN, которая классифицирует породы собак. Мы создали собственную CNN с нуля и должны добиться точности тестирования не менее 10 %. На шаге 4 этой записной книжки у нас будет возможность использовать трансферное обучение для создания CNN, которая достигает значительно более высокой точности.

### returns "True" if a dog is detected in the image stored at img_path
def dog_detector(img_path):
    prediction = ResNet50_predict_labels(img_path)
    return ((prediction <= 268) & (prediction >= 151))
### TODO: Test the performance of the dog_detector function
### on the images in human_files_short and dog_files_short.
humans = 0
dogs = 0
for i in range(100):
    if dog_detector(human_files_short[i]):
        humans += 1
        print("this human: " , i , " looks like a dog.")
        human_dog_image = Image.open(human_files_short[i])
        plt.imshow(human_dog_image)
        plt.show()
    if dog_detector(dog_files_short[i]):
        dogs += 1
print(humans , "% of the images in humans detected as dogs")
print(dogs , "% of the images in dogs detected as dogs")
  • 0 процентов изображений в human_files_short имеют обнаруженную собаку
  • На 100 % изображений в dog_files_short обнаружена собака

Отметим, что задача определения породы собак по изображениям считается исключительно сложной. Чтобы понять, зачем считать, что даже человеку будет трудно отличить бретонца от вельш-спрингер-спаниеля.

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

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

Мы также отмечаем, что случайность представляет собой исключительно низкую планку: не считая того факта, что классы немного несбалансированы, случайное предположение даст правильный ответ примерно 1 раз из 133, что соответствует точности менее 1%.

(РЕАЛИЗАЦИЯ) Укажите загрузчики данных для набора данных Dog:

from PIL import ImageFile                            
ImageFile.LOAD_TRUNCATED_IMAGES = True
# pre-process the data for Keras
train_tensors = paths_to_tensor(train_files).astype('float32')/255
valid_tensors = paths_to_tensor(valid_files).astype('float32')/255
test_tensors = paths_to_tensor(test_files).astype('float32')/255

(РЕАЛИЗАЦИЯ) Архитектура модели:

from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D
from keras.layers import Dropout, Flatten, Dense
from keras.models import Sequential
model = Sequential()
### TODO: Define your architecture.
model.add(Conv2D(input_shape = (224, 224, 3), filters = 16, kernel_size=2, activation='relu'))
model.add(MaxPooling2D(pool_size= 2, data_format='channels_last'))
model.add(Conv2D(filters = 32, kernel_size=2, activation='relu'))
model.add(MaxPooling2D(pool_size= 2, data_format='channels_last'))
model.add(Conv2D(filters = 64, kernel_size=2, activation='relu'))
model.add(MaxPooling2D(pool_size= 2, data_format='channels_last'))
model.add(GlobalAveragePooling2D())
model.add(Dense(133, activation='relu'))
model.summary()

(РЕАЛИЗАЦИЯ) Укажите функцию потерь и оптимизатор¶ и обучите модель:

from keras.callbacks import ModelCheckpoint
### TODO: specify the number of epochs that you would like to use to train the model.
epochs = 15
### Do NOT modify the code below this line.
checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.from_scratch.hdf5', 
                               verbose=1, save_best_only=True)
model.fit(train_tensors, train_targets, 
          validation_data=(valid_tensors, valid_targets),
          epochs=epochs, batch_size=20, callbacks=[checkpointer], verbose=1)

Выбор модели с лучшими потерями при проверке и тестирование модели:

model.load_weights('saved_models/weights.best.from_scratch.hdf5')
# get index of predicted dog breed for each image in test set
dog_breed_predictions = [np.argmax(model.predict(np.expand_dims(tensor, axis=0))) for tensor in test_tensors]
# report test accuracy
test_accuracy = 100*np.sum(np.array(dog_breed_predictions)==np.argmax(test_targets, axis=1))/len(dog_breed_predictions)
print('Test accuracy: %.4f%%' % test_accuracy)
  • Точность теста: 1,0766%

Шаг 4. Создайте CNN для классификации пород собак (с помощью трансферного обучения)¶

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

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

Получить узкие места, смоделировать архитектуру и скомпилировать модель:

bottleneck_features = np.load('bottleneck_features/DogVGG16Data.npz')
train_VGG16 = bottleneck_features['train']
valid_VGG16 = bottleneck_features['valid']
test_VGG16 = bottleneck_features['test']
VGG16_model = Sequential()
VGG16_model.add(GlobalAveragePooling2D(input_shape=train_VGG16.shape[1:]))
VGG16_model.add(Dense(133, activation='softmax'))
VGG16_model.summary()

VGG16_model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])

Обучение модели:

checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.VGG16.hdf5', 
                               verbose=1, save_best_only=True)
VGG16_model.fit(train_VGG16, train_targets, 
          validation_data=(valid_VGG16, valid_targets),
          epochs=20, batch_size=20, callbacks=[checkpointer], verbose=1)

Выбор модели с лучшими потерями при проверке и тестирование модели:

VGG16_model.load_weights('saved_models/weights.best.VGG16.hdf5')
# get index of predicted dog breed for each image in test set
VGG16_predictions = [np.argmax(VGG16_model.predict(np.expand_dims(feature, axis=0))) for feature in test_VGG16]
# report test accuracy
test_accuracy = 100*np.sum(np.array(VGG16_predictions)==np.argmax(test_targets, axis=1))/len(VGG16_predictions)
print('Test accuracy: %.4f%%' % test_accuracy)
  • Точность теста: 40,1914%
from extract_bottleneck_features import *
def VGG16_predict_breed(img_path):
    # extract bottleneck features
    bottleneck_feature = extract_VGG16(path_to_tensor(img_path))
    # obtain predicted vector
    predicted_vector = VGG16_model.predict(bottleneck_feature)
    # return dog breed that is predicted by the model
    return dog_names[np.argmax(predicted_vector)]

Повторяя описанные выше шаги с архитектурой ResNet-50:

### TODO: Obtain bottleneck features from another pre-trained CNN.
bottleneck_features = np.load('bottleneck_features/DogResnet50Data.npz')
train_Resnet50 = bottleneck_features['train']
valid_Resnet50 = bottleneck_features['valid']
test_Resnet50 = bottleneck_features['test']
### TODO: Define your architecture.
Resnet50_model = Sequential()
Resnet50_model.add(GlobalAveragePooling2D(input_shape=train_Resnet50.shape[1:]))
Resnet50_model.add(Dense(133, activation='softmax'))
Resnet50_model.summary()

### TODO: Compile the model.
Resnet50_model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])

Обучение модели:

### TODO: Train the model.
checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.resnet50.hdf5', 
                               verbose=1, save_best_only=True)
Resnet50_model.fit(train_Resnet50, train_targets, 
          validation_data=(valid_Resnet50, valid_targets),
          epochs=10, batch_size=25, callbacks=[checkpointer], verbose=1)

Выбор модели с лучшими потерями при проверке и тестирование модели:

### TODO: Load the model weights with the best validation loss.
Resnet50_model.load_weights('saved_models/weights.best.resnet50.hdf5')
### TODO: Calculate classification accuracy on the test dataset.
Resnet50_predictions = [np.argmax(Resnet50_model.predict(np.expand_dims(feature, axis=0))) for feature in test_Resnet50]
# report test accuracy
test_accuracy = 100*np.sum(np.array(Resnet50_predictions)==np.argmax(test_targets, axis=1))/len(Resnet50_predictions)
print('Test accuracy: ' ,test_accuracy , '%')
  • Точность теста: 81,1004784689%

Функция предсказания породы собаки по изображению:

### TODO: Write a function that takes a path to an image as input
### and returns the dog breed that is predicted by the model.
def dog_breed_pred(img_path):
    bottleneck_feature = extract_Resnet50(path_to_tensor(img_path))
    pred_vector = Resnet50_model.predict(bottleneck_feature)
    
    return dog_names[np.argmax(pred_vector)]
### TODO: Write your algorithm.
### Feel free to use as many code cells as needed.
def pred_breed(img_path):
    display(Image(img_path, width=200, height=200))
    if dog_detector(img_path) == 1:
        return print("This is the photo of a dog whose breed is: ", dog_breed_pred(img_path).partition('.')[-1])
        
    elif face_detector(img_path) == 1:
        return print("This is the photo of a human and they look like a: " , dog_breed_pred(img_path).partition('.')[-1])
else:
        return print("Please try again.")

Результаты/тестирование:

Ресурсы:¶