Алгоритм backpropagation через уровень локализации локального ответа (LRN)

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

Поскольку уровень нормализации не имеет параметров, я мог бы предположить две возможные опции:

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

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

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

EDIT1:

Слой представляет собой стандартный слой в caffe, как описано здесь http://caffe.berkeleyvision.org/tutorial/layers.html (см. "Нормализация локального ответа (LRN)" ).

Реализация уровня в переходе вперед описана в разделе 3.3 документа alexNet: http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf

EDIT2:

Я считаю, что алгоритмы прямого и обратного прохода описаны в обеих библиотеках Torch: https://github.com/soumith/cudnn.torch/blob/master/SpatialCrossMapLRN.lua

и в библиотеке Caffe: https://github.com/BVLC/caffe/blob/master/src/caffe/layers/lrn_layer.cpp

Пожалуйста, может ли кто-нибудь, кто знаком с любым из них, перевести метод для этапа обратного прохода на простой английский?

Ответ 1

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

Из кода в Caffe, с которым вы связаны, я вижу, что они берут ошибку в каждом нейроне в качестве параметра и вычисляют ошибку для предыдущего слоя, выполняя следующие действия:

Во-первых, в прямом проходе они кэшируют так называемую шкалу, которая вычисляется (в терминах бумаги AlexNet, см. формулу из раздела 3.3) как:

scale_i = k + alpha / n * sum(a_j ^ 2)

Здесь и далее sum суммируется индексом j и идет от max(0, i - n/2) до min(N, i + n/2)

(обратите внимание, что в статье они не нормализуются на n, поэтому я предполагаю, что это то, что Caffe делает иначе, чем AlexNet). Первой проход затем вычисляется как b_i = a_i + scale_i ^ -beta.

Для обратного распространения ошибки скажем, что ошибка, исходящая из следующего уровня, равна be_i, а ошибка, которую нам нужно вычислить, равна ae_i. Затем ae_i вычисляется как:

ae_i = scale_i ^ -b * be_i - (2 * alpha * beta / n) * a_i * sum(be_j * b_j / scale_j)

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

  • Когда вы вычисляете слагаемые для суммы, выделите массив размером N + n - 1 и поместите его с n/2 нулями на каждом конце. Таким образом вы можете вычислить сумму от i - n/2 до i + n/2, не заботясь о том, чтобы идти ниже нуля и выше n.

  • Вам не нужно перекомпоновать sum на каждой итерации, вместо этого заранее вычислите слагаемые (a_j^2 для переднего прохода, be_j * b_j / scale_j для обратного прохода), затем вычислите sum для i = 0, а затем для каждого последовательного i просто добавьте addend[i + n/2] и вычтите addend[i - n/2 - 1], он даст вам значение суммы для нового значения i в постоянное время.

Ответ 2

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

Ответ 3

У меня есть альтернативная формулировка назад, и я не знаю, эквивалентен ли она кофе:

Итак, кофе:

ae_i = scale_i ^ -b * be_i - (2 * alpha * beta / n) * a_i * sum(be_j * b_j / scale_j)

дифференцируя исходное выражение

b_i = a_i/(scale_i^-b)

Я получаю

ae_i = scale_i ^ -b * be_i - (2 * alpha * beta / n) * a_i * be_i*sum(ae_j)/scale_i^(-b-1)