Google предложил глубокую нейронную сеть свертки под названием Inception, которая показала лучшие результаты по классификации и обнаружению в ILSVRC 2014.

В конкурсе ImageNet Large Scale Visual Recognition Challenge (ILSVRC) оцениваются алгоритмы обнаружения объектов и классификации изображений в больших масштабах. Одной из мотиваций высокого уровня является предоставление исследователям возможности сравнивать прогресс в обнаружении более широкого круга объектов, используя в своих интересах довольно дорогостоящие усилия по маркировке. Другой мотивацией является измерение прогресса компьютерного зрения для крупномасштабного индексирования изображений для поиска и аннотирования

https://www.image-net.org/challenges/LSVRC/

«Погружаемся глубже с извилинами» на самом деле вдохновлен интернет-мемом: «Нам нужно идти глубже».

В ILSVRC 2014 GoogLeNet использовал в 12 раз меньше параметров, чем AlexNet, использовавшийся 2 года назад в соревновании 2012 года.

Проблемы, которые Inception v1 пытается решить

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

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

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

Решение

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

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

Одна из проблем «наивного» подхода заключается в том, что даже свертки 5×5 могут потребовать больших вычислительных ресурсов. Эта проблема возникает чаще после добавления пула.

Чтобы сделать наши сети недорогими в вычислительном отношении, авторы применили уменьшение размерности, добавив свертки 1×1 перед свертками 3×3 и 5×5. Давайте посмотрим, как это влияет на количество параметров в нейронной сети.

Давайте посмотрим, какой будет свертка 5 × 5 в вычислительном отношении.

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

(⁵²)(192)(32)(2⁸²) = 120 422 400 операций

Чтобы уменьшить такое большое количество операций, можно использовать уменьшение размерности. Здесь это делается путем свертки с фильтрами 1 × 1 перед выполнением свертки с более крупными фильтрами.

После уменьшения размерности количество операций для свертки 5×5 становится:

Количество операций для приведенной выше свертки становится

(¹²)(192)(16)(2⁸²) = 2 408 448 операций для свертки 1 × 1 и,

(⁵²)(16)(32)(2⁸²) = 10 035 200 операций для свертки 5 × 5.

Всего будет 2 408 448 + 10 035 200 = 12 443 648 операций. Существует большое количество сокращений в вычислениях.

Итак, после применения уменьшения размерности наш начальный модуль становится:

GoogLeNet был построен с использованием начального модуля с уменьшением размерности. GoogLeNet состоит из 22-уровневой глубокой сети (27 с включенными слоями пула). Все свертки, включая свертки внутри начального модуля, используют выпрямленную линейную активацию.

Все свертки, в том числе внутри начальных модулей, используют выпрямленную линейную активацию. Размер рецептивного поля в нашей сети составляет 224×224 с использованием цветовых каналов RGB со средним вычитанием. «# 3x3reduce» и «# 5x5reduce» обозначают количество фильтров 1 × 1 в слое сокращения, используемом до сверток 3 × 3 и 5 × 5. Можно увидеть количество фильтров 1 × 1 в слое проекции после встроенного максимального объединения в столбце pool proj. Все эти слои редукции/проекции также используют выпрямленную линейную активацию

Исходный документ

GoogLeNet представляет собой 22-уровневую структуру, в которую входят только слои с параметрами. При такой глубокой сети может возникнуть такая проблема, как исчезновение градиента. Чтобы устранить это, авторы ввели вспомогательные классификаторы, которые связаны с промежуточными слоями и помогают обратному распространению сигналов градиента. Эти вспомогательные классификаторы добавляются поверх выходных данных модулей Inception (4a) и (4d). Потери от вспомогательных классификаторов добавляются во время обучения и отбрасываются во время логического вывода.

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

  • Средний объединяющий слой с размером фильтра 5×5 и шагом 3, в результате чего получается 4×4 512 выходных данных для этапа (4a) и 4×4 528 для этапа (4d).
  • Свертка 1×1 со 128 фильтрами для уменьшения размерности и выпрямленной линейной активации.
  • Полносвязный слой с 1024 единицами и выпрямленной линейной активацией.
  • Слой отсева с коэффициентом отброшенных выходов 70%.
  • Линейный слой с softmax loss в качестве классификатора (предсказывает те же 1000 классов, что и основной классификатор, но удаляется во время вывода).

Систематический взгляд на архитектуру GoogLeNet показан ниже:

GoogLeNet состоит из 9 начальных модулей, а именно 3a, 3b, 4a, 4b, 4c, 4d, 4e, 5a и 5b.

Реализация GoogLeNet

Зная о начальном модуле и его включении в архитектуру GoogLeNet, мы теперь реализуем GoogLeNet в тензорном потоке. Эта реализация GoogLeNet вдохновлена ​​​​аналитической статьей видья в начальной сети.

Импорт необходимых библиотек:

from tensorflow.keras.layers import Layer
import tensorflow.keras.backend as K
import tensorflow as tf
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Conv2D, MaxPool2D, Dropout, Dense, Input, concatenate, GlobalAveragePooling2D, AveragePooling2D, Flatten
import cv2
import numpy as np
from keras.utils import np_utils
import math
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.callbacks import LearningRateScheduler

Далее мы используем набор данных cifar10 в качестве наших данных.

num_classes = 10
def load_cifar_data(img_rows, img_cols):
    #Loading training and validation datasets
    (X_train, Y_train), (X_valid, Y_valid) = cifar10.load_data()
    #Resizing images
    X_train = np.array([cv2.resize(img, (img_rows, img_cols)) for       img in X_train[:,:,:,:]])
    X_valid = np.array([cv2.resize(img, (img_rows, img_cols)) for img in X_valid[:,:,:,:]])
    #Transform targets to keras compatible format
    Y_train = np_utils.to_categorical(Y_train, num_classes)
    Y_valid = np_utils.to_categorical(Y_valid, num_classes)
    X_train = X_train.astype('float32')
    X_valid = X_valid.astype('float32')
    #Preprocessing data
    X_train = X_train / 255.0
    Y_train = X_valid / 255.0
return X_train, Y_train, X_valid, Y_valid
X_train, Y_trian, X_test, y_test = load_cifar_data(224,224)

Далее идет наш начальный модуль

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

def inception_module(x, filters_1x1, filters_3x3_reduce, filters_3x3, filters_5x5_reduce, filters_5x5, filters_pool_proj, name=None):
    
    conv_1x1 = Conv2D(filters_1x1, (1,1), activation='relu', kernel_initializer=kernel_init, bias_initializer=bias_init)(x)
    conv_3x3 = Conv2D(filters_3x3_reduce, (1,1), padding='same', activation='relu', kernel_initializer=kernel_init, bias_initializer=bias_init)(x)  
    conv_3x3 = Conv2D(filters_3x3, (3,3), padding='same', activation='relu', kernel_initializer=kernel_init, bias_initializer=bias_init)(conv_3x3)
    conv_5x5 = Conv2D(filters_5x5_reduce, (1,1), padding='same', activation='relu', kernel_initializer=kernel_init, bias_initializer=bias_init)(x)
    conv_5x5 = Conv2D(filters_5x5, (3,3), padding='same', activation='relu', kernel_initializer=kernel_init, bias_initializer=bias_init)(conv_5x5)
    pool_proj = MaxPool2D((3,3), strides=(1,1), padding='same')(x)
    pool_proj = Conv2D(filters_pool_proj, (1, 1), padding='same',  activation='relu', kernel_initializer=kernel_init,  bias_initializer=bias_init)(pool_proj)
    output = concatenate([conv_1x1, conv_3x3, conv_5x5, pool_proj], axis=3, name=name)
return output

Получение сводки по модели

model.summary()
epochs = 25 
initial_lrate = 0.01 
def decay(epoch, steps=100): 
    initial_lrate = 0.01 
    drop = 0.96 
    epochs_drop = 8 
    lrate = initial_lrate * math.pow(drop, math.floor((1+epoch)/epochs_drop)) 
return lrate sgd = SGD(lr=initial_lrate, momentum=0.9, nesterov=False) 
lr_sc = LearningRateScheduler(decay, verbose=1) 
model.compile(loss=['categorical_crossentropy', 'categorical_crossentropy', 'categorical_crossentropy'], loss_weights=[1, 0.3, 0.3], optimizer=sgd, metrics=['accuracy'])

Использование нашей модели для подбора обучающих данных

history = model.fit(X_train, [y_train, y_train, y_train], validation_data=(X_test, [y_test, y_test, y_test]), epochs=epochs, batch_size=256, callbacks=[lr_sc])

Ссылки

Первоначально опубликовано на https://prabinnepal.com 5 июня 2020 г.