Матричная форма обратного распространения с нормализацией партии


12

Нормализация партии была приписана существенным улучшениям производительности в глубоких нейронных сетях. Много материала в интернете показывает, как реализовать его на основе активации за активацию. Я уже реализовал backprop, используя матричную алгебру, и учитывая, что я работаю на языках высокого уровня (полагаясь Rcpp(и, в конечном итоге, на GPU) на плотное матричное умножение), вырывая все и прибегая к for-loops, возможно, замедлю мой код по существу, в дополнение к огромной боли.

Функция нормализации партии имеет вид где

b(xp)=γ(xp−μxp)σxp−1+β
  • xp - это й узел, прежде чем он активируетсяp
  • γ и - скалярные параметрыβ
  • μxp и - среднее значение и SD для . (Обратите внимание, что обычно используется квадратный корень из дисперсии плюс коэффициент выдумки - давайте предположим ненулевые элементы для компактности)σxpxp

В матричной форме пакетная нормализация для всего слоя будет где

b(X)=(γ⊗1p)⊙(X−μX)⊙σX−1+(β⊗1p)
  • N × pX равноN×p
  • 1N является вектором столбцов
  • β pγ и теперь являются строчными векторами параметров нормализации для каждого слояβp
  • σ X N × p NμX и - это матриц, где каждый столбец является вектором по столбцам средних значений и стандартных отклоненийσXN×pN
  • ⊙⊗ - произведение Кронекера, а - поэлементное произведение (Адамар).⊙

Очень простая однослойная нейронная сеть без пакетной нормализации и непрерывного результата:

y=a(XΓ1)Γ2+ϵ

где

  • p 1 × p 2Γ1 - этоp1×p2
  • p 2 × 1Γ2 - этоp2×1
  • a(.) является функцией активации

Если потеря равна , то градиенты будут ∂ RR=N−1∑(y−y^)2

∂R∂Γ1=−2VTϵ^∂R∂Γ2=XT(a′(XΓ1)⊙−2ϵ^Γ2T)

где

  • V=a(XΓ1)
  • ϵ^=y−y^

При нормализации партии сеть становится или Я не знаю, как вычислить производные произведений Адамара и Кронекера. Что касается продуктов Kronecker, литература становится довольно загадочной. y = a ( ( γ ⊗ 1 N ) ⊙ ( X Γ 1 - μ X Γ 1 ) ⊙ σ - 1 X Γ 1 + ( β ⊗ 1 N ) ) Γ 2

y=a(b(XΓ1))Γ2
y=a((γ⊗1N)⊙(XΓ1−μXΓ1)⊙σXΓ1−1+(β⊗1N))Γ2

Существуют ли практические способы вычисления , и в рамках матрицы? Простое выражение, не прибегая к вычислениям по узлам?∂ R / ∂ β ∂ R / ∂ Γ 1∂R/∂γ∂R/∂β∂R/∂Γ1

Обновление 1:

Я разобрался с . Это: Некоторый код R демонстрирует, что это эквивалентно циклическому способу сделать это. Сначала настройте поддельные данные:∂R/∂β

1NT(a′(XΓ1)⊙−2ϵ^Γ2T)
set.seed(1)
library(dplyr)
library(foreach)

#numbers of obs, variables, and hidden layers
N <- 10
p1 <- 7
p2 <- 4
a <- function (v) {
  v[v < 0] <- 0
  v
}
ap <- function (v) {
  v[v < 0] <- 0
  v[v >= 0] <- 1
  v
}

# parameters
G1 <- matrix(rnorm(p1*p2), nrow = p1)
G2 <- rnorm(p2)
gamma <- 1:p2+1
beta <- (1:p2+1)*-1
# error
u <- rnorm(10)

