Parti normalizasyonu , derin sinir ağlarında önemli performans iyileştirmeleri ile kredilendirilmiştir. İnternetteki birçok malzeme, aktivasyon bazında nasıl uygulanacağını göstermektedir. Zaten matris cebiri kullanarak backprop uyguladım ve yüksek seviyeli dillerde çalıştığımı Rcpp
(yoğun matris çarpımı için (ve sonunda for
GPU'lara güvenerek) çalışırken , her şeyi kopyalayıp-döngülere başvurmak muhtemelen kodumu yavaşlatacaktır) büyük bir acıya ek olarak.
Toplu normalizasyon fonksiyonu olan
- p , etkinleştirilmeden önceki düğümüdür
- β ve skaler parametrelerdir
- σ x p x p ve ortalaması ve . (Varyansın karekökü artı bir geçiştirme faktörünün normal olarak kullanıldığını unutmayın - kompaktlık için sıfır olmayan öğeleri varsayalım)
Matris biçiminde, tüm katmanın toplu normalleştirmesi burada
- olan
- , bunların sütun vektörüdür
- ve artık katman başına normalleştirme parametrelerinin satır vektörlerip
- ve olan , her sütun a, matrisler, -vector columnwise ortalama ve standart sapma N × p N
- Kronecker ürünü ve elementwise (Hadamard) ürünü
normalizasyonu ve sürekli sonucu olmayan çok basit bir tek katmanlı sinir ağı
nerede
- olan
- p 2 × 1 olan
- etkinleştirme işlevidir
Kayıp , degradeler ∂ R
nerede
Toplu normalleştirme altında, net veya Hadamard ve Kronecker ürünlerinin türevlerini nasıl hesaplayacağımı bilmiyorum. Kronecker ürünleri konusunda, literatür oldukça gizlidir. y = a ( ( γ ⊗ 1 N ) ⊙ ( X Γ 1 - μ X Γ 1 ) ⊙ σ - 1 X Γ 1
Matris çerçevesi içinde , ve hesaplamanın pratik bir yolu var mı ? Düğüm başına hesaplamaya başvurmadan basit bir ifade?∂ R / ∂ β ∂ R / ∂ Γ 1
Güncelleme 1:
Ben - tür anladım . Bu: Bazı R kodları bunun döngüsel yolla eşdeğer olduğunu gösterir. Önce sahte verileri ayarlayın:1 T N ( a ′ ( X Γ 1 ) ⊙ - 2
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
Sonra türevleri hesaplayın:
# 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
Eşleşiyorlar. Ama hala kafam karıştı, çünkü bunun neden işe yaradığını gerçekten bilmiyorum. @ Mark L. Stone tarafından başvurulan MatCalc notlar türevi söylemek olmalıdır
# 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
Bu uygun değil. Açıkçası şu Kronecker türev kurallarını anlamıyorum. Bunlara yardım etmek harika olurdu. ve için diğer türevler üzerinde hala takılı - bunlar daha zordur çünkü gibi ek olarak .
Güncelleme 2
Ders kitaplarını okurken, ve operatörün kullanılmasını gerektireceğinden oldukça eminim . Ama görünüşe göre onları koda çevirmek için türevleri yeterince takip edemiyorum. Örneğin, türevi alınarak dahil olacak göre , burada (bu an için sabit bir matris olarak değerlendirebiliriz). vec()
İçgüdülerim basitçe "cevabı demek ki " nedeniyle, ama besbelli değil o işin ile uyumlu değildir .
Biliyorum
ve gelen bu , o
Güncelleme 3
Burada ilerleme kaydediyoruz. Dün gece saat 2'de bu fikirle uyandım. Matematik uyku için iyi değildir.
İşte bazı notasyonel şekerden sonra :
Zincir kuralının sonuna geldikten sonra sahip olduklarınız: Bunu döngüsel bir şekilde yaparak başlayın - ve , alt sütunları ve uyumlu bir kimlik matrisidir:
Ve aslında:
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
Güncelleme 4
Burada, sanırım . İlk
Daha önce olduğu gibi, zincir kuralı sizi Döngü size Daha önce olduğu gibi temel olarak saplamayı önceden çarpıyor. Bu nedenle:
Bir çeşit eşleşme:
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
Birincideki köşegen, ikincideki vektörle aynıdır. Ancak gerçekten de türev, belirli bir yapıya sahip olsa da, bir matrise göre olduğu için, çıktı aynı yapıya sahip benzer bir matris olmalıdır. Matris yaklaşımının köşegenini almalı ve basitçe mı almalıyım ? Emin değilim.
Görünüşe göre kendi sorumu cevapladım ama doğru olup olmadığımdan emin değilim. Bu noktada, birlikte hacklendiğim şeyi titizlikle kanıtlayan (veya çürüten) bir yanıtı kabul edeceğim.
while(not_answered){
print("Bueller?")
Sys.sleep(1)
}
Rcpp
verimli bir şekilde uygulamak için yeterince öğrenme de faydalıdır.