Сверточные нейронные сети — различия между версиями
| Строка 44: | Строка 44: | ||
=== VGG === | === VGG === | ||
Семейство архитектур нейронных сетей, которое включает в себя, в частности, VGG-11, VGG-13, VGG-16 и VGG-19. Победитель соревнования ImageNet 2013-ого года (VGG-16), набравший точность 92.7%. Одной из отличительных особенностей является использование ядер свертки небольшого размера (3x3, в отличие от больших ядер размера 7x7 или 11x11). | Семейство архитектур нейронных сетей, которое включает в себя, в частности, VGG-11, VGG-13, VGG-16 и VGG-19. Победитель соревнования ImageNet 2013-ого года (VGG-16), набравший точность 92.7%. Одной из отличительных особенностей является использование ядер свертки небольшого размера (3x3, в отличие от больших ядер размера 7x7 или 11x11). | ||
| + | |||
| + | == Примеры кода == | ||
| + | ===Scala=== | ||
| + | Пример кода с библиотекой DeepLearning.scala<ref>[https://deeplearning.thoughtworks.school/index.html DeepLearning.scala]</ref> | ||
| + | // Загрузка датасета | ||
| + | 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 } | ||
| + | } | ||
| + | } | ||
Версия 20:38, 17 января 2019
Сверточная нейронная сеть (англ. convolutional neural network, CNN) — специальная архитектура нейронных сетей, предложенная Яном Лекуном, изначально нацеленная на эффективное распознавание изображений.
Содержание
Свертка
Свертка (англ. convolution) — операция над парой матриц (размера ) и (размера ), результатом которой является матрица размера . Каждый элемент результата вычисляется как скалярное произведение матрицы и некоторой подматрицы такого же размера (подматрица определяется положением элемента в результате). То есть, . На изображении справа можно видеть, как матрица «двигается» по матрице , и в каждом положении считается скалярное произведение матрицы и той части матрицы , на которую она сейчас наложена. Получившееся число записывается в соответствующий элемент результата.
Логический смысл свертки такой — чем больше величина элемента свертки, тем больше эта часть матрицы была похожа на матрицу (похожа в смысле скалярного произведения). Поэтому матрицу называют изображением, а матрицу — фильтром или образцом.
Структура сверточной нейронной сети
В сверточной нейронной сети выходы промежуточных слоев образуют матрицу (изображение) или набор матриц (несколько слоёв изображения). Так, например, на вход сверточной нейронной сети можно подавать три слоя изображения (R-, G-, B-каналы изображения). Основными видами слоев в сверточной нейронной сети являются сверточные слои (англ. convolutional layer), пулинговые слои (англ. pooling layer) и полносвязные слои[на 09.01.19 не создан] (англ. fully-connected layer).
Сверточный слой
Сверточный слой нейронной сети представляет из себя применение операции свертки к выходам с предыдущего слоя, где веса ядра свертки являются обучаемыми параметрами. Еще один обучаемый вес используется в качестве константного сдвига (англ. bias). При этом есть несколько важных деталей:
- В одном сверточном слое может быть несколько сверток. В этом случае для каждой свертки на выходе получится своё изображение. Например, если вход имел размерность , а в слое было сверток с ядром размерности , то выход будет иметь размерность .
- Ядра свертки могут быть трёхмерными. Свертка трехмерного входа с трехмерным ядром происходит аналогично, просто скалярное произведение считается еще и по всем слоям изображения. Например, для усреднения информации о цветах исходного изображения, на первом слое можно использовать свертку размерности . На выходе такого слоя будет уже одно изображение (вместо трёх).
- Можно заметить, что применение операции свертки уменьшает изображение. Также пиксели, которые находятся на границе изображения учавствуют в меньшем количестве сверток, чем внутренние. В связи с этим в сверточных слоях используется дополнение изображения (англ. padding). Выходы с предыдущего слоя дополняются пикселями так, чтобы после свертки сохранился размер изображения (распространенной практикой является дополнять изображение нулями (англ. zero padding), но возможны и другие подходы). Такие свертки называют одинаковыми (англ. same convolution), а свертки без дополнения изображения называются правильными (англ. valid convolution).
- Еще одним параметром сверточного слоя является сдвиг (англ. stride). Хоть обычно свертка применяется подряд для каждого пикселя, иногда используется сдвиг, отличный от единицы — скалярное произведение считается не со всеми возможными положениями ядра, а только с положениями, кратными некоторому сдвигу . Тогда, если если вход имел размерность , а ядро свертки имело размерность и использовался сдвиг , то выход будет иметь размерность .
Пулинговый слой
Пулинговый слой призван снижать размерность изображения. Исходное изображение делится на блоки размером и для каждого блока вычисляется некоторая функция. Чаще всего используется функция максимума (англ. max pooling) или (взвешенного) среднего (англ. (weighted) average pooling). Обучаемых параметров у этого слоя нет. Основные цели пулингового слоя:
- уменьшение изображения, чтобы последующие свертки оперировали над большей областью исходного изображения;
- увеличение инвариантности выхода сети по отношению к малому переносу входа;
- ускорение вычислений.
Известные архитектуры сверточных нейронных сетей
LeNet-5
Нейронная сеть, предложенная Яном Лекуном, для распознавания рукописных цифр MNIST.
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 }
}
}