Всем привет и добро пожаловать в новый блог о науке о данных и машинном обучении!

Этот код для проекта, о котором я рассказываю в этом блоге, можно найти в следующем репозитории GitHub: https://github.com/ariehlev/clasification-dog-breed-cnn.

Обзор проекта и постановка проблемы

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

  • Если это изображение собаки, оно должно сказать нам, что это за порода.
  • Если это человек, он должен признать, что это человек, но он также должен сказать нам, какой породы он был бы, если бы был собакой!
  • Если это не человек и не собака, он должен обнаружить это и отобразить сообщение о том, что он не знает, что это такое.

Чтобы решить эту проблему, наша стратегия будет заключаться в построении сверточной нейронной сети, которая получает изображение и предоставляет один из возможных выходных данных, перечисленных выше. Ожидаемое решение состоит в том, что сеть успешно выполнит эту задачу классификации. Способ, которым мы будем определять «успех» операции, подробно объясняется в разделе «Метрики» ниже.

Метрики

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

Точность = количество правильных прогнозов / общее количество прогнозов

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

EDA (исследование и визуализация данных)

Для этой задачи мы будем использовать как набор данных о собаках, так и набор данных о людях. Кроме того, очень важно разделить эти данные на наборы данных для обучения, тестирования и проверки. Популярный раскол имеет 70%-15%-15% соответственно. «Тестовый» патрон набора данных — это тот, который мы будем использовать для проверки точности, описанной в разделе «Метрики» выше. Важно знать, что размеры/разрешения изображений несовместимы, и поэтому позже мы изменим все до квадратного изображения 224x224.

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

Вот также некоторая статистическая информация о частотах для классов набора данных:

С другой стороны, было использовано 13233 изображения человека. Нет никаких аномалий или характеристик данных, на которые следует указать. Вот некоторые из изображений, с которыми мы будем работать:

Предварительная обработка данных

Как упоминалось ранее, первое, что мы сделали, это преобразовали изображения в квадрат 224x224.

Кроме того, при использовании TensorFlow в качестве серверной части используемым нами CNN (Keras) требуется 4D-массив (обычно называемый 4D-тензором) в качестве входных данных со следующей формой:

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

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

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

Обнаружение людей

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

Мы протестировали наш детектор человеческих лиц, предоставив ему 100 изображений людей и 100 изображений собак. Из изображений людей он обнаружил 100 лиц (100%). Однако из изображений собак он обнаружил на них 11 человеческих лиц. Таким образом, он отлично работал на людях и довольно хорошо на собаках. Достаточно хорошо для того, что мы хотим в этом проекте :)

Обнаружение собак

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

Мы также протестировали этот детектор, предоставив ему 100 изображений людей и 100 изображений собак. Он обнаружил 100 собак на изображениях собак и 0 собак на изображениях людей. Очевидно, что это достаточно хороший результат!

Моделирование и настройка гиперпараметров (реализация и уточнение)

Теперь, когда мы знаем, как распознавать собак или людей по изображениям, мы можем погрузиться в задачу классификации пород собак! Для этого я построил сверточную нейронную сеть с 4 сверточными слоями, 4 операциями максимального объединения после каждого слоя, 1 операцией глобального среднего объединения после 4 слоев и последним плотным слоем. Пожалуйста, обратитесь к коду и изображению ниже для более подробной информации.

model = Sequential()
### Defining the architecture.
model.add(Conv2D(filters=16, kernel_size=2, padding='same', activation='relu', input_shape=(224, 224, 3)))
model.add(MaxPooling2D(pool_size=2))
model.add(Conv2D(filters=32, kernel_size=2, padding='same', activation='relu', input_shape=(110, 110, 32)))
model.add(MaxPooling2D(pool_size=2))
model.add(Conv2D(filters=32, kernel_size=2, padding='same', activation='relu', input_shape=(110, 110, 32)))
model.add(MaxPooling2D(pool_size=2))
model.add(Conv2D(filters=64, kernel_size=2, padding='same', activation='relu', input_shape=(54, 54, 64)))
model.add(MaxPooling2D(pool_size=2))
model.add(GlobalAveragePooling2D(input_shape=train_tensors.shape[1:]))
model.add(Dense(133, activation='softmax'))
model.summary()

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

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

