Проект: Классификация пород собак¶
-Шаурья Синха
Постановка задачи¶
Учитывая изображение собаки, наш алгоритм должен определить оценку породы собаки. Если предоставлено изображение человека, код должен идентифицировать похожую породу собаки.
Ожидаемые результаты:
Наборы данных и входные данные¶
Мы будем использовать 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, чтобы сравнить их.
- Проигрыш – результат плохого предсказания. Потеря — это число, указывающее, насколько плохим был прогноз модели на одном примере. Таким образом, потеря теста - это потеря в наборе тестовых данных.
- Точность — это количество правильно предсказанных точек данных из всех точек данных, поэтому точность теста — это точность модели в тестовом наборе данных.
Дизайн проекта:¶
Процесс, которому необходимо следовать:
- Найдите правильный набор данных — набор данных о людях и набор данных о собаках, указанные выше.
- Импортируйте набор данных и предварительно обработайте его (при необходимости) — увеличение данных, обрезка, дополнение и т. д.
- Обнаружение людей
- Обнаружение собак
- Создайте собственную модель CNN с нуля, чтобы классифицировать породы собак.
- Создайте модель CNN, используя Transfer Learning для классификации пород собак — ResNet50 / VGG16 / и т. д.
- Протестируйте обе модели
Проект:¶
Импорт наборов данных:¶
- Загрузите набор данных о собаках. Разархивируйте папку и поместите ее в домашнюю директорию этого проекта по адресу /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.")
Результаты/тестирование:
Ресурсы:¶
- Исследовательская работа по глубокому остаточному обучению.
- VGG-19 от Аакаша Кошика (opengenus).
- Операций с плавающей запятой в секунду (FLOPS) моделей машинного обучения.
- Сверточная нейронная сеть Пиюша Мишры и Джунаида Н. З. (OpenGenus)
- https://github.com/vdumoulin/conv_arithmetic
- https://deepai.org/machine-learning-glossary-and-terms/accuracy-error-rate
- https://intellipaat.com/community/368/how-to-interpret-loss-and-accuracy-for-a-machine-learning-model
- Рабочее пространство Udacity для классификации собак.
- Resnet-50 от Аакаша Кошика (opengenus).