diff options
| author | schneefux <schneefux+commit@schneefux.xyz> | 2016-03-29 20:52:48 +0200 |
|---|---|---|
| committer | schneefux <schneefux+commit@schneefux.xyz> | 2016-03-29 20:52:48 +0200 |
| commit | a2c8168a21e2abf79f05bbb2e6d8ec61cb1ddca9 (patch) | |
| tree | 67559291b1f625990cc1c1f0f315d26b2f682224 | |
| parent | 016989874c9be3d61080a6fd1396e11fa5feeee1 (diff) | |
| download | vortrag-knn-a2c8168a21e2abf79f05bbb2e6d8ec61cb1ddca9.tar.gz vortrag-knn-a2c8168a21e2abf79f05bbb2e6d8ec61cb1ddca9.zip | |
fit network layout to data [WIP]
| -rw-r--r-- | index.js | 239 |
1 files changed, 176 insertions, 63 deletions
@@ -1,20 +1,76 @@ -function drawNodes(nodes, netSpec, designSpec, env) { +/* custom d3 widgets */ + +// slider +function makeSlider(labelName, x, y, scale, cb) { + var brush = d3.svg.brush() + .x(scale) + .extent([0, 0]) + .on("brush", brushed); + + var label = svg.append("text") + .attr("x", x-5) + .attr("y", y) + .text(labelName); + + var brushg = svg.append("g") + .attr("transform", "translate(" + x + "," + (y+10) + ")"); + + brushg.append("g") + .attr("class", "x axis") + .call(d3.svg.axis() + .scale(scale) + .ticks(5) + .tickSize(1) + .tickFormat(function(d) { return Math.floor(d*100) + "%"; }) + .tickPadding(10) + .orient("bottom")); + + var slider = brushg.append("g") + .attr("class", "slider") + .call(brush); + + var handle = slider.append("circle") + .attr("r", 5) + .attr("cx", 0) + .attr("cy", 0) + .attr("stroke", "#000") + .attr("fill", "#DDD"); + + slider.call(brush); + + function brushed() { + var value = brush.extent()[0]; + + if (d3.event.sourceEvent) { // not a programmatic event + value = scale.invert(d3.mouse(this)[0]); + brush.extent([value, value]); + } + + handle.attr("cx", scale(value)); + cb(value); + } +} + + +/* network construction */ + +function drawNodes(nodes, layout, env) { var cnt = 0; var biasLayers = 0; - for (layer=0; layer<designSpec.length; layer++) { - if (designSpec[layer] == "bias") biasLayers++; + for (layer=0; layer<layout.net.layers; layer++) { + if (layout.net[layer].isBias) biasLayers++; } - for (layer=0; layer<netSpec.length; layer++) { - for (node=0; node<netSpec[layer]; node++) { + 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 (designSpec[layer] == "bias") { - nodes[cnt].x = (layer - biasLayers + 1.25) * (env.width / (netSpec.length - biasLayers)); - nodes[cnt].y = (node + 0.25) * (env.height / netSpec[layer]); - nodes[cnt].r = d3.min([env.width, env.height]) / d3.max(netSpec.concat(netSpec.length)) * 0.2; + 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 / (netSpec.length - biasLayers)); - nodes[cnt].y = (node + 0.5) * (env.height / netSpec[layer]); - nodes[cnt].r = d3.min([env.width, env.height]) / d3.max(netSpec.concat(netSpec.length)) * 0.3; + 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++; } @@ -34,11 +90,11 @@ function drawLinks(links, weights) { return links; } -function createNet(netSpec, designSpec) { +function createNet(layout) { // create layers var nodes = []; - for (layer=0; layer<netSpec.length; layer++) { - for (node=0; node<netSpec[layer]; node++) { + 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 @@ -49,21 +105,21 @@ function createNet(netSpec, designSpec) { nodes.forEach(function (n) { return nodesMap.set(n.id, n); }); - // create links + // create links between adjacent layers var links = []; - for (layer=0; layer<netSpec.length-1; layer++) { - for (src=0; src<netSpec[layer]; src++) { - if (designSpec[layer] == "bias") { - links.push({ + 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()), + target: nodesMap.get((layer+1).toString() + "_" + src.toString()), weight: 1 }); - } else { - for (tar=0; tar<netSpec[layer+1]; tar++) { - links.push({ + } 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 source: nodesMap.get(layer.toString() + "_" + src.toString()), - target: nodesMap.get((layer + 1).toString() + "_" + tar.toString()), + target: nodesMap.get((layer+1).toString() + "_" + tar.toString()), weight: 1 }); } @@ -77,28 +133,11 @@ function createNet(netSpec, designSpec) { }; } -// create empty elements and determine the visualisation"s size -var svg = d3.select("#visContainer").append("svg") - .attr("width", "100%") - .attr("height", "100%"); - -// create network and links -var netSpec = [13, 13, 11, 1], - designSpec = ["bias", "standard", "standard", "standard"]; -var net = createNet(netSpec, designSpec); +/* visualisation */ // create visualisation function envRedraw(weights, duration) { - // "cache" the values for pure resize events - if (!duration) { - duration = 0; - } - if (!weights) { - weights = this._weigths; - } else { - this._weigths = weights; - } var width = parseInt(svg.style("width")) || 800, height = parseInt(svg.style("height")) || 600; net.nodes = drawNodes(net.nodes, netSpec, designSpec, {"width": width, "height": height}); @@ -121,26 +160,9 @@ function envRedraw(weights, duration) { .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); } -window.addEventListener("resize", envRedraw, true); -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; }) - .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; }); +/* data handling */ // now we're getting to the grips function preload(callback) { @@ -153,10 +175,101 @@ function preload(callback) { var cache = {}; preload(function (json) { - cache = json; - animate(); + // 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; + } + 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! + } + } + } + + setup(layout, json); }); +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; + + /*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);*/ + + 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]); + + 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; }); + + window.addEventListener("resize", envRedraw, true); + // TODO animation & refresh +} + var maxBatch = 8, maxEpoch = 300; var epochStep = 50; var numLayers = 3; |