Чтобы добиться лучших результатов, но не тратя на это слишком много времени, мы можем использовать трансферное обучение для обучения CNN. Мы будем использовать предварительно обученную модель VGG-16 в качестве экстрактора фиксированных признаков.

После обучения модели и ее тестирования мы получаем точность 35,17%, что намного лучше достигнутых ранее 3,23%! Тем не менее, мы все еще не полностью удовлетворены этим результатом и хотим добиться гораздо большей точности.

Поэтому мы попробуем использовать узкие места из другой предварительно обученной модели. В нашем случае модель ResNet50. Мы следуем той же процедуре, что и раньше, обучаем и тестируем нашу модель и обнаруживаем, что достигли точности 80%! Это определенно лучше, чем все, что мы достигли ранее!

Оценка и проверка модели — результаты

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

Одним из возможных объяснений лучших результатов последней модели по сравнению с двумя предыдущими является количество параметров. CNN, которую мы построили с нуля, содержала 23 317 параметров, модель VGG16 — 68 229, а модель ResNet50 — 272 517. Как мы видим, существует очень большая разница между ResNet50 и двумя другими, и это может быть причиной лучшей точности. Можно было бы возразить, что большее количество параметров приводит к переоснащению, но эта точность была рассчитана на основе тестовых данных, а это означает, что в нашем случае переобучения не произошло, так как мы получили хороший результат.

Заключение — обоснование и размышление

Подводя итог, мы сначала предварительно обработали наши данные и создали детектор лиц и собак. Затем мы создали CNN с нуля для выполнения этой задачи классификации. Чтобы повысить нашу точность, мы использовали предварительно обученные модели, такие как VGG16 и ResNet50, которые оказались очень успешными! Наконец, мы оценили нашу модель и весело провели время, тестируя ее на реальных фотографиях!

Модель удалась очень хорошо! Когда собаке дали фотографию, она успешно определила ее породу. Кроме того, когда ему давали изображение человека, он обнаруживал человеческое лицо на изображении и предоставлял породу собак, которая подходит к человеческому внешнему виду. Наконец, когда ему предоставили случайное изображение, не соответствующее изображению собаки или человека, он распознал, что это так, и напечатал сообщение об этом. Он дал правильный результат на всех изображениях, которые мы использовали для тестирования сети (изображения, показанные в разделе «Результаты»), но мы должны помнить, что он достиг 80% точности, когда был задан «тестовый фрагмент» данные. Поэтому определенно есть над чем поработать, чтобы добиться лучших результатов. Однако мы можем определенно заключить, что последняя модель (точность 80%) была лучше, чем две другие (3,23% и 35,17%). Причина этого в том, что мы использовали предварительно обученные модели, которые уже показали отличную производительность в прошлом, вместо моделей, созданных и обученных только нами.

Мне показалось очень интересным, что между каждой из моделей был такой большой разрыв. Я ожидал большой разницы между первым и вторым, потому что это был первый раз, когда использовалась предварительно обученная модель. Но я был удивлен, увидев такой скачок в точности между второй (VGG16) и третьей моделью (ResNet50). Я рад узнать больше о том, почему это произошло, и поделиться этим с вами, ребята, в будущем блоге!

Будущие улучшения

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

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

До скорой встречи!

Я надеюсь, что вы нашли этот блог полезным, и что вам понравилось читать его так же, как мне понравилось работать над ним! Пожалуйста, свяжитесь со мной, если у вас есть какие-либо вопросы по этому поводу!

Оставайтесь в безопасности!

Арье Леви