├── LICENSE ├── README.md ├── index.html ├── index.js └── package.json /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Heather Arthur Organization 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rnn-viewer 2 | 3 | #### A recurrent neural net utility for brain.js 4 | ![](https://media.giphy.com/media/mHPBX322BZLtm/giphy.gif) 5 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | rnn-viewer 6 | 23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 | 31 | 32 |
33 | 34 | 35 | 36 | 37 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function RNNViewer(settings) { 2 | Object.assign(this, RNNViewer.defaults, settings); 3 | 4 | this.net = settings.net; 5 | this.boundingGrid = null; 6 | this.values = []; 7 | this.grids = []; 8 | this.matrices = []; 9 | this.controls = null; 10 | this.scene = null; 11 | this.camera = null; 12 | this.light = null; 13 | this.renderer = null; 14 | this.stats = null; 15 | 16 | this.init(); 17 | 18 | if (this.net) { 19 | var model = this.net.model; 20 | this.addMatrix(model.input); 21 | //this.addMatrix(model.inputConnector); 22 | 23 | model.hiddenLayers.forEach(this.addMatrix.bind(this)); 24 | 25 | this.addMatrix(model.outputConnector); 26 | this.addMatrix(model.output); 27 | } 28 | 29 | this.animate(); 30 | } 31 | 32 | RNNViewer.defaults = { 33 | net: null, 34 | container: null, 35 | height: window.innerHeight, 36 | width: window.innerWidth, 37 | depth: 400, 38 | hotColor: new THREE.Color(0xff55f9), 39 | coldColor: new THREE.Color(0x050638), 40 | squareWidth: 10, 41 | squareHeight: 10, 42 | devicePixelRatio: window.devicePixelRatio, 43 | includeStats: false 44 | }; 45 | 46 | RNNViewer.prototype = { 47 | init: function() { 48 | //Set up camera 49 | var vFOVRadians = 2 * Math.atan(this.height / (2 * 1500)), 50 | fov = vFOVRadians * 180 / Math.PI, 51 | startPosition = this.startPosition = new THREE.Vector3(0, 0, 3000); 52 | 53 | var camera = this.camera = new THREE.PerspectiveCamera(fov, this.width / this.height, 1, 30000); 54 | camera.position.set(startPosition.x, startPosition.y, startPosition.z); 55 | 56 | var controls = this.controls = new THREE.OrbitControls(camera); 57 | controls.damping = 0.2; 58 | controls.addEventListener('change', this.render.bind(this)); 59 | 60 | //Create scenes for webGL 61 | var scene = this.scene = new THREE.Scene(); 62 | //Add a light source & create Canvas 63 | var light = this.light = new THREE.DirectionalLight( 0xffffff ); 64 | light.position.set(0, 0, 1); 65 | scene.add(light); 66 | 67 | //set up webGL renderer 68 | var renderer = this.renderer = new THREE.WebGLRenderer(); 69 | renderer.setPixelRatio(this.devicePixelRatio); 70 | renderer.setSize(this.width, this.height); 71 | this.container.appendChild(renderer.domElement); 72 | 73 | //stats 74 | if (this.includeStats) { 75 | var stats = this.stats = new Stats(); 76 | stats.domElement.style.position = 'absolute'; 77 | stats.domElement.style.bottom = '10px'; 78 | stats.domElement.style.left = '10px'; 79 | this.container.appendChild(stats.domElement); 80 | } 81 | 82 | var boundingGrid = this.boundingGrid = new THREE.Object3D(); 83 | scene.add(boundingGrid); 84 | return this; 85 | }, 86 | update: function() { 87 | var hotColor = this.settings.hotColor; 88 | var coldColor = this.settings.coldColor; 89 | return this; 90 | }, 91 | render: function() { 92 | var depth = this.depth; 93 | this.grids.forEach(function(grid, i, grids) { 94 | grid.position.z = (grids.length - i) * depth; 95 | }); 96 | 97 | this.camera.lookAt(this.scene.position); 98 | this.renderer.render(this.scene, this.camera); 99 | if (this.stats) this.stats.update(); 100 | return this; 101 | }, 102 | animate: function() { 103 | this.controls.update(); 104 | window.requestAnimationFrame(this.animate.bind(this)); 105 | return this; 106 | }, 107 | addMatrix: function (matrix) { 108 | var grid = new THREE.Object3D(), 109 | depth = this.depth, 110 | rows = matrix.rows, 111 | columns = matrix.columns, 112 | xPixel = -(this.squareWidth * columns)/ 2, 113 | yPixel = -(this.squareHeight * rows) / 2, 114 | lowValue = 0, 115 | highValue = 0, 116 | index = 0; 117 | 118 | //height 119 | for (var row = 1; row <= rows; row++) { 120 | xPixel = -(this.squareWidth * columns) / 2; 121 | for (var column = 1; column <= columns; column++) { 122 | var color = this.coldColor.clone(); 123 | var material = new THREE.MeshBasicMaterial({ 124 | color: color, 125 | side: THREE.DoubleSide, 126 | vertexColors: THREE.FaceColors 127 | }); 128 | var square = new THREE.Geometry(); 129 | square.vertices.push(new THREE.Vector3(xPixel , yPixel , 0)); 130 | square.vertices.push(new THREE.Vector3(xPixel , yPixel + this.squareHeight , 0)); 131 | square.vertices.push(new THREE.Vector3(xPixel + this.squareWidth, yPixel + this.squareHeight , 0)); 132 | square.vertices.push(new THREE.Vector3(xPixel + this.squareWidth, yPixel , 0)); 133 | 134 | square.faces.push(new THREE.Face3(0, 1, 2)); 135 | square.faces.push(new THREE.Face3(0, 3, 2)); 136 | var mesh = new THREE.Mesh(square, material); 137 | grid.add(mesh); 138 | 139 | this.values.push({ 140 | color: color, 141 | row: row - 1, 142 | column: column - 1, 143 | matrixIndex: this.grids.length, 144 | square: square, 145 | mesh: mesh, 146 | frontFace: mesh.geometry.faces[0], 147 | rearFace: mesh.geometry.faces[1], 148 | index: index, 149 | matrix: matrix, 150 | get value() { 151 | var value = this.matrix.weights[this.index]; 152 | if (value > highValue) { 153 | highValue = value; 154 | } 155 | if (value < lowValue) { 156 | lowValue = value; 157 | } 158 | return value || 0; 159 | }, 160 | get percentValue() { 161 | var value = this.value; 162 | var normalizedHigh = highValue - lowValue; 163 | var normalizedValue = value - lowValue; 164 | return (normalizedHigh - normalizedValue) / normalizedHigh; 165 | } 166 | }); 167 | 168 | xPixel += this.squareWidth; 169 | index++; 170 | } 171 | yPixel += this.squareHeight; 172 | } 173 | 174 | this.grids.push(grid); 175 | this.matrices.push(matrix); 176 | this.boundingGrid.add(grid); 177 | 178 | return this; 179 | }, 180 | viewTop: function() { 181 | this.controls.reset(); 182 | 183 | var vFOVRadians = 2 * Math.atan(this.height / ( 2 * 35000 )), 184 | fov = vFOVRadians * 180 / Math.PI; 185 | 186 | this.camera.fov = fov; 187 | this.controls.rotateUp(90 * Math.PI / 180); 188 | this.camera.position.z = this.startPosition.z * 23; 189 | this.camera.position.y = this.startPosition.z * 55; 190 | this.camera.far = 1000000; 191 | this.camera.updateProjectionMatrix(); 192 | return this.render(); 193 | }, 194 | viewSide: function() { 195 | this.controls.reset(); 196 | 197 | var vFOVRadians = 2 * Math.atan(this.height / ( 2 * 35000 )), 198 | fov = vFOVRadians * 180 / Math.PI; 199 | 200 | this.camera.fov = fov; 201 | this.camera.position.z = this.startPosition.z * 58; 202 | this.camera.far = 1000000; 203 | this.camera.updateProjectionMatrix(); 204 | return this.render(); 205 | }, 206 | viewDefault: function() { 207 | this.controls.reset(); 208 | 209 | this.camera.fov = 30; 210 | this.camera.updateProjectionMatrix(); 211 | return this.render(); 212 | }, 213 | setSize: function(width, height) { 214 | this.width = width; 215 | this.height = height; 216 | this.renderer.setSize(this.width, this.height); 217 | return this.render(); 218 | }, 219 | setValue: function(v) { 220 | var v = Math.random() * 2, 221 | r = (coldColor.r + hotColor.r) / v, 222 | g = (coldColor.g + hotColor.g) / v, 223 | b = (coldColor.b + hotColor.b) / v; 224 | 225 | value.frontFace.color.setRGB( 226 | r, 227 | g, 228 | b 229 | ); 230 | value.rearFace.color.setRGB( 231 | r, 232 | g, 233 | b 234 | ); 235 | value.square.colorsNeedUpdate = true; 236 | //value.mesh.geometry.elementsNeedUpdate = true; 237 | value.mesh.geometry.colorsNeedUpdate = true; 238 | } 239 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rnn-viewer", 3 | "version": "1.0.0", 4 | "description": "a brain.js utility for viewing recurrent neural networks using three.js", 5 | "main": "index.js", 6 | "dependencies": { 7 | "three": "^0.81.2" 8 | }, 9 | "devDependencies": {}, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/harthur-org/rnn-viewer.git" 16 | }, 17 | "author": "", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/harthur-org/rnn-viewer/issues" 21 | }, 22 | "homepage": "https://github.com/harthur-org/rnn-viewer#readme" 23 | } 24 | --------------------------------------------------------------------------------