diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..2f21222a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +build/convnet-min.js -diff +build/convnet.js -diff \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..39904dbc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +demo/mnist/ diff --git a/build/convnet-min.js b/build/convnet-min.js index a97c95f9..8c762ee0 100644 --- a/build/convnet-min.js +++ b/build/convnet-min.js @@ -1 +1 @@ -var convnetjs=convnetjs||{REVISION:"ALPHA"};(function(c){var h=false;var d=0;var i=function(){if(h){h=false;return d}var k=2*Math.random()-1;var j=2*Math.random()-1;var l=k*k+j*j;if(l==0||l>1){return i()}var m=Math.sqrt(-2*Math.log(l)/l);d=j*m;h=true;return k*m};var g=function(k,j){return Math.random()*(j-k)+k};var f=function(k,j){return Math.floor(Math.random()*(j-k)+k)};var b=function(k,j){return k+i()*j};var e=function(l){if(typeof(l)==="undefined"||isNaN(l)){return[]}if(typeof ArrayBuffer==="undefined"){var j=new Array(l);for(var k=0;kj){j=k[o];l=o}if(k[o]=f.sx||k+m<0||k+m>=f.sy){continue}for(var j=0;j=0&&g=0&&h=0&&g=0&&h=0&&f=0&&gr){r=t;o=g;k=f}}}}this.switchx[i]=o;this.switchy[i]=k;i++;h.set(e,w,p,r)}}}this.out_act=h;return this.out_act},backward:function(){var h=this.in_act;h.dw=c.zeros(h.w.length);var f=this.out_act;var g=0;for(var j=0;jk){k=j[l]}}var n=e.zeros(this.out_depth);var g=0;for(var l=0;l0){g.dw[h]+=1;g.dw[l]-=1;j+=-f+g.w[h]+k}}return j},getParamsAndGrads:function(){return[]},toJSON:function(){var f={};f.out_depth=this.out_depth;f.out_sx=this.out_sx;f.out_sy=this.out_sy;f.layer_type=this.layer_type;f.num_inputs=this.num_inputs;return f},fromJSON:function(f){this.out_depth=f.out_depth;this.out_sx=f.out_sx;this.out_sy=f.out_sy;this.layer_type=f.layer_type;this.num_inputs=f.num_inputs}};e.RegressionLayer=d;e.SoftmaxLayer=c;e.SVMLayer=b})(convnetjs);(function(d){var a=d.Vol;var e=function(h){var h=h||{};this.out_sx=h.in_sx;this.out_sy=h.in_sy;this.out_depth=h.in_depth;this.layer_type="relu"};e.prototype={forward:function(j,l){this.in_act=j;var h=j.clone();var m=j.w.length;var n=h.w;for(var k=0;ku){u=h;r=o}}v.w[p]=u;this.switches[p]=m+r}}else{var k=0;for(var t=0;tu){u=h;r=o}}v.set(t,s,p,u);this.switches[k]=m+r;k++}}}}this.out_act=v;return this.out_act},backward:function(){var k=this.in_act;var j=this.out_act;var o=this.out_depth;k.dw=d.zeros(k.w.length);if(this.out_sx===1&&this.out_sy===1){for(var l=0;l0){var g=this.layers[f-1];h.in_sx=g.out_sx;h.in_sy=g.out_sy;h.in_depth=g.out_depth}switch(h.type){case"fc":this.layers.push(new c.FullyConnLayer(h));break;case"lrn":this.layers.push(new c.LocalResponseNormalizationLayer(h));break;case"dropout":this.layers.push(new c.DropoutLayer(h));break;case"input":this.layers.push(new c.InputLayer(h));break;case"softmax":this.layers.push(new c.SoftmaxLayer(h));break;case"regression":this.layers.push(new c.RegressionLayer(h));break;case"conv":this.layers.push(new c.ConvLayer(h));break;case"pool":this.layers.push(new c.PoolLayer(h));break;case"relu":this.layers.push(new c.ReluLayer(h));break;case"sigmoid":this.layers.push(new c.SigmoidLayer(h));break;case"tanh":this.layers.push(new c.TanhLayer(h));break;case"maxout":this.layers.push(new c.MaxoutLayer(h));break;case"quadtransform":this.layers.push(new c.QuadTransformLayer(h));break;case"svm":this.layers.push(new c.SVMLayer(h));break;default:console.log("ERROR: UNRECOGNIZED LAYER TYPE!")}}},forward:function(e,g){if(typeof(g)==="undefined"){g=false}var d=this.layers[0].forward(e,g);for(var f=1;f=0;d--){this.layers[d].backward()}return e},getParamsAndGrads:function(){var d=[];for(var f=0;fd){d=h[f];e=f}}return e},toJSON:function(){var e={};e.layers=[];for(var d=0;d0)){for(var E=0;E0?1:-1);var o=l*(w[B]);var t=(o+D+F[B])/this.batch_size;var m=this.gsum[E];var C=this.xsum[E];if(this.method==="adagrad"){m[B]=m[B]+t*t;var v=-this.learning_rate/Math.sqrt(m[B]+this.eps)*t;w[B]+=v}else{if(this.method==="windowgrad"){m[B]=this.ro*m[B]+(1-this.ro)*t*t;var v=-this.learning_rate/Math.sqrt(m[B]+this.eps)*t;w[B]+=v}else{if(this.method==="adadelta"){m[B]=this.ro*m[B]+(1-this.ro)*t*t;var v=-Math.sqrt((C[B]+this.eps)/(m[B]+this.eps))*t;C[B]=this.ro*C[B]+(1-this.ro)*v*v;w[B]+=v}else{if(this.momentum>0){var v=this.momentum*m[B]-this.learning_rate*t;m[B]=v;w[B]+=v}else{w[B]+=-this.learning_rate*t}}}}F[B]=0}}}return{fwd_time:q,bwd_time:G,l2_decay_loss:k,l1_decay_loss:d,cost_loss:A,softmax_loss:A,loss:A+d+k}}};b.Trainer=c;b.SGDTrainer=c})(convnetjs);(function(a){if(typeof module==="undefined"||typeof module.exports==="undefined"){window.jsfeat=a}else{module.exports=a}})(convnetjs); \ No newline at end of file +var convnetjs=convnetjs||{REVISION:"ALPHA"};(function(c){var h=false;var d=0;var i=function(){if(h){h=false;return d}var l=2*Math.random()-1;var k=2*Math.random()-1;var m=l*l+k*k;if(m==0||m>1){return i()}var n=Math.sqrt(-2*Math.log(m)/m);d=k*n;h=true;return l*n};var g=function(l,k){return Math.random()*(k-l)+l};var f=function(l,k){return Math.floor(Math.random()*(k-l)+l)};var b=function(l,k){return l+i()*k};var e=function(m){if(typeof(m)==="undefined"||isNaN(m)){return[]}if(typeof ArrayBuffer==="undefined"){var k=new Array(m);for(var l=0;lk){k=l[p];m=p}if(l[p]=f.sx||k+m<0||k+m>=f.sy){continue}for(var j=0;j=0&&h=0&&i=0&&h=0&&i=0&&f=0&&gr){r=t;o=g;k=f}}}}this.switchx[i]=o;this.switchy[i]=k;i++;h.set(e,w,p,r)}}}this.out_act=h;return this.out_act},backward:function(){var h=this.in_act;h.dw=c.zeros(h.w.length);var f=this.out_act;var g=0;for(var j=0;jk){k=j[l]}}var n=e.zeros(this.out_depth);var g=0;for(var l=0;l0){g.dw[h]+=1;g.dw[l]-=1;j+=-f+g.w[h]+k}}return j},getParamsAndGrads:function(){return[]},toJSON:function(){var f={};f.out_depth=this.out_depth;f.out_sx=this.out_sx;f.out_sy=this.out_sy;f.layer_type=this.layer_type;f.num_inputs=this.num_inputs;return f},fromJSON:function(f){this.out_depth=f.out_depth;this.out_sx=f.out_sx;this.out_sy=f.out_sy;this.layer_type=f.layer_type;this.num_inputs=f.num_inputs}};e.RegressionLayer=d;e.SoftmaxLayer=c;e.SVMLayer=b})(convnetjs);(function(d){var a=d.Vol;var e=function(h){var h=h||{};this.out_sx=h.in_sx;this.out_sy=h.in_sy;this.out_depth=h.in_depth;this.layer_type="relu"};e.prototype={forward:function(j,l){this.in_act=j;var h=j.clone();var m=j.w.length;var n=h.w;for(var k=0;ku){u=h;r=o}}v.w[p]=u;this.switches[p]=m+r}}else{var k=0;for(var t=0;tu){u=h;r=o}}v.set(t,s,p,u);this.switches[k]=m+r;k++}}}}this.out_act=v;return this.out_act},backward:function(){var k=this.in_act;var j=this.out_act;var o=this.out_depth;k.dw=d.zeros(k.w.length);if(this.out_sx===1&&this.out_sy===1){for(var l=0;l0){var h=this.layers[g-1];j.in_sx=h.out_sx;j.in_sy=h.out_sy;j.in_depth=h.out_depth}this.layers.push(c.build_layer(j))}},forward:function(f,h){if(typeof(h)==="undefined"){h=false}var e=this.layers[0].forward(f,h);for(var g=1;g=0;e--){this.layers[e].backward()}return f},getParamsAndGrads:function(){var e=[];for(var g=0;ge){e=j[g];f=g}}return f},toJSON:function(){var f={};f.layers=[];for(var e=0;e0)){for(var E=0;E0?1:-1);var o=l*(w[B]);var t=(o+D+F[B])/this.batch_size;var m=this.gsum[E];var C=this.xsum[E];if(this.method==="adagrad"){m[B]=m[B]+t*t;var v=-this.learning_rate/Math.sqrt(m[B]+this.eps)*t;w[B]+=v}else{if(this.method==="windowgrad"){m[B]=this.ro*m[B]+(1-this.ro)*t*t;var v=-this.learning_rate/Math.sqrt(m[B]+this.eps)*t;w[B]+=v}else{if(this.method==="adadelta"){m[B]=this.ro*m[B]+(1-this.ro)*t*t;var v=-Math.sqrt((C[B]+this.eps)/(m[B]+this.eps))*t;C[B]=this.ro*C[B]+(1-this.ro)*v*v;w[B]+=v}else{if(this.momentum>0){var v=this.momentum*m[B]-this.learning_rate*t;m[B]=v;w[B]+=v}else{w[B]+=-this.learning_rate*t}}}}F[B]=0}}}return{fwd_time:q,bwd_time:G,l2_decay_loss:k,l1_decay_loss:d,cost_loss:A,softmax_loss:A,loss:A+d+k}}};b.Trainer=c;b.SGDTrainer=c})(convnetjs);(function(a){if(typeof module==="undefined"||typeof module.exports==="undefined"){window.jsfeat=a}else{module.exports=a}})(convnetjs); \ No newline at end of file diff --git a/build/convnet.js b/build/convnet.js index d8a7403c..93dc42d4 100644 --- a/build/convnet.js +++ b/build/convnet.js @@ -49,13 +49,26 @@ var convnetjs = convnetjs || { REVISION: 'ALPHA' }; if(w[i] < minv) { minv = w[i]; mini = i; } } return {maxi: maxi, maxv: maxv, mini: mini, minv: minv, dv:maxv-minv}; - } + }; + + + var bernoulliMask = function(x, y, d, probability) { + var weight_mask = new global.Vol(x, y, d, 0); + for(var i = 0; i < weight_mask.w.length; i++) { + weight_mask.w[i] = randf(0, 1) < probability; + } + return { + weight_mask: weight_mask, + bias_mask: randf(0, 1) < probability + }; + }; global.randf = randf; global.randi = randi; global.randn = randn; global.zeros = zeros; global.maxmin = maxmin; + global.bernoulliMask = bernoulliMask; })(convnetjs); (function(global) { @@ -264,13 +277,47 @@ var convnetjs = convnetjs || { REVISION: 'ALPHA' }; (function(global) { "use strict"; var Vol = global.Vol; // convenience - + // This file contains all layers that do dot products with input, // but usually in a different connectivity pattern and weight sharing // schemes: // - FullyConn is fully connected dot products // - ConvLayer does convolutions (so weight sharing spatially) // putting them together in one file because they are very similar + var DropConnect = function(opt) { + var opt = opt || {}; + this.keep_probability = opt.keep_probability; + this.num_gaussian_samples = opt.num_gaussian_samples; + this.activation_layer = global.build_layer({ + // Unused, as we just cal forward() + in_sx: 1, + in_sy: 1, + in_depth: 1, + type: opt.activation + }); + }; + + DropConnect.prototype = { + fromJSON: function(json) { + this.keep_probability = json.keep_probability; + this.num_gaussian_samples = json.num_gaussian_samples; + this.activation_layer = global.build_layer({ + // Unused, as we just cal forward() + in_sx: 1, + in_sy: 1, + in_depth: 1, + type: json.activation + }); + }, + + toJSON: function() { + var opt = {}; + opt.keep_probability = this.keep_probability; + opt.num_gaussian_samples = this.num_gaussian_samples; + opt.activation_type = this.activation_layer.layer_type; + } + }; + var ConvLayer = function(opt) { var opt = opt || {}; @@ -280,7 +327,7 @@ var convnetjs = convnetjs || { REVISION: 'ALPHA' }; this.in_depth = opt.in_depth; this.in_sx = opt.in_sx; this.in_sy = opt.in_sy; - + // optional this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx; this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 1; // stride at which we apply filters to input volume @@ -404,6 +451,7 @@ var convnetjs = convnetjs || { REVISION: 'ALPHA' }; json.filters.push(this.filters[i].toJSON()); } json.biases = this.biases.toJSON(); + json.drop_connect = this.drop_connect.toJSON(); return json; }, fromJSON: function(json) { @@ -428,7 +476,7 @@ var convnetjs = convnetjs || { REVISION: 'ALPHA' }; this.biases.fromJSON(json.biases); } } - + var FullyConnLayer = function(opt) { var opt = opt || {}; @@ -439,13 +487,12 @@ var convnetjs = convnetjs || { REVISION: 'ALPHA' }; // optional this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0; this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0; - + this.drop_connect = typeof opt.drop_connect !== 'undefined' ? DropConnect(opt.drop_connect) : null; // computed this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth; this.out_sx = 1; this.out_sy = 1; this.layer_type = 'fc'; - // initializations var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0; this.filters = []; @@ -454,43 +501,126 @@ var convnetjs = convnetjs || { REVISION: 'ALPHA' }; } FullyConnLayer.prototype = { + dropConnectEnabled: function() { + return this.drop_connect !== null; + }, + + initializeDropConnectMask: function() { + this.drop_connect_masks = []; + var keep_probability = this.dropConnectEnabled() ? this.drop_connect.keep_probability : 1.0; + for(var i = 0; i < this.filters.length; i++) { + this.drop_connect_masks[i] = + global.bernoulliMask(1, 1, this.num_inputs, keep_probability); + } + }, + forward: function(V, is_training) { + if (is_training) { + return this.forwardTrain(V); + } else { + return this.forwardPredict(V); + } + }, + + forwardTrain: function(V) { this.in_act = V; var A = new Vol(1, 1, this.out_depth, 0.0); + this.initializeDropConnectMask(); var Vw = V.w; for(var i=0;i [x, x_i*x_j forall i,j] - // so the fully connected layer afters will essentially be doing tensor multiplies - var QuadTransformLayer = function(opt) { - var opt = opt || {}; - - // computed - this.out_sx = opt.in_sx; - this.out_sy = opt.in_sy; - // linear terms, and then quadratic terms, of which there are 1/2*n*(n+1), - // (offdiagonals and the diagonal total) and arithmetic series. - // Actually never mind, lets not be fancy here yet and just include - // terms x_ix_j and x_jx_i twice. Half as efficient but much less - // headache. - this.out_depth = opt.in_depth + opt.in_depth * opt.in_depth; - this.layer_type = 'quadtransform'; - - } - QuadTransformLayer.prototype = { - forward: function(V, is_training) { - this.in_act = V; - var N = this.out_depth; - var Ni = V.depth; - var V2 = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0); - for(var x=0;x