# matrix batch norm function
b <- function(x, bet = beta, gam = gamma){
  xs <- scale(x)
  gk <- t(matrix(gam)) %x% matrix(rep(1, N))
  bk <- t(matrix(bet)) %x% matrix(rep(1, N))
  gk*xs+bk
}
# activation-wise batch norm function
bi <- function(x, i){
  xs <- scale(x)
  gk <- t(matrix(gamma[i]))
  bk <- t(matrix(beta[i]))
  suppressWarnings(gk*xs[,i]+bk)
}

X <- round(runif(N*p1, -5, 5)) %>% matrix(nrow = N)
# the neural net
y <- a(b(X %*% G1)) %*% G2 + u

Затем вычислите производные:

# drdbeta -- the matrix way
drdb <- matrix(rep(1, N*1), nrow = 1) %*% (-2*u %*% t(G2) * ap(b(X%*%G1)))
drdb
           [,1]      [,2]    [,3]        [,4]
[1,] -0.4460901 0.3899186 1.26758 -0.09589582
# the looping way
foreach(i = 1:4, .combine = c) %do%{
  sum(-2*u*matrix(ap(bi(X[,i, drop = FALSE]%*%G1[i,], i)))*G2[i])
}
[1] -0.44609015  0.38991862  1.26758024 -0.09589582

Они совпадают. Но я все еще смущен, потому что я действительно не знаю, почему это работает. В заметках MatCalc, на которые ссылается @Mark L. Stone, говорится, что производная от должна бытьβ⊗1N

∂A⊗B∂A=(Inq⊗Tmp)(In⊗vec(B)⊗Im)
где нижние индексы , и , , являются размерами и . - это матрица коммутации, которая здесь равна 1, поскольку оба входа являются векторами. Я пробую это и получаю результат, который не кажется полезным:mnpqABT
# playing with the kroneker derivative rule
A <- t(matrix(beta)) 
B <- matrix(rep(1, N))
diag(rep(1, ncol(A) *ncol(B))) %*% diag(rep(1, ncol(A))) %x% (B) %x% diag(nrow(A))
     [,1] [,2] [,3] [,4]
 [1,]    1    0    0    0
 [2,]    1    0    0    0
 snip
[13,]    0    1    0    0
[14,]    0    1    0    0
snip
[28,]    0    0    1    0
[29,]    0    0    1    0
[snip
[39,]    0    0    0    1
[40,]    0    0    0    1

Это не соответствует. Очевидно, я не понимаю эти производные правила Кронекера. Помочь с этим было бы здорово. Я все еще застрял на других производных, для и - они сложнее, потому что они не вводятся аддитивно, как .γΓ1β⊗1

Обновление 2

Читая учебники, я вполне уверен, что и потребует использования оператора. Но я, очевидно, не в состоянии достаточно следовать выводам, чтобы можно было перевести их в код. Например, будет включать в себя получение производной от по , где (который мы можем рассматривать как постоянную матрицу на данный момент). ∂R/∂Γ1∂R/∂γvec()∂R/∂Γ1w⊙XΓ1Γ1w≡(γ⊗1)⊙σXΓ1−1

Мой инстинкт должен просто сказать «ответ », но это, очевидно, не работает, потому что не совместимо с .w⊙XwX

Я знаю, что

∂(A⊙B)=∂A⊙B+A⊙∂B

и из этого , что

∂vec(w⊙XΓ1)∂vec(Γ1)T=vec(XΓ1)I∂vec(w)∂vec(Γ1)T+vec(w)I∂vec(XΓ1)∂vec(Γ1)T
Но я не уверен, как это оценить, не говоря уже о кодировании.

Обновление 3

Делать успехи здесь. Я проснулся в 2 часа ночи прошлой ночью с этой идеей. Математика не подходит для сна.

Здесь есть после некоторого сахара:∂R/∂Γ1

  • w≡(γ⊗1)⊙σXΓ1−1
  • "stub"≡a′(b(XΓ1))⊙−2ϵ^Γ2T

Вот что у вас есть после того, как вы доберетесь до конца правила цепочки: Начните с этого цикла: и будут индексировать столбцы, а является согласованной единичной матрицей:

∂R∂Γ1=∂w⊙XΓ1∂Γ1("stub")
ijI
∂R∂Γij=(wi⊙Xi)T("stub"j)
∂R∂Γij=(IwiXi)T("stub"j)
∂R∂Γij=XiTIwi("stub"j)
tl; dr вы в основном предварительно умножаете заглушку на масштабные коэффициенты batchnorm. Это должно быть эквивалентно:
∂R∂Γ=XT("stub"⊙w)

И на самом деле это:

stub <- (-2*u %*% t(G2) * ap(b(X%*%G1)))
w <- t(matrix(gamma)) %x% matrix(rep(1, N)) * (apply(X%*%G1, 2, sd) %>% t %x% matrix(rep(1, N)))
drdG1 <- t(X) %*% (stub*w)

loop_drdG1 <- drdG1*NA
for (i in 1:7){
  for (j in 1:4){
    loop_drdG1[i,j] <- t(X[,i]) %*% diag(w[,j]) %*% (stub[,j])
  }
}

> loop_drdG1
           [,1]       [,2]       [,3]       [,4]
[1,] -61.531877  122.66157  360.08132 -51.666215
[2,]   7.047767  -14.04947  -41.24316   5.917769
[3,] 124.157678 -247.50384 -726.56422 104.250961
[4,]  44.151682  -88.01478 -258.37333  37.072659
[5,]  22.478082  -44.80924 -131.54056  18.874078
[6,]  22.098857  -44.05327 -129.32135  18.555655
[7,]  79.617345 -158.71430 -465.91653  66.851965
> drdG1
           [,1]       [,2]       [,3]       [,4]
[1,] -61.531877  122.66157  360.08132 -51.666215
[2,]   7.047767  -14.04947  -41.24316   5.917769
[3,] 124.157678 -247.50384 -726.56422 104.250961
[4,]  44.151682  -88.01478 -258.37333  37.072659
[5,]  22.478082  -44.80924 -131.54056  18.874078
[6,]  22.098857  -44.05327 -129.32135  18.555655
[7,]  79.617345 -158.71430 -465.91653  66.851965

Обновление 4

Здесь, я думаю, есть . Первый∂R/∂γ

  • XΓ~≡(XΓ−μXΓ)⊙σXΓ−1
  • γ~≡γ⊗1N

Как и раньше, правило цепочки приводит вас к Зацикливание дает вам Что, как и прежде, в основном является предварительным умножением заглушки. Следовательно, он должен быть эквивалентен:

∂R∂γ~=∂γ~⊙XΓ~∂γ~("stub")
∂R∂γ~i=(XΓ~)iTIγ~i("stub"i)
∂R∂γ~=(XΓ~)T("stub"⊙γ~)

Это своего рода совпадения:

drdg <- t(scale(X %*% G1)) %*% (stub * t(matrix(gamma)) %x% matrix(rep(1, N)))

loop_drdg <- foreach(i = 1:4, .combine = c) %do% {
  t(scale(X %*% G1)[,i]) %*% (stub[,i, drop = F] * gamma[i])  
}

> drdg
           [,1]      [,2]       [,3]       [,4]
[1,]  0.8580574 -1.125017  -4.876398  0.4611406
[2,] -4.5463304  5.960787  25.837103 -2.4433071
[3,]  2.0706860 -2.714919 -11.767849  1.1128364
[4,] -8.5641868 11.228681  48.670853 -4.6025996
> loop_drdg
[1]   0.8580574   5.9607870 -11.7678486  -4.6025996

Диагональ на первом совпадает с вектором на втором. Но на самом деле, поскольку производная относится к матрице, хотя и с определенной структурой, на выходе должна быть похожая матрица с такой же структурой. Должен ли я взять диагональ матричного подхода и просто принять его за ? Я не уверен.γ

Кажется, я ответил на свой вопрос, но я не уверен, что я прав. На этом этапе я приму ответ, который строго доказывает (или опровергает) то, что я вроде как взломал вместе.

while(not_answered){
  print("Bueller?")
  Sys.sleep(1)
}

2
Раздел 9 главы 9 «Матричное дифференциальное исчисление с приложениями в статистике и эконометрике» Магнуса и Нойдекера, 3-е издание janmagnus.nl/misc/mdc2007-3rdedition посвящен дифференциалам продуктов Кронекера и завершается упражнением по дифференциалу продуктов Адамара. «Заметки о матричном исчислении» Пола Л. Факлера www4.ncsu.edu/~pfackler/MatCalc.pdf содержит много материалов по дифференциации продуктов Kronceker
— Марк Л. Стоун

Спасибо за ссылки. Я нашел эти заметки MatCalc и раньше, но они не охватывают Адамара, и в любом случае я никогда не уверен, применимо ли правило из нематричного исчисления к случаю матрицы. Правила продукта, правила цепочки и т. Д. Я посмотрю книгу. Я бы принял ответ, который укажет мне на все ингредиенты, которые мне нужны, чтобы самому его
— нарисовать

почему ты это делаешь? почему бы не использовать фреймворки, такие как Keras / TensorFlow? Реализация этих алгоритмов низкого уровня - бесполезная трата времени, которую вы могли бы использовать для решения актуальных задач
— Аксакал

1
Точнее, я подгоняю сети, которые используют известную параметрическую структуру - как с точки зрения линейного представления параметров входных данных, так и продольной / панельной структуры. Установленные фреймворки настолько сильно оптимизированы, что выходят за пределы моей способности взломать / изменить. Плюс математика полезна вообще. Многие кодовые обезьяны понятия не имеют, что они делают. Аналогично, обучение, достаточное Rcppдля эффективной реализации, полезно.
— generic_user

1
@ MarkL. Камень не только теоретически звучит, но и практически прост! Более или менее механический процесс! &% # $!
— generic_user

Ответы:


1

Не полный ответ, но чтобы продемонстрировать то, что я предложил в своем комментарии, если где , и является вектором единиц, тогда по правилу цепочки Отметив, что и , мы видим, что

b(X)=(X−eNμXT)ΓΣX−1/2+eNβT
Γ=diag⁡(γ)ΣX−1/2=diag⁡(σX1−1,σX2−1,…)eN
∇βR=[−2ϵ^(Γ2T⊗I)JX(a)(I⊗eN)]T
−2ϵ^(Γ2T⊗I)=vec⁡(−2ϵ^Γ2T)TJX(a)=diag⁡(vec⁡(a′(b(XΓ1))))
∇βR=(I⊗eNT)vec⁡(a′(b(XΓ1))⊙−2ϵ^Γ2T)=eNT(a′(b(XΓ1))⊙−2ϵ^Γ2T)
через тождество . Аналогично, где («заглушка») и - этоvec⁡(AXB)=(BT⊗A)vec⁡(X)
∇γR=[−2ϵ^(Γ2T⊗I)JX(a)(ΣXΓ1−1/2⊗(XΓ1−eNμXΓ1T))K]T=KTvec⁡((XΓ1−eNμXΓ1T)TWΣXΓ1−1/2)=diag⁡((XΓ1−eNμXΓ1T)TWΣXΓ1−1/2)
W=a′(b(XΓ1))⊙−2ϵ^Γ2TKNp×pдвоичная матрица, которая выбирает столбцы произведения Кронекера, соответствующие диагональным элементам квадратной матрицы. Это следует из того, что . В отличие от первого градиента, это выражение не эквивалентно полученному вами выражению. Учитывая , что является линейной функцией WRT , не должно быть фактором в градиенте. Я оставляю градиент для OP, но я скажу, что для деривации с фиксированным создается «взрыв», которого авторы статьи стремятся избежать. На практике, вы также должны найти якобианы и WRTb γ i γ i Γ 1 w Σ X μ X XdΓi≠j=0bγiγiΓ1wΣXμXX и используйте правило продукта.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.