summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorschneefux <schneefux+commit@schneefux.xyz>2016-03-29 20:52:48 +0200
committerschneefux <schneefux+commit@schneefux.xyz>2016-03-29 20:52:48 +0200
commita2c8168a21e2abf79f05bbb2e6d8ec61cb1ddca9 (patch)
tree67559291b1f625990cc1c1f0f315d26b2f682224
parent016989874c9be3d61080a6fd1396e11fa5feeee1 (diff)
downloadvortrag-knn-a2c8168a21e2abf79f05bbb2e6d8ec61cb1ddca9.tar.gz
vortrag-knn-a2c8168a21e2abf79f05bbb2e6d8ec61cb1ddca9.zip
fit network layout to data [WIP]
-rw-r--r--index.js239
1 files changed, 176 insertions, 63 deletions
diff --git a/index.js b/index.js
index 8c6f14e..56b2e5d 100644
--- a/index.js
+++ b/index.js
@@ -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;