Сверточные нейронные сети

Материал из Викиконспекты
Перейти к: навигация, поиск

Сверточная нейронная сеть (англ. convolutional neural network, CNN) — специальная архитектура нейронных сетей, предложенная Яном Лекуном, изначально нацеленная на эффективное распознавание изображений.

Свертка

Пример свертки двух матриц размера 5x5 и 3x3

Свертка (англ. convolution) — операция над парой матриц [math]A[/math] (размера [math]n_x\times n_y[/math]) и [math]B[/math] (размера [math]m_x \times m_y[/math]), результатом которой является матрица [math]C = A * B[/math] размера [math](n_x-m_x+1)\times (n_y-m_y+1)[/math]. Каждый элемент результата вычисляется как скалярное произведение матрицы [math]B[/math] и некоторой подматрицы [math]A[/math] такого же размера (подматрица определяется положением элемента в результате). То есть, [math]C_{i,j} = \sum_{u = 0}^{m_x-1}\sum_{v = 0}^{m_y - 1}A_{i+u,j+v}B_{u,v}[/math]. На изображении справа можно видеть, как матрица [math]B[/math] «двигается» по матрице [math]A[/math], и в каждом положении считается скалярное произведение матрицы [math]B[/math] и той части матрицы [math]A[/math], на которую она сейчас наложена. Получившееся число записывается в соответствующий элемент результата.

Логический смысл свертки такой — чем больше величина элемента свертки, тем больше эта часть матрицы [math]A[/math] была похожа на матрицу [math]B[/math] (похожа в смысле скалярного произведения). Поэтому матрицу [math]A[/math] называют изображением, а матрицу [math]B[/math]фильтром или образцом.

Структура сверточной нейронной сети

В сверточной нейронной сети выходы промежуточных слоев образуют матрицу (изображение) или набор матриц (несколько слоёв изображения). Так, например, на вход сверточной нейронной сети можно подавать три слоя изображения (R-, G-, B-каналы изображения). Основными видами слоев в сверточной нейронной сети являются сверточные слои (англ. convolutional layer), пулинговые слои (англ. pooling layer) и полносвязные слои (англ. fully-connected layer).

Сверточный слой

Пример свертки двух матриц с дополнением нулями и сдвигом 2
Пример свертки с трехмерным ядром

Сверточный слой нейронной сети представляет из себя применение операции свертки к выходам с предыдущего слоя, где веса ядра свертки являются обучаемыми параметрами. Еще один обучаемый вес используется в качестве константного сдвига (англ. bias). При этом есть несколько важных деталей:

  • В одном сверточном слое может быть несколько сверток. В этом случае для каждой свертки на выходе получится своё изображение. Например, если вход имел размерность [math]w\times h[/math], а в слое было [math]n[/math] сверток с ядром размерности [math]k_x\times k_y[/math], то выход будет иметь размерность [math]n\times(w - k_x + 1)\times(h - k_y + 1)[/math].
  • Ядра свертки могут быть трёхмерными. Свертка трехмерного входа с трехмерным ядром происходит аналогично, просто скалярное произведение считается еще и по всем слоям изображения. Например, для усреднения информации о цветах исходного изображения, на первом слое можно использовать свертку размерности [math]3\times w \times h[/math]. На выходе такого слоя будет уже одно изображение (вместо трёх).
  • Можно заметить, что применение операции свертки уменьшает изображение. Также пиксели, которые находятся на границе изображения учавствуют в меньшем количестве сверток, чем внутренние. В связи с этим в сверточных слоях используется дополнение изображения (англ. padding). Выходы с предыдущего слоя дополняются пикселями так, чтобы после свертки сохранился размер изображения. Такие свертки называют одинаковыми (англ. same convolution), а свертки без дополнения изображения называются правильными (англ. valid convolution). Среди способов, которыми можно заполнить новые пиксели, можно выделить следующие:
    • zero shift: 00[ABC]00
    • border extension: AA[ABC]CC
    • mirror shift: BA[ABC]CB
    • cyclic shift: BC[ABC]AB
  • Еще одним параметром сверточного слоя является сдвиг (англ. stride). Хоть обычно свертка применяется подряд для каждого пикселя, иногда используется сдвиг, отличный от единицы — скалярное произведение считается не со всеми возможными положениями ядра, а только с положениями, кратными некоторому сдвигу [math]s[/math]. Тогда, если если вход имел размерность [math]w\times h[/math], а ядро свертки имело размерность [math]k_x\times k_y[/math] и использовался сдвиг [math]s[/math], то выход будет иметь размерность [math]\lfloor\frac{w - k_x}{s} + 1\rfloor\times\lfloor\frac{h - k_y}{s} + 1\rfloor[/math].


