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

В недавних научных статьях, таких как статья Чжи и др. [1], удалось использовать функции, извлеченные из последнего слоя объединения, и использовать вектор для целей поиска изображений на основе содержимого (CBIR).

Возможно, еще более интересным открытием является тот факт, что слабо обученные CNN по-прежнему очень хорошо справляются с задачами CBIR. Говоря о слабом обучении, я не имею в виду, что они обучены на небольшом наборе данных, но я имею в виду, что CBIR продолжает хорошо работать даже над задачами, не связанными с теми, для которых ранее была обучена CNN.

На рисунке выше показана популярная архитектура VGG-16. Извлечение пространственных объектов из полностью связанного слоя приведет к вектору длиной 4096. Что является .. большим, но, возможно, не таким уж большим. Однако, если вы извлечете из слоя максимального пула, у вас будет вектор объектов размером 7x7x512, что в сумме составляет 25088. С другой стороны, его сжатие приведет к вектору меньшего размера, обычно в вашей дружественной среде будут методы которые учитывают многомерное векторное сжатие.

Итак, как извлечь эти функции? Библиотеки Python, такие как Caffè, хорошо подходят для этой задачи, поскольку на самом деле caffè - это естественная эволюция decaf, которая является одной из первых структур, созданных для специального извлечения векторов признаков из CNN.

В этом проекте я использовал Deeplearning4j - потрясающую среду Java, которая позволяет проводить вычисления с использованием графического процессора с нейронными сетями.
Основные шаги для извлечения функций следующие:

  1. Создайте экземпляр ComputationGraph
  2. Изменить размер и нормализовать изображение с учетом особенностей данной CNN (224x224 для VGG-16)
  3. Прямая связь с изображением
  4. Получите INDArray из желаемого выходного слоя (в моем случае «fc2» или «pool5»)
  5. Сравните этот массив INDArray с другими с помощью простой меры моделирования, такой как L2 или косинус

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

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

Map<String, INDArray> stringINDArrayMap = extract(file, vgg16transfer);
INDArray fc2 = stringINDArrayMap.get(EXTRACTION_LAYER);
INDArray normalized = fc2.div(fc2.norm2Number());
saveCompressed(file,normalized);

Если метод извлечения в основном содержит операции шага 2 и vgg16.feedForward (image, false) для шага 3. А saveCompressed - это просто вызов метода, который сжимает функции с помощью базового компрессора, предоставляемого ND4j, но вы, возможно, можете использовать массив как есть.