Классификация цветов с помощью байесовской CNN и ее отличия от стандартной модели CNN

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

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

Ниже приведены шаги, которые мы собираемся сделать в этом посте:

  • Загрузка набора данных изображения
  • Создание стандартной модели CNN
  • Создание байесовской модели CNN

Давайте начнем с обзора набора данных, над которым мы будем работать.

О данных изображения

Для данных мы собираемся использовать набор данных цветов, который вы можете бесплатно скачать на Kaggle. Эти данные содержат коллекцию пяти различных типов цветов: ромашки, одуванчика, тюльпана, розы и подсолнечника. Основная цель - классифицировать, какой цветок к какому типу принадлежит.

Всего насчитывается 4242 изображения цветов, из которых около 800 изображений каждого вида.

Теперь, если вы только что скачали данные, вы получите zip-файл. Если вы хотите продолжить, следующим шагом будет разархивирование файла и размещение разархивированного файла в предпочитаемом вами каталоге. Затем приступим к кодированию и загрузим данные.

Загрузка данных

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

flowers/
    daisy/
        img1.jpg
        img2.jpg
        ...
    sunflower/
        img11.jpg
        ...
    ...

И мы хотим разделить папку на учебную папку и тестовую папку, чтобы у нас был следующий формат:

flowers/
    train/
        daisy/
            img1.jpg
            ...
        sunflower/
            imga.jpg
            ...
    test/
        daisy/
            img3.jpg
            ...
        sunflower/
            imgc.jpg
            ...

Чтобы разделить папку на учебную и тестовую, мы можем использовать библиотеку под названием split-folders. Если вы еще не установили его, вы можете ввести в командной строке следующую команду pip.

pip install split-folders

Разделить папку с этой библиотекой очень просто. Все, что вам нужно сделать, это указать путь и имя папки, которую вы хотите разделить. Далее вам нужно также указать путь и имя папки выходной папки. Ниже приведена реализация кода для разделения папки с использованием библиотеки split-folders.

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

Давайте определим наши обучающие данные и тестовые данные из этих новых папок.

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

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

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

Построение модели классификации изображений с использованием стандартной модели CNN

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

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

А ниже - реализация кода для создания модели CNN, аналогичной показанной выше.

Как видите, в модели всего 198 061 параметр, из которых 6152 параметра в начале и 2565 в конце. Мы собираемся сравнить эти параметры с байесовской моделью позже.

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

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

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

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

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

Как вы можете видеть выше, модель предсказывает с вероятностью 0,999, что наше изображение принадлежит к классу одуванчиков. Это означает, что наша модель абсолютно уверена, что наш образ - одуванчик. Большой!

Теперь посмотрим на другое изображение из тестовой выборки. Посмотрим, сможет ли модель правильно предсказать изображение розы.

И наша модель снова правильно предсказывает наш образ. Он предсказывает с вероятностью 0,999, что на нашем изображении будет роза.

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

Однако мы не знаем, насколько уверена наша модель в присвоении значения вероятности 0,999 каждому из наших изображений. Действительно ли модель уверена в том, что нашим изображениям присваивается значение вероятности 0,999? Мы также не знаем, знает ли модель то, чего она не знает.

Мы не можем ответить на все вышеперечисленные вопросы с помощью стандартного CNN. Вот почему нам нужно построить модель CNN с байесовской точки зрения. Затем давайте построим модель CNN на основе байесовской точки зрения.

Построение модели классификации изображений с использованием байесовских нейронных сетей

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

Если вы еще не установили библиотеку TensorFlow Probability, вы можете сделать это, набрав в командной строке следующую команду pip.

pip install tensorflow-probability 

Обратите внимание, что вам необходимо обновить библиотеку TensorFlow как минимум до версии 2.3.0, чтобы вы могли установить TensorFlow Probability.

Затем давайте импортируем необходимые библиотеки для построения наших нейронных сетей.

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

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

Слой Convolutional2DReparameterization

Для первого слоя свертки вместо обычного слоя CNN Conv2D мы собираемся использовать Convolutional2DReparameterization layer. Смысл этого слоя состоит в том, чтобы учесть алеаторическую неопределенность, то есть неопределенность, которая возникает из-за качества данных. Таким образом, вместо создания вывода с тем же детерминированным значением, что и для стандартной CNN, он будет создавать вывод, полученный из распределения.

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

  • Приоритет для параметров ядра и смещения - это предварительное представление о том, как выглядит распределение, до того, как данные будут приняты во внимание. В этом посте мы собираемся определить приоритет как для параметров ядра, так и для параметров смещения с помощью функции нормального распределения по умолчанию из TensorFlow. Это нормальное распределение содержит необучаемые параметры, что имеет смысл для нашего априорного значения.
kernel_prior_fn = tfpl.default_multivariate_normal_fn
bias_prior_fn = tfpl.default_multivariate_normal_fn,
  • Апостериорный параметр ядра и смещения - это апостериорное представление о том, как выглядит распределение после просмотра свидетельств из данных. Поскольку это убеждение после просмотра данных, нам нужно определить апостериор с обучаемыми параметрами. По этой причине мы можем использовать функцию по умолчанию из TensorFlow для нормального распределения.
kernel_posterior_fn = tfpl.default_mean_field_normal_fn(is_singular=False)
bias_posterior_fn = tfpl.default_mean_field_normal_fn(is_singular=False)
  • Дивергенция Кульбака-Лейблера - это метод измерения того, чем одно распределение отличается от эталонного. В нашем случае этот метод используется для измерения расхождения наших априорных и апостериорных значений. Чем ниже значение или когда его значение равно 0, это означает, что два распределения происходят из одного и того же распределения. Если вы хотите узнать больше о расхождении Кульбака-Лейблера, я отсылаю вас к следующей статье:


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

divergence_fn = lambda q,p,_:tfd.kl_divergence(q,p)/3457
kernel_divergence_fn = divergence_fn
bias_divergence_fn = divergence_fn

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

divergence_fn = lambda q,p,_:tfd.kl_divergence(q,p)/3457
tfpl.Convolution2DReparameterization(
     input_shape=(75,75,3), 
     filters=8, 
     kernel_size=16,
     activation='relu',
     kernel_prior_fn = tfpl.default_multivariate_normal_fn,
     kernel_posterior_fn =                                                                                                                                                                                                                                                                           tfpl.default_mean_field_normal_fn(is_singular=False)
     kernel_divergence_fn = divergence_fn,
     bias_prior_fn = tfpl.default_multivariate_normal_fn,
     bias_posterior_fn = tfpl.default_mean_field_normal_fn(is_singular=False),
     bias_divergence_fn = divergence_fn)

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

Набор слоев после этого слоя такой же, как и в предыдущем стандартном CNN. Отличается последний плотный слой. Вместо обычного плотного слоя мы будем использовать слой DenseReparameterization.

Слой плотной репараметризации

Если слой Convolutional2DReparameterization в начале должен учитывать алеаторическую неопределенность, этот слой DenseReparameterization применяется для учета эпистемической неопределенности. Эпистемическая неопределенность - это неопределенность, которая возникает из самой модели.

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

tfpl.DenseReparameterization(
     units = tfpl.OneHotCategorical.params_size(5), 
     activation=None,
     kernel_prior_fn = tfpl.default_multivariate_normal_fn,
     kernel_posterior_fn =                                                                                                                                                                                                                                                                           tfpl.default_mean_field_normal_fn(is_singular=False)
     kernel_divergence_fn = divergence_fn,
     bias_prior_fn = tfpl.default_multivariate_normal_fn,
     bias_posterior_fn = tfpl.default_mean_field_normal_fn(is_singular=False),
     bias_divergence_fn = divergence_fn)

Ниже приведена полная реализация для построения нашей байесовской модели CNN.

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

Это связано с тем, что мы изменили вес параметров с одного детерминированного значения на значение, полученное из распределения. Поскольку мы использовали нормальное распределение, оно имеет два параметра вместо одного: среднее значение и стандартное отклонение.

Компиляция и обучение модели

Далее мы можем скомпилировать модель. Как и раньше, мы собираемся использовать оптимизатор Adam, и метриками будет точность модели. Однако мы собираемся использовать отрицательное логарифмическое правдоподобие в качестве функции потерь для нашей байесовской модели CNN, чтобы максимизировать оценку правдоподобия среднего и стандартного отклонения весов.

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

Прогнозирование изображений на основе тестовых данных

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

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

Затем, основываясь на этих 300 прогнозах, мы можем создать 95% интервал прогнозирования для вероятностей. Ниже приведена функция для реализации этого.

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

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

Теперь давайте посмотрим на наше второе изображение - розу.

С нашей предыдущей стандартной CNN наша модель правильно предсказывает, что наше изображение - роза, со значением вероятности 0,999. Выглядит отлично. Однако, если вы посмотрите на результат нашей байесовской модели CNN выше, алеаторическая и эпистемологическая неопределенность действительно существует при прогнозировании нашего изображения розы:

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

Как упоминалось ранее, мы назначаем модель для предсказания изображения розы 300 раз, а не только один раз. Из этих 300 предсказаний за один прогон модель может предсказать наш образ в виде розы с вероятностью 0,1 и одуванчика с вероятностью 0,8. В другом прогоне он может предсказать наш образ в виде розы с вероятностью 1,0 и одуванчика с вероятностью 0. Вариабельность значения вероятности огромна в нашей модели CNN.

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

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

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