Пулинговый слой

Пример операции пулинга с функцией максимума

Пулинговый слой призван снижать размерность изображения. Исходное изображение делится на блоки размером [math]w\times h[/math] и для каждого блока вычисляется некоторая функция. Чаще всего используется функция максимума (англ. max pooling) или (взвешенного) среднего (англ. (weighted) average pooling). Обучаемых параметров у этого слоя нет. Основные цели пулингового слоя:

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

Известные архитектуры сверточных нейронных сетей

LeNet-5

Архитектура LeNet-5

Нейронная сеть, предложенная Яном Лекуном, для распознавания рукописных цифр MNIST.

AlexNet

Архитектура AlexNet

Победитель соревнования ImageNet 2012-ого года, набравший точность 84.6%. Была реализована с использованием CUDA для повышения производительности. Состоит из двух отдельных частей, которые слабо взаимодействуют друг с другом, что позволяет исполнять их параллельно на разных GPU с минимальным обменом данными.

VGG

Семейство архитектур нейронных сетей, которое включает в себя, в частности, VGG-11, VGG-13, VGG-16 и VGG-19. Победитель соревнования ImageNet 2013-ого года (VGG-16), набравший точность 92.7%. Одной из отличительных особенностей является использование ядер свертки небольшого размера (3x3, в отличие от больших ядер размера 7x7 или 11x11).

Примеры кода

Scala

Пример кода с библиотекой DeepLearning.scala[1]

   // Загрузка датасета
   val cifar10 = Cifar10.load().blockingAwait
   // Определение слоёв
   def myNeuralNetwork(input: INDArray):  INDArrayLayer = {
       val cnnLayer = maxPool(relu(conv2d(input.reshape(input.shape()(0), Cifar10.NumberOfChannels, PixelHeight, PixelWidth), cnnWeight, cnnBias, (KernelHeight, KernelWidth), (Stride, Stride), (Padding, Padding))), (PoolSize, PoolSize))
       val affineRuleOfCnnLayer = relu(affine(cnnLayer.reshape(input.shape()(0), NumFilters * (PixelHeight / PoolSize) * (PixelWidth / PoolSize)), affineWeight, affineBias))
       val affineOfaffineRuleOfCnnLayer = affine(affineRuleOfCnnLayer.reshape(input.shape()(0), HiddenDim), affineLastWeight, affineLastBias)
       val softmaxValue = softmax(affineOfaffineRuleOfCnnLayer)
       softmaxValue
   }
   // Определение функции потерь
   def lossFunction(input: INDArray, expectOutput: INDArray): DoubleLayer = { 
       val probabilities = myNeuralNetwork(input)
       -(hyperparameters.log(probabilities) * expectOutput).mean   
   }
   class Trainer(batchSize: Int, numberOfEpoches: Int = 5) {
       import scalaz.std.anyVal._
       import scalaz.syntax.all._
       @volatile
       private var isShuttingDown: Boolean = false
       private val lossBuffer = scala.collection.mutable.Buffer.empty[Double]
       def plotLoss(): Unit = Seq(Scatter(lossBuffer.indices, lossBuffer)).plot(title = "loss by time")
       def interrupt(): Unit = isShuttingDown = true
       def startTrain(): Unit = {
           @monadic[Future]
           def trainTask: Future[Unit] = {
               isShuttingDown = false
               var epoch = 0
           
               while (epoch < numberOfEpoches && !isShuttingDown) {
                   val cifar10 = Cifar10.load().blockingAwait
                   val iterator = cifar10.epoch(batchSize).zipWithIndex
                   while (iterator.hasNext && !isShuttingDown) {
                       val (Cifar10.Batch(labels, batch), i) = iterator.next()
                       val loss = lossFunction(batch, labels).train.each
                       lossBuffer += loss
                       hyperparameters.logger.info(s"epoch=epoch iteration=i batchSize=batchSize loss=loss")
                   }
                   epoch += 1
               }
               hyperparameters.logger.info("Done")
           }
           trainTask.onComplete { tryUnit: scala.util.Try[Unit] => tryUnit.get }
       }
   }

См. также

  • DeepLearning.scala
  • Источник — «http://neerc.ifmo.ru/wiki/index.php?title=Сверточные_нейронные_сети&oldid=68586»