Files
karpathy--convnetjs/src/convnet_layers_normalization.js
T
2026-07-13 12:49:29 +08:00

115 lines
3.4 KiB
JavaScript

(function(global) {
"use strict";
var Vol = global.Vol; // convenience
// a bit experimental layer for now. I think it works but I'm not 100%
// the gradient check is a bit funky. I'll look into this a bit later.
// Local Response Normalization in window, along depths of volumes
var LocalResponseNormalizationLayer = function(opt) {
var opt = opt || {};
// required
this.k = opt.k;
this.n = opt.n;
this.alpha = opt.alpha;
this.beta = opt.beta;
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'lrn';
// checks
if(this.n%2 === 0) { console.log('WARNING n should be odd for LRN layer'); }
}
LocalResponseNormalizationLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var A = V.cloneAndZero();
this.S_cache_ = V.cloneAndZero();
var n2 = Math.floor(this.n/2);
for(var x=0;x<V.sx;x++) {
for(var y=0;y<V.sy;y++) {
for(var i=0;i<V.depth;i++) {
var ai = V.get(x,y,i);
// normalize in a window of size n
var den = 0.0;
for(var j=Math.max(0,i-n2);j<=Math.min(i+n2,V.depth-1);j++) {
var aa = V.get(x,y,j);
den += aa*aa;
}
den *= this.alpha / this.n;
den += this.k;
this.S_cache_.set(x,y,i,den); // will be useful for backprop
den = Math.pow(den, this.beta);
A.set(x,y,i,ai/den);
}
}
}
this.out_act = A;
return this.out_act; // dummy identity function for now
},
backward: function() {
// evaluate gradient wrt data
var V = this.in_act; // we need to set dw of this
V.dw = global.zeros(V.w.length); // zero out gradient wrt data
var A = this.out_act; // computed in forward pass
var n2 = Math.floor(this.n/2);
for(var x=0;x<V.sx;x++) {
for(var y=0;y<V.sy;y++) {
for(var i=0;i<V.depth;i++) {
var chain_grad = this.out_act.get_grad(x,y,i);
var S = this.S_cache_.get(x,y,i);
var SB = Math.pow(S, this.beta);
var SB2 = SB*SB;
// normalize in a window of size n
for(var j=Math.max(0,i-n2);j<=Math.min(i+n2,V.depth-1);j++) {
var aj = V.get(x,y,j);
var g = -aj*this.beta*Math.pow(S,this.beta-1)*this.alpha/this.n*2*aj;
if(j===i) g+= SB;
g /= SB2;
g *= chain_grad;
V.add_grad(x,y,j,g);
}
}
}
}
},
getParamsAndGrads: function() { return []; },
toJSON: function() {
var json = {};
json.k = this.k;
json.n = this.n;
json.alpha = this.alpha; // normalize by size
json.beta = this.beta;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.out_depth = this.out_depth;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.k = json.k;
this.n = json.n;
this.alpha = json.alpha; // normalize by size
this.beta = json.beta;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.out_depth = json.out_depth;
this.layer_type = json.layer_type;
}
}
global.LocalResponseNormalizationLayer = LocalResponseNormalizationLayer;
})(convnetjs);