diff options
| author | schneefux <schneefux+commit@schneefux.xyz> | 2016-03-31 12:22:50 +0200 |
|---|---|---|
| committer | schneefux <schneefux+commit@schneefux.xyz> | 2016-03-31 12:22:50 +0200 |
| commit | 5f954f48f8ff55c339faed709ebd26b0984abfb2 (patch) | |
| tree | 7049f17843b41e4b6408a82ec850c427fc0ca032 /index.js | |
| parent | a2c8168a21e2abf79f05bbb2e6d8ec61cb1ddca9 (diff) | |
| download | vortrag-knn-5f954f48f8ff55c339faed709ebd26b0984abfb2.tar.gz vortrag-knn-5f954f48f8ff55c339faed709ebd26b0984abfb2.zip | |
move everything into classes
Diffstat (limited to 'index.js')
| -rw-r--r-- | index.js | 472 |
1 files changed, 274 insertions, 198 deletions
@@ -54,237 +54,313 @@ function makeSlider(labelName, x, y, scale, cb) { /* network construction */ -function drawNodes(nodes, layout, env) { - var cnt = 0; - var biasLayers = 0; - for (layer=0; layer<layout.net.layers; layer++) { - if (layout.net[layer].isBias) biasLayers++; +var Network = (function() { + function Network(layout) { + this.layout = layout; } - for (layer=0; layer<layout.net.layers; layer++) { - for (node=0; node<layout.net[layer].size; node++) { - // dynamic ("responsive") attributes for all window sizes - if (layout.net[layer].isBias) { - nodes[cnt].x = (layer - biasLayers + 1.25) * (env.width / (layout.net.layers - biasLayers)); - nodes[cnt].y = (node + 0.25) * (env.height / layout.net[layer].size); - nodes[cnt].r = d3.min([env.width, env.height]) / d3.max([layout.net.layers, layout.net[layer].size]) * 0.2; - } else { - nodes[cnt].x = (layer - biasLayers + 0.5) * (env.width / (layout.net.layers - biasLayers)); - nodes[cnt].y = (node + 0.5) * (env.height / layout.net[layer].size); - nodes[cnt].r = d3.min([env.width, env.height]) / d3.max([layout.net.layers, layout.net[layer].size]) * 0.3; - } - cnt++; + + Network.prototype.init = function() { + this._createNodes(); + this._createLinks(); + }; + + Network.prototype.resize = function(env) { + var largestLayerSize = 0; + for (layer=0; layer<this.layout.net.layers; layer++) { + if (this.layout.net[layer].size > largestLayerSize) largestLayerSize = this.layout.net[layer].size; } - } - return nodes; -} + this.responsiveRadius = d3.min([env.width, env.height]) / d3.max([this.layout.net.layers, largestLayerSize]); + this._updateNodes(env); + }; -function drawLinks(links, weights) { - var cnt = 0; - for (layer=0; layer<weights.length; layer++) { - for (layer2=0; layer2<weights[layer].length; layer2++) { - for (node=0; node<weights[layer][layer2].length; node++) { - links[cnt++].weight = weights[layer][layer2][node]; + Network.prototype._createNodes = function() { + // create layers + this.nodes = []; + for (layer=0; layer<this.layout.net.layers; layer++) { + for (node=0; node<this.layout.net[layer].size; node++) { + this.nodes.push({ + id: layer.toString() + "_" + node.toString(), + fixed: true + }); } } - } - return links; -} + }; -function createNet(layout) { - // create layers - var nodes = []; - for (layer=0; layer<layout.net.layers; layer++) { - for (node=0; node<layout.net[layer].size; node++) { - nodes.push({ - id: layer.toString() + "_" + node.toString(), - fixed: true - }); - } - } - var nodesMap = d3.map(); - nodes.forEach(function (n) { - return nodesMap.set(n.id, n); - }); - // create links between adjacent layers - var links = []; - for (layer=0; layer<layout.net.layers-1; layer++) { // for every layer except the last - for (src=0; src<layout.net[layer].size; src++) { // get all nodes - if (layout.net[layer].isBias) { - links.push({ // in case of a bias layer, connect 1:1 - source: nodesMap.get(layer.toString() + "_" + src.toString()), - target: nodesMap.get((layer+1).toString() + "_" + src.toString()), - weight: 1 - }); - } else { // normal layer - for (tar=0; tar<layout.net[layer+1].size; tar++) { // get all nodes of the next layer - links.push({ // connect both nodes + Network.prototype._createLinks = function() { + this.links = []; + var nodesMap = d3.map(); + this.nodes.forEach(function (n) { + return nodesMap.set(n.id, n); + }); + // create links between adjacent layers + for (layer=0; layer<this.layout.net.layers-1; layer++) { // for every layer except the last + for (src=0; src<this.layout.net[layer].size; src++) { // get all nodes + if (this.layout.net[layer].isBias) { + this.links.push({ // in case of a bias layer, connect 1:1 source: nodesMap.get(layer.toString() + "_" + src.toString()), - target: nodesMap.get((layer+1).toString() + "_" + tar.toString()), + target: nodesMap.get((layer+1).toString() + "_" + src.toString()), weight: 1 }); + } else { // normal layer + for (tar=0; tar<this.layout.net[layer+1].size; tar++) { // get all nodes of the next layer + this.links.push({ // connect both nodes + source: nodesMap.get(layer.toString() + "_" + src.toString()), + target: nodesMap.get((layer+1).toString() + "_" + tar.toString()), + weight: 1 + }); + } } } } - } - - return { - "nodes": nodes, - "links": links }; -} - -/* visualisation */ + Network.prototype._updateNodes = function(env) { + biasLayers = 0; + for (layer=0; layer<this.layout.net.layers; layer++) { + if (this.layout.net[layer].isBias) biasLayers++; + } + var cnt = 0; + for (layer=0; layer<this.layout.net.layers; layer++) { + for (node=0; node<this.layout.net[layer].size; node++) { + // dynamic ("responsive") attributes for all window sizes + if (this.layout.net[layer].isBias) { + this.nodes[cnt].x = (layer - biasLayers + 1.25) * (env.width / (this.layout.net.layers - biasLayers)); + this.nodes[cnt].y = (node + 0.25) * (env.height / this.layout.net[layer].size); + this.nodes[cnt].r = this.responsiveRadius * 0.2; + } else { + this.nodes[cnt].x = (layer - biasLayers + 0.5) * (env.width / (this.layout.net.layers - biasLayers)); + this.nodes[cnt].y = (node + 0.5) * (env.height / this.layout.net[layer].size); + this.nodes[cnt].r = this.responsiveRadius * 0.3; + } + cnt++; + } + } + }; -// create visualisation -function envRedraw(weights, duration) { - var width = parseInt(svg.style("width")) || 800, - height = parseInt(svg.style("height")) || 600; - net.nodes = drawNodes(net.nodes, netSpec, designSpec, {"width": width, "height": height}); - net.links = drawLinks(net.links, weights); + Network.prototype.updateWeights = function(weights, epochLayout) { + var cnt = 0; + for (layer=0; layer<weights.length; layer++) { + for (layer2=0; layer2<weights[layer].length; layer2++) { + for (node=0; node<weights[layer][layer2].length; node++) { + // scaled = (x - min) / (max - min) + var scaled = (weights[layer][layer2][node] - epochLayout.min) / (epochLayout.max - epochLayout.min); + this.links[cnt++].weight = scaled; + } + } + } + }; - node = svg.selectAll(".node"); - node - .transition() - .duration(duration) - .attr("cx", function(d) { return d.x; }) - .attr("cy", function(d) { return d.y; }) - .attr("r", function(d) { return d.r; }); - link = svg.selectAll(".link"); - link - .transition() - .duration(duration) - .style("stroke-width", function(d) { return d.weight * 4; }) - .attr("x1", function(d) { return d.source.x; }) - .attr("y1", function(d) { return d.source.y; }) - .attr("x2", function(d) { return d.target.x; }) - .attr("y2", function(d) { return d.target.y; }); -} + return Network; +})(); /* data handling */ -// now we're getting to the grips -function preload(callback) { - d3.json("data/log.json") - .get(function(e, json) { - if (e) console.log(e); - callback(json); +var Loader = (function() { + function Loader() { + } + + Loader.prototype.load = function(url, callback) { + var self = this; + d3.json(url) + .get(function(e, json) { + if (e) console.log(e); + self._parse(json); }); -} + this._finished = callback; + }; -var cache = {}; -preload(function (json) { - // json format: - // json. - // [batch][epoch] [[sourceIndex x targetIndex]] (int) - // careful - root and first level nodes are objects and have no '.length' + Loader.prototype._parse = function(json) { + // json format: + // json. + // [batch][epoch] [[sourceIndex x targetIndex]] (int) + // careful - root and first level nodes are objects and have no '.length' - var layout = {}; - // layout format: - // layout. - // net.layers (int) - // net.[net.layers]. - // isBias (bool) - // size (int) - // layers.[batch][epoch]. - // min (int) - // max (int) - layout.net = {}; - // network structure - console.log(json); - layout.net.layers = json[0][0].length; - for (layer=0; layer<layout.net.layers; layer++) { - layerObj = {}; - if (json[0][0][layer].length == 1) { // bias - layerObj.isBias = true; - layerObj.size = json[0][0][0][layer].length; // workaround - Matrix should be rotatet serverside - } else { - layerObj.isBias = false; - layerObj.size = json[0][0][layer].length; + var layout = {}; + // layout format: + // layout. + // net.layers (int) + // net.[net.layers]. + // isBias (bool) + // size (int) + // layers.[batch][epoch]. + // min (int) + // max (int) + // data. + // batches (int) + // epochs (int) + layout.data = {}; + layout.data.batches = Object.keys(json).length; + layout.data.epochs = Object.keys(json[0]).length; + layout.net = {}; + // network structure + layout.net.layers = json[0][0].length; + for (layer=0; layer<layout.net.layers; layer++) { + layerObj = {}; + if (json[0][0][layer].length == 1) { // bias + layerObj.isBias = true; + layerObj.size = json[0][0][0][layer].length; // workaround - Matrix should be rotatet serverside + } else { + layerObj.isBias = false; + layerObj.size = json[0][0][layer].length; + } + layout.net[layer] = layerObj; } - layout.net[layer] = layerObj; - } - layout.net[layout.net.layers] = {'isBias': false, 'size': json[0][0][layout.net.layers-1][0].length}; - layout.net.layers += 1; // output layer - // layer properties - layout.layers = {}; - for (batch=0; batch<Object.keys(json).length; batch++) { - layout.layers[batch] = {}; - for (epoch=0; epoch<Object.keys(json[batch]).length; epoch++) { - layout.layers[batch][epoch] = {}; - for (layer=0; layer<json[batch][epoch].length; layer++) { - layout.layers[batch][epoch][layer] = {}; - layout.layers[batch][epoch][layer].min = d3.min(json[batch][epoch][layer]); - layout.layers[batch][epoch][layer].max = d3.max(json[batch][epoch][layer]); - // TODO use these values while drawing links! + layout.net[layout.net.layers] = {'isBias': false, 'size': json[0][0][layout.net.layers-1][0].length}; + layout.net.layers += 1; // output layer + // layer properties + layout.layers = {}; + for (batch=0; batch<layout.data.batches; batch++) { + layout.layers[batch] = {}; + for (epoch=0; epoch<layout.data.epochs; epoch++) { + layout.layers[batch][epoch] = {}; + layout.layers[batch][epoch].max = 0; + layout.layers[batch][epoch].min = Infinity; + for (layer=0; layer<json[batch][epoch].length; layer++) { + var layerMin = d3.min(json[batch][epoch][layer]); + var layerMax = d3.max(json[batch][epoch][layer]); + if (layerMin < layout.layers[batch][epoch].min) layout.layers[batch][epoch].min = layerMin; + if (layerMax > layout.layers[batch][epoch].max) layout.layers[batch][epoch].max = layerMax; + } } } + console.log(layout); // DEBUG + + if (this._finished) this._finished(layout, json); + }; + + return Loader; +})(); + +var Animator = (function() { + function Animator(layout, time, step) { + this.layout = layout; + this.step = step; + this.batch = this.epoch = 0; + this.frameDuration = time / (this.layout.data.batches * this.layout.data.epochs / this.step); + console.log("frame duration:" + this.frameDuration); // DEBUG } - setup(layout, json); -}); + Animator.prototype.start = function(callback) { + var self = this; + wrapper = function() { + callback(self.batch, self.epoch, self.frameDuration); + self.animate(); + }; + this.pid = setInterval(wrapper, this.frameDuration); + }; + + Animator.prototype.animate = function() { + this.epoch += this.step; + if (this.epoch >= this.layout.data.epochs) { + this.epoch = 0; + if (++this.batch>= this.layout.data.batches) { + this.batch = 0; + clearInterval(this.pid); // suicide + console.log("finished"); + } + } + }; -function setup(layout, data) { - // create empty elements and determine the visualisation's size - var svg = d3.select("#visContainer").append("svg") - .attr("width", "100%") - .attr("height", "100%"); - var width = parseInt(svg.style("width")) || 800, - height = parseInt(svg.style("height")) || 600; + return Animator; +})(); - /*var slideScaler = d3.scale.linear() - .domain([0, 180]) - .range([0, 200]) - .clamp(true); - function slid(val) { - console.log(val); +var Visualizer = (function() { + function Visualizer(svg) { + this.loader = new Loader(); + this.svg = svg; } - makeSlider("Test", 10, 20, slideScaler, slid);*/ - var net = createNet(layout); - console.log(layout); - console.log(net); - net.nodes = drawNodes(net.nodes, layout, {"width": width, "height": height}); - net.links = drawLinks(net.links, data[0][0]); + Visualizer.prototype.start = function(url, animationTime, resolution) { + // animationTime in seconds per frame + // resolution in epochs per frame + var self = this; + this.animationTime = animationTime; + this.resolution = resolution; + function wrapper(layout, data) { + self.layout = layout; + self.data = data; + self._setup(); + } + this.loader.load("data/log.json", wrapper); + }; - var link = svg.selectAll(".link") - .data(net.links) - .enter().append("line") - .attr("class", "link") - .style("stroke", "grey") - .style("stroke-width", function(d) { return d.weight * 4; }) // TODO make width always stay between 0-1 - .attr("x1", function(d) { return d.source.x; }) - .attr("y1", function(d) { return d.source.y; }) - .attr("x2", function(d) { return d.target.x; }) - .attr("y2", function(d) { return d.target.y; }); - var node = svg.selectAll(".node") - .data(net.nodes) - .enter().append("circle") - .attr("class", "node") - .attr("cx", function(d) { return d.x; }) - .attr("cy", function(d) { return d.y; }) - .attr("r", function(d) { return d.r; }); + Visualizer.prototype._setup = function() { + console.log("setup"); // DEBUG + var self = this; + // create empty elements and determine the visualisation's size + this.svg + .attr("width", "100%") + .attr("height", "100%"); + var width = parseInt(this.svg.style("width")) || 800, + height = parseInt(this.svg.style("height")) || 600; - window.addEventListener("resize", envRedraw, true); - // TODO animation & refresh -} + /*var slideScaler = d3.scale.linear() + .domain([0, 180]) + .range([0, 200]) + .clamp(true); + function slid(val) { + console.log(val); + } + makeSlider("Test", 10, 20, slideScaler, slid);*/ + + this.network = new Network(this.layout); + this.network.init(); + this.network.resize({"width": width, "height": height}); + this.network.updateWeights(this.data[0][0], this.layout.layers[0][0]); -var maxBatch = 8, maxEpoch = 300; -var epochStep = 50; -var numLayers = 3; -var currBatch = 0, currEpoch = 0; -function animate() { - frameDuration = 20000 / (maxBatch * maxEpoch / epochStep); - envRedraw(cache[currBatch][currEpoch], frameDuration); - currEpoch += epochStep; - if (currEpoch >= maxEpoch) { - currEpoch = 0; - console.log("batch " + currBatch.toString()); - if (++currBatch >= maxBatch) { - currBatch = 0; - return; // stop + function resized() { + width = parseInt(self.svg.style("width")) || 800; + height = parseInt(self.svg.style("height")) || 600; + + self.network.resize({"width": width, "height": height}); + + node + .attr("cx", function(d) { return d.x; }) + .attr("cy", function(d) { return d.y; }) + .attr("r", function(d) { return d.r; }); + link + .attr("x1", function(d) { return d.source.x; }) + .attr("y1", function(d) { return d.source.y; }) + .attr("x2", function(d) { return d.target.x; }) + .attr("y2", function(d) { return d.target.y; }); } - } - setTimeout(animate, frameDuration); -} + window.addEventListener("resize", resized, true); + + var link = this.svg.selectAll(".link") + .data(this.network.links) + .enter().append("line") + .attr("class", "link") + .style("stroke", "grey") + .style("stroke-width", function(d) { return d.weight * 10; }) + .attr("x1", function(d) { return d.source.x; }) + .attr("y1", function(d) { return d.source.y; }) + .attr("x2", function(d) { return d.target.x; }) + .attr("y2", function(d) { return d.target.y; }); + var node = this.svg.selectAll(".node") + .data(this.network.nodes) + .enter().append("circle") + .attr("class", "node") + .attr("cx", function(d) { return d.x; }) + .attr("cy", function(d) { return d.y; }) + .attr("r", function(d) { return d.r; }); + + this.animator = new Animator(this.layout, this.animationTime * 1000, this.resolution); + this.animator.start(function(batch, epoch, duration) { + self.network.updateWeights(self.data[batch][epoch], self.layout.layers[batch][epoch]); + + // 10% extra time per frame for the code execution + duration = duration / 1.1; + link + .transition() + .duration(duration) + .style("stroke-width", function(d) { return d.weight * 10; }); + }); + }; + + return Visualizer; +})(); + +var svg = d3.select("#visContainer").append("svg"); +var visualizer = new Visualizer(svg); +visualizer.start("data/log.json", 30, 10); |
