Chuẩn hóa hàng loạt đã được ghi nhận với những cải tiến hiệu suất đáng kể trong mạng lưới thần kinh sâu. Rất nhiều tài liệu trên internet cho thấy cách triển khai nó trên cơ sở kích hoạt bằng cách kích hoạt. Tôi đã triển khai backprop bằng cách sử dụng đại số ma trận và cho rằng tôi đang làm việc với các ngôn ngữ cấp cao (trong khi dựa vào Rcpp
(và cuối cùng là GPU) để nhân ma trận dày đặc), trích xuất mọi thứ và sử dụng for
mã nguồn có thể sẽ làm chậm mã của tôi thực chất, ngoài việc là một nỗi đau rất lớn
Chức năng batch bình thường là
- lànút thứ , trước khi nó được kích hoạt
- và là các thông số vô hướng
- và là giá trị trung bình và SD của . (Lưu ý rằng căn bậc hai của phương sai cộng với hệ số mờ thường được sử dụng - giả sử các phần tử khác không cho độ gọn)
Ở dạng ma trận, hàng loạt bình thường cho toàn bộ một lớp sẽ là nơi
- là N × p
- là một vectơ cột của những cái
- và β hiện nay có hàng p -vectors các thông số bình thường mỗi lớp
- và σ X là N × p ma trận, trong đó mỗi cột là một N -vector phương tiện theo cột và độ lệch chuẩn
- là sản phẩm Kronecker và ⊙ là elementwise (Hadamard) Sản phẩm
Một một lớp lưới thần kinh rất đơn giản với không bình thường hàng loạt và một kết quả liên tục là
Ở đâu
- là p 1 × p 2
- là p 2 × 1
- là hàm kích hoạt
Nếu sự mất mát là , sau đó các gradient là ∂ R
Ở đâu
Dưới bình thường hàng loạt, net trở thành hoặc y = một ( ( gamma ⊗ 1 N ) ⊙ ( X Γ 1 - μ X Γ 1 ) ⊙ σ - 1 X Γ 1 + ( beta ⊗ 1 N ) ) Γ 2
Có cách nào thực tế của máy tính , ∂ R / ∂ beta , và ∂ R / ∂ gamma 1 trong khuôn khổ ma trận? Một biểu thức đơn giản, không dùng đến tính toán theo nút?
Cập nhật 1:
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
Sau đó tính đạo hàm:
# 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
# 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
Cập nhật 2
vec()
và từ cái này , cái kia
Cập nhật 3
Tiến bộ ở đây. Tôi thức dậy lúc 2 giờ tối qua với ý tưởng này. Toán học không tốt cho giấc ngủ.
Và, trên thực tế, đó là:
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
Cập nhật 4
It sort of matches:
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
The diagonal on the first is the same as the vector on the second. But really since the derivative is with respect to a matrix -- albeit one with a certain structure, the output should be a similar matrix with the same structure. Should I take the diagonal of the matrix approach and simply take it to be ? I'm not sure.
It seems that I have answered my own question but I am unsure whether I am correct. At this point I will accept an answer that rigorously proves (or disproves) what I've sort of hacked together.
while(not_answered){
print("Bueller?")
Sys.sleep(1)
}
Rcpp
to implement it efficiently is useful.