Comparar commits
16 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| f1a33e5ccc | |||
| bc284c4369 | |||
| 51b9dfcdbc | |||
| 48649b2fb3 | |||
| 79630b7757 | |||
| 8e4366e548 | |||
| 83b60ccf8e | |||
| 39558f99b7 | |||
| 80350be7dc | |||
| 971165838b | |||
| c70c076b41 | |||
| db0eca8385 | |||
| 02a9df3f8d | |||
| a5ad5b401b | |||
| 8ddc9a8f2c | |||
| 42264f3483 |
@@ -14,7 +14,3 @@ node_modules
|
||||
# Demo.
|
||||
demo.js
|
||||
|
||||
# Degub
|
||||
debug.html
|
||||
|
||||
.settings
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
// List of configurations. Add new configurations or edit existing ones.
|
||||
// ONLY "node" and "mono" are supported, change "type" to switch.
|
||||
"configurations": [
|
||||
{
|
||||
// Name of configuration; appears in the launch configuration drop down menu.
|
||||
"name": "Launch app.js",
|
||||
// Type of configuration. Possible values: "node", "mono".
|
||||
"type": "node",
|
||||
// Workspace relative or absolute path to the program.
|
||||
"program": "app.js",
|
||||
// Automatically stop program after launch.
|
||||
"stopOnEntry": true,
|
||||
// Command line arguments passed to the program.
|
||||
"args": [],
|
||||
// Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace.
|
||||
"cwd": ".",
|
||||
// Workspace relative or absolute path to the runtime executable to be used. Default is the runtime executable on the PATH.
|
||||
"runtimeExecutable": null,
|
||||
// Optional arguments passed to the runtime executable.
|
||||
"runtimeArguments": [],
|
||||
// Environment variables passed to the program.
|
||||
"env": { },
|
||||
// Use JavaScript source maps (if they exist).
|
||||
"sourceMaps": false
|
||||
},
|
||||
{
|
||||
"name": "Attach",
|
||||
"type": "node",
|
||||
// TCP/IP address. Default is "localhost".
|
||||
"address": "localhost",
|
||||
// Port to attach to.
|
||||
"port": 5858,
|
||||
"sourceMaps": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"editor.fontSize": 15
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"command": "gulp",
|
||||
"isShellCommand": true,
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "node",
|
||||
"problemMatcher": "$tsc"
|
||||
},
|
||||
{
|
||||
"taskName": "test",
|
||||
"isTestCommand": true,
|
||||
"problemMatcher": "$tsc"
|
||||
},
|
||||
{
|
||||
"taskName": "test-ntm",
|
||||
"isTestCommand": true,
|
||||
"problemMatcher": "$tsc"
|
||||
},
|
||||
{
|
||||
"isBuildCommand": true,
|
||||
"taskName": "build",
|
||||
"problemMatcher": "$tsc"
|
||||
},
|
||||
{
|
||||
"taskName": "default",
|
||||
"problemMatcher": "$tsc"
|
||||
},
|
||||
{
|
||||
"taskName": "min",
|
||||
"problemMatcher": "$tsc"
|
||||
},
|
||||
{
|
||||
"taskName": "debug",
|
||||
"problemMatcher": "$tsc"
|
||||
}
|
||||
]
|
||||
}
|
||||
+1
-7
@@ -1,9 +1,3 @@
|
||||
language: node_js
|
||||
script: "npm run test:travis"
|
||||
node_js:
|
||||
# always latest release
|
||||
- "node"
|
||||
# previous releases
|
||||
- "6"
|
||||
- "5"
|
||||
- "4"
|
||||
- "0.10"
|
||||
|
||||
Arquivo normal → Arquivo executável
+5
-29
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Juan Cazala - juancazala.com
|
||||
Copyright (c) 2014 Juan Cazala (juancazala.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -9,38 +9,14 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
|
||||
********************************************************************************************
|
||||
SYNAPTIC (v1.0.8)
|
||||
********************************************************************************************
|
||||
|
||||
Synaptic is a javascript neural network library for node.js and the browser, its generalized
|
||||
algorithm is architecture-free, so you can build and train basically any type of first order
|
||||
or even second order neural network architectures.
|
||||
|
||||
http://en.wikipedia.org/wiki/Recurrent_neural_network#Second_Order_Recurrent_Neural_Network
|
||||
|
||||
The library includes a few built-in architectures like multilayer perceptrons, multilayer
|
||||
long-short term memory networks (LSTM) or liquid state machines, and a trainer capable of
|
||||
training any given network, and includes built-in training tasks/tests like solving an XOR,
|
||||
passing a Distracted Sequence Recall test or an Embeded Reber Grammar test.
|
||||
|
||||
The algorithm implemented by this library has been taken from Derek D. Monner's paper:
|
||||
|
||||
|
||||
A generalized LSTM-like training algorithm for second-order recurrent neural networks
|
||||
http://www.overcomplete.net/papers/nn2012.pdf
|
||||
|
||||
There are references to the equations in that paper commented through the source code.
|
||||
|
||||
+20
-29
@@ -1,8 +1,6 @@
|
||||
Synaptic [](https://travis-ci.org/cazala/synaptic) [](https://synaptic-slack-ugiqacqvmd.now.sh/)
|
||||
Synaptic [](https://travis-ci.org/cazala/synaptic)
|
||||
========
|
||||
|
||||
## Important: [Synaptic 2.x](https://github.com/cazala/synaptic/issues/140) is in stage of discussion now! Feel free to participate
|
||||
|
||||
Synaptic is a javascript neural network library for **node.js** and the **browser**, its generalized algorithm is architecture-free, so you can build and train basically any type of first order or even [second order neural network](http://en.wikipedia.org/wiki/Recurrent_neural_network#Second_Order_Recurrent_Neural_Network) architectures.
|
||||
|
||||
This library includes a few built-in architectures like [multilayer perceptrons](http://en.wikipedia.org/wiki/Multilayer_perceptron), [multilayer long-short term memory](http://en.wikipedia.org/wiki/Long_short_term_memory) networks (LSTM), [liquid state machines](http://en.wikipedia.org/wiki/Liquid_state_machine) or [Hopfield](http://en.wikipedia.org/wiki/Hopfield_network) networks, and a trainer capable of training any given network, which includes built-in training tasks/tests like solving an XOR, completing a Distracted Sequence Recall task or an [Embedded Reber Grammar](http://www.willamette.edu/~gorr/classes/cs449/reber.html) test, so you can easily test and compare the performance of different architectures.
|
||||
@@ -19,19 +17,14 @@ There are references to the equations in that paper commented through the source
|
||||
|
||||
If you have no prior knowledge about Neural Networks, you should start by [reading this guide](https://github.com/cazala/synaptic/wiki/Neural-Networks-101).
|
||||
|
||||
|
||||
If you want a practical example on how to feed data to a neural network, then take a look at [this article](https://github.com/cazala/synaptic/wiki/Normalization-101).
|
||||
|
||||
You may also want to take a look at [this article](http://blog.webkid.io/neural-networks-in-javascript/).
|
||||
|
||||
####Demos
|
||||
|
||||
- [Solve an XOR](http://caza.la/synaptic/#/xor)
|
||||
- [Discrete Sequence Recall Task](http://caza.la/synaptic/#/dsr)
|
||||
- [Learn Image Filters](http://caza.la/synaptic/#/image-filters)
|
||||
- [Paint an Image](http://caza.la/synaptic/#/paint-an-image)
|
||||
- [Self Organizing Map](http://caza.la/synaptic/#/self-organizing-map)
|
||||
- [Read from Wikipedia](http://caza.la/synaptic/#/wikipedia)
|
||||
- [Solve an XOR](http://synaptic.juancazala.com/#/xor)
|
||||
- [Discrete Sequence Recall Task](http://synaptic.juancazala.com/#/dsr)
|
||||
- [Learn Image Filters](http://synaptic.juancazala.com/#/image-filters)
|
||||
- [Paint an Image](http://synaptic.juancazala.com/#/paint-an-image)
|
||||
- [Self Organizing Map](http://synaptic.juancazala.com/#/self-organizing-map)
|
||||
- [Read from Wikipedia](http://synaptic.juancazala.com/#/wikipedia)
|
||||
|
||||
The source code of these demos can be found in [this branch](https://github.com/cazala/synaptic/tree/gh-pages/scripts).
|
||||
|
||||
@@ -43,17 +36,12 @@ The source code of these demos can be found in [this branch](https://github.com/
|
||||
- [Trainer](https://github.com/cazala/synaptic/wiki/Trainer/)
|
||||
- [Architect](https://github.com/cazala/synaptic/wiki/Architect/)
|
||||
|
||||
To try out the examples, checkout the [gh-pages](https://github.com/cazala/synaptic/tree/gh-pages) branch.
|
||||
|
||||
`git checkout gh-pages`
|
||||
|
||||
|
||||
##Overview
|
||||
|
||||
###Installation
|
||||
|
||||
#####In node
|
||||
|
||||
You can install synaptic with [npm](http://npmjs.org):
|
||||
|
||||
```cmd
|
||||
@@ -61,17 +49,10 @@ npm install synaptic --save
|
||||
```
|
||||
|
||||
#####In the browser
|
||||
|
||||
You can install synaptic with [bower](http://bower.io):
|
||||
|
||||
```cmd
|
||||
bower install synaptic
|
||||
```
|
||||
|
||||
Or you can simply use the CDN link, kindly provided by [CDNjs](https://cdnjs.com/)
|
||||
Just include the file synaptic.js from `/dist` directory with a script tag in your HTML:
|
||||
|
||||
```html
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/synaptic/1.0.8/synaptic.js"></script>
|
||||
<script src="synaptic.js"></script>
|
||||
```
|
||||
|
||||
###Usage
|
||||
@@ -87,6 +68,16 @@ var Neuron = synaptic.Neuron,
|
||||
|
||||
Now you can start to create networks, train them, or use built-in networks from the [Architect](http://github.com/cazala/synaptic#architect).
|
||||
|
||||
###Gulp Tasks
|
||||
|
||||
- **gulp**: runs all the tests and builds the minified and unminified bundles into `/dist`.
|
||||
- **gulp build**: builds the bundle: `/dist/synaptic.js`.
|
||||
- **gulp min**: builds the minified bundle: `/dist/synaptic.min.js`.
|
||||
- **gulp debug**: builds the bundle `/dist/synaptic.js` with sourcemaps.
|
||||
- **gulp dev**: same as `gulp debug`, but watches the source files and rebuilds when any change is detected.
|
||||
- **gulp test**: runs all the tests.
|
||||
- **gulp node**: builds the typescipt code into `/src`
|
||||
|
||||
###Examples
|
||||
|
||||
#####Perceptron
|
||||
@@ -196,6 +187,6 @@ Multilayer LSTM network architectures.
|
||||
|
||||
**Synaptic** is an Open Source project that started in Buenos Aires, Argentina. Anybody in the world is welcome to contribute to the development of the project.
|
||||
|
||||
If you want to contribute feel free to send PR's, just make sure to run **npm run test** and **npm run build** before submiting it. This way you'll run all the test specs and build the web distribution files.
|
||||
If you want to contribute feel free to send PR's, just make sure to run the default **gulp** task before submiting it. This way you'll run all the test specs and build the web distribution files.
|
||||
|
||||
<3
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"name": "synaptic",
|
||||
"version": "1.0.8",
|
||||
"homepage": "https://github.com/cazala/synaptic",
|
||||
"authors": [
|
||||
"Juan Cazala <juancazala@gmail.com>"
|
||||
],
|
||||
"description": "architecture-free neural network library for node.js and the browser",
|
||||
"main": "./dist/synaptic.min.js",
|
||||
"moduleType": [
|
||||
"amd",
|
||||
"globals",
|
||||
"node"
|
||||
],
|
||||
"keywords": [
|
||||
"neural",
|
||||
"network",
|
||||
"deep",
|
||||
"learning",
|
||||
"machine",
|
||||
"learning",
|
||||
"lstm",
|
||||
"perceptron"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
externo
+8
@@ -0,0 +1,8 @@
|
||||
import hopfield = require('./architect/Hopfield');
|
||||
import lstm = require('./architect/LSTM');
|
||||
import lsm = require('./architect/Liquid');
|
||||
import perceptron = require('./architect/Perceptron');
|
||||
export declare var LSTM: typeof lstm.LSTM;
|
||||
export declare var Liquid: typeof lsm.Liquid;
|
||||
export declare var Hopfield: typeof hopfield.Hopfield;
|
||||
export declare var Perceptron: typeof perceptron.Perceptron;
|
||||
externo
+10
@@ -0,0 +1,10 @@
|
||||
var hopfield = require('./architect/Hopfield');
|
||||
var lstm = require('./architect/LSTM');
|
||||
var lsm = require('./architect/Liquid');
|
||||
var perceptron = require('./architect/Perceptron');
|
||||
exports.LSTM = lstm.LSTM;
|
||||
exports.Liquid = lsm.Liquid;
|
||||
exports.Hopfield = hopfield.Hopfield;
|
||||
exports.Perceptron = perceptron.Perceptron;
|
||||
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9hcmNoaXRlY3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsSUFBTyxRQUFRLFdBQVcsc0JBQXNCLENBQUMsQ0FBQztBQUNsRCxJQUFPLElBQUksV0FBVyxrQkFBa0IsQ0FBQyxDQUFDO0FBQzFDLElBQU8sR0FBRyxXQUFXLG9CQUFvQixDQUFDLENBQUM7QUFDM0MsSUFBTyxVQUFVLFdBQVcsd0JBQXdCLENBQUMsQ0FBQztBQUUzQyxZQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQztBQUNqQixjQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQztBQUNwQixnQkFBUSxHQUFHLFFBQVEsQ0FBQyxRQUFRLENBQUM7QUFDN0Isa0JBQVUsR0FBRyxVQUFVLENBQUMsVUFBVSxDQUFDIiwiZmlsZSI6InNyYy9hcmNoaXRlY3QuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgaG9wZmllbGQgPSByZXF1aXJlKCcuL2FyY2hpdGVjdC9Ib3BmaWVsZCcpO1xuaW1wb3J0IGxzdG0gPSByZXF1aXJlKCcuL2FyY2hpdGVjdC9MU1RNJyk7XG5pbXBvcnQgbHNtID0gcmVxdWlyZSgnLi9hcmNoaXRlY3QvTGlxdWlkJyk7XG5pbXBvcnQgcGVyY2VwdHJvbiA9IHJlcXVpcmUoJy4vYXJjaGl0ZWN0L1BlcmNlcHRyb24nKTtcblxuZXhwb3J0IHZhciBMU1RNID0gbHN0bS5MU1RNO1xuZXhwb3J0IHZhciBMaXF1aWQgPSBsc20uTGlxdWlkO1xuZXhwb3J0IHZhciBIb3BmaWVsZCA9IGhvcGZpZWxkLkhvcGZpZWxkO1xuZXhwb3J0IHZhciBQZXJjZXB0cm9uID0gcGVyY2VwdHJvbi5QZXJjZXB0cm9uO1xuIl0sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9
|
||||
externo
+12
@@ -0,0 +1,12 @@
|
||||
import network = require('../network');
|
||||
import trainer = require('../trainer');
|
||||
export declare class Hopfield extends network.Network {
|
||||
trainer: trainer.Trainer;
|
||||
constructor(size: number);
|
||||
learn(patterns: any): {
|
||||
error: number;
|
||||
iterations: number;
|
||||
time: number;
|
||||
};
|
||||
feed(pattern: any): any[];
|
||||
}
|
||||
externo
+47
@@ -0,0 +1,47 @@
|
||||
var __extends = this.__extends || function (d, b) {
|
||||
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
||||
function __() { this.constructor = d; }
|
||||
__.prototype = b.prototype;
|
||||
d.prototype = new __();
|
||||
};
|
||||
var network = require('../network');
|
||||
var trainer = require('../trainer');
|
||||
var layer = require('../layer');
|
||||
var Hopfield = (function (_super) {
|
||||
__extends(Hopfield, _super);
|
||||
function Hopfield(size) {
|
||||
var inputLayer = new layer.Layer(size);
|
||||
var outputLayer = new layer.Layer(size);
|
||||
inputLayer.project(outputLayer, layer.Layer.connectionType.ALL_TO_ALL);
|
||||
_super.call(this, {
|
||||
input: inputLayer,
|
||||
hidden: [],
|
||||
output: outputLayer
|
||||
});
|
||||
this.trainer = new trainer.Trainer(this);
|
||||
}
|
||||
Hopfield.prototype.learn = function (patterns) {
|
||||
var set = [];
|
||||
for (var p in patterns)
|
||||
set.push({
|
||||
input: patterns[p],
|
||||
output: patterns[p]
|
||||
});
|
||||
return this.trainer.train(set, {
|
||||
iterations: 500000,
|
||||
error: .00005,
|
||||
rate: 1
|
||||
});
|
||||
};
|
||||
Hopfield.prototype.feed = function (pattern) {
|
||||
var output = this.activate(pattern);
|
||||
var patterns = [];
|
||||
for (var i in output)
|
||||
patterns[i] = output[i] > .5 ? 1 : 0;
|
||||
return patterns;
|
||||
};
|
||||
return Hopfield;
|
||||
})(network.Network);
|
||||
exports.Hopfield = Hopfield;
|
||||
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9hcmNoaXRlY3QvSG9wZmllbGQudHMiXSwibmFtZXMiOlsiSG9wZmllbGQiLCJIb3BmaWVsZC5jb25zdHJ1Y3RvciIsIkhvcGZpZWxkLmxlYXJuIiwiSG9wZmllbGQuZmVlZCJdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsSUFBTyxPQUFPLFdBQVksWUFBWSxDQUFDLENBQUM7QUFDeEMsSUFBTyxPQUFPLFdBQVksWUFBWSxDQUFDLENBQUM7QUFDeEMsSUFBTyxLQUFLLFdBQVksVUFBVSxDQUFDLENBQUM7QUFHcEMsSUFBYSxRQUFRO0lBQVNBLFVBQWpCQSxRQUFRQSxVQUF3QkE7SUFHM0NBLFNBSFdBLFFBQVFBLENBR1BBLElBQVlBO1FBQ3RCQyxJQUFJQSxVQUFVQSxHQUFHQSxJQUFJQSxLQUFLQSxDQUFDQSxLQUFLQSxDQUFDQSxJQUFJQSxDQUFDQSxDQUFDQTtRQUN2Q0EsSUFBSUEsV0FBV0EsR0FBR0EsSUFBSUEsS0FBS0EsQ0FBQ0EsS0FBS0EsQ0FBQ0EsSUFBSUEsQ0FBQ0EsQ0FBQ0E7UUFFeENBLFVBQVVBLENBQUNBLE9BQU9BLENBQUNBLFdBQVdBLEVBQUVBLEtBQUtBLENBQUNBLEtBQUtBLENBQUNBLGNBQWNBLENBQUNBLFVBQVVBLENBQUNBLENBQUNBO1FBRXZFQSxrQkFBTUE7WUFDSkEsS0FBS0EsRUFBRUEsVUFBVUE7WUFDakJBLE1BQU1BLEVBQUVBLEVBQUVBO1lBQ1ZBLE1BQU1BLEVBQUVBLFdBQVdBO1NBQ3BCQSxDQUFDQSxDQUFDQTtRQUVIQSxJQUFJQSxDQUFDQSxPQUFPQSxHQUFHQSxJQUFJQSxPQUFPQSxDQUFDQSxPQUFPQSxDQUFDQSxJQUFJQSxDQUFDQSxDQUFDQTtJQUMzQ0EsQ0FBQ0E7SUFFREQsd0JBQUtBLEdBQUxBLFVBQU1BLFFBQVFBO1FBQ1pFLElBQUlBLEdBQUdBLEdBQUdBLEVBQUVBLENBQUNBO1FBQ2JBLEdBQUdBLENBQUNBLENBQUNBLEdBQUdBLENBQUNBLENBQUNBLElBQUlBLFFBQVFBLENBQUNBO1lBQ3JCQSxHQUFHQSxDQUFDQSxJQUFJQSxDQUFDQTtnQkFDUEEsS0FBS0EsRUFBRUEsUUFBUUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0E7Z0JBQ2xCQSxNQUFNQSxFQUFFQSxRQUFRQSxDQUFDQSxDQUFDQSxDQUFDQTthQUNwQkEsQ0FBQ0EsQ0FBQ0E7UUFFTEEsTUFBTUEsQ0FBQ0EsSUFBSUEsQ0FBQ0EsT0FBT0EsQ0FBQ0EsS0FBS0EsQ0FBQ0EsR0FBR0EsRUFBRUE7WUFDN0JBLFVBQVVBLEVBQUVBLE1BQU1BO1lBQ2xCQSxLQUFLQSxFQUFFQSxNQUFNQTtZQUNiQSxJQUFJQSxFQUFFQSxDQUFDQTtTQUNSQSxDQUFDQSxDQUFDQTtJQUNMQSxDQUFDQTtJQUVERix1QkFBSUEsR0FBSkEsVUFBS0EsT0FBT0E7UUFDVkcsSUFBSUEsTUFBTUEsR0FBR0EsSUFBSUEsQ0FBQ0EsUUFBUUEsQ0FBQ0EsT0FBT0EsQ0FBQ0EsQ0FBQ0E7UUFFcENBLElBQUlBLFFBQVFBLEdBQUdBLEVBQUVBLENBQUNBO1FBQ2xCQSxHQUFHQSxDQUFDQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxJQUFJQSxNQUFNQSxDQUFDQTtZQUNuQkEsUUFBUUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsR0FBR0EsTUFBTUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsR0FBR0EsRUFBRUEsR0FBR0EsQ0FBQ0EsR0FBR0EsQ0FBQ0EsQ0FBQ0E7UUFFdkNBLE1BQU1BLENBQUNBLFFBQVFBLENBQUNBO0lBQ2xCQSxDQUFDQTtJQUNISCxlQUFDQTtBQUFEQSxDQTFDQSxBQTBDQ0EsRUExQzZCLE9BQU8sQ0FBQyxPQUFPLEVBMEM1QztBQTFDWSxnQkFBUSxHQUFSLFFBMENaLENBQUEiLCJmaWxlIjoic3JjL2FyY2hpdGVjdC9Ib3BmaWVsZC5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBuZXR3b3JrICA9IHJlcXVpcmUoJy4uL25ldHdvcmsnKTtcbmltcG9ydCB0cmFpbmVyICA9IHJlcXVpcmUoJy4uL3RyYWluZXInKTtcbmltcG9ydCBsYXllciAgPSByZXF1aXJlKCcuLi9sYXllcicpO1xuaW1wb3J0IG5ldXJvbiA9IHJlcXVpcmUoJy4uL25ldXJvbicpO1xuXG5leHBvcnQgY2xhc3MgSG9wZmllbGQgZXh0ZW5kcyBuZXR3b3JrLk5ldHdvcmsge1xuICB0cmFpbmVyOiB0cmFpbmVyLlRyYWluZXI7XG5cbiAgY29uc3RydWN0b3Ioc2l6ZTogbnVtYmVyKSB7XG4gICAgdmFyIGlucHV0TGF5ZXIgPSBuZXcgbGF5ZXIuTGF5ZXIoc2l6ZSk7XG4gICAgdmFyIG91dHB1dExheWVyID0gbmV3IGxheWVyLkxheWVyKHNpemUpO1xuXG4gICAgaW5wdXRMYXllci5wcm9qZWN0KG91dHB1dExheWVyLCBsYXllci5MYXllci5jb25uZWN0aW9uVHlwZS5BTExfVE9fQUxMKTtcblxuICAgIHN1cGVyKHtcbiAgICAgIGlucHV0OiBpbnB1dExheWVyLFxuICAgICAgaGlkZGVuOiBbXSxcbiAgICAgIG91dHB1dDogb3V0cHV0TGF5ZXJcbiAgICB9KTtcblxuICAgIHRoaXMudHJhaW5lciA9IG5ldyB0cmFpbmVyLlRyYWluZXIodGhpcyk7XG4gIH1cblxuICBsZWFybihwYXR0ZXJucykge1xuICAgIHZhciBzZXQgPSBbXTtcbiAgICBmb3IgKHZhciBwIGluIHBhdHRlcm5zKVxuICAgICAgc2V0LnB1c2goe1xuICAgICAgICBpbnB1dDogcGF0dGVybnNbcF0sXG4gICAgICAgIG91dHB1dDogcGF0dGVybnNbcF1cbiAgICAgIH0pO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhaW5lci50cmFpbihzZXQsIHtcbiAgICAgIGl0ZXJhdGlvbnM6IDUwMDAwMCxcbiAgICAgIGVycm9yOiAuMDAwMDUsXG4gICAgICByYXRlOiAxXG4gICAgfSk7XG4gIH1cblxuICBmZWVkKHBhdHRlcm4pIHtcbiAgICB2YXIgb3V0cHV0ID0gdGhpcy5hY3RpdmF0ZShwYXR0ZXJuKTtcblxuICAgIHZhciBwYXR0ZXJucyA9IFtdO1xuICAgIGZvciAodmFyIGkgaW4gb3V0cHV0KVxuICAgICAgcGF0dGVybnNbaV0gPSBvdXRwdXRbaV0gPiAuNSA/IDEgOiAwO1xuXG4gICAgcmV0dXJuIHBhdHRlcm5zO1xuICB9XG59Il0sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9
|
||||
externo
+6
@@ -0,0 +1,6 @@
|
||||
import network = require('../network');
|
||||
import trainer = require('../trainer');
|
||||
export declare class LSTM extends network.Network {
|
||||
trainer: trainer.Trainer;
|
||||
constructor(...args: any[]);
|
||||
}
|
||||
externo
+122
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+6
@@ -0,0 +1,6 @@
|
||||
import network = require('../network');
|
||||
import trainer = require('../trainer');
|
||||
export declare class Liquid extends network.Network {
|
||||
trainer: trainer.Trainer;
|
||||
constructor(inputs: any, hidden: any, outputs: any, connections: any, gates: any);
|
||||
}
|
||||
externo
+51
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+47
@@ -0,0 +1,47 @@
|
||||
import network = require('../network');
|
||||
import trainer = require('../trainer');
|
||||
import Layer = require('../layer');
|
||||
import Synaptic = require('../synaptic');
|
||||
export declare class NTM extends network.Network {
|
||||
trainer: trainer.Trainer;
|
||||
data: Float64Array[];
|
||||
blockWidth: number;
|
||||
blocks: number;
|
||||
heads: Head[];
|
||||
inputValues: Float64Array;
|
||||
inputLayer: Layer.Layer;
|
||||
hiddenLayer: Layer.Layer;
|
||||
outputLayer: Layer.Layer;
|
||||
dirty: boolean;
|
||||
constructor(inputs: number, outputs: number, memBlocks: number, blockWidth: number, heads: number, hiddenSize: number);
|
||||
clean(): void;
|
||||
activate(input: Synaptic.INumericArray): Synaptic.INumericArray;
|
||||
propagate(rate: number, target: Synaptic.INumericArray): void;
|
||||
addHead(subArray: Float64Array): Head;
|
||||
doTimeStep(): void;
|
||||
doAdd(w: Synaptic.INumericArray, addGate: Synaptic.INumericArray): void;
|
||||
doErase(w: Synaptic.INumericArray, eraseGate: Synaptic.INumericArray): void;
|
||||
}
|
||||
export declare class Head {
|
||||
static ADDITIONAL_INPUT_VALUES: number;
|
||||
memory: NTM;
|
||||
w_weightings: Float64Array;
|
||||
eraseGate: Float64Array;
|
||||
addGate: Float64Array;
|
||||
k_keys: Float64Array;
|
||||
g_interpolation: number;
|
||||
Y_focus: number;
|
||||
s_shiftingValue: number;
|
||||
s_shiftingVector: Float64Array;
|
||||
wc_focusedWeights: Float64Array;
|
||||
readVector: Float64Array;
|
||||
ß_keyStrength: number;
|
||||
prevFocus: number;
|
||||
shiftLength: number;
|
||||
layer: Layer.Layer;
|
||||
shiftingLayer: Layer.Layer;
|
||||
constructor(memory: NTM, destinationArray?: Float64Array);
|
||||
private readParams(activation);
|
||||
doShiftings(): void;
|
||||
doTimeStep(): void;
|
||||
}
|
||||
externo
+181
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+6
@@ -0,0 +1,6 @@
|
||||
import network = require('../network');
|
||||
import trainer = require('../trainer');
|
||||
export declare class Perceptron extends network.Network {
|
||||
trainer: trainer.Trainer;
|
||||
constructor(...args: number[]);
|
||||
}
|
||||
externo
+49
@@ -0,0 +1,49 @@
|
||||
var __extends = this.__extends || function (d, b) {
|
||||
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
||||
function __() { this.constructor = d; }
|
||||
__.prototype = b.prototype;
|
||||
d.prototype = new __();
|
||||
};
|
||||
var network = require('../network');
|
||||
var trainer = require('../trainer');
|
||||
var layer = require('../layer');
|
||||
// Multilayer Perceptron
|
||||
var Perceptron = (function (_super) {
|
||||
__extends(Perceptron, _super);
|
||||
function Perceptron() {
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i - 0] = arguments[_i];
|
||||
}
|
||||
if (args.length < 3)
|
||||
throw "Error: not enough layers (minimum 3) !!";
|
||||
var inputs = args.shift(); // first argument
|
||||
var outputs = args.pop(); // last argument
|
||||
var layers = args; // all the arguments in the middle
|
||||
var input = new layer.Layer(inputs);
|
||||
var hidden = [];
|
||||
var output = new layer.Layer(outputs);
|
||||
var previous = input;
|
||||
for (var level in layers) {
|
||||
var size = layers[level];
|
||||
var theLayer = new layer.Layer(size);
|
||||
hidden.push(theLayer);
|
||||
previous.project(theLayer);
|
||||
previous = theLayer;
|
||||
}
|
||||
previous.project(output);
|
||||
// set layers of the neural network
|
||||
_super.call(this, {
|
||||
input: input,
|
||||
hidden: hidden,
|
||||
output: output
|
||||
});
|
||||
// trainer for the network
|
||||
this.trainer = new trainer.Trainer(this);
|
||||
}
|
||||
return Perceptron;
|
||||
})(network.Network);
|
||||
exports.Perceptron = Perceptron;
|
||||
;
|
||||
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9hcmNoaXRlY3QvUGVyY2VwdHJvbi50cyJdLCJuYW1lcyI6WyJQZXJjZXB0cm9uIiwiUGVyY2VwdHJvbi5jb25zdHJ1Y3RvciJdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsSUFBTyxPQUFPLFdBQVksWUFBWSxDQUFDLENBQUM7QUFDeEMsSUFBTyxPQUFPLFdBQVksWUFBWSxDQUFDLENBQUM7QUFDeEMsSUFBTyxLQUFLLFdBQVksVUFBVSxDQUFDLENBQUM7QUFFcEMsQUFDQSx3QkFEd0I7SUFDWCxVQUFVO0lBQVNBLFVBQW5CQSxVQUFVQSxVQUF3QkE7SUFHN0NBLFNBSFdBLFVBQVVBO1FBR1RDLGNBQWlCQTthQUFqQkEsV0FBaUJBLENBQWpCQSxzQkFBaUJBLENBQWpCQSxJQUFpQkE7WUFBakJBLDZCQUFpQkE7O1FBRTNCQSxFQUFFQSxDQUFDQSxDQUFDQSxJQUFJQSxDQUFDQSxNQUFNQSxHQUFHQSxDQUFDQSxDQUFDQTtZQUNsQkEsTUFBTUEseUNBQXlDQSxDQUFDQTtRQUVsREEsSUFBSUEsTUFBTUEsR0FBR0EsSUFBSUEsQ0FBQ0EsS0FBS0EsRUFBRUEsRUFBRUEsaUJBQWlCQTtRQUM1Q0EsSUFBSUEsT0FBT0EsR0FBR0EsSUFBSUEsQ0FBQ0EsR0FBR0EsRUFBRUEsRUFBRUEsZ0JBQWdCQTtRQUMxQ0EsSUFBSUEsTUFBTUEsR0FBR0EsSUFBSUEsRUFBRUEsa0NBQWtDQTtRQUVyREEsSUFBSUEsS0FBS0EsR0FBR0EsSUFBSUEsS0FBS0EsQ0FBQ0EsS0FBS0EsQ0FBQ0EsTUFBTUEsQ0FBQ0EsQ0FBQ0E7UUFDcENBLElBQUlBLE1BQU1BLEdBQUdBLEVBQUVBLENBQUNBO1FBQ2hCQSxJQUFJQSxNQUFNQSxHQUFHQSxJQUFJQSxLQUFLQSxDQUFDQSxLQUFLQSxDQUFDQSxPQUFPQSxDQUFDQSxDQUFDQTtRQUV0Q0EsSUFBSUEsUUFBUUEsR0FBR0EsS0FBS0EsQ0FBQ0E7UUFHckJBLEdBQUdBLENBQUNBLENBQUNBLEdBQUdBLENBQUNBLEtBQUtBLElBQUlBLE1BQU1BLENBQUNBLENBQUNBLENBQUNBO1lBQ3pCQSxJQUFJQSxJQUFJQSxHQUFHQSxNQUFNQSxDQUFDQSxLQUFLQSxDQUFDQSxDQUFDQTtZQUN6QkEsSUFBSUEsUUFBUUEsR0FBR0EsSUFBSUEsS0FBS0EsQ0FBQ0EsS0FBS0EsQ0FBQ0EsSUFBSUEsQ0FBQ0EsQ0FBQ0E7WUFDckNBLE1BQU1BLENBQUNBLElBQUlBLENBQUNBLFFBQVFBLENBQUNBLENBQUNBO1lBQ3RCQSxRQUFRQSxDQUFDQSxPQUFPQSxDQUFDQSxRQUFRQSxDQUFDQSxDQUFDQTtZQUMzQkEsUUFBUUEsR0FBR0EsUUFBUUEsQ0FBQ0E7UUFDdEJBLENBQUNBO1FBQ0RBLFFBQVFBLENBQUNBLE9BQU9BLENBQUNBLE1BQU1BLENBQUNBLENBQUNBO1FBRXpCQSxBQUVBQSxtQ0FGbUNBO1FBRW5DQSxrQkFBTUE7WUFDSkEsS0FBS0EsRUFBRUEsS0FBS0E7WUFDWkEsTUFBTUEsRUFBRUEsTUFBTUE7WUFDZEEsTUFBTUEsRUFBRUEsTUFBTUE7U0FDZkEsQ0FBQ0EsQ0FBQ0E7UUFFSEEsQUFDQUEsMEJBRDBCQTtRQUMxQkEsSUFBSUEsQ0FBQ0EsT0FBT0EsR0FBR0EsSUFBSUEsT0FBT0EsQ0FBQ0EsT0FBT0EsQ0FBQ0EsSUFBSUEsQ0FBQ0EsQ0FBQ0E7SUFDM0NBLENBQUNBO0lBQ0hELGlCQUFDQTtBQUFEQSxDQXZDQSxBQXVDQ0EsRUF2QytCLE9BQU8sQ0FBQyxPQUFPLEVBdUM5QztBQXZDWSxrQkFBVSxHQUFWLFVBdUNaLENBQUE7QUFBQSxDQUFDIiwiZmlsZSI6InNyYy9hcmNoaXRlY3QvUGVyY2VwdHJvbi5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBuZXR3b3JrICA9IHJlcXVpcmUoJy4uL25ldHdvcmsnKTtcbmltcG9ydCB0cmFpbmVyICA9IHJlcXVpcmUoJy4uL3RyYWluZXInKTtcbmltcG9ydCBsYXllciAgPSByZXF1aXJlKCcuLi9sYXllcicpO1xuaW1wb3J0IG5ldXJvbiA9IHJlcXVpcmUoJy4uL25ldXJvbicpO1xuLy8gTXVsdGlsYXllciBQZXJjZXB0cm9uXG5leHBvcnQgY2xhc3MgUGVyY2VwdHJvbiBleHRlbmRzIG5ldHdvcmsuTmV0d29yayB7XG4gIHRyYWluZXI6IHRyYWluZXIuVHJhaW5lcjtcblxuICBjb25zdHJ1Y3RvciguLi5hcmdzOiBudW1iZXJbXSkge1xuXG4gICAgaWYgKGFyZ3MubGVuZ3RoIDwgMylcbiAgICAgIHRocm93IFwiRXJyb3I6IG5vdCBlbm91Z2ggbGF5ZXJzIChtaW5pbXVtIDMpICEhXCI7XG5cbiAgICB2YXIgaW5wdXRzID0gYXJncy5zaGlmdCgpOyAvLyBmaXJzdCBhcmd1bWVudFxuICAgIHZhciBvdXRwdXRzID0gYXJncy5wb3AoKTsgLy8gbGFzdCBhcmd1bWVudFxuICAgIHZhciBsYXllcnMgPSBhcmdzOyAvLyBhbGwgdGhlIGFyZ3VtZW50cyBpbiB0aGUgbWlkZGxlXG4gIFxuICAgIHZhciBpbnB1dCA9IG5ldyBsYXllci5MYXllcihpbnB1dHMpO1xuICAgIHZhciBoaWRkZW4gPSBbXTtcbiAgICB2YXIgb3V0cHV0ID0gbmV3IGxheWVyLkxheWVyKG91dHB1dHMpO1xuXG4gICAgdmFyIHByZXZpb3VzID0gaW5wdXQ7XG4gIFxuICAgIC8vIGdlbmVyYXRlIGhpZGRlbiBsYXllcnNcbiAgICBmb3IgKHZhciBsZXZlbCBpbiBsYXllcnMpIHtcbiAgICAgIHZhciBzaXplID0gbGF5ZXJzW2xldmVsXTtcbiAgICAgIHZhciB0aGVMYXllciA9IG5ldyBsYXllci5MYXllcihzaXplKTtcbiAgICAgIGhpZGRlbi5wdXNoKHRoZUxheWVyKTtcbiAgICAgIHByZXZpb3VzLnByb2plY3QodGhlTGF5ZXIpO1xuICAgICAgcHJldmlvdXMgPSB0aGVMYXllcjtcbiAgICB9XG4gICAgcHJldmlvdXMucHJvamVjdChvdXRwdXQpO1xuICBcbiAgICAvLyBzZXQgbGF5ZXJzIG9mIHRoZSBuZXVyYWwgbmV0d29ya1xuICAgICAgXG4gICAgc3VwZXIoe1xuICAgICAgaW5wdXQ6IGlucHV0LFxuICAgICAgaGlkZGVuOiBoaWRkZW4sXG4gICAgICBvdXRwdXQ6IG91dHB1dFxuICAgIH0pO1xuICBcbiAgICAvLyB0cmFpbmVyIGZvciB0aGUgbmV0d29ya1xuICAgIHRoaXMudHJhaW5lciA9IG5ldyB0cmFpbmVyLlRyYWluZXIodGhpcyk7XG4gIH1cbn07ICJdLCJzb3VyY2VSb290IjoiL3NvdXJjZS8ifQ==
|
||||
externo
+52
@@ -0,0 +1,52 @@
|
||||
import neuron = require('./neuron');
|
||||
import network = require('./network');
|
||||
import Synaptic = require('./synaptic');
|
||||
/*******************************************************************************************
|
||||
LAYER
|
||||
*******************************************************************************************/
|
||||
export declare class Layer {
|
||||
optimizable: boolean;
|
||||
list: neuron.Neuron[];
|
||||
label: string;
|
||||
connectedto: any[];
|
||||
size: number;
|
||||
currentActivation: Float64Array;
|
||||
constructor(size: number, label?: string);
|
||||
activate(input?: Synaptic.INumericArray): Synaptic.INumericArray;
|
||||
propagate(rate: number, target?: Synaptic.INumericArray): void;
|
||||
project(layer: network.Network | Layer, type?: string, weights?: Synaptic.INumericArray): Layer.LayerConnection;
|
||||
gate(connection: any, type: any): void;
|
||||
selfconnected(): boolean;
|
||||
connected(layer: any): string;
|
||||
clear(): void;
|
||||
reset(): void;
|
||||
neurons(): neuron.Neuron[];
|
||||
add(neuron: any): void;
|
||||
set(options: any): Layer;
|
||||
}
|
||||
export declare module Layer {
|
||||
var layerQty: number;
|
||||
function uid(): number;
|
||||
var connectionType: {
|
||||
ALL_TO_ALL: string;
|
||||
ONE_TO_ONE: string;
|
||||
ALL_TO_ELSE: string;
|
||||
};
|
||||
var gateType: {
|
||||
INPUT: string;
|
||||
OUTPUT: string;
|
||||
ONE_TO_ONE: string;
|
||||
};
|
||||
class LayerConnection {
|
||||
ID: number;
|
||||
from: Layer;
|
||||
to: Layer;
|
||||
selfconnection: boolean;
|
||||
type: string;
|
||||
connections: Synaptic.Dictionary<neuron.Neuron.Connection>;
|
||||
list: neuron.Neuron.Connection[];
|
||||
size: number;
|
||||
gatedfrom: any[];
|
||||
constructor(fromLayer: any, toLayer: any, type: any, weights: any);
|
||||
}
|
||||
}
|
||||
externo
+255
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+42
@@ -0,0 +1,42 @@
|
||||
import Synaptic = require('./synaptic');
|
||||
import _neuron = require('./neuron');
|
||||
export declare class Network {
|
||||
optimized: any;
|
||||
layers: {
|
||||
input: any;
|
||||
hidden: {};
|
||||
output: any;
|
||||
};
|
||||
constructor(layers?: any);
|
||||
activate(input: Synaptic.INumericArray): any;
|
||||
propagate(rate: number, target?: Synaptic.INumericArray): void;
|
||||
project(unit: any, type: any, weights: any): any;
|
||||
gate(connection: any, type: any): void;
|
||||
clear(): void;
|
||||
reset(): void;
|
||||
optimize(): void;
|
||||
restore(): void;
|
||||
neurons(): Network.INetworkNeuron[];
|
||||
inputs(): number;
|
||||
outputs(): number;
|
||||
set(layers: any): void;
|
||||
setOptimize(bool: any): void;
|
||||
toJSON(ignoreTraces: any): {
|
||||
neurons: any[];
|
||||
connections: any[];
|
||||
};
|
||||
toDot(edgeconnection: any): {
|
||||
code: string;
|
||||
link: string;
|
||||
};
|
||||
standalone(): any;
|
||||
worker(): Worker;
|
||||
clone(ignoreTraces: any): Network;
|
||||
static fromJSON(json: any): Network;
|
||||
}
|
||||
export declare module Network {
|
||||
interface INetworkNeuron {
|
||||
neuron: _neuron.Neuron;
|
||||
layer: string;
|
||||
}
|
||||
}
|
||||
externo
+491
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+71
@@ -0,0 +1,71 @@
|
||||
/// <reference path="synaptic.d.ts" />
|
||||
import Synaptic = require('./synaptic');
|
||||
import Squash = require('./squash');
|
||||
/******************************************************************************************
|
||||
NEURON
|
||||
*******************************************************************************************/
|
||||
export declare class Neuron {
|
||||
optimizable: boolean;
|
||||
ID: number;
|
||||
label: any;
|
||||
connections: Neuron.INeuronConnections;
|
||||
error: {
|
||||
responsibility: number;
|
||||
projected: number;
|
||||
gated: number;
|
||||
};
|
||||
trace: {
|
||||
elegibility: {};
|
||||
extended: {};
|
||||
influences: {};
|
||||
};
|
||||
state: number;
|
||||
old: number;
|
||||
activation: number;
|
||||
selfconnection: Neuron.Connection;
|
||||
squash: typeof Squash.LOGISTIC;
|
||||
neighboors: {};
|
||||
bias: number;
|
||||
derivative: number;
|
||||
constructor();
|
||||
readIncommingConnections(input?: number): number;
|
||||
updateTraces(): void;
|
||||
activate(input?: number): number;
|
||||
propagate(rate: number, target?: number): void;
|
||||
project(neuron: any, weight?: number): Neuron.Connection;
|
||||
gate(connection: any): void;
|
||||
selfconnected(): boolean;
|
||||
connected(neuron: any): {
|
||||
type: string;
|
||||
connection: Neuron.Connection;
|
||||
};
|
||||
clear(): void;
|
||||
reset(): void;
|
||||
optimize(optimized: any, layer: any): Synaptic.ICompiledParameters;
|
||||
}
|
||||
export declare module Neuron {
|
||||
interface INeuronConnections {
|
||||
inputs: Synaptic.Dictionary<Neuron.Connection>;
|
||||
projected: {};
|
||||
gated: {};
|
||||
}
|
||||
class Connection {
|
||||
ID: number;
|
||||
from: any;
|
||||
to: any;
|
||||
gain: number;
|
||||
weight: number;
|
||||
gater: any;
|
||||
constructor(from: any, to: any, weight?: number);
|
||||
}
|
||||
var neuronQty: number;
|
||||
function uid(): number;
|
||||
function quantity(): {
|
||||
neurons: number;
|
||||
connections: number;
|
||||
};
|
||||
}
|
||||
export declare module Neuron.Connection {
|
||||
var connectionQty: number;
|
||||
function uid(): number;
|
||||
}
|
||||
externo
+667
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+7
@@ -0,0 +1,7 @@
|
||||
import Synaptic = require('./synaptic');
|
||||
import Layer = require('./layer');
|
||||
export declare class SoftMaxLayer extends Layer.Layer {
|
||||
constructor(size: number, label?: string);
|
||||
activate(input?: Synaptic.INumericArray): Synaptic.INumericArray;
|
||||
static NormalizeConnectionWeights(layerConnection: Layer.Layer.LayerConnection): void;
|
||||
}
|
||||
externo
+70
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+6
@@ -0,0 +1,6 @@
|
||||
export declare function LOGISTIC(x: number, derivate?: boolean): number;
|
||||
export declare function TANH(x: number, derivate?: boolean): any;
|
||||
export declare function IDENTITY(x: number, derivate?: boolean): number;
|
||||
export declare function HLIM(x: number, derivate?: boolean): number;
|
||||
export declare function SOFTPLUS(x: number, derivate?: boolean): number;
|
||||
export declare function EXP(x: number, derivate?: boolean): number;
|
||||
externo
+37
@@ -0,0 +1,37 @@
|
||||
// squashing functions
|
||||
function LOGISTIC(x, derivate) {
|
||||
if (derivate) {
|
||||
var fx = LOGISTIC(x);
|
||||
return fx * (1 - fx);
|
||||
}
|
||||
return 1 / (1 + Math.exp(-x));
|
||||
}
|
||||
exports.LOGISTIC = LOGISTIC;
|
||||
function TANH(x, derivate) {
|
||||
if (derivate)
|
||||
return 1 - Math.pow(TANH(x), 2);
|
||||
var eP = Math.exp(x);
|
||||
var eN = 1 / eP;
|
||||
return (eP - eN) / (eP + eN);
|
||||
}
|
||||
exports.TANH = TANH;
|
||||
function IDENTITY(x, derivate) {
|
||||
return derivate ? 1 : x;
|
||||
}
|
||||
exports.IDENTITY = IDENTITY;
|
||||
function HLIM(x, derivate) {
|
||||
return derivate ? 1 : +(x > 0);
|
||||
}
|
||||
exports.HLIM = HLIM;
|
||||
function SOFTPLUS(x, derivate) {
|
||||
if (derivate)
|
||||
return 1 - 1 / (1 + Math.exp(x));
|
||||
return Math.log(1 + Math.exp(x));
|
||||
}
|
||||
exports.SOFTPLUS = SOFTPLUS;
|
||||
function EXP(x, derivate) {
|
||||
return Math.exp(x);
|
||||
}
|
||||
exports.EXP = EXP;
|
||||
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9zcXVhc2gudHMiXSwibmFtZXMiOlsiTE9HSVNUSUMiLCJUQU5IIiwiSURFTlRJVFkiLCJITElNIiwiU09GVFBMVVMiLCJFWFAiXSwibWFwcGluZ3MiOiJBQUVBLEFBRUEsc0JBRnNCO1NBRU4sUUFBUSxDQUFDLENBQVMsRUFBRSxRQUFrQjtJQUNyREEsRUFBRUEsQ0FBQ0EsQ0FBQ0EsUUFBUUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0E7UUFDZEEsSUFBSUEsRUFBRUEsR0FBR0EsUUFBUUEsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0E7UUFDckJBLE1BQU1BLENBQUNBLEVBQUVBLEdBQUdBLENBQUNBLENBQUNBLEdBQUdBLEVBQUVBLENBQUNBLENBQUNBO0lBQ3RCQSxDQUFDQTtJQUNEQSxNQUFNQSxDQUFDQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxHQUFHQSxJQUFJQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQTtBQUUvQkEsQ0FBQ0E7QUFQZSxnQkFBUSxHQUFSLFFBT2YsQ0FBQTtBQUVELFNBQWdCLElBQUksQ0FBQyxDQUFTLEVBQUUsUUFBa0I7SUFDakRDLEVBQUVBLENBQUNBLENBQUNBLFFBQVFBLENBQUNBO1FBQ1pBLE1BQU1BLENBQUNBLENBQUNBLEdBQUdBLElBQUlBLENBQUNBLEdBQUdBLENBQUNBLElBQUlBLENBQUNBLENBQUNBLENBQUNBLEVBQUVBLENBQUNBLENBQUNBLENBQUNBO0lBQ2pDQSxJQUFJQSxFQUFFQSxHQUFHQSxJQUFJQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQTtJQUNyQkEsSUFBSUEsRUFBRUEsR0FBR0EsQ0FBQ0EsR0FBR0EsRUFBRUEsQ0FBQ0E7SUFDaEJBLE1BQU1BLENBQUNBLENBQUNBLEVBQUVBLEdBQUdBLEVBQUVBLENBQUNBLEdBQUdBLENBQUNBLEVBQUVBLEdBQUdBLEVBQUVBLENBQUNBLENBQUNBO0FBQzlCQSxDQUFDQTtBQU5lLFlBQUksR0FBSixJQU1mLENBQUE7QUFFRCxTQUFnQixRQUFRLENBQUMsQ0FBUyxFQUFFLFFBQWtCO0lBQ3JEQyxNQUFNQSxDQUFDQSxRQUFRQSxHQUFHQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQTtBQUN6QkEsQ0FBQ0E7QUFGZSxnQkFBUSxHQUFSLFFBRWYsQ0FBQTtBQUVELFNBQWdCLElBQUksQ0FBQyxDQUFTLEVBQUUsUUFBa0I7SUFDakRDLE1BQU1BLENBQUNBLFFBQVFBLEdBQUdBLENBQUNBLEdBQUdBLENBQUNBLENBQUNBLENBQUNBLEdBQUdBLENBQUNBLENBQUNBLENBQUNBO0FBQ2hDQSxDQUFDQTtBQUZlLFlBQUksR0FBSixJQUVmLENBQUE7QUFFRCxTQUFnQixRQUFRLENBQUMsQ0FBUyxFQUFFLFFBQWtCO0lBQ3JEQyxFQUFFQSxDQUFDQSxDQUFDQSxRQUFRQSxDQUFDQTtRQUNaQSxNQUFNQSxDQUFDQSxDQUFDQSxHQUFHQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxHQUFHQSxJQUFJQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQTtJQUNsQ0EsTUFBTUEsQ0FBQ0EsSUFBSUEsQ0FBQ0EsR0FBR0EsQ0FBQ0EsQ0FBQ0EsR0FBR0EsSUFBSUEsQ0FBQ0EsR0FBR0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0EsQ0FBQ0E7QUFDbENBLENBQUNBO0FBSmUsZ0JBQVEsR0FBUixRQUlmLENBQUE7QUFFRCxTQUFnQixHQUFHLENBQUMsQ0FBUyxFQUFFLFFBQWtCO0lBQ2hEQyxNQUFNQSxDQUFDQSxJQUFJQSxDQUFDQSxHQUFHQSxDQUFDQSxDQUFDQSxDQUFDQSxDQUFDQTtBQUNwQkEsQ0FBQ0E7QUFGZSxXQUFHLEdBQUgsR0FFZixDQUFBIiwiZmlsZSI6InNyYy9zcXVhc2guanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgU3luYXB0aWMgPSByZXF1aXJlKCcuL3N5bmFwdGljJyk7XG5cbi8vIHNxdWFzaGluZyBmdW5jdGlvbnNcblxuZXhwb3J0IGZ1bmN0aW9uIExPR0lTVElDKHg6IG51bWJlciwgZGVyaXZhdGU/OiBib29sZWFuKSB7XG5cdGlmIChkZXJpdmF0ZSkge1xuXHRcdHZhciBmeCA9IExPR0lTVElDKHgpO1xuXHRcdHJldHVybiBmeCAqICgxIC0gZngpO1xuXHR9XG5cdHJldHVybiAxIC8gKDEgKyBNYXRoLmV4cCgteCkpO1xuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBUQU5IKHg6IG51bWJlciwgZGVyaXZhdGU/OiBib29sZWFuKSB7XG5cdGlmIChkZXJpdmF0ZSlcblx0XHRyZXR1cm4gMSAtIE1hdGgucG93KFRBTkgoeCksIDIpO1xuXHR2YXIgZVAgPSBNYXRoLmV4cCh4KTtcblx0dmFyIGVOID0gMSAvIGVQO1xuXHRyZXR1cm4gKGVQIC0gZU4pIC8gKGVQICsgZU4pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gSURFTlRJVFkoeDogbnVtYmVyLCBkZXJpdmF0ZT86IGJvb2xlYW4pIHtcblx0cmV0dXJuIGRlcml2YXRlID8gMSA6IHg7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBITElNKHg6IG51bWJlciwgZGVyaXZhdGU/OiBib29sZWFuKSB7XG5cdHJldHVybiBkZXJpdmF0ZSA/IDEgOiArKHggPiAwKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFNPRlRQTFVTKHg6IG51bWJlciwgZGVyaXZhdGU/OiBib29sZWFuKSB7XG5cdGlmIChkZXJpdmF0ZSlcblx0XHRyZXR1cm4gMSAtIDEgLyAoMSArIE1hdGguZXhwKHgpKTtcblx0cmV0dXJuIE1hdGgubG9nKDEgKyBNYXRoLmV4cCh4KSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBFWFAoeDogbnVtYmVyLCBkZXJpdmF0ZT86IGJvb2xlYW4pIHtcblx0cmV0dXJuIE1hdGguZXhwKHgpO1xufSAiXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0=
|
||||
externo
+37
@@ -0,0 +1,37 @@
|
||||
import network = require('./network');
|
||||
import layer = require('./layer');
|
||||
import neuron = require('./neuron');
|
||||
import trainer = require('./trainer');
|
||||
import architect = require('./architect');
|
||||
import squash = require('./squash');
|
||||
import utils = require('./utils');
|
||||
declare module Synaptic {
|
||||
interface Dictionary<T> {
|
||||
[id: string]: T;
|
||||
}
|
||||
function ninja(): typeof Synaptic;
|
||||
interface ICompiledParameters {
|
||||
memory?: any;
|
||||
neurons?: number;
|
||||
inputs?: any[];
|
||||
outputs?: any[];
|
||||
targets?: any[];
|
||||
variables?: any;
|
||||
activation_sentences?: any[];
|
||||
trace_sentences?: any[];
|
||||
propagation_sentences?: any[];
|
||||
layers?: any;
|
||||
}
|
||||
interface INumericArray {
|
||||
[index: number]: number;
|
||||
length: number;
|
||||
}
|
||||
var Neuron: typeof neuron.Neuron;
|
||||
var Layer: typeof layer.Layer;
|
||||
var Network: typeof network.Network;
|
||||
var Trainer: typeof trainer.Trainer;
|
||||
var Squash: typeof squash;
|
||||
var Architect: typeof architect;
|
||||
var Utils: typeof utils.Utils;
|
||||
}
|
||||
export = Synaptic;
|
||||
externo
+53
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+47
@@ -0,0 +1,47 @@
|
||||
import net = require('./network');
|
||||
/*******************************************************************************************
|
||||
TRAINER
|
||||
*******************************************************************************************/
|
||||
export declare class Trainer {
|
||||
network: net.Network;
|
||||
rate: any;
|
||||
iterations: number;
|
||||
error: number;
|
||||
cost: Trainer.ITrainerCostFn;
|
||||
schedule: any;
|
||||
constructor(network: net.Network, options?: any);
|
||||
train(set: any, options: any): {
|
||||
error: number;
|
||||
iterations: number;
|
||||
time: number;
|
||||
};
|
||||
workerTrain(set: any, callback: any, options: any): void;
|
||||
XOR(options: any): {
|
||||
error: number;
|
||||
iterations: number;
|
||||
time: number;
|
||||
};
|
||||
DSR(options: any): {
|
||||
iterations: number;
|
||||
success: number;
|
||||
error: number;
|
||||
time: number;
|
||||
};
|
||||
ERG(options: any): {
|
||||
iterations: number;
|
||||
error: number;
|
||||
time: number;
|
||||
test: (str: any) => boolean;
|
||||
generate: () => string;
|
||||
};
|
||||
}
|
||||
export declare module Trainer {
|
||||
interface ITrainerCostFn {
|
||||
(target: any, output: any): number;
|
||||
}
|
||||
var cost: {
|
||||
CROSS_ENTROPY: (target: any, output: any) => number;
|
||||
CROSS_ENTROPY_SOFTMAX: (target: any, output: any) => number;
|
||||
MSE: (target: any, output: any) => number;
|
||||
};
|
||||
}
|
||||
externo
+535
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+17
@@ -0,0 +1,17 @@
|
||||
import Synaptic = require('./synaptic');
|
||||
export declare class Utils {
|
||||
static transformationMatrixCache: {
|
||||
[index: number]: Float64Array[];
|
||||
};
|
||||
static softMax<T extends Synaptic.INumericArray>(outputArray: T): T;
|
||||
static softMaxDerivative<T extends Synaptic.INumericArray>(outputArray: T): T;
|
||||
static softMaxReinforcement<T extends Synaptic.INumericArray>(array: T, temperature?: number): T;
|
||||
static getCosineSimilarity(arrayA: Synaptic.INumericArray, arrayB: Synaptic.INumericArray): number;
|
||||
static interpolateArray(output_inputA: Synaptic.INumericArray, inputB: Synaptic.INumericArray, g: any): Synaptic.INumericArray;
|
||||
static sharpArray(output: Synaptic.INumericArray, wn: Synaptic.INumericArray, Y: number): void;
|
||||
static scalarShifting(wg: Synaptic.INumericArray, shiftScalar: number): Float64Array;
|
||||
static normalizeShift(shift: Float64Array): void;
|
||||
static vectorInvertedShifting(wg: Float64Array, shiftings: Synaptic.INumericArray): void;
|
||||
static initRandomSoftmaxArray(array: Float64Array): void;
|
||||
static buildCirculantMatrix(length: number, offset?: number): Float64Array[];
|
||||
}
|
||||
externo
+172
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+2551
-2848
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
externo
+2
-2848
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,92 @@
|
||||
'use strict';
|
||||
|
||||
var license = '/*\n\nThe MIT License (MIT)\n\nCopyright (c) 2014 Juan Cazala - juancazala.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE\n\n\n\n********************************************************************************************\n SYNAPTIC\n********************************************************************************************\n\nSynaptic is a javascript neural network library for node.js and the browser, its generalized\nalgorithm is architecture-free, so you can build and train basically any type of first order\nor even second order neural network architectures.\n\nhttp://en.wikipedia.org/wiki/Recurrent_neural_network#Second_Order_Recurrent_Neural_Network\n\nThe library includes a few built-in architectures like multilayer perceptrons, multilayer\nlong-short term memory networks (LSTM) or liquid state machines, and a trainer capable of\ntraining any given network, and includes built-in training tasks/tests like solving an XOR,\npassing a Distracted Sequence Recall test or an Embeded Reber Grammar test.\n\nThe algorithm implemented by this library has been taken from Derek D. Monner\'s paper:\n\n\nA generalized LSTM-like training algorithm for second-order recurrent neural networks\nhttp://www.overcomplete.net/papers/nn2012.pdf\n\nThere are references to the equations in that paper commented through the source code.\n\n\n********************************************************************************************/\n';
|
||||
var globals = 'var synaptic = synaptic || Synaptic;var Neuron = synaptic.Neuron, Layer = synaptic.Layer, Network = synaptic.Network, Trainer = synaptic.Trainer, Architect = synaptic.Architect;';
|
||||
|
||||
// import
|
||||
var gulp = require('gulp');
|
||||
var browserify = require('browserify');
|
||||
var uglify = require('gulp-uglify');
|
||||
var mocha = require('gulp-mocha');
|
||||
var prepend = require('gulp-insert').prepend;
|
||||
var append = require('gulp-insert').append;
|
||||
var source = require('vinyl-source-stream');
|
||||
var buffer = require('vinyl-buffer');
|
||||
var ts = require('gulp-typescript');
|
||||
var merge2 = require('merge2');
|
||||
|
||||
var sm = require('gulp-sourcemaps');
|
||||
|
||||
// default task: runs all the tests, and builds all the files into dist (minified and unminifed)
|
||||
gulp.task('default', ['test', 'build', 'min', 'node']);
|
||||
|
||||
|
||||
var tsProject = ts.createProject('tsconfig.json');
|
||||
|
||||
// build typescript sources
|
||||
gulp.task('node', function () {
|
||||
var tsResult = tsProject.src() // instead of gulp.src(...)
|
||||
.pipe(sm.init())
|
||||
.pipe(ts(tsProject));
|
||||
|
||||
return merge2([
|
||||
tsResult.js.pipe(sm.write()).pipe(gulp.dest('./dist')),
|
||||
tsResult.dts.pipe(gulp.dest('./dist'))
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
// build source into /dist for the web
|
||||
gulp.task('build', function () {
|
||||
return browserify()
|
||||
.add('./src/synaptic.ts')
|
||||
.plugin('tsify')
|
||||
.bundle()
|
||||
.pipe(source('synaptic.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(append(globals))
|
||||
.pipe(gulp.dest('./dist'));
|
||||
});
|
||||
|
||||
// build source into /dist for web (minified)
|
||||
gulp.task('min', function () {
|
||||
return browserify({})
|
||||
.add('./src/synaptic.ts')
|
||||
.plugin('tsify')
|
||||
.bundle()
|
||||
.pipe(source('synaptic.min.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(uglify())
|
||||
.pipe(append(globals))
|
||||
.pipe(gulp.dest('./dist'));
|
||||
});
|
||||
|
||||
// build source into /dist with sourcemaps for debugging
|
||||
gulp.task('debug', function () {
|
||||
return browserify({ debug: true })
|
||||
.add('./src/synaptic.ts')
|
||||
.plugin('tsify')
|
||||
.bundle()
|
||||
.pipe(source('synaptic.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(append(globals))
|
||||
.pipe(gulp.dest('./dist'));
|
||||
});
|
||||
|
||||
// run all the tests with mocha
|
||||
gulp.task('test', ['node'], function () {
|
||||
return gulp.src('test/synaptic.js', {read: false})
|
||||
.pipe(mocha());
|
||||
});
|
||||
|
||||
// run all the tests with mocha
|
||||
gulp.task('test-ntm', ['node'], function () {
|
||||
return gulp.src('test/ntm.js', {read: false})
|
||||
.pipe(mocha());
|
||||
});
|
||||
|
||||
|
||||
// watch for changed and re-build (debug)
|
||||
gulp.task('dev', function () {
|
||||
gulp.watch('./src/*.ts', ['debug']);
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
// Karma configuration
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['mocha'],
|
||||
files: [
|
||||
'dist/synaptic.js',
|
||||
'test/[^_]*.js'
|
||||
],
|
||||
exclude: [
|
||||
],
|
||||
preprocessors: {
|
||||
'test/*.js': ['webpack'],
|
||||
},
|
||||
reporters: ['progress'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
singleRun: false,
|
||||
concurrency: Infinity,
|
||||
browserNoActivityTimeout: 60000,
|
||||
})
|
||||
}
|
||||
Arquivo normal → Arquivo executável
+25
-34
@@ -1,36 +1,26 @@
|
||||
{
|
||||
"name": "synaptic",
|
||||
"version": "1.0.9",
|
||||
"description": "architecture-free neural network library",
|
||||
"main": "./src/synaptic",
|
||||
"version": "0.1.7",
|
||||
"description": "Architecture-free neural network library",
|
||||
"main": "./node-dist/src/synaptic",
|
||||
"scripts": {
|
||||
"test": "npm run test:src",
|
||||
"test:src": "mocha test --require src/synaptic.js ./test",
|
||||
"test:dist": "npm run build && npm run test:mocha:dist && npm run test:karma:browsers",
|
||||
"test:mocha:src": "mocha test --require src/synaptic.js ./test",
|
||||
"test:mocha:dist": "mocha test --require dist/synaptic.js ./test",
|
||||
"test:karma:browsers": "karma start --single-run --browsers Chrome,Firefox,SafariPrivate",
|
||||
"test:karma:phantomjs": "karma start --single-run --browsers PhantomJS",
|
||||
"test:travis": "npm run test:mocha:src && npm run build && npm run test:mocha:dist",
|
||||
"build": "webpack --config webpack.config.js"
|
||||
"test": "mocha test"
|
||||
},
|
||||
"prepush": [
|
||||
"test",
|
||||
"build"
|
||||
],
|
||||
"devDependencies": {
|
||||
"chai": "^3.5.0",
|
||||
"chai-stats": "^0.3.0",
|
||||
"karma": "^1.1.2",
|
||||
"karma-chrome-launcher": "^1.0.1",
|
||||
"karma-firefox-launcher": "^1.0.0",
|
||||
"karma-mocha": "^1.1.1",
|
||||
"karma-phantomjs-launcher": "^1.0.1",
|
||||
"karma-safari-launcher": "^1.0.0",
|
||||
"karma-webpack": "^1.7.0",
|
||||
"browserify": "^10.1.3",
|
||||
"gulp": "^3.8.11",
|
||||
"gulp-insert": "^0.4.0",
|
||||
"gulp-mocha": "^2.0.1",
|
||||
"gulp-sourcemaps": "^1.5.2",
|
||||
"gulp-uglify": "^1.2.0",
|
||||
"gulp-util": "^3.0.4",
|
||||
"vinyl-buffer": "^1.0.0",
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
"mocha": "^2.2.4",
|
||||
"pre-push": "^0.1.1",
|
||||
"webpack": "^1.13.1"
|
||||
"typescript": "^1.5.0-beta",
|
||||
"gulp-typescript": "*",
|
||||
"merge2": "*",
|
||||
"tsify": "*"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -44,15 +34,16 @@
|
||||
"architecture free"
|
||||
],
|
||||
"author": "Juan Cazala <juancazala@gmail.com> (http://juancazala.com/)",
|
||||
"license": {
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/cazala/synaptic/blob/master/LICENSE"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Agustin Mendez",
|
||||
"email": "agustin@soflex.com.ar"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/cazala/synaptic/issues"
|
||||
},
|
||||
"homepage": "http://synaptic.juancazala.com",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
"engines" : { "node" : "^0.10.0" }
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// update license year and version
|
||||
var fs = require('fs')
|
||||
module.exports = function() {
|
||||
var year = (new Date).getFullYear()
|
||||
var version = require('./package.json').version
|
||||
// LICENSE
|
||||
var license = fs.readFileSync('LICENSE', 'utf-8')
|
||||
.replace(/\(c\) ([0-9]+)/, `(c) ${year}`)
|
||||
.replace(/SYNAPTIC \(v(.*)\)/, `SYNAPTIC (v${version})`)
|
||||
fs.writeFileSync('LICENSE', license)
|
||||
// bower.json
|
||||
var bower = fs.readFileSync('bower.json', 'utf-8')
|
||||
.replace(/\"version\": \"(.*)\",/, `"version": "${version}",`)
|
||||
fs.writeFileSync('bower.json', bower)
|
||||
// README.md
|
||||
var readme = fs.readFileSync('README.md', 'utf-8')
|
||||
.replace(/ajax\/libs\/synaptic\/(.*)\/synaptic.js/, `ajax/libs/synaptic/${version}/synaptic.js`)
|
||||
fs.writeFileSync('README.md', readme)
|
||||
// return license for dist banner
|
||||
return license
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
// import
|
||||
var Layer = require('./layer')
|
||||
, Network = require('./network')
|
||||
, Trainer = require('./trainer')
|
||||
|
||||
/*******************************************************************************************
|
||||
ARCHITECT
|
||||
*******************************************************************************************/
|
||||
|
||||
// Collection of useful built-in architectures
|
||||
var Architect = {
|
||||
|
||||
// Multilayer Perceptron
|
||||
Perceptron: function Perceptron() {
|
||||
|
||||
var args = Array.prototype.slice.call(arguments); // convert arguments to Array
|
||||
if (args.length < 3)
|
||||
throw new Error("not enough layers (minimum 3) !!");
|
||||
|
||||
var inputs = args.shift(); // first argument
|
||||
var outputs = args.pop(); // last argument
|
||||
var layers = args; // all the arguments in the middle
|
||||
|
||||
var input = new Layer(inputs);
|
||||
var hidden = [];
|
||||
var output = new Layer(outputs);
|
||||
|
||||
var previous = input;
|
||||
|
||||
// generate hidden layers
|
||||
for (var level in layers) {
|
||||
var size = layers[level];
|
||||
var layer = new Layer(size);
|
||||
hidden.push(layer);
|
||||
previous.project(layer);
|
||||
previous = layer;
|
||||
}
|
||||
previous.project(output);
|
||||
|
||||
// set layers of the neural network
|
||||
this.set({
|
||||
input: input,
|
||||
hidden: hidden,
|
||||
output: output
|
||||
});
|
||||
|
||||
// trainer for the network
|
||||
this.trainer = new Trainer(this);
|
||||
},
|
||||
|
||||
// Multilayer Long Short-Term Memory
|
||||
LSTM: function LSTM() {
|
||||
|
||||
var args = Array.prototype.slice.call(arguments); // convert arguments to array
|
||||
if (args.length < 3)
|
||||
throw new Error("not enough layers (minimum 3) !!");
|
||||
|
||||
var last = args.pop();
|
||||
var option = {
|
||||
peepholes: Layer.connectionType.ALL_TO_ALL,
|
||||
hiddenToHidden: false,
|
||||
outputToHidden: false,
|
||||
outputToGates: false,
|
||||
inputToOutput: true,
|
||||
};
|
||||
if (typeof last != 'number') {
|
||||
var outputs = args.pop();
|
||||
if (last.hasOwnProperty('peepholes'))
|
||||
option.peepholes = last.peepholes;
|
||||
if (last.hasOwnProperty('hiddenToHidden'))
|
||||
option.hiddenToHidden = last.hiddenToHidden;
|
||||
if (last.hasOwnProperty('outputToHidden'))
|
||||
option.outputToHidden = last.outputToHidden;
|
||||
if (last.hasOwnProperty('outputToGates'))
|
||||
option.outputToGates = last.outputToGates;
|
||||
if (last.hasOwnProperty('inputToOutput'))
|
||||
option.inputToOutput = last.inputToOutput;
|
||||
} else
|
||||
var outputs = last;
|
||||
|
||||
var inputs = args.shift();
|
||||
var layers = args;
|
||||
|
||||
var inputLayer = new Layer(inputs);
|
||||
var hiddenLayers = [];
|
||||
var outputLayer = new Layer(outputs);
|
||||
|
||||
var previous = null;
|
||||
|
||||
// generate layers
|
||||
for (var layer in layers) {
|
||||
// generate memory blocks (memory cell and respective gates)
|
||||
var size = layers[layer];
|
||||
|
||||
var inputGate = new Layer(size).set({
|
||||
bias: 1
|
||||
});
|
||||
var forgetGate = new Layer(size).set({
|
||||
bias: 1
|
||||
});
|
||||
var memoryCell = new Layer(size);
|
||||
var outputGate = new Layer(size).set({
|
||||
bias: 1
|
||||
});
|
||||
|
||||
hiddenLayers.push(inputGate);
|
||||
hiddenLayers.push(forgetGate);
|
||||
hiddenLayers.push(memoryCell);
|
||||
hiddenLayers.push(outputGate);
|
||||
|
||||
// connections from input layer
|
||||
var input = inputLayer.project(memoryCell);
|
||||
inputLayer.project(inputGate);
|
||||
inputLayer.project(forgetGate);
|
||||
inputLayer.project(outputGate);
|
||||
|
||||
// connections from previous memory-block layer to this one
|
||||
if (previous != null) {
|
||||
var cell = previous.project(memoryCell);
|
||||
previous.project(inputGate);
|
||||
previous.project(forgetGate);
|
||||
previous.project(outputGate);
|
||||
}
|
||||
|
||||
// connections from memory cell
|
||||
var output = memoryCell.project(outputLayer);
|
||||
|
||||
// self-connection
|
||||
var self = memoryCell.project(memoryCell);
|
||||
|
||||
// hidden to hidden recurrent connection
|
||||
if (option.hiddenToHidden)
|
||||
memoryCell.project(memoryCell, Layer.connectionType.ALL_TO_ELSE);
|
||||
|
||||
// out to hidden recurrent connection
|
||||
if (option.outputToHidden)
|
||||
outputLayer.project(memoryCell);
|
||||
|
||||
// out to gates recurrent connection
|
||||
if (option.outputToGates) {
|
||||
outputLayer.project(inputGate);
|
||||
outputLayer.project(outputGate);
|
||||
outputLayer.project(forgetGate);
|
||||
}
|
||||
|
||||
// peepholes
|
||||
memoryCell.project(inputGate, option.peepholes);
|
||||
memoryCell.project(forgetGate, option.peepholes);
|
||||
memoryCell.project(outputGate, option.peepholes);
|
||||
|
||||
// gates
|
||||
inputGate.gate(input, Layer.gateType.INPUT);
|
||||
forgetGate.gate(self, Layer.gateType.ONE_TO_ONE);
|
||||
outputGate.gate(output, Layer.gateType.OUTPUT);
|
||||
if (previous != null)
|
||||
inputGate.gate(cell, Layer.gateType.INPUT);
|
||||
|
||||
previous = memoryCell;
|
||||
}
|
||||
|
||||
// input to output direct connection
|
||||
if (option.inputToOutput)
|
||||
inputLayer.project(outputLayer);
|
||||
|
||||
// set the layers of the neural network
|
||||
this.set({
|
||||
input: inputLayer,
|
||||
hidden: hiddenLayers,
|
||||
output: outputLayer
|
||||
});
|
||||
|
||||
// trainer
|
||||
this.trainer = new Trainer(this);
|
||||
},
|
||||
|
||||
// Liquid State Machine
|
||||
Liquid: function Liquid(inputs, hidden, outputs, connections, gates) {
|
||||
|
||||
// create layers
|
||||
var inputLayer = new Layer(inputs);
|
||||
var hiddenLayer = new Layer(hidden);
|
||||
var outputLayer = new Layer(outputs);
|
||||
|
||||
// make connections and gates randomly among the neurons
|
||||
var neurons = hiddenLayer.neurons();
|
||||
var connectionList = [];
|
||||
|
||||
for (var i = 0; i < connections; i++) {
|
||||
// connect two random neurons
|
||||
var from = Math.random() * neurons.length | 0;
|
||||
var to = Math.random() * neurons.length | 0;
|
||||
var connection = neurons[from].project(neurons[to]);
|
||||
connectionList.push(connection);
|
||||
}
|
||||
|
||||
for (var j = 0; j < gates; j++) {
|
||||
// pick a random gater neuron
|
||||
var gater = Math.random() * neurons.length | 0;
|
||||
// pick a random connection to gate
|
||||
var connection = Math.random() * connectionList.length | 0;
|
||||
// let the gater gate the connection
|
||||
neurons[gater].gate(connectionList[connection]);
|
||||
}
|
||||
|
||||
// connect the layers
|
||||
inputLayer.project(hiddenLayer);
|
||||
hiddenLayer.project(outputLayer);
|
||||
|
||||
// set the layers of the network
|
||||
this.set({
|
||||
input: inputLayer,
|
||||
hidden: [hiddenLayer],
|
||||
output: outputLayer
|
||||
});
|
||||
|
||||
// trainer
|
||||
this.trainer = new Trainer(this);
|
||||
},
|
||||
|
||||
Hopfield: function Hopfield(size) {
|
||||
|
||||
var inputLayer = new Layer(size);
|
||||
var outputLayer = new Layer(size);
|
||||
|
||||
inputLayer.project(outputLayer, Layer.connectionType.ALL_TO_ALL);
|
||||
|
||||
this.set({
|
||||
input: inputLayer,
|
||||
hidden: [],
|
||||
output: outputLayer
|
||||
});
|
||||
|
||||
var trainer = new Trainer(this);
|
||||
|
||||
var proto = Architect.Hopfield.prototype;
|
||||
|
||||
proto.learn = proto.learn || function(patterns)
|
||||
{
|
||||
var set = [];
|
||||
for (var p in patterns)
|
||||
set.push({
|
||||
input: patterns[p],
|
||||
output: patterns[p]
|
||||
});
|
||||
|
||||
return trainer.train(set, {
|
||||
iterations: 500000,
|
||||
error: .00005,
|
||||
rate: 1
|
||||
});
|
||||
};
|
||||
|
||||
proto.feed = proto.feed || function(pattern)
|
||||
{
|
||||
var output = this.activate(pattern);
|
||||
|
||||
var pattern = [];
|
||||
for (var i in output)
|
||||
pattern[i] = output[i] > .5 ? 1 : 0;
|
||||
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extend prototype chain (so every architectures is an instance of Network)
|
||||
for (var architecture in Architect) {
|
||||
Architect[architecture].prototype = new Network();
|
||||
Architect[architecture].prototype.constructor = Architect[architecture];
|
||||
}
|
||||
|
||||
// export
|
||||
if (module) module.exports = Architect;
|
||||
@@ -0,0 +1,9 @@
|
||||
import hopfield = require('./architect/Hopfield');
|
||||
import lstm = require('./architect/LSTM');
|
||||
import lsm = require('./architect/Liquid');
|
||||
import perceptron = require('./architect/Perceptron');
|
||||
|
||||
export var LSTM = lstm.LSTM;
|
||||
export var Liquid = lsm.Liquid;
|
||||
export var Hopfield = hopfield.Hopfield;
|
||||
export var Perceptron = perceptron.Perceptron;
|
||||
@@ -0,0 +1,48 @@
|
||||
import network = require('../network');
|
||||
import trainer = require('../trainer');
|
||||
import layer = require('../layer');
|
||||
import neuron = require('../neuron');
|
||||
|
||||
export class Hopfield extends network.Network {
|
||||
trainer: trainer.Trainer;
|
||||
|
||||
constructor(size: number) {
|
||||
var inputLayer = new layer.Layer(size);
|
||||
var outputLayer = new layer.Layer(size);
|
||||
|
||||
inputLayer.project(outputLayer, layer.Layer.connectionType.ALL_TO_ALL);
|
||||
|
||||
super({
|
||||
input: inputLayer,
|
||||
hidden: [],
|
||||
output: outputLayer
|
||||
});
|
||||
|
||||
this.trainer = new trainer.Trainer(this);
|
||||
}
|
||||
|
||||
learn(patterns) {
|
||||
var set = [];
|
||||
for (var p in patterns)
|
||||
set.push({
|
||||
input: patterns[p],
|
||||
output: patterns[p]
|
||||
});
|
||||
|
||||
return this.trainer.train(set, {
|
||||
iterations: 500000,
|
||||
error: .00005,
|
||||
rate: 1
|
||||
});
|
||||
}
|
||||
|
||||
feed(pattern) {
|
||||
var output = this.activate(pattern);
|
||||
|
||||
var patterns = [];
|
||||
for (var i in output)
|
||||
patterns[i] = output[i] > .5 ? 1 : 0;
|
||||
|
||||
return patterns;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import network = require('../network');
|
||||
import trainer = require('../trainer');
|
||||
import Layer = require('../layer');
|
||||
import neuron = require('../neuron');
|
||||
import softMaxLayer = require('../softmaxLayer');
|
||||
|
||||
export class LSTM extends network.Network {
|
||||
trainer: trainer.Trainer;
|
||||
|
||||
constructor(...args: any[]) {
|
||||
|
||||
if (args.length < 3)
|
||||
throw "Error: not enough layers (minimum 3) !!";
|
||||
|
||||
var last = args.pop();
|
||||
var option = {
|
||||
peepholes: Layer.Layer.connectionType.ALL_TO_ALL,
|
||||
hiddentohidden: false,
|
||||
outtohidden: false,
|
||||
outtogates: false,
|
||||
intoout: true,
|
||||
};
|
||||
|
||||
if (typeof last != 'number') {
|
||||
var outputs = args.pop();
|
||||
if (last.hasOwnProperty('peepholes'))
|
||||
option.peepholes = last.peepholes;
|
||||
if (last.hasOwnProperty('hiddentohidden'))
|
||||
option.hiddentohidden = last.hiddentohidden;
|
||||
if (last.hasOwnProperty('outtohidden'))
|
||||
option.outtohidden = last.outtohidden;
|
||||
if (last.hasOwnProperty('outtogates'))
|
||||
option.outtogates = last.outtogates;
|
||||
if (last.hasOwnProperty('intoout'))
|
||||
option.intoout = last.intoout;
|
||||
} else
|
||||
var outputs = last;
|
||||
|
||||
var inputs = args.shift();
|
||||
var layers = args;
|
||||
|
||||
var inputLayer = new Layer.Layer(inputs);
|
||||
var hiddenLayers = [];
|
||||
var outputLayer = new Layer.Layer(outputs);
|
||||
|
||||
var previous = null;
|
||||
|
||||
// generate layers
|
||||
for (var layer in layers) {
|
||||
// generate memory blocks (memory cell and respective gates)
|
||||
var size = layers[layer];
|
||||
|
||||
var inputGate = new Layer.Layer(size).set({
|
||||
bias: 1
|
||||
});
|
||||
var forgetGate = new Layer.Layer(size).set({
|
||||
bias: 1
|
||||
});
|
||||
var memoryCell = new Layer.Layer(size);
|
||||
var outputGate = new Layer.Layer(size).set({
|
||||
bias: 1
|
||||
});
|
||||
|
||||
hiddenLayers.push(inputGate);
|
||||
hiddenLayers.push(forgetGate);
|
||||
hiddenLayers.push(memoryCell);
|
||||
hiddenLayers.push(outputGate);
|
||||
|
||||
// connections from input layer
|
||||
var input = inputLayer.project(memoryCell);
|
||||
inputLayer.project(inputGate);
|
||||
inputLayer.project(forgetGate);
|
||||
inputLayer.project(outputGate);
|
||||
|
||||
// connections from previous memory-block layer to this one
|
||||
if (previous != null) {
|
||||
var cell = previous.project(memoryCell);
|
||||
previous.project(inputGate);
|
||||
previous.project(forgetGate);
|
||||
previous.project(outputGate);
|
||||
}
|
||||
|
||||
// connections from memory cell
|
||||
var output = memoryCell.project(outputLayer);
|
||||
|
||||
// self-connection
|
||||
var self = memoryCell.project(memoryCell);
|
||||
|
||||
// hidden to hidden recurrent connection
|
||||
if (option.hiddentohidden)
|
||||
memoryCell.project(memoryCell, Layer.Layer.connectionType.ALL_TO_ELSE);
|
||||
|
||||
// out to hidden recurrent connection
|
||||
if (option.outtohidden)
|
||||
outputLayer.project(memoryCell);
|
||||
|
||||
// out to gates recurrent connection
|
||||
if (option.outtogates) {
|
||||
outputLayer.project(inputGate);
|
||||
outputLayer.project(outputGate);
|
||||
outputLayer.project(forgetGate);
|
||||
}
|
||||
|
||||
// peepholes
|
||||
memoryCell.project(inputGate, option.peepholes);
|
||||
memoryCell.project(forgetGate, option.peepholes);
|
||||
memoryCell.project(outputGate, option.peepholes);
|
||||
|
||||
// gates
|
||||
inputGate.gate(input, Layer.Layer.gateType.INPUT);
|
||||
forgetGate.gate(self, Layer.Layer.gateType.ONE_TO_ONE);
|
||||
outputGate.gate(output, Layer.Layer.gateType.OUTPUT);
|
||||
if (previous != null)
|
||||
inputGate.gate(cell, Layer.Layer.gateType.INPUT);
|
||||
|
||||
previous = memoryCell;
|
||||
}
|
||||
|
||||
// input to output direct connection
|
||||
if (option.intoout)
|
||||
inputLayer.project(outputLayer);
|
||||
|
||||
// set the layers of the neural network
|
||||
super({
|
||||
input: inputLayer,
|
||||
hidden: hiddenLayers,
|
||||
output: outputLayer
|
||||
});
|
||||
|
||||
// trainer
|
||||
this.trainer = new trainer.Trainer(this);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
import network = require('../network');
|
||||
import trainer = require('../trainer');
|
||||
import layer = require('../layer');
|
||||
import neuron = require('../neuron');
|
||||
|
||||
export class Liquid extends network.Network {
|
||||
trainer: trainer.Trainer;
|
||||
|
||||
constructor(inputs, hidden, outputs, connections, gates) {
|
||||
|
||||
// create layers
|
||||
var inputLayer = new layer.Layer(inputs);
|
||||
var hiddenLayer = new layer.Layer(hidden);
|
||||
var outputLayer = new layer.Layer(outputs);
|
||||
|
||||
// make connections and gates randomly among the neurons
|
||||
var neurons = hiddenLayer.neurons();
|
||||
var connectionList: neuron.Neuron.Connection[] = [];
|
||||
|
||||
for (var i = 0; i < connections; i++) {
|
||||
// connect two random neurons
|
||||
var from = Math.random() * neurons.length | 0;
|
||||
var to = Math.random() * neurons.length | 0;
|
||||
var connection = neurons[from].project(neurons[to]);
|
||||
connectionList.push(connection);
|
||||
}
|
||||
|
||||
for (var j = 0; j < gates; j++) {
|
||||
// pick a random gater neuron
|
||||
var gater = Math.random() * neurons.length | 0;
|
||||
// pick a random connection to gate
|
||||
var connectionNumber = Math.random() * connectionList.length | 0;
|
||||
// let the gater gate the connection
|
||||
neurons[gater].gate(connectionList[connectionNumber]);
|
||||
}
|
||||
|
||||
// connect the layers
|
||||
inputLayer.project(hiddenLayer);
|
||||
hiddenLayer.project(outputLayer);
|
||||
|
||||
// set the layers of the network
|
||||
super({
|
||||
input: inputLayer,
|
||||
hidden: [hiddenLayer],
|
||||
output: outputLayer
|
||||
});
|
||||
|
||||
// trainer
|
||||
this.trainer = new trainer.Trainer(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import network = require('../network');
|
||||
import trainer = require('../trainer');
|
||||
import layer = require('../layer');
|
||||
import neuron = require('../neuron');
|
||||
// Multilayer Perceptron
|
||||
export class Perceptron extends network.Network {
|
||||
trainer: trainer.Trainer;
|
||||
|
||||
constructor(...args: number[]) {
|
||||
|
||||
if (args.length < 3)
|
||||
throw "Error: not enough layers (minimum 3) !!";
|
||||
|
||||
var inputs = args.shift(); // first argument
|
||||
var outputs = args.pop(); // last argument
|
||||
var layers = args; // all the arguments in the middle
|
||||
|
||||
var input = new layer.Layer(inputs);
|
||||
var hidden = [];
|
||||
var output = new layer.Layer(outputs);
|
||||
|
||||
var previous = input;
|
||||
|
||||
// generate hidden layers
|
||||
for (var level in layers) {
|
||||
var size = layers[level];
|
||||
var theLayer = new layer.Layer(size);
|
||||
hidden.push(theLayer);
|
||||
previous.project(theLayer);
|
||||
previous = theLayer;
|
||||
}
|
||||
previous.project(output);
|
||||
|
||||
// set layers of the neural network
|
||||
|
||||
super({
|
||||
input: input,
|
||||
hidden: hidden,
|
||||
output: output
|
||||
});
|
||||
|
||||
// trainer for the network
|
||||
this.trainer = new trainer.Trainer(this);
|
||||
}
|
||||
};
|
||||
-276
@@ -1,276 +0,0 @@
|
||||
// export
|
||||
if (module) module.exports = Layer;
|
||||
|
||||
// import
|
||||
var Neuron = require('./neuron')
|
||||
, Network = require('./network')
|
||||
|
||||
/*******************************************************************************************
|
||||
LAYER
|
||||
*******************************************************************************************/
|
||||
|
||||
function Layer(size, label) {
|
||||
this.size = size | 0;
|
||||
this.list = [];
|
||||
this.label = label || null;
|
||||
this.connectedTo = [];
|
||||
|
||||
while (size--) {
|
||||
var neuron = new Neuron();
|
||||
this.list.push(neuron);
|
||||
}
|
||||
}
|
||||
|
||||
Layer.prototype = {
|
||||
|
||||
// activates all the neurons in the layer
|
||||
activate: function(input) {
|
||||
|
||||
var activations = [];
|
||||
|
||||
if (typeof input != 'undefined') {
|
||||
if (input.length != this.size)
|
||||
throw new Error("INPUT size and LAYER size must be the same to activate!");
|
||||
|
||||
for (var id in this.list) {
|
||||
var neuron = this.list[id];
|
||||
var activation = neuron.activate(input[id]);
|
||||
activations.push(activation);
|
||||
}
|
||||
} else {
|
||||
for (var id in this.list) {
|
||||
var neuron = this.list[id];
|
||||
var activation = neuron.activate();
|
||||
activations.push(activation);
|
||||
}
|
||||
}
|
||||
return activations;
|
||||
},
|
||||
|
||||
// propagates the error on all the neurons of the layer
|
||||
propagate: function(rate, target) {
|
||||
|
||||
if (typeof target != 'undefined') {
|
||||
if (target.length != this.size)
|
||||
throw new Error("TARGET size and LAYER size must be the same to propagate!");
|
||||
|
||||
for (var id = this.list.length - 1; id >= 0; id--) {
|
||||
var neuron = this.list[id];
|
||||
neuron.propagate(rate, target[id]);
|
||||
}
|
||||
} else {
|
||||
for (var id = this.list.length - 1; id >= 0; id--) {
|
||||
var neuron = this.list[id];
|
||||
neuron.propagate(rate);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// projects a connection from this layer to another one
|
||||
project: function(layer, type, weights) {
|
||||
|
||||
if (layer instanceof Network)
|
||||
layer = layer.layers.input;
|
||||
|
||||
if (layer instanceof Layer) {
|
||||
if (!this.connected(layer))
|
||||
return new Layer.connection(this, layer, type, weights);
|
||||
} else
|
||||
throw new Error("Invalid argument, you can only project connections to LAYERS and NETWORKS!");
|
||||
|
||||
|
||||
},
|
||||
|
||||
// gates a connection betwenn two layers
|
||||
gate: function(connection, type) {
|
||||
|
||||
if (type == Layer.gateType.INPUT) {
|
||||
if (connection.to.size != this.size)
|
||||
throw new Error("GATER layer and CONNECTION.TO layer must be the same size in order to gate!");
|
||||
|
||||
for (var id in connection.to.list) {
|
||||
var neuron = connection.to.list[id];
|
||||
var gater = this.list[id];
|
||||
for (var input in neuron.connections.inputs) {
|
||||
var gated = neuron.connections.inputs[input];
|
||||
if (gated.ID in connection.connections)
|
||||
gater.gate(gated);
|
||||
}
|
||||
}
|
||||
} else if (type == Layer.gateType.OUTPUT) {
|
||||
if (connection.from.size != this.size)
|
||||
throw new Error("GATER layer and CONNECTION.FROM layer must be the same size in order to gate!");
|
||||
|
||||
for (var id in connection.from.list) {
|
||||
var neuron = connection.from.list[id];
|
||||
var gater = this.list[id];
|
||||
for (var projected in neuron.connections.projected) {
|
||||
var gated = neuron.connections.projected[projected];
|
||||
if (gated.ID in connection.connections)
|
||||
gater.gate(gated);
|
||||
}
|
||||
}
|
||||
} else if (type == Layer.gateType.ONE_TO_ONE) {
|
||||
if (connection.size != this.size)
|
||||
throw new Error("The number of GATER UNITS must be the same as the number of CONNECTIONS to gate!");
|
||||
|
||||
for (var id in connection.list) {
|
||||
var gater = this.list[id];
|
||||
var gated = connection.list[id];
|
||||
gater.gate(gated);
|
||||
}
|
||||
}
|
||||
connection.gatedfrom.push({layer: this, type: type});
|
||||
},
|
||||
|
||||
// true or false whether the whole layer is self-connected or not
|
||||
selfconnected: function() {
|
||||
|
||||
for (var id in this.list) {
|
||||
var neuron = this.list[id];
|
||||
if (!neuron.selfconnected())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// true of false whether the layer is connected to another layer (parameter) or not
|
||||
connected: function(layer) {
|
||||
// Check if ALL to ALL connection
|
||||
var connections = 0;
|
||||
for (var here in this.list) {
|
||||
for (var there in layer.list) {
|
||||
var from = this.list[here];
|
||||
var to = layer.list[there];
|
||||
var connected = from.connected(to);
|
||||
if (connected.type == 'projected')
|
||||
connections++;
|
||||
}
|
||||
}
|
||||
if (connections == this.size * layer.size)
|
||||
return Layer.connectionType.ALL_TO_ALL;
|
||||
|
||||
// Check if ONE to ONE connection
|
||||
connections = 0;
|
||||
for (var neuron in this.list) {
|
||||
var from = this.list[neuron];
|
||||
var to = layer.list[neuron];
|
||||
var connected = from.connected(to);
|
||||
if (connected.type == 'projected')
|
||||
connections++;
|
||||
}
|
||||
if (connections == this.size)
|
||||
return Layer.connectionType.ONE_TO_ONE;
|
||||
},
|
||||
|
||||
// clears all the neuorns in the layer
|
||||
clear: function() {
|
||||
for (var id in this.list) {
|
||||
var neuron = this.list[id];
|
||||
neuron.clear();
|
||||
}
|
||||
},
|
||||
|
||||
// resets all the neurons in the layer
|
||||
reset: function() {
|
||||
for (var id in this.list) {
|
||||
var neuron = this.list[id];
|
||||
neuron.reset();
|
||||
}
|
||||
},
|
||||
|
||||
// returns all the neurons in the layer (array)
|
||||
neurons: function() {
|
||||
return this.list;
|
||||
},
|
||||
|
||||
// adds a neuron to the layer
|
||||
add: function(neuron) {
|
||||
this.neurons[neuron.ID] = neuron || new Neuron();
|
||||
this.list.push(neuron);
|
||||
this.size++;
|
||||
},
|
||||
|
||||
set: function(options) {
|
||||
options = options || {};
|
||||
|
||||
for (var i in this.list) {
|
||||
var neuron = this.list[i];
|
||||
if (options.label)
|
||||
neuron.label = options.label + '_' + neuron.ID;
|
||||
if (options.squash)
|
||||
neuron.squash = options.squash;
|
||||
if (options.bias)
|
||||
neuron.bias = options.bias;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
// represents a connection from one layer to another, and keeps track of its weight and gain
|
||||
Layer.connection = function LayerConnection(fromLayer, toLayer, type, weights) {
|
||||
this.ID = Layer.connection.uid();
|
||||
this.from = fromLayer;
|
||||
this.to = toLayer;
|
||||
this.selfconnection = toLayer == fromLayer;
|
||||
this.type = type;
|
||||
this.connections = {};
|
||||
this.list = [];
|
||||
this.size = 0;
|
||||
this.gatedfrom = [];
|
||||
|
||||
if (typeof this.type == 'undefined')
|
||||
{
|
||||
if (fromLayer == toLayer)
|
||||
this.type = Layer.connectionType.ONE_TO_ONE;
|
||||
else
|
||||
this.type = Layer.connectionType.ALL_TO_ALL;
|
||||
}
|
||||
|
||||
if (this.type == Layer.connectionType.ALL_TO_ALL ||
|
||||
this.type == Layer.connectionType.ALL_TO_ELSE) {
|
||||
for (var here in this.from.list) {
|
||||
for (var there in this.to.list) {
|
||||
var from = this.from.list[here];
|
||||
var to = this.to.list[there];
|
||||
if(this.type == Layer.connectionType.ALL_TO_ELSE && from == to)
|
||||
continue;
|
||||
var connection = from.project(to, weights);
|
||||
|
||||
this.connections[connection.ID] = connection;
|
||||
this.size = this.list.push(connection);
|
||||
}
|
||||
}
|
||||
} else if (this.type == Layer.connectionType.ONE_TO_ONE) {
|
||||
|
||||
for (var neuron in this.from.list) {
|
||||
var from = this.from.list[neuron];
|
||||
var to = this.to.list[neuron];
|
||||
var connection = from.project(to, weights);
|
||||
|
||||
this.connections[connection.ID] = connection;
|
||||
this.size = this.list.push(connection);
|
||||
}
|
||||
}
|
||||
|
||||
fromLayer.connectedTo.push(this);
|
||||
}
|
||||
|
||||
// types of connections
|
||||
Layer.connectionType = {};
|
||||
Layer.connectionType.ALL_TO_ALL = "ALL TO ALL";
|
||||
Layer.connectionType.ONE_TO_ONE = "ONE TO ONE";
|
||||
Layer.connectionType.ALL_TO_ELSE = "ALL TO ELSE";
|
||||
|
||||
// types of gates
|
||||
Layer.gateType = {};
|
||||
Layer.gateType.INPUT = "INPUT";
|
||||
Layer.gateType.OUTPUT = "OUTPUT";
|
||||
Layer.gateType.ONE_TO_ONE = "ONE TO ONE";
|
||||
|
||||
(function() {
|
||||
var connections = 0;
|
||||
Layer.connection.uid = function() {
|
||||
return connections++;
|
||||
}
|
||||
})();
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
import neuron = require('./neuron');
|
||||
import network = require('./network');
|
||||
import Synaptic = require('./synaptic');
|
||||
|
||||
/*******************************************************************************************
|
||||
LAYER
|
||||
*******************************************************************************************/
|
||||
export class Layer {
|
||||
optimizable = true;
|
||||
list: neuron.Neuron[] = [];
|
||||
label: string = null;
|
||||
connectedto = [];
|
||||
size = 0;
|
||||
|
||||
currentActivation: Float64Array;
|
||||
|
||||
constructor(size: number, label?: string) {
|
||||
this.size = size | 0;
|
||||
this.list = [];
|
||||
this.label = label || null;
|
||||
this.connectedto = [];
|
||||
|
||||
this.currentActivation = new Float64Array(size);
|
||||
|
||||
while (size--) {
|
||||
var theNeuron = new neuron.Neuron();
|
||||
this.list.push(theNeuron);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// activates all the neurons in the layer
|
||||
activate(input?: Synaptic.INumericArray): Synaptic.INumericArray {
|
||||
|
||||
if (this.currentActivation.length != this.list.length)
|
||||
this.currentActivation = new Float64Array(this.list.length);
|
||||
|
||||
var activationIndex = 0;
|
||||
|
||||
if (typeof input != 'undefined') {
|
||||
if (input.length != this.size)
|
||||
throw "INPUT size and LAYER size must be the same to activate!";
|
||||
|
||||
for (var id in this.list) {
|
||||
this.currentActivation[activationIndex++] = this.list[id].activate(input[id]);
|
||||
}
|
||||
} else {
|
||||
for (var id in this.list) {
|
||||
this.currentActivation[activationIndex++] = this.list[id].activate();
|
||||
}
|
||||
}
|
||||
|
||||
return this.currentActivation;
|
||||
}
|
||||
|
||||
// propagates the error on all the neurons of the layer
|
||||
propagate(rate: number, target?: Synaptic.INumericArray) {
|
||||
if (typeof target != 'undefined') {
|
||||
if (target.length != this.size)
|
||||
throw "TARGET size and LAYER size must be the same to propagate!";
|
||||
|
||||
for (var id = this.list.length - 1; id >= 0; id--) {
|
||||
var neuron = this.list[id];
|
||||
neuron.propagate(rate, target[id]);
|
||||
}
|
||||
} else {
|
||||
for (var id = this.list.length - 1; id >= 0; id--) {
|
||||
var neuron = this.list[id];
|
||||
neuron.propagate(rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// projects a connection from this layer to another one
|
||||
project(layer: network.Network | Layer, type?: string, weights?: Synaptic.INumericArray): Layer.LayerConnection {
|
||||
|
||||
if (layer instanceof network.Network)
|
||||
layer = (<network.Network>layer).layers.input;
|
||||
|
||||
if (layer instanceof Layer) {
|
||||
if (!this.connected(layer))
|
||||
return new Layer.LayerConnection(this, layer, type, weights);
|
||||
} else
|
||||
throw "Invalid argument, you can only project connections to LAYERS and NETWORKS!";
|
||||
|
||||
|
||||
}
|
||||
|
||||
// gates a connection betwenn two layers
|
||||
gate(connection, type) {
|
||||
|
||||
if (type == Layer.gateType.INPUT) {
|
||||
if (connection.to.size != this.size)
|
||||
throw "GATER layer and CONNECTION.TO layer must be the same size in order to gate!";
|
||||
|
||||
for (var id in connection.to.list) {
|
||||
var neuron = connection.to.list[id];
|
||||
var gater = this.list[id];
|
||||
for (var input in neuron.connections.inputs) {
|
||||
var gated = neuron.connections.inputs[input];
|
||||
if (gated.ID in connection.connections)
|
||||
gater.gate(gated);
|
||||
}
|
||||
}
|
||||
} else if (type == Layer.gateType.OUTPUT) {
|
||||
if (connection.from.size != this.size)
|
||||
throw "GATER layer and CONNECTION.FROM layer must be the same size in order to gate!";
|
||||
|
||||
for (var id in connection.from.list) {
|
||||
var neuron = connection.from.list[id];
|
||||
var gater = this.list[id];
|
||||
for (var projected in neuron.connections.projected) {
|
||||
var gated = neuron.connections.projected[projected];
|
||||
if (gated.ID in connection.connections)
|
||||
gater.gate(gated);
|
||||
}
|
||||
}
|
||||
} else if (type == Layer.gateType.ONE_TO_ONE) {
|
||||
if (connection.size != this.size)
|
||||
throw "The number of GATER UNITS must be the same as the number of CONNECTIONS to gate!";
|
||||
|
||||
for (var id in connection.list) {
|
||||
var gater = this.list[id];
|
||||
var gated = connection.list[id];
|
||||
gater.gate(gated);
|
||||
}
|
||||
}
|
||||
connection.gatedfrom.push({ layer: this, type: type });
|
||||
}
|
||||
|
||||
// true or false whether the whole layer is self-connected or not
|
||||
selfconnected(): boolean {
|
||||
|
||||
for (var id in this.list) {
|
||||
var neuron = this.list[id];
|
||||
if (!neuron.selfconnected())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// true of false whether the layer is connected to another layer (parameter) or not
|
||||
connected(layer) {
|
||||
// Check if ALL to ALL connection
|
||||
var connections = 0;
|
||||
for (var here in this.list) {
|
||||
for (var there in layer.list) {
|
||||
var from = this.list[here];
|
||||
var to = layer.list[there];
|
||||
var connected = from.connected(to);
|
||||
if (connected && connected.type == 'projected')
|
||||
connections++;
|
||||
}
|
||||
}
|
||||
if (connections == this.size * layer.size)
|
||||
return Layer.connectionType.ALL_TO_ALL;
|
||||
|
||||
// Check if ONE to ONE connection
|
||||
connections = 0;
|
||||
for (var neuron in this.list) {
|
||||
var from = this.list[neuron];
|
||||
var to = layer.list[neuron];
|
||||
var connected = from.connected(to);
|
||||
if (connected && connected.type == 'projected')
|
||||
connections++;
|
||||
}
|
||||
if (connections == this.size)
|
||||
return Layer.connectionType.ONE_TO_ONE;
|
||||
}
|
||||
|
||||
// clears all the neuorns in the layer
|
||||
clear() {
|
||||
for (var id in this.list) {
|
||||
var neuron = this.list[id];
|
||||
neuron.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// resets all the neurons in the layer
|
||||
reset() {
|
||||
for (var id in this.list) {
|
||||
var neuron = this.list[id];
|
||||
neuron.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// returns all the neurons in the layer (array)
|
||||
neurons(): neuron.Neuron[] {
|
||||
return this.list;
|
||||
}
|
||||
|
||||
// adds a neuron to the layer
|
||||
add(neuron) {
|
||||
neuron = neuron || new neuron.Neuron();
|
||||
this.neurons[neuron.ID] = neuron;
|
||||
this.list.push(neuron);
|
||||
this.size++;
|
||||
}
|
||||
|
||||
set(options) {
|
||||
options = options || {};
|
||||
|
||||
for (var i in this.list) {
|
||||
var neuron = this.list[i];
|
||||
if (options.label)
|
||||
neuron.label = options.label + '_' + neuron.ID;
|
||||
if (options.squash)
|
||||
neuron.squash = options.squash;
|
||||
if (options.bias)
|
||||
neuron.bias = options.bias;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export module Layer {
|
||||
export var layerQty = 0;
|
||||
export function uid() {
|
||||
return layerQty++;
|
||||
}
|
||||
|
||||
// types of connections
|
||||
export var connectionType = {
|
||||
ALL_TO_ALL: "ALL TO ALL",
|
||||
ONE_TO_ONE: "ONE TO ONE",
|
||||
ALL_TO_ELSE: "ALL TO ELSE"
|
||||
};
|
||||
|
||||
// types of gates
|
||||
export var gateType = {
|
||||
INPUT: "INPUT",
|
||||
OUTPUT: "OUTPUT",
|
||||
ONE_TO_ONE: "ONE TO ONE"
|
||||
};
|
||||
|
||||
// represents a connection from one layer to another, and keeps track of its weight and gain
|
||||
export class LayerConnection {
|
||||
ID = uid();
|
||||
from: Layer;
|
||||
to: Layer;
|
||||
selfconnection: boolean = false;
|
||||
type: string;
|
||||
connections: Synaptic.Dictionary<neuron.Neuron.Connection>;
|
||||
list: neuron.Neuron.Connection[];
|
||||
size = 0;
|
||||
gatedfrom = [];
|
||||
|
||||
constructor(fromLayer, toLayer, type, weights) {
|
||||
this.from = fromLayer;
|
||||
this.to = toLayer;
|
||||
this.selfconnection = toLayer == fromLayer;
|
||||
this.type = type;
|
||||
this.connections = {};
|
||||
this.list = [];
|
||||
this.size = 0;
|
||||
this.gatedfrom = [];
|
||||
|
||||
|
||||
if (typeof this.type == 'undefined') {
|
||||
if (fromLayer == toLayer)
|
||||
this.type = Layer.connectionType.ONE_TO_ONE;
|
||||
else
|
||||
this.type = Layer.connectionType.ALL_TO_ALL;
|
||||
}
|
||||
|
||||
if (this.type == Layer.connectionType.ALL_TO_ALL ||
|
||||
this.type == Layer.connectionType.ALL_TO_ELSE) {
|
||||
for (var here in this.from.list) {
|
||||
for (var there in this.to.list) {
|
||||
var from = this.from.list[here];
|
||||
var to = this.to.list[there];
|
||||
if (this.type == Layer.connectionType.ALL_TO_ELSE && from == to)
|
||||
continue;
|
||||
var connection = from.project(to, weights);
|
||||
|
||||
this.connections[connection.ID] = connection;
|
||||
this.size = this.list.push(connection);
|
||||
}
|
||||
}
|
||||
} else if (this.type == Layer.connectionType.ONE_TO_ONE) {
|
||||
|
||||
for (var neuron in this.from.list) {
|
||||
var from = this.from.list[neuron];
|
||||
var to = this.to.list[neuron];
|
||||
var connection = from.project(to, weights);
|
||||
|
||||
this.connections[connection.ID] = connection;
|
||||
this.size = this.list.push(connection);
|
||||
}
|
||||
}
|
||||
|
||||
fromLayer.connectedto.push(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,645 +0,0 @@
|
||||
// export
|
||||
if (module) module.exports = Network;
|
||||
|
||||
// import
|
||||
var Neuron = require('./neuron')
|
||||
, Layer = require('./layer')
|
||||
, Trainer = require('./trainer')
|
||||
|
||||
/*******************************************************************************************
|
||||
NETWORK
|
||||
*******************************************************************************************/
|
||||
|
||||
function Network(layers) {
|
||||
if (typeof layers != 'undefined') {
|
||||
this.layers = layers || {
|
||||
input: null,
|
||||
hidden: {},
|
||||
output: null
|
||||
};
|
||||
this.optimized = null;
|
||||
}
|
||||
}
|
||||
Network.prototype = {
|
||||
|
||||
// feed-forward activation of all the layers to produce an ouput
|
||||
activate: function(input) {
|
||||
|
||||
if (this.optimized === false)
|
||||
{
|
||||
this.layers.input.activate(input);
|
||||
for (var layer in this.layers.hidden)
|
||||
this.layers.hidden[layer].activate();
|
||||
return this.layers.output.activate();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.optimized == null)
|
||||
this.optimize();
|
||||
return this.optimized.activate(input);
|
||||
}
|
||||
},
|
||||
|
||||
// back-propagate the error thru the network
|
||||
propagate: function(rate, target) {
|
||||
|
||||
if (this.optimized === false)
|
||||
{
|
||||
this.layers.output.propagate(rate, target);
|
||||
var reverse = [];
|
||||
for (var layer in this.layers.hidden)
|
||||
reverse.push(this.layers.hidden[layer]);
|
||||
reverse.reverse();
|
||||
for (var layer in reverse)
|
||||
reverse[layer].propagate(rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.optimized == null)
|
||||
this.optimize();
|
||||
this.optimized.propagate(rate, target);
|
||||
}
|
||||
},
|
||||
|
||||
// project a connection to another unit (either a network or a layer)
|
||||
project: function(unit, type, weights) {
|
||||
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
|
||||
if (unit instanceof Network)
|
||||
return this.layers.output.project(unit.layers.input, type, weights);
|
||||
|
||||
if (unit instanceof Layer)
|
||||
return this.layers.output.project(unit, type, weights);
|
||||
|
||||
throw new Error("Invalid argument, you can only project connections to LAYERS and NETWORKS!");
|
||||
},
|
||||
|
||||
// let this network gate a connection
|
||||
gate: function(connection, type) {
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
this.layers.output.gate(connection, type);
|
||||
},
|
||||
|
||||
// clear all elegibility traces and extended elegibility traces (the network forgets its context, but not what was trained)
|
||||
clear: function() {
|
||||
|
||||
this.restore();
|
||||
|
||||
var inputLayer = this.layers.input,
|
||||
outputLayer = this.layers.output;
|
||||
|
||||
inputLayer.clear();
|
||||
for (var layer in this.layers.hidden) {
|
||||
var hiddenLayer = this.layers.hidden[layer];
|
||||
hiddenLayer.clear();
|
||||
}
|
||||
outputLayer.clear();
|
||||
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
},
|
||||
|
||||
// reset all weights and clear all traces (ends up like a new network)
|
||||
reset: function() {
|
||||
|
||||
this.restore();
|
||||
|
||||
var inputLayer = this.layers.input,
|
||||
outputLayer = this.layers.output;
|
||||
|
||||
inputLayer.reset();
|
||||
for (var layer in this.layers.hidden) {
|
||||
var hiddenLayer = this.layers.hidden[layer];
|
||||
hiddenLayer.reset();
|
||||
}
|
||||
outputLayer.reset();
|
||||
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
},
|
||||
|
||||
// hardcodes the behaviour of the whole network into a single optimized function
|
||||
optimize: function() {
|
||||
|
||||
var that = this;
|
||||
var optimized = {};
|
||||
var neurons = this.neurons();
|
||||
|
||||
for (var i in neurons) {
|
||||
var neuron = neurons[i].neuron;
|
||||
var layer = neurons[i].layer;
|
||||
while (neuron.neuron)
|
||||
neuron = neuron.neuron;
|
||||
optimized = neuron.optimize(optimized, layer);
|
||||
}
|
||||
for (var i in optimized.propagation_sentences)
|
||||
optimized.propagation_sentences[i].reverse();
|
||||
optimized.propagation_sentences.reverse();
|
||||
|
||||
var hardcode = "";
|
||||
hardcode += "var F = Float64Array ? new Float64Array(" + optimized.memory +
|
||||
") : []; ";
|
||||
for (var i in optimized.variables)
|
||||
hardcode += "F[" + optimized.variables[i].id + "] = " + (optimized.variables[
|
||||
i].value || 0) + "; ";
|
||||
hardcode += "var activate = function(input){\n";
|
||||
for (var i in optimized.inputs)
|
||||
hardcode += "F[" + optimized.inputs[i] + "] = input[" + i + "]; ";
|
||||
for (var currentLayer in optimized.activation_sentences) {
|
||||
if (optimized.activation_sentences[currentLayer].length > 0) {
|
||||
for (var currentNeuron in optimized.activation_sentences[currentLayer]) {
|
||||
hardcode += optimized.activation_sentences[currentLayer][currentNeuron].join(" ");
|
||||
hardcode += optimized.trace_sentences[currentLayer][currentNeuron].join(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
hardcode += " var output = []; "
|
||||
for (var i in optimized.outputs)
|
||||
hardcode += "output[" + i + "] = F[" + optimized.outputs[i] + "]; ";
|
||||
hardcode += "return output; }; "
|
||||
hardcode += "var propagate = function(rate, target){\n";
|
||||
hardcode += "F[" + optimized.variables.rate.id + "] = rate; ";
|
||||
for (var i in optimized.targets)
|
||||
hardcode += "F[" + optimized.targets[i] + "] = target[" + i + "]; ";
|
||||
for (var currentLayer in optimized.propagation_sentences)
|
||||
for (var currentNeuron in optimized.propagation_sentences[currentLayer])
|
||||
hardcode += optimized.propagation_sentences[currentLayer][currentNeuron].join(" ") + " ";
|
||||
hardcode += " };\n";
|
||||
hardcode +=
|
||||
"var ownership = function(memoryBuffer){\nF = memoryBuffer;\nthis.memory = F;\n};\n";
|
||||
hardcode +=
|
||||
"return {\nmemory: F,\nactivate: activate,\npropagate: propagate,\nownership: ownership\n};";
|
||||
hardcode = hardcode.split(";").join(";\n");
|
||||
|
||||
var constructor = new Function(hardcode);
|
||||
|
||||
var network = constructor();
|
||||
network.data = {
|
||||
variables: optimized.variables,
|
||||
activate: optimized.activation_sentences,
|
||||
propagate: optimized.propagation_sentences,
|
||||
trace: optimized.trace_sentences,
|
||||
inputs: optimized.inputs,
|
||||
outputs: optimized.outputs,
|
||||
check_activation: this.activate,
|
||||
check_propagation: this.propagate
|
||||
}
|
||||
|
||||
network.reset = function() {
|
||||
if (that.optimized) {
|
||||
that.optimized = null;
|
||||
that.activate = network.data.check_activation;
|
||||
that.propagate = network.data.check_propagation;
|
||||
}
|
||||
}
|
||||
|
||||
this.optimized = network;
|
||||
this.activate = network.activate;
|
||||
this.propagate = network.propagate;
|
||||
},
|
||||
|
||||
// restores all the values from the optimized network the their respective objects in order to manipulate the network
|
||||
restore: function() {
|
||||
if (!this.optimized)
|
||||
return;
|
||||
|
||||
var optimized = this.optimized;
|
||||
|
||||
var getValue = function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
|
||||
var unit = args.shift();
|
||||
var prop = args.pop();
|
||||
|
||||
var id = prop + '_';
|
||||
for (var property in args)
|
||||
id += args[property] + '_';
|
||||
id += unit.ID;
|
||||
|
||||
var memory = optimized.memory;
|
||||
var variables = optimized.data.variables;
|
||||
|
||||
if (id in variables)
|
||||
return memory[variables[id].id];
|
||||
return 0;
|
||||
}
|
||||
|
||||
var list = this.neurons();
|
||||
|
||||
// link id's to positions in the array
|
||||
var ids = {};
|
||||
for (var i in list) {
|
||||
var neuron = list[i].neuron;
|
||||
while (neuron.neuron)
|
||||
neuron = neuron.neuron;
|
||||
|
||||
neuron.state = getValue(neuron, 'state');
|
||||
neuron.old = getValue(neuron, 'old');
|
||||
neuron.activation = getValue(neuron, 'activation');
|
||||
neuron.bias = getValue(neuron, 'bias');
|
||||
|
||||
for (var input in neuron.trace.elegibility)
|
||||
neuron.trace.elegibility[input] = getValue(neuron, 'trace',
|
||||
'elegibility', input);
|
||||
|
||||
for (var gated in neuron.trace.extended)
|
||||
for (var input in neuron.trace.extended[gated])
|
||||
neuron.trace.extended[gated][input] = getValue(neuron, 'trace',
|
||||
'extended', gated, input);
|
||||
}
|
||||
|
||||
// get connections
|
||||
for (var i in list) {
|
||||
var neuron = list[i].neuron;
|
||||
while (neuron.neuron)
|
||||
neuron = neuron.neuron;
|
||||
|
||||
for (var j in neuron.connections.projected) {
|
||||
var connection = neuron.connections.projected[j];
|
||||
connection.weight = getValue(connection, 'weight');
|
||||
connection.gain = getValue(connection, 'gain');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// returns all the neurons in the network
|
||||
neurons: function() {
|
||||
|
||||
var neurons = [];
|
||||
|
||||
var inputLayer = this.layers.input.neurons(),
|
||||
outputLayer = this.layers.output.neurons();
|
||||
|
||||
for (var neuron in inputLayer)
|
||||
neurons.push({
|
||||
neuron: inputLayer[neuron],
|
||||
layer: 'input'
|
||||
});
|
||||
|
||||
for (var layer in this.layers.hidden) {
|
||||
var hiddenLayer = this.layers.hidden[layer].neurons();
|
||||
for (var neuron in hiddenLayer)
|
||||
neurons.push({
|
||||
neuron: hiddenLayer[neuron],
|
||||
layer: layer
|
||||
});
|
||||
}
|
||||
for (var neuron in outputLayer)
|
||||
neurons.push({
|
||||
neuron: outputLayer[neuron],
|
||||
layer: 'output'
|
||||
});
|
||||
|
||||
return neurons;
|
||||
},
|
||||
|
||||
// returns number of inputs of the network
|
||||
inputs: function() {
|
||||
return this.layers.input.size;
|
||||
},
|
||||
|
||||
// returns number of outputs of hte network
|
||||
outputs: function() {
|
||||
return this.layers.output.size;
|
||||
},
|
||||
|
||||
// sets the layers of the network
|
||||
set: function(layers) {
|
||||
|
||||
this.layers = layers;
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
},
|
||||
|
||||
setOptimize: function(bool){
|
||||
this.restore();
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
this.optimized = bool? null : false;
|
||||
},
|
||||
|
||||
// returns a json that represents all the neurons and connections of the network
|
||||
toJSON: function(ignoreTraces) {
|
||||
|
||||
this.restore();
|
||||
|
||||
var list = this.neurons();
|
||||
var neurons = [];
|
||||
var connections = [];
|
||||
|
||||
// link id's to positions in the array
|
||||
var ids = {};
|
||||
for (var i in list) {
|
||||
var neuron = list[i].neuron;
|
||||
while (neuron.neuron)
|
||||
neuron = neuron.neuron;
|
||||
ids[neuron.ID] = i;
|
||||
|
||||
var copy = {
|
||||
trace: {
|
||||
elegibility: {},
|
||||
extended: {}
|
||||
},
|
||||
state: neuron.state,
|
||||
old: neuron.old,
|
||||
activation: neuron.activation,
|
||||
bias: neuron.bias,
|
||||
layer: list[i].layer
|
||||
};
|
||||
|
||||
copy.squash = neuron.squash == Neuron.squash.LOGISTIC ? "LOGISTIC" :
|
||||
neuron.squash == Neuron.squash.TANH ? "TANH" :
|
||||
neuron.squash == Neuron.squash.IDENTITY ? "IDENTITY" :
|
||||
neuron.squash == Neuron.squash.HLIM ? "HLIM" :
|
||||
null;
|
||||
|
||||
neurons.push(copy);
|
||||
}
|
||||
|
||||
// get connections
|
||||
for (var i in list) {
|
||||
var neuron = list[i].neuron;
|
||||
while (neuron.neuron)
|
||||
neuron = neuron.neuron;
|
||||
|
||||
for (var j in neuron.connections.projected) {
|
||||
var connection = neuron.connections.projected[j];
|
||||
connections.push({
|
||||
from: ids[connection.from.ID],
|
||||
to: ids[connection.to.ID],
|
||||
weight: connection.weight,
|
||||
gater: connection.gater ? ids[connection.gater.ID] : null,
|
||||
});
|
||||
}
|
||||
if (neuron.selfconnected())
|
||||
connections.push({
|
||||
from: ids[neuron.ID],
|
||||
to: ids[neuron.ID],
|
||||
weight: neuron.selfconnection.weight,
|
||||
gater: neuron.selfconnection.gater ? ids[neuron.selfconnection.gater.ID] : null,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
neurons: neurons,
|
||||
connections: connections
|
||||
}
|
||||
},
|
||||
|
||||
// export the topology into dot language which can be visualized as graphs using dot
|
||||
/* example: ... console.log(net.toDotLang());
|
||||
$ node example.js > example.dot
|
||||
$ dot example.dot -Tpng > out.png
|
||||
*/
|
||||
toDot: function(edgeConnection) {
|
||||
if (! typeof edgeConnection)
|
||||
edgeConnection = false;
|
||||
var code = "digraph nn {\n rankdir = BT\n";
|
||||
var layers = [this.layers.input].concat(this.layers.hidden, this.layers.output);
|
||||
for (var layer in layers) {
|
||||
for (var to in layers[layer].connectedTo) { // projections
|
||||
var connection = layers[layer].connectedTo[to];
|
||||
var layerTo = connection.to;
|
||||
var size = connection.size;
|
||||
var layerID = layers.indexOf(layers[layer]);
|
||||
var layerToID = layers.indexOf(layerTo);
|
||||
/* http://stackoverflow.com/questions/26845540/connect-edges-with-graph-dot
|
||||
* DOT does not support edge-to-edge connections
|
||||
* This workaround produces somewhat weird graphs ...
|
||||
*/
|
||||
if ( edgeConnection) {
|
||||
if (connection.gatedfrom.length) {
|
||||
var fakeNode = "fake" + layerID + "_" + layerToID;
|
||||
code += " " + fakeNode +
|
||||
" [label = \"\", shape = point, width = 0.01, height = 0.01]\n";
|
||||
code += " " + layerID + " -> " + fakeNode + " [label = " + size + ", arrowhead = none]\n";
|
||||
code += " " + fakeNode + " -> " + layerToID + "\n";
|
||||
} else
|
||||
code += " " + layerID + " -> " + layerToID + " [label = " + size + "]\n";
|
||||
for (var from in connection.gatedfrom) { // gatings
|
||||
var layerfrom = connection.gatedfrom[from].layer;
|
||||
var layerfromID = layers.indexOf(layerfrom);
|
||||
code += " " + layerfromID + " -> " + fakeNode + " [color = blue]\n";
|
||||
}
|
||||
} else {
|
||||
code += " " + layerID + " -> " + layerToID + " [label = " + size + "]\n";
|
||||
for (var from in connection.gatedfrom) { // gatings
|
||||
var layerfrom = connection.gatedfrom[from].layer;
|
||||
var layerfromID = layers.indexOf(layerfrom);
|
||||
code += " " + layerfromID + " -> " + layerToID + " [color = blue]\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
code += "}\n";
|
||||
return {
|
||||
code: code,
|
||||
link: "https://chart.googleapis.com/chart?chl=" + escape(code.replace("/ /g", "+")) + "&cht=gv"
|
||||
}
|
||||
},
|
||||
|
||||
// returns a function that works as the activation of the network and can be used without depending on the library
|
||||
standalone: function() {
|
||||
if (!this.optimized)
|
||||
this.optimize();
|
||||
|
||||
var data = this.optimized.data;
|
||||
|
||||
// build activation function
|
||||
var activation = "function (input) {\n";
|
||||
|
||||
// build inputs
|
||||
for (var i in data.inputs)
|
||||
activation += "F[" + data.inputs[i] + "] = input[" + i + "];\n";
|
||||
|
||||
// build network activation
|
||||
for (var neuron in data.activate) { // shouldn't this be layer?
|
||||
for (var sentence in data.activate[neuron])
|
||||
activation += data.activate[neuron][sentence].join('') + "\n";
|
||||
}
|
||||
|
||||
// build outputs
|
||||
activation += "var output = [];\n";
|
||||
for (var i in data.outputs)
|
||||
activation += "output[" + i + "] = F[" + data.outputs[i] + "];\n";
|
||||
activation += "return output;\n}";
|
||||
|
||||
// reference all the positions in memory
|
||||
var memory = activation.match(/F\[(\d+)\]/g);
|
||||
var dimension = 0;
|
||||
var ids = {};
|
||||
for (var address in memory) {
|
||||
var tmp = memory[address].match(/\d+/)[0];
|
||||
if (!(tmp in ids)) {
|
||||
ids[tmp] = dimension++;
|
||||
}
|
||||
}
|
||||
var hardcode = "F = {\n";
|
||||
for (var i in ids)
|
||||
hardcode += ids[i] + ": " + this.optimized.memory[i] + ",\n";
|
||||
hardcode = hardcode.substring(0, hardcode.length - 2) + "\n};\n";
|
||||
hardcode = "var run = " + activation.replace(/F\[(\d+)]/g, function(
|
||||
index) {
|
||||
return 'F[' + ids[index.match(/\d+/)[0]] + ']'
|
||||
}).replace("{\n", "{\n" + hardcode + "") + ";\n";
|
||||
hardcode += "return run";
|
||||
|
||||
// return standalone function
|
||||
return new Function(hardcode)();
|
||||
},
|
||||
|
||||
|
||||
// Return a HTML5 WebWorker specialized on training the network stored in `memory`.
|
||||
// Train based on the given dataSet and options.
|
||||
// The worker returns the updated `memory` when done.
|
||||
worker: function(memory, set, options) {
|
||||
|
||||
// Copy the options and set defaults (options might be different for each worker)
|
||||
var workerOptions = {};
|
||||
if(options) workerOptions = options;
|
||||
workerOptions.rate = options.rate || .2;
|
||||
workerOptions.iterations = options.iterations || 100000;
|
||||
workerOptions.error = options.error || .005;
|
||||
workerOptions.cost = options.cost || null;
|
||||
workerOptions.crossValidate = options.crossValidate || null;
|
||||
|
||||
// Cost function might be different for each worker
|
||||
costFunction = "var cost = " + (options && options.cost || this.cost || Trainer.cost.MSE) + ";\n";
|
||||
var workerFunction = Network.getWorkerSharedFunctions();
|
||||
workerFunction = workerFunction.replace(/var cost = options && options\.cost \|\| this\.cost \|\| Trainer\.cost\.MSE;/g, costFunction);
|
||||
|
||||
// Set what we do when training is finished
|
||||
workerFunction = workerFunction.replace('return results;',
|
||||
'postMessage({action: "done", message: results, memoryBuffer: F}, [F.buffer]);');
|
||||
|
||||
// Replace log with postmessage
|
||||
workerFunction = workerFunction.replace("console.log('iterations', iterations, 'error', error, 'rate', currentRate)",
|
||||
"postMessage({action: 'log', message: {\n" +
|
||||
"iterations: iterations,\n" +
|
||||
"error: error,\n" +
|
||||
"rate: currentRate\n" +
|
||||
"}\n" +
|
||||
"})");
|
||||
|
||||
// Replace schedule with postmessage
|
||||
workerFunction = workerFunction.replace("abort = this.schedule.do({ error: error, iterations: iterations, rate: currentRate })",
|
||||
"postMessage({action: 'schedule', message: {\n" +
|
||||
"iterations: iterations,\n" +
|
||||
"error: error,\n" +
|
||||
"rate: currentRate\n" +
|
||||
"}\n" +
|
||||
"})");
|
||||
|
||||
if (!this.optimized)
|
||||
this.optimize();
|
||||
|
||||
var hardcode = "var inputs = " + this.optimized.data.inputs.length + ";\n";
|
||||
hardcode += "var outputs = " + this.optimized.data.outputs.length + ";\n";
|
||||
hardcode += "var F = new Float64Array([" + this.optimized.memory.toString() + "]);\n";
|
||||
hardcode += "var activate = " + this.optimized.activate.toString() + ";\n";
|
||||
hardcode += "var propagate = " + this.optimized.propagate.toString() + ";\n";
|
||||
hardcode +=
|
||||
"onmessage = function(e) {\n" +
|
||||
"if (e.data.action == 'startTraining') {\n" +
|
||||
"train(" + JSON.stringify(set) + "," + JSON.stringify(workerOptions) + ");\n" +
|
||||
"}\n" +
|
||||
"}";
|
||||
|
||||
var workerSourceCode = workerFunction + '\n' + hardcode;
|
||||
var blob = new Blob([workerSourceCode]);
|
||||
var blobURL = window.URL.createObjectURL(blob);
|
||||
|
||||
return new Worker(blobURL);
|
||||
},
|
||||
|
||||
// returns a copy of the network
|
||||
clone: function() {
|
||||
return Network.fromJSON(this.toJSON());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a static String to store the source code of the functions
|
||||
* that are identical for all the workers (train, _trainSet, test)
|
||||
*
|
||||
* @return {String} Source code that can train a network inside a worker.
|
||||
* @static
|
||||
*/
|
||||
Network.getWorkerSharedFunctions = function() {
|
||||
// If we already computed the source code for the shared functions
|
||||
if(typeof Network._SHARED_WORKER_FUNCTIONS !== 'undefined')
|
||||
return Network._SHARED_WORKER_FUNCTIONS;
|
||||
|
||||
// Otherwise compute and return the source code
|
||||
// We compute them by simply copying the source code of the train, _trainSet and test functions
|
||||
// using the .toString() method
|
||||
|
||||
// Load and name the train function
|
||||
var train_f = Trainer.prototype.train.toString();
|
||||
train_f = train_f.replace('function (set', 'function train(set') + '\n';
|
||||
|
||||
// Load and name the _trainSet function
|
||||
var _trainSet_f = Trainer.prototype._trainSet.toString().replace(/this.network./g, '');
|
||||
_trainSet_f = _trainSet_f.replace('function (set', 'function _trainSet(set') + '\n';
|
||||
_trainSet_f = _trainSet_f.replace('this.crossValidate', 'crossValidate');
|
||||
_trainSet_f = _trainSet_f.replace('crossValidate = true', 'crossValidate = { }');
|
||||
|
||||
// Load and name the test function
|
||||
var test_f = Trainer.prototype.test.toString().replace(/this.network./g, '');
|
||||
test_f = test_f.replace('function (set', 'function test(set') + '\n';
|
||||
|
||||
return Network._SHARED_WORKER_FUNCTIONS = train_f + _trainSet_f + test_f;
|
||||
};
|
||||
|
||||
// rebuild a network that has been stored in a json using the method toJSON()
|
||||
Network.fromJSON = function(json) {
|
||||
|
||||
var neurons = [];
|
||||
|
||||
var layers = {
|
||||
input: new Layer(),
|
||||
hidden: [],
|
||||
output: new Layer()
|
||||
};
|
||||
|
||||
for (var i in json.neurons) {
|
||||
var config = json.neurons[i];
|
||||
|
||||
var neuron = new Neuron();
|
||||
neuron.trace.elegibility = {};
|
||||
neuron.trace.extended = {};
|
||||
neuron.state = config.state;
|
||||
neuron.old = config.old;
|
||||
neuron.activation = config.activation;
|
||||
neuron.bias = config.bias;
|
||||
neuron.squash = config.squash in Neuron.squash ? Neuron.squash[config.squash] : Neuron.squash.LOGISTIC;
|
||||
neurons.push(neuron);
|
||||
|
||||
if (config.layer == 'input')
|
||||
layers.input.add(neuron);
|
||||
else if (config.layer == 'output')
|
||||
layers.output.add(neuron);
|
||||
else {
|
||||
if (typeof layers.hidden[config.layer] == 'undefined')
|
||||
layers.hidden[config.layer] = new Layer();
|
||||
layers.hidden[config.layer].add(neuron);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i in json.connections) {
|
||||
var config = json.connections[i];
|
||||
var from = neurons[config.from];
|
||||
var to = neurons[config.to];
|
||||
var weight = config.weight;
|
||||
var gater = neurons[config.gater];
|
||||
|
||||
var connection = from.project(to, weight);
|
||||
if (gater)
|
||||
gater.gate(connection);
|
||||
}
|
||||
|
||||
return new Network(layers);
|
||||
};
|
||||
@@ -0,0 +1,630 @@
|
||||
import layer = require('./layer');
|
||||
import Squash = require('./squash');
|
||||
import Synaptic = require('./synaptic');
|
||||
import _neuron = require('./neuron');
|
||||
|
||||
/*******************************************************************************************
|
||||
NETWORK
|
||||
*******************************************************************************************/
|
||||
|
||||
declare function escape(a: string): string;
|
||||
|
||||
|
||||
export class Network {
|
||||
optimized = null;
|
||||
layers = {
|
||||
input: null,
|
||||
hidden: {},
|
||||
output: null
|
||||
};
|
||||
constructor(layers?) {
|
||||
if (typeof layers != 'undefined') {
|
||||
this.layers = layers || {
|
||||
input: null,
|
||||
hidden: [],
|
||||
output: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// feed-forward activation of all the layers to produce an ouput
|
||||
activate(input : Synaptic.INumericArray) {
|
||||
|
||||
if (this.optimized === false) {
|
||||
this.layers.input.activate(input);
|
||||
for (var layer in this.layers.hidden)
|
||||
this.layers.hidden[layer].activate();
|
||||
return this.layers.output.activate();
|
||||
}
|
||||
else {
|
||||
if (this.optimized == null)
|
||||
this.optimize();
|
||||
return this.optimized.activate(input);
|
||||
}
|
||||
}
|
||||
|
||||
// back-propagate the error thru the network
|
||||
propagate(rate: number, target?: Synaptic.INumericArray) {
|
||||
|
||||
if (this.optimized === false) {
|
||||
this.layers.output.propagate(rate, target);
|
||||
var reverse = [];
|
||||
for (var layer in this.layers.hidden)
|
||||
reverse.push(this.layers.hidden[layer]);
|
||||
reverse.reverse();
|
||||
for (var layer in reverse)
|
||||
reverse[layer].propagate(rate);
|
||||
}
|
||||
else {
|
||||
if (this.optimized == null)
|
||||
this.optimize();
|
||||
this.optimized.propagate(rate, target);
|
||||
}
|
||||
}
|
||||
|
||||
// project a connection to another unit (either a network or a layer)
|
||||
project(unit, type, weights) {
|
||||
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
|
||||
if (unit instanceof Network)
|
||||
return this.layers.output.project(unit.layers.input, type, weights);
|
||||
|
||||
if (unit instanceof layer.Layer)
|
||||
return this.layers.output.project(unit, type, weights);
|
||||
|
||||
throw "Invalid argument, you can only project connections to LAYERS and NETWORKS!";
|
||||
}
|
||||
|
||||
// let this network gate a connection
|
||||
gate(connection, type) {
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
this.layers.output.gate(connection, type);
|
||||
}
|
||||
|
||||
// clear all elegibility traces and extended elegibility traces (the network forgets its context, but not what was trained)
|
||||
clear() {
|
||||
|
||||
this.restore();
|
||||
|
||||
var inputLayer = this.layers.input,
|
||||
outputLayer = this.layers.output;
|
||||
|
||||
inputLayer.clear();
|
||||
for (var layer in this.layers.hidden) {
|
||||
var hiddenLayer = this.layers.hidden[layer];
|
||||
hiddenLayer.clear();
|
||||
}
|
||||
outputLayer.clear();
|
||||
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
}
|
||||
|
||||
// reset all weights and clear all traces (ends up like a new network)
|
||||
reset() {
|
||||
|
||||
this.restore();
|
||||
|
||||
var inputLayer = this.layers.input,
|
||||
outputLayer = this.layers.output;
|
||||
|
||||
inputLayer.reset();
|
||||
for (var layer in this.layers.hidden) {
|
||||
var hiddenLayer = this.layers.hidden[layer];
|
||||
hiddenLayer.reset();
|
||||
}
|
||||
outputLayer.reset();
|
||||
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
}
|
||||
|
||||
// hardcodes the behaviour of the whole network into a single optimized function
|
||||
optimize() {
|
||||
|
||||
var that = this;
|
||||
var optimized: Synaptic.ICompiledParameters = {};
|
||||
var neurons = this.neurons();
|
||||
|
||||
for (var i in neurons) {
|
||||
var neuron = neurons[i].neuron;
|
||||
var layer = neurons[i].layer;
|
||||
/*
|
||||
FIXME: does this worked once?
|
||||
|
||||
while (neuron.neuron)
|
||||
neuron = neuron.neuron;
|
||||
*/
|
||||
|
||||
optimized = neuron.optimize(optimized, layer);
|
||||
}
|
||||
|
||||
for (var i in optimized.propagation_sentences)
|
||||
optimized.propagation_sentences[i].reverse();
|
||||
optimized.propagation_sentences.reverse();
|
||||
|
||||
var hardcode = "";
|
||||
hardcode += "var F = Float64Array ? new Float64Array(" + optimized.memory +
|
||||
") : []; ";
|
||||
for (var i in optimized.variables)
|
||||
hardcode += "F[" + optimized.variables[i].id + "] = " + (optimized.variables[
|
||||
i].value || 0) + "; ";
|
||||
hardcode += "var activate = function(input){\n";
|
||||
hardcode += "influences = [];";
|
||||
for (var i in optimized.inputs)
|
||||
hardcode += "F[" + optimized.inputs[i] + "] = input[" + i + "]; ";
|
||||
for (var currentLayer in optimized.activation_sentences) {
|
||||
if (optimized.activation_sentences[currentLayer].length > 0) {
|
||||
for (var currentNeuron in optimized.activation_sentences[currentLayer]) {
|
||||
hardcode += optimized.activation_sentences[currentLayer][currentNeuron].join(" ");
|
||||
hardcode += optimized.trace_sentences[currentLayer][currentNeuron].join(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
hardcode += " var output = []; "
|
||||
for (var i in optimized.outputs)
|
||||
hardcode += "output[" + i + "] = F[" + optimized.outputs[i] + "]; ";
|
||||
hardcode += "return output; }; "
|
||||
hardcode += "var propagate = function(rate, target){\n";
|
||||
hardcode += "F[" + optimized.variables.rate.id + "] = rate; ";
|
||||
for (var i in optimized.targets)
|
||||
hardcode += "F[" + optimized.targets[i] + "] = target[" + i + "]; ";
|
||||
for (var currentLayer in optimized.propagation_sentences)
|
||||
for (var currentNeuron in optimized.propagation_sentences[currentLayer])
|
||||
hardcode += optimized.propagation_sentences[currentLayer][currentNeuron].join(" ") + " ";
|
||||
hardcode += " };\n";
|
||||
hardcode +=
|
||||
"var ownership = function(memoryBuffer){\nF = memoryBuffer;\nthis.memory = F;\n};\n";
|
||||
hardcode +=
|
||||
"return {\nmemory: F,\nactivate: activate,\npropagate: propagate,\nownership: ownership\n};";
|
||||
hardcode = hardcode.split(";").join(";\n");
|
||||
|
||||
var constructor = new Function(hardcode);
|
||||
|
||||
var network = constructor();
|
||||
|
||||
network.data = {
|
||||
variables: optimized.variables,
|
||||
activate: optimized.activation_sentences,
|
||||
propagate: optimized.propagation_sentences,
|
||||
trace: optimized.trace_sentences,
|
||||
inputs: optimized.inputs,
|
||||
outputs: optimized.outputs,
|
||||
check_activation: this.activate,
|
||||
check_propagation: this.propagate
|
||||
}
|
||||
|
||||
network.reset = function() {
|
||||
if (that.optimized) {
|
||||
that.optimized = null;
|
||||
that.activate = network.data.check_activation;
|
||||
that.propagate = network.data.check_propagation;
|
||||
}
|
||||
}
|
||||
|
||||
this.optimized = network;
|
||||
this.activate = network.activate;
|
||||
this.propagate = network.propagate;
|
||||
}
|
||||
|
||||
// restores all the values from the optimized network the their respective objects in order to manipulate the network
|
||||
restore() {
|
||||
if (!this.optimized)
|
||||
return;
|
||||
|
||||
var optimized = this.optimized;
|
||||
|
||||
var getValue = function(...args: any[]) {
|
||||
var unit = args.shift();
|
||||
var prop = args.pop();
|
||||
|
||||
var id = prop + '_';
|
||||
for (var property in args)
|
||||
id += args[property] + '_';
|
||||
id += unit.ID;
|
||||
|
||||
var memory = optimized.memory;
|
||||
var variables = optimized.data.variables;
|
||||
|
||||
if (id in variables)
|
||||
return memory[variables[id].id];
|
||||
return 0;
|
||||
}
|
||||
|
||||
var list = this.neurons();
|
||||
|
||||
// link id's to positions in the array
|
||||
var ids = {};
|
||||
for (var i in list) {
|
||||
var neuron = list[i].neuron;
|
||||
/*
|
||||
FIXME: does this worked once?
|
||||
|
||||
while (neuron.neuron)
|
||||
neuron = neuron.neuron;
|
||||
*/
|
||||
|
||||
neuron.state = getValue(neuron, 'state');
|
||||
neuron.old = getValue(neuron, 'old');
|
||||
neuron.activation = getValue(neuron, 'activation');
|
||||
neuron.bias = getValue(neuron, 'bias');
|
||||
|
||||
for (var input in neuron.trace.elegibility)
|
||||
neuron.trace.elegibility[input] = getValue(neuron, 'trace',
|
||||
'elegibility', input);
|
||||
|
||||
for (var gated in neuron.trace.extended)
|
||||
for (var input in neuron.trace.extended[gated])
|
||||
neuron.trace.extended[gated][input] = getValue(neuron, 'trace',
|
||||
'extended', gated, input);
|
||||
}
|
||||
|
||||
// get connections
|
||||
for (var i in list) {
|
||||
var neuron = list[i].neuron;
|
||||
/*
|
||||
FIXME: does this worked once?
|
||||
|
||||
while (neuron.neuron)
|
||||
neuron = neuron.neuron;
|
||||
*/
|
||||
|
||||
for (var j in neuron.connections.projected) {
|
||||
var connection = neuron.connections.projected[j];
|
||||
connection.weight = getValue(connection, 'weight');
|
||||
connection.gain = getValue(connection, 'gain');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns all the neurons in the network
|
||||
neurons(): Network.INetworkNeuron[] {
|
||||
var neurons: Network.INetworkNeuron[] = [];
|
||||
|
||||
var inputLayer = this.layers.input.neurons(),
|
||||
outputLayer = this.layers.output.neurons();
|
||||
|
||||
for (var neuron in inputLayer)
|
||||
neurons.push({
|
||||
neuron: inputLayer[neuron],
|
||||
layer: 'input'
|
||||
});
|
||||
|
||||
for (var layer in this.layers.hidden) {
|
||||
var hiddenLayer = this.layers.hidden[layer].neurons();
|
||||
for (var neuron in hiddenLayer)
|
||||
neurons.push({
|
||||
neuron: hiddenLayer[neuron],
|
||||
layer: layer
|
||||
});
|
||||
}
|
||||
for (var neuron in outputLayer)
|
||||
neurons.push({
|
||||
neuron: outputLayer[neuron],
|
||||
layer: 'output'
|
||||
});
|
||||
|
||||
return neurons;
|
||||
}
|
||||
|
||||
// returns number of inputs of the network
|
||||
inputs(): number {
|
||||
return this.layers.input.size;
|
||||
}
|
||||
|
||||
// returns number of outputs of hte network
|
||||
outputs(): number {
|
||||
return this.layers.output.size;
|
||||
}
|
||||
|
||||
// sets the layers of the network
|
||||
set(layers) {
|
||||
|
||||
this.layers = layers;
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
}
|
||||
|
||||
setOptimize(bool) {
|
||||
this.restore();
|
||||
if (this.optimized)
|
||||
this.optimized.reset();
|
||||
this.optimized = bool ? null : false;
|
||||
}
|
||||
|
||||
// returns a json that represents all the neurons and connections of the network
|
||||
toJSON(ignoreTraces) {
|
||||
|
||||
this.restore();
|
||||
|
||||
var list = this.neurons();
|
||||
var neurons = [];
|
||||
var connections = [];
|
||||
|
||||
// link id's to positions in the array
|
||||
var ids = {};
|
||||
for (var i in list) {
|
||||
var neuron = list[i].neuron;
|
||||
/*
|
||||
FIXME: does this worked once?
|
||||
|
||||
while (neuron.neuron)
|
||||
neuron = neuron.neuron;
|
||||
*/
|
||||
|
||||
ids[neuron.ID] = i;
|
||||
|
||||
var copy = {
|
||||
trace: {
|
||||
elegibility: {},
|
||||
extended: {}
|
||||
},
|
||||
state: neuron.state,
|
||||
old: neuron.old,
|
||||
activation: neuron.activation,
|
||||
bias: neuron.bias,
|
||||
layer: list[i].layer,
|
||||
squash: null
|
||||
};
|
||||
|
||||
copy.squash = neuron.squash == Squash.LOGISTIC ? "LOGISTIC" :
|
||||
neuron.squash == Squash.TANH ? "TANH" :
|
||||
neuron.squash == Squash.IDENTITY ? "IDENTITY" :
|
||||
neuron.squash == Squash.HLIM ? "HLIM" :
|
||||
null;
|
||||
|
||||
neurons.push(copy);
|
||||
}
|
||||
|
||||
if (!ignoreTraces)
|
||||
for (var i in neurons) {
|
||||
var copiedNeuron = neurons[i];
|
||||
|
||||
for (var input in neuron.trace.elegibility)
|
||||
copiedNeuron.trace.elegibility[input] = neuron.trace.elegibility[input];
|
||||
|
||||
for (var gated in neuron.trace.extended) {
|
||||
copiedNeuron.trace.extended[gated] = {};
|
||||
for (var input in neuron.trace.extended[gated])
|
||||
copiedNeuron.trace.extended[ids[gated]][input] = neuron.trace.extended[
|
||||
gated][input];
|
||||
}
|
||||
}
|
||||
|
||||
// get connections
|
||||
for (var i in list) {
|
||||
var neuron = list[i].neuron;
|
||||
|
||||
/*
|
||||
FIXME: does this worked once?
|
||||
|
||||
while (neuron.neuron)
|
||||
neuron = neuron.neuron;
|
||||
*/
|
||||
|
||||
for (var j in neuron.connections.projected) {
|
||||
var connection = neuron.connections.projected[j];
|
||||
connections.push({
|
||||
from: ids[connection.from.ID],
|
||||
to: ids[connection.to.ID],
|
||||
weight: connection.weight,
|
||||
gater: connection.gater ? ids[connection.gater.ID] : null,
|
||||
});
|
||||
}
|
||||
if (neuron.selfconnected())
|
||||
connections.push({
|
||||
from: ids[neuron.ID],
|
||||
to: ids[neuron.ID],
|
||||
weight: neuron.selfconnection.weight,
|
||||
gater: neuron.selfconnection.gater ? ids[neuron.selfconnection.gater
|
||||
.ID] : null,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
neurons: neurons,
|
||||
connections: connections
|
||||
}
|
||||
}
|
||||
|
||||
// export the topology into dot language which can be visualized as graphs using dot
|
||||
/* example: ... console.log(net.toDotLang());
|
||||
$ node example.js > example.dot
|
||||
$ dot example.dot -Tpng > out.png
|
||||
*/
|
||||
toDot(edgeconnection) {
|
||||
if (! typeof edgeconnection)
|
||||
edgeconnection = false;
|
||||
var code = "digraph nn {\n rankdir = BT\n";
|
||||
var layers = [this.layers.input].concat(this.layers.hidden, this.layers.output);
|
||||
for (var layer in layers) {
|
||||
for (var to in layers[layer].connectedto) { // projections
|
||||
var connection = layers[layer].connectedto[to];
|
||||
var layerto = connection.to;
|
||||
var size = connection.size;
|
||||
var layerID = layers.indexOf(layers[layer]);
|
||||
var layertoID = layers.indexOf(layerto);
|
||||
/* http://stackoverflow.com/questions/26845540/connect-edges-with-graph-dot
|
||||
* DOT does not support edge-to-edge connections
|
||||
* This workaround produces somewhat weird graphs ...
|
||||
*/
|
||||
if (edgeconnection) {
|
||||
if (connection.gatedfrom.length) {
|
||||
var fakeNode = "fake" + layerID + "_" + layertoID;
|
||||
code += " " + fakeNode +
|
||||
" [label = \"\", shape = point, width = 0.01, height = 0.01]\n";
|
||||
code += " " + layerID + " -> " + fakeNode + " [label = " + size + ", arrowhead = none]\n";
|
||||
code += " " + fakeNode + " -> " + layertoID + "\n";
|
||||
} else
|
||||
code += " " + layerID + " -> " + layertoID + " [label = " + size + "]\n";
|
||||
for (var from in connection.gatedfrom) { // gatings
|
||||
var layerfrom = connection.gatedfrom[from].layer;
|
||||
var type = connection.gatedfrom[from].type;
|
||||
var layerfromID = layers.indexOf(layerfrom);
|
||||
code += " " + layerfromID + " -> " + fakeNode + " [color = blue]\n";
|
||||
}
|
||||
} else {
|
||||
code += " " + layerID + " -> " + layertoID + " [label = " + size + "]\n";
|
||||
for (var from in connection.gatedfrom) { // gatings
|
||||
var layerfrom = connection.gatedfrom[from].layer;
|
||||
var type = connection.gatedfrom[from].type;
|
||||
var layerfromID = layers.indexOf(layerfrom);
|
||||
code += " " + layerfromID + " -> " + layertoID + " [color = blue]\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
code += "}\n";
|
||||
return {
|
||||
code: code,
|
||||
link: "https://chart.googleapis.com/chart?chl=" + escape(code.replace("/ /g", "+")) + "&cht=gv"
|
||||
}
|
||||
}
|
||||
|
||||
// returns a function that works as the activation of the network and can be used without depending on the library
|
||||
standalone() {
|
||||
if (!this.optimized)
|
||||
this.optimize();
|
||||
|
||||
var data = this.optimized.data;
|
||||
|
||||
// build activation function
|
||||
var activation = "function (input) {\n";
|
||||
|
||||
// build inputs
|
||||
for (var i in data.inputs)
|
||||
activation += "F[" + data.inputs[i] + "] = input[" + i + "];\n";
|
||||
|
||||
// build network activation
|
||||
for (var neuron in data.activate) { // shouldn't this be layer?
|
||||
for (var sentence in data.activate[neuron])
|
||||
activation += data.activate[neuron][sentence] + "\n";
|
||||
}
|
||||
|
||||
// build outputs
|
||||
activation += "var output = [];\n";
|
||||
for (var i in data.outputs)
|
||||
activation += "output[" + i + "] = F[" + data.outputs[i] + "];\n";
|
||||
activation += "return output;\n}";
|
||||
|
||||
// reference all the positions in memory
|
||||
var memory = activation.match(/F\[(\d+)\]/g);
|
||||
var dimension = 0;
|
||||
var ids = {};
|
||||
for (var address in memory) {
|
||||
var tmp = memory[address].match(/\d+/)[0];
|
||||
if (!(tmp in ids)) {
|
||||
ids[tmp] = dimension++;
|
||||
}
|
||||
}
|
||||
var hardcode = "F = {\n";
|
||||
for (var i in ids)
|
||||
hardcode += ids[i] + ": " + this.optimized.memory[i] + ",\n";
|
||||
hardcode = hardcode.substring(0, hardcode.length - 2) + "\n};\n";
|
||||
hardcode = "var run = " + activation.replace(/F\[(\d+)]/g, function(
|
||||
index) {
|
||||
return 'F[' + ids[index.match(/\d+/)[0]] + ']'
|
||||
}).replace("{\n", "{\n" + hardcode + "") + ";\n";
|
||||
hardcode += "return run";
|
||||
|
||||
// return standalone function
|
||||
return new Function(hardcode)();
|
||||
}
|
||||
|
||||
worker() {
|
||||
if (!this.optimized)
|
||||
this.optimize();
|
||||
|
||||
var hardcode = "var inputs = " + this.optimized.data.inputs.length +
|
||||
";\n";
|
||||
hardcode += "var outputs = " + this.optimized.data.outputs.length +
|
||||
";\n";
|
||||
hardcode += "var F = null;\n";
|
||||
hardcode += "var activate = " + this.optimized.activate.toString() +
|
||||
";\n";
|
||||
hardcode += "var propagate = " + this.optimized.propagate.toString() +
|
||||
";\n";
|
||||
hardcode += "onmessage = function(e){\n";
|
||||
hardcode += "F = e.data.memoryBuffer;\n";
|
||||
hardcode += "if (e.data.action == 'activate'){\n";
|
||||
hardcode += "if (e.data.input.length == inputs){\n";
|
||||
hardcode +=
|
||||
"postMessage( { action: 'activate', output: activate(e.data.input), memoryBuffer: F }, [F.buffer]);\n";
|
||||
hardcode += "}\n}\nelse if (e.data.action == 'propagate'){\n";
|
||||
hardcode += "propagate(e.data.rate, e.data.target);\n";
|
||||
hardcode +=
|
||||
"postMessage({ action: 'propagate', memoryBuffer: F }, [F.buffer]);\n";
|
||||
hardcode += "}\n}\n";
|
||||
|
||||
var blob = new Blob([hardcode]);
|
||||
var blobURL = (<any>window).URL.createObjectURL(blob);
|
||||
|
||||
return new Worker(blobURL);
|
||||
}
|
||||
|
||||
// returns a copy of the network
|
||||
clone(ignoreTraces) {
|
||||
return Network.fromJSON(this.toJSON(ignoreTraces));
|
||||
}
|
||||
|
||||
static fromJSON(json) {
|
||||
|
||||
var neurons = [];
|
||||
|
||||
var layers = {
|
||||
input: new layer.Layer(0),
|
||||
hidden: [],
|
||||
output: new layer.Layer(0)
|
||||
}
|
||||
|
||||
|
||||
for (var i in json.neurons) {
|
||||
var config = json.neurons[i];
|
||||
|
||||
var neuron = new _neuron.Neuron();
|
||||
neuron.trace.elegibility = config.trace.elegibility;
|
||||
neuron.trace.extended = config.trace.extended;
|
||||
neuron.state = config.state;
|
||||
neuron.old = config.old;
|
||||
neuron.activation = config.activation;
|
||||
neuron.bias = config.bias;
|
||||
neuron.squash = config.squash in Squash ? Squash[config.squash] :
|
||||
Squash.LOGISTIC;
|
||||
neurons.push(neuron);
|
||||
|
||||
if (config.layer == 'input')
|
||||
layers.input.add(neuron);
|
||||
else if (config.layer == 'output')
|
||||
layers.output.add(neuron);
|
||||
else {
|
||||
if (typeof layers.hidden[config.layer] == 'undefined')
|
||||
layers.hidden[config.layer] = new layer.Layer(0);
|
||||
layers.hidden[config.layer].add(neuron);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i in json.connections) {
|
||||
var config = json.connections[i];
|
||||
var from = neurons[config.from];
|
||||
var to = neurons[config.to];
|
||||
var weight = config.weight
|
||||
var gater = neurons[config.gater];
|
||||
|
||||
var connection = from.project(to, weight);
|
||||
if (gater)
|
||||
gater.gate(connection);
|
||||
}
|
||||
|
||||
return new Network(layers);
|
||||
}
|
||||
}
|
||||
|
||||
export module Network {
|
||||
export interface INetworkNeuron {
|
||||
neuron: _neuron.Neuron;
|
||||
layer: string;
|
||||
}
|
||||
}
|
||||
-796
@@ -1,796 +0,0 @@
|
||||
// export
|
||||
if (module) module.exports = Neuron;
|
||||
|
||||
/******************************************************************************************
|
||||
NEURON
|
||||
*******************************************************************************************/
|
||||
|
||||
function Neuron() {
|
||||
this.ID = Neuron.uid();
|
||||
this.label = null;
|
||||
this.connections = {
|
||||
inputs: {},
|
||||
projected: {},
|
||||
gated: {}
|
||||
};
|
||||
this.error = {
|
||||
responsibility: 0,
|
||||
projected: 0,
|
||||
gated: 0
|
||||
};
|
||||
this.trace = {
|
||||
elegibility: {},
|
||||
extended: {},
|
||||
influences: {}
|
||||
};
|
||||
this.state = 0;
|
||||
this.old = 0;
|
||||
this.activation = 0;
|
||||
this.selfconnection = new Neuron.connection(this, this, 0); // weight = 0 -> not connected
|
||||
this.squash = Neuron.squash.LOGISTIC;
|
||||
this.neighboors = {};
|
||||
this.bias = Math.random() * .2 - .1;
|
||||
}
|
||||
|
||||
Neuron.prototype = {
|
||||
|
||||
// activate the neuron
|
||||
activate: function(input) {
|
||||
// activation from enviroment (for input neurons)
|
||||
if (typeof input != 'undefined') {
|
||||
this.activation = input;
|
||||
this.derivative = 0;
|
||||
this.bias = 0;
|
||||
return this.activation;
|
||||
}
|
||||
|
||||
// old state
|
||||
this.old = this.state;
|
||||
|
||||
// eq. 15
|
||||
this.state = this.selfconnection.gain * this.selfconnection.weight *
|
||||
this.state + this.bias;
|
||||
|
||||
for (var i in this.connections.inputs) {
|
||||
var input = this.connections.inputs[i];
|
||||
this.state += input.from.activation * input.weight * input.gain;
|
||||
}
|
||||
|
||||
// eq. 16
|
||||
this.activation = this.squash(this.state);
|
||||
|
||||
// f'(s)
|
||||
this.derivative = this.squash(this.state, true);
|
||||
|
||||
// update traces
|
||||
var influences = [];
|
||||
for (var id in this.trace.extended) {
|
||||
// extended elegibility trace
|
||||
var neuron = this.neighboors[id];
|
||||
|
||||
// if gated neuron's selfconnection is gated by this unit, the influence keeps track of the neuron's old state
|
||||
var influence = neuron.selfconnection.gater == this ? neuron.old : 0;
|
||||
|
||||
// index runs over all the incoming connections to the gated neuron that are gated by this unit
|
||||
for (var incoming in this.trace.influences[neuron.ID]) { // captures the effect that has an input connection to this unit, on a neuron that is gated by this unit
|
||||
influence += this.trace.influences[neuron.ID][incoming].weight *
|
||||
this.trace.influences[neuron.ID][incoming].from.activation;
|
||||
}
|
||||
influences[neuron.ID] = influence;
|
||||
}
|
||||
|
||||
for (var i in this.connections.inputs) {
|
||||
var input = this.connections.inputs[i];
|
||||
|
||||
// elegibility trace - Eq. 17
|
||||
this.trace.elegibility[input.ID] = this.selfconnection.gain * this.selfconnection
|
||||
.weight * this.trace.elegibility[input.ID] + input.gain * input.from
|
||||
.activation;
|
||||
|
||||
for (var id in this.trace.extended) {
|
||||
// extended elegibility trace
|
||||
var xtrace = this.trace.extended[id];
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = influences[neuron.ID];
|
||||
|
||||
// eq. 18
|
||||
xtrace[input.ID] = neuron.selfconnection.gain * neuron.selfconnection
|
||||
.weight * xtrace[input.ID] + this.derivative * this.trace.elegibility[
|
||||
input.ID] * influence;
|
||||
}
|
||||
}
|
||||
|
||||
// update gated connection's gains
|
||||
for (var connection in this.connections.gated) {
|
||||
this.connections.gated[connection].gain = this.activation;
|
||||
}
|
||||
|
||||
return this.activation;
|
||||
},
|
||||
|
||||
// back-propagate the error
|
||||
propagate: function(rate, target) {
|
||||
// error accumulator
|
||||
var error = 0;
|
||||
|
||||
// whether or not this neuron is in the output layer
|
||||
var isOutput = typeof target != 'undefined';
|
||||
|
||||
// output neurons get their error from the enviroment
|
||||
if (isOutput)
|
||||
this.error.responsibility = this.error.projected = target - this.activation; // Eq. 10
|
||||
|
||||
else // the rest of the neuron compute their error responsibilities by backpropagation
|
||||
{
|
||||
// error responsibilities from all the connections projected from this neuron
|
||||
for (var id in this.connections.projected) {
|
||||
var connection = this.connections.projected[id];
|
||||
var neuron = connection.to;
|
||||
// Eq. 21
|
||||
error += neuron.error.responsibility * connection.gain * connection.weight;
|
||||
}
|
||||
|
||||
// projected error responsibility
|
||||
this.error.projected = this.derivative * error;
|
||||
|
||||
error = 0;
|
||||
// error responsibilities from all the connections gated by this neuron
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id]; // gated neuron
|
||||
var influence = neuron.selfconnection.gater == this ? neuron.old : 0; // if gated neuron's selfconnection is gated by this neuron
|
||||
|
||||
// index runs over all the connections to the gated neuron that are gated by this neuron
|
||||
for (var input in this.trace.influences[id]) { // captures the effect that the input connection of this neuron have, on a neuron which its input/s is/are gated by this neuron
|
||||
influence += this.trace.influences[id][input].weight * this.trace.influences[
|
||||
neuron.ID][input].from.activation;
|
||||
}
|
||||
// eq. 22
|
||||
error += neuron.error.responsibility * influence;
|
||||
}
|
||||
|
||||
// gated error responsibility
|
||||
this.error.gated = this.derivative * error;
|
||||
|
||||
// error responsibility - Eq. 23
|
||||
this.error.responsibility = this.error.projected + this.error.gated;
|
||||
}
|
||||
|
||||
// learning rate
|
||||
rate = rate || .1;
|
||||
|
||||
// adjust all the neuron's incoming connections
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
|
||||
// Eq. 24
|
||||
var gradient = this.error.projected * this.trace.elegibility[input.ID];
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id];
|
||||
gradient += neuron.error.responsibility * this.trace.extended[
|
||||
neuron.ID][input.ID];
|
||||
}
|
||||
input.weight += rate * gradient; // adjust weights - aka learn
|
||||
}
|
||||
|
||||
// adjust bias
|
||||
this.bias += rate * this.error.responsibility;
|
||||
},
|
||||
|
||||
project: function(neuron, weight) {
|
||||
// self-connection
|
||||
if (neuron == this) {
|
||||
this.selfconnection.weight = 1;
|
||||
return this.selfconnection;
|
||||
}
|
||||
|
||||
// check if connection already exists
|
||||
var connected = this.connected(neuron);
|
||||
if (connected && connected.type == "projected") {
|
||||
// update connection
|
||||
if (typeof weight != 'undefined')
|
||||
connected.connection.weight = weight;
|
||||
// return existing connection
|
||||
return connected.connection;
|
||||
} else {
|
||||
// create a new connection
|
||||
var connection = new Neuron.connection(this, neuron, weight);
|
||||
}
|
||||
|
||||
// reference all the connections and traces
|
||||
this.connections.projected[connection.ID] = connection;
|
||||
this.neighboors[neuron.ID] = neuron;
|
||||
neuron.connections.inputs[connection.ID] = connection;
|
||||
neuron.trace.elegibility[connection.ID] = 0;
|
||||
|
||||
for (var id in neuron.trace.extended) {
|
||||
var trace = neuron.trace.extended[id];
|
||||
trace[connection.ID] = 0;
|
||||
}
|
||||
|
||||
return connection;
|
||||
},
|
||||
|
||||
gate: function(connection) {
|
||||
// add connection to gated list
|
||||
this.connections.gated[connection.ID] = connection;
|
||||
|
||||
var neuron = connection.to;
|
||||
if (!(neuron.ID in this.trace.extended)) {
|
||||
// extended trace
|
||||
this.neighboors[neuron.ID] = neuron;
|
||||
var xtrace = this.trace.extended[neuron.ID] = {};
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
xtrace[input.ID] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// keep track
|
||||
if (neuron.ID in this.trace.influences)
|
||||
this.trace.influences[neuron.ID].push(connection);
|
||||
else
|
||||
this.trace.influences[neuron.ID] = [connection];
|
||||
|
||||
// set gater
|
||||
connection.gater = this;
|
||||
},
|
||||
|
||||
// returns true or false whether the neuron is self-connected or not
|
||||
selfconnected: function() {
|
||||
return this.selfconnection.weight !== 0;
|
||||
},
|
||||
|
||||
// returns true or false whether the neuron is connected to another neuron (parameter)
|
||||
connected: function(neuron) {
|
||||
var result = {
|
||||
type: null,
|
||||
connection: false
|
||||
};
|
||||
|
||||
if (this == neuron) {
|
||||
if (this.selfconnected()) {
|
||||
result.type = 'selfconnection';
|
||||
result.connection = this.selfconnection;
|
||||
return result;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var type in this.connections) {
|
||||
for (var connection in this.connections[type]) {
|
||||
var connection = this.connections[type][connection];
|
||||
if (connection.to == neuron) {
|
||||
result.type = type;
|
||||
result.connection = connection;
|
||||
return result;
|
||||
} else if (connection.from == neuron) {
|
||||
result.type = type;
|
||||
result.connection = connection;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
// clears all the traces (the neuron forgets it's context, but the connections remain intact)
|
||||
clear: function() {
|
||||
|
||||
for (var trace in this.trace.elegibility)
|
||||
this.trace.elegibility[trace] = 0;
|
||||
|
||||
for (var trace in this.trace.extended)
|
||||
for (var extended in this.trace.extended[trace])
|
||||
this.trace.extended[trace][extended] = 0;
|
||||
|
||||
this.error.responsibility = this.error.projected = this.error.gated = 0;
|
||||
},
|
||||
|
||||
// all the connections are randomized and the traces are cleared
|
||||
reset: function() {
|
||||
this.clear();
|
||||
|
||||
for (var type in this.connections)
|
||||
for (var connection in this.connections[type])
|
||||
this.connections[type][connection].weight = Math.random() * .2 - .1;
|
||||
this.bias = Math.random() * .2 - .1;
|
||||
|
||||
this.old = this.state = this.activation = 0;
|
||||
},
|
||||
|
||||
// hardcodes the behaviour of the neuron into an optimized function
|
||||
optimize: function(optimized, layer) {
|
||||
|
||||
optimized = optimized || {};
|
||||
var store_activation = [];
|
||||
var store_trace = [];
|
||||
var store_propagation = [];
|
||||
var varID = optimized.memory || 0;
|
||||
var neurons = optimized.neurons || 1;
|
||||
var inputs = optimized.inputs || [];
|
||||
var targets = optimized.targets || [];
|
||||
var outputs = optimized.outputs || [];
|
||||
var variables = optimized.variables || {};
|
||||
var activation_sentences = optimized.activation_sentences || [];
|
||||
var trace_sentences = optimized.trace_sentences || [];
|
||||
var propagation_sentences = optimized.propagation_sentences || [];
|
||||
var layers = optimized.layers || { __count: 0, __neuron: 0 };
|
||||
|
||||
// allocate sentences
|
||||
var allocate = function(store){
|
||||
var allocated = layer in layers && store[layers.__count];
|
||||
if (!allocated)
|
||||
{
|
||||
layers.__count = store.push([]) - 1;
|
||||
layers[layer] = layers.__count;
|
||||
}
|
||||
};
|
||||
allocate(activation_sentences);
|
||||
allocate(trace_sentences);
|
||||
allocate(propagation_sentences);
|
||||
var currentLayer = layers.__count;
|
||||
|
||||
// get/reserve space in memory by creating a unique ID for a variablel
|
||||
var getVar = function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
|
||||
if (args.length == 1) {
|
||||
if (args[0] == 'target') {
|
||||
var id = 'target_' + targets.length;
|
||||
targets.push(varID);
|
||||
} else
|
||||
var id = args[0];
|
||||
if (id in variables)
|
||||
return variables[id];
|
||||
return variables[id] = {
|
||||
value: 0,
|
||||
id: varID++
|
||||
};
|
||||
} else {
|
||||
var extended = args.length > 2;
|
||||
if (extended)
|
||||
var value = args.pop();
|
||||
|
||||
var unit = args.shift();
|
||||
var prop = args.pop();
|
||||
|
||||
if (!extended)
|
||||
var value = unit[prop];
|
||||
|
||||
var id = prop + '_';
|
||||
for (var property in args)
|
||||
id += args[property] + '_';
|
||||
id += unit.ID;
|
||||
if (id in variables)
|
||||
return variables[id];
|
||||
|
||||
return variables[id] = {
|
||||
value: value,
|
||||
id: varID++
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// build sentence
|
||||
var buildSentence = function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var store = args.pop();
|
||||
var sentence = "";
|
||||
for (var i in args)
|
||||
if (typeof args[i] == 'string')
|
||||
sentence += args[i];
|
||||
else
|
||||
sentence += 'F[' + args[i].id + ']';
|
||||
|
||||
store.push(sentence + ';');
|
||||
};
|
||||
|
||||
// helper to check if an object is empty
|
||||
var isEmpty = function(obj) {
|
||||
for (var prop in obj) {
|
||||
if (obj.hasOwnProperty(prop))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// characteristics of the neuron
|
||||
var noProjections = isEmpty(this.connections.projected);
|
||||
var noGates = isEmpty(this.connections.gated);
|
||||
var isInput = layer == 'input' ? true : isEmpty(this.connections.inputs);
|
||||
var isOutput = layer == 'output' ? true : noProjections && noGates;
|
||||
|
||||
// optimize neuron's behaviour
|
||||
var rate = getVar('rate');
|
||||
var activation = getVar(this, 'activation');
|
||||
if (isInput)
|
||||
inputs.push(activation.id);
|
||||
else {
|
||||
activation_sentences[currentLayer].push(store_activation);
|
||||
trace_sentences[currentLayer].push(store_trace);
|
||||
propagation_sentences[currentLayer].push(store_propagation);
|
||||
var old = getVar(this, 'old');
|
||||
var state = getVar(this, 'state');
|
||||
var bias = getVar(this, 'bias');
|
||||
if (this.selfconnection.gater)
|
||||
var self_gain = getVar(this.selfconnection, 'gain');
|
||||
if (this.selfconnected())
|
||||
var self_weight = getVar(this.selfconnection, 'weight');
|
||||
buildSentence(old, ' = ', state, store_activation);
|
||||
if (this.selfconnected())
|
||||
if (this.selfconnection.gater)
|
||||
buildSentence(state, ' = ', self_gain, ' * ', self_weight, ' * ',
|
||||
state, ' + ', bias, store_activation);
|
||||
else
|
||||
buildSentence(state, ' = ', self_weight, ' * ', state, ' + ',
|
||||
bias, store_activation);
|
||||
else
|
||||
buildSentence(state, ' = ', bias, store_activation);
|
||||
for (var i in this.connections.inputs) {
|
||||
var input = this.connections.inputs[i];
|
||||
var input_activation = getVar(input.from, 'activation');
|
||||
var input_weight = getVar(input, 'weight');
|
||||
if (input.gater)
|
||||
var input_gain = getVar(input, 'gain');
|
||||
if (this.connections.inputs[i].gater)
|
||||
buildSentence(state, ' += ', input_activation, ' * ',
|
||||
input_weight, ' * ', input_gain, store_activation);
|
||||
else
|
||||
buildSentence(state, ' += ', input_activation, ' * ',
|
||||
input_weight, store_activation);
|
||||
}
|
||||
var derivative = getVar(this, 'derivative');
|
||||
switch (this.squash) {
|
||||
case Neuron.squash.LOGISTIC:
|
||||
buildSentence(activation, ' = (1 / (1 + Math.exp(-', state, ')))',
|
||||
store_activation);
|
||||
buildSentence(derivative, ' = ', activation, ' * (1 - ',
|
||||
activation, ')', store_activation);
|
||||
break;
|
||||
case Neuron.squash.TANH:
|
||||
var eP = getVar('aux');
|
||||
var eN = getVar('aux_2');
|
||||
buildSentence(eP, ' = Math.exp(', state, ')', store_activation);
|
||||
buildSentence(eN, ' = 1 / ', eP, store_activation);
|
||||
buildSentence(activation, ' = (', eP, ' - ', eN, ') / (', eP, ' + ', eN, ')', store_activation);
|
||||
buildSentence(derivative, ' = 1 - (', activation, ' * ', activation, ')', store_activation);
|
||||
break;
|
||||
case Neuron.squash.IDENTITY:
|
||||
buildSentence(activation, ' = ', state, store_activation);
|
||||
buildSentence(derivative, ' = 1', store_activation);
|
||||
break;
|
||||
case Neuron.squash.HLIM:
|
||||
buildSentence(activation, ' = +(', state, ' > 0)', store_activation);
|
||||
buildSentence(derivative, ' = 1', store_activation);
|
||||
case Neuron.squash.RELU:
|
||||
buildSentence(activation, ' = ', state, ' > 0 ? ', state, ' : 0', store_activation);
|
||||
buildSentence(derivative, ' = ', state, ' > 0 ? 1 : 0', store_activation);
|
||||
break;
|
||||
}
|
||||
|
||||
for (var id in this.trace.extended) {
|
||||
// calculate extended elegibility traces in advance
|
||||
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('influences[' + neuron.ID + ']');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
var initialized = false;
|
||||
if (neuron.selfconnection.gater == this)
|
||||
{
|
||||
buildSentence(influence, ' = ', neuron_old, store_trace);
|
||||
initialized = true;
|
||||
}
|
||||
for (var incoming in this.trace.influences[neuron.ID]) {
|
||||
var incoming_weight = getVar(this.trace.influences[neuron.ID]
|
||||
[incoming], 'weight');
|
||||
var incoming_activation = getVar(this.trace.influences[neuron.ID]
|
||||
[incoming].from, 'activation');
|
||||
|
||||
if (initialized)
|
||||
buildSentence(influence, ' += ', incoming_weight, ' * ', incoming_activation, store_trace);
|
||||
else {
|
||||
buildSentence(influence, ' = ', incoming_weight, ' * ', incoming_activation, store_trace);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i in this.connections.inputs) {
|
||||
var input = this.connections.inputs[i];
|
||||
if (input.gater)
|
||||
var input_gain = getVar(input, 'gain');
|
||||
var input_activation = getVar(input.from, 'activation');
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
|
||||
.elegibility[input.ID]);
|
||||
if (this.selfconnected()) {
|
||||
if (this.selfconnection.gater) {
|
||||
if (input.gater)
|
||||
buildSentence(trace, ' = ', self_gain, ' * ', self_weight,
|
||||
' * ', trace, ' + ', input_gain, ' * ', input_activation,
|
||||
store_trace);
|
||||
else
|
||||
buildSentence(trace, ' = ', self_gain, ' * ', self_weight,
|
||||
' * ', trace, ' + ', input_activation, store_trace);
|
||||
} else {
|
||||
if (input.gater)
|
||||
buildSentence(trace, ' = ', self_weight, ' * ', trace, ' + ',
|
||||
input_gain, ' * ', input_activation, store_trace);
|
||||
else
|
||||
buildSentence(trace, ' = ', self_weight, ' * ', trace, ' + ',
|
||||
input_activation, store_trace);
|
||||
}
|
||||
} else {
|
||||
if (input.gater)
|
||||
buildSentence(trace, ' = ', input_gain, ' * ', input_activation,
|
||||
store_trace);
|
||||
else
|
||||
buildSentence(trace, ' = ', input_activation, store_trace);
|
||||
}
|
||||
for (var id in this.trace.extended) {
|
||||
// extended elegibility trace
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('influences[' + neuron.ID + ']');
|
||||
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
|
||||
.elegibility[input.ID]);
|
||||
var xtrace = getVar(this, 'trace', 'extended', neuron.ID, input.ID,
|
||||
this.trace.extended[neuron.ID][input.ID]);
|
||||
if (neuron.selfconnected())
|
||||
var neuron_self_weight = getVar(neuron.selfconnection, 'weight');
|
||||
if (neuron.selfconnection.gater)
|
||||
var neuron_self_gain = getVar(neuron.selfconnection, 'gain');
|
||||
if (neuron.selfconnected())
|
||||
if (neuron.selfconnection.gater)
|
||||
buildSentence(xtrace, ' = ', neuron_self_gain, ' * ',
|
||||
neuron_self_weight, ' * ', xtrace, ' + ', derivative, ' * ',
|
||||
trace, ' * ', influence, store_trace);
|
||||
else
|
||||
buildSentence(xtrace, ' = ', neuron_self_weight, ' * ',
|
||||
xtrace, ' + ', derivative, ' * ', trace, ' * ',
|
||||
influence, store_trace);
|
||||
else
|
||||
buildSentence(xtrace, ' = ', derivative, ' * ', trace, ' * ',
|
||||
influence, store_trace);
|
||||
}
|
||||
}
|
||||
for (var connection in this.connections.gated) {
|
||||
var gated_gain = getVar(this.connections.gated[connection], 'gain');
|
||||
buildSentence(gated_gain, ' = ', activation, store_activation);
|
||||
}
|
||||
}
|
||||
if (!isInput) {
|
||||
var responsibility = getVar(this, 'error', 'responsibility', this.error
|
||||
.responsibility);
|
||||
if (isOutput) {
|
||||
var target = getVar('target');
|
||||
buildSentence(responsibility, ' = ', target, ' - ', activation,
|
||||
store_propagation);
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
|
||||
.elegibility[input.ID]);
|
||||
var input_weight = getVar(input, 'weight');
|
||||
buildSentence(input_weight, ' += ', rate, ' * (', responsibility,
|
||||
' * ', trace, ')', store_propagation);
|
||||
}
|
||||
outputs.push(activation.id);
|
||||
} else {
|
||||
if (!noProjections && !noGates) {
|
||||
var error = getVar('aux');
|
||||
for (var id in this.connections.projected) {
|
||||
var connection = this.connections.projected[id];
|
||||
var neuron = connection.to;
|
||||
var connection_weight = getVar(connection, 'weight');
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
if (connection.gater) {
|
||||
var connection_gain = getVar(connection, 'gain');
|
||||
buildSentence(error, ' += ', neuron_responsibility, ' * ',
|
||||
connection_gain, ' * ', connection_weight,
|
||||
store_propagation);
|
||||
} else
|
||||
buildSentence(error, ' += ', neuron_responsibility, ' * ',
|
||||
connection_weight, store_propagation);
|
||||
}
|
||||
var projected = getVar(this, 'error', 'projected', this.error.projected);
|
||||
buildSentence(projected, ' = ', derivative, ' * ', error,
|
||||
store_propagation);
|
||||
buildSentence(error, ' = 0', store_propagation);
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('aux_2');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
if (neuron.selfconnection.gater == this)
|
||||
buildSentence(influence, ' = ', neuron_old, store_propagation);
|
||||
else
|
||||
buildSentence(influence, ' = 0', store_propagation);
|
||||
for (var input in this.trace.influences[neuron.ID]) {
|
||||
var connection = this.trace.influences[neuron.ID][input];
|
||||
var connection_weight = getVar(connection, 'weight');
|
||||
var neuron_activation = getVar(connection.from, 'activation');
|
||||
buildSentence(influence, ' += ', connection_weight, ' * ',
|
||||
neuron_activation, store_propagation);
|
||||
}
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
buildSentence(error, ' += ', neuron_responsibility, ' * ',
|
||||
influence, store_propagation);
|
||||
}
|
||||
var gated = getVar(this, 'error', 'gated', this.error.gated);
|
||||
buildSentence(gated, ' = ', derivative, ' * ', error,
|
||||
store_propagation);
|
||||
buildSentence(responsibility, ' = ', projected, ' + ', gated,
|
||||
store_propagation);
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
var gradient = getVar('aux');
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this
|
||||
.trace.elegibility[input.ID]);
|
||||
buildSentence(gradient, ' = ', projected, ' * ', trace,
|
||||
store_propagation);
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id];
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
var xtrace = getVar(this, 'trace', 'extended', neuron.ID,
|
||||
input.ID, this.trace.extended[neuron.ID][input.ID]);
|
||||
buildSentence(gradient, ' += ', neuron_responsibility, ' * ',
|
||||
xtrace, store_propagation);
|
||||
}
|
||||
var input_weight = getVar(input, 'weight');
|
||||
buildSentence(input_weight, ' += ', rate, ' * ', gradient,
|
||||
store_propagation);
|
||||
}
|
||||
|
||||
} else if (noGates) {
|
||||
buildSentence(responsibility, ' = 0', store_propagation);
|
||||
for (var id in this.connections.projected) {
|
||||
var connection = this.connections.projected[id];
|
||||
var neuron = connection.to;
|
||||
var connection_weight = getVar(connection, 'weight');
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
if (connection.gater) {
|
||||
var connection_gain = getVar(connection, 'gain');
|
||||
buildSentence(responsibility, ' += ', neuron_responsibility,
|
||||
' * ', connection_gain, ' * ', connection_weight,
|
||||
store_propagation);
|
||||
} else
|
||||
buildSentence(responsibility, ' += ', neuron_responsibility,
|
||||
' * ', connection_weight, store_propagation);
|
||||
}
|
||||
buildSentence(responsibility, ' *= ', derivative,
|
||||
store_propagation);
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this
|
||||
.trace.elegibility[input.ID]);
|
||||
var input_weight = getVar(input, 'weight');
|
||||
buildSentence(input_weight, ' += ', rate, ' * (',
|
||||
responsibility, ' * ', trace, ')', store_propagation);
|
||||
}
|
||||
} else if (noProjections) {
|
||||
buildSentence(responsibility, ' = 0', store_propagation);
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('aux');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
if (neuron.selfconnection.gater == this)
|
||||
buildSentence(influence, ' = ', neuron_old, store_propagation);
|
||||
else
|
||||
buildSentence(influence, ' = 0', store_propagation);
|
||||
for (var input in this.trace.influences[neuron.ID]) {
|
||||
var connection = this.trace.influences[neuron.ID][input];
|
||||
var connection_weight = getVar(connection, 'weight');
|
||||
var neuron_activation = getVar(connection.from, 'activation');
|
||||
buildSentence(influence, ' += ', connection_weight, ' * ',
|
||||
neuron_activation, store_propagation);
|
||||
}
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
buildSentence(responsibility, ' += ', neuron_responsibility,
|
||||
' * ', influence, store_propagation);
|
||||
}
|
||||
buildSentence(responsibility, ' *= ', derivative,
|
||||
store_propagation);
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
var gradient = getVar('aux');
|
||||
buildSentence(gradient, ' = 0', store_propagation);
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id];
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
var xtrace = getVar(this, 'trace', 'extended', neuron.ID,
|
||||
input.ID, this.trace.extended[neuron.ID][input.ID]);
|
||||
buildSentence(gradient, ' += ', neuron_responsibility, ' * ',
|
||||
xtrace, store_propagation);
|
||||
}
|
||||
var input_weight = getVar(input, 'weight');
|
||||
buildSentence(input_weight, ' += ', rate, ' * ', gradient,
|
||||
store_propagation);
|
||||
}
|
||||
}
|
||||
}
|
||||
buildSentence(bias, ' += ', rate, ' * ', responsibility,
|
||||
store_propagation);
|
||||
}
|
||||
return {
|
||||
memory: varID,
|
||||
neurons: neurons + 1,
|
||||
inputs: inputs,
|
||||
outputs: outputs,
|
||||
targets: targets,
|
||||
variables: variables,
|
||||
activation_sentences: activation_sentences,
|
||||
trace_sentences: trace_sentences,
|
||||
propagation_sentences: propagation_sentences,
|
||||
layers: layers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// represents a connection between two neurons
|
||||
Neuron.connection = function Connection(from, to, weight) {
|
||||
|
||||
if (!from || !to)
|
||||
throw new Error("Connection Error: Invalid neurons");
|
||||
|
||||
this.ID = Neuron.connection.uid();
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.weight = typeof weight == 'undefined' ? Math.random() * .2 - .1 :
|
||||
weight;
|
||||
this.gain = 1;
|
||||
this.gater = null;
|
||||
}
|
||||
|
||||
|
||||
// squashing functions
|
||||
Neuron.squash = {};
|
||||
|
||||
// eq. 5 & 5'
|
||||
Neuron.squash.LOGISTIC = function(x, derivate) {
|
||||
if (!derivate)
|
||||
return 1 / (1 + Math.exp(-x));
|
||||
var fx = Neuron.squash.LOGISTIC(x);
|
||||
return fx * (1 - fx);
|
||||
};
|
||||
Neuron.squash.TANH = function(x, derivate) {
|
||||
if (derivate)
|
||||
return 1 - Math.pow(Neuron.squash.TANH(x), 2);
|
||||
var eP = Math.exp(x);
|
||||
var eN = 1 / eP;
|
||||
return (eP - eN) / (eP + eN);
|
||||
};
|
||||
Neuron.squash.IDENTITY = function(x, derivate) {
|
||||
return derivate ? 1 : x;
|
||||
};
|
||||
Neuron.squash.HLIM = function(x, derivate) {
|
||||
return derivate ? 1 : x > 0 ? 1 : 0;
|
||||
};
|
||||
Neuron.squash.RELU = function(x, derivate) {
|
||||
if (derivate)
|
||||
return x > 0 ? 1 : 0;
|
||||
return x > 0 ? x : 0;
|
||||
};
|
||||
|
||||
// unique ID's
|
||||
(function() {
|
||||
var neurons = 0;
|
||||
var connections = 0;
|
||||
Neuron.uid = function() {
|
||||
return neurons++;
|
||||
}
|
||||
Neuron.connection.uid = function() {
|
||||
return connections++;
|
||||
}
|
||||
Neuron.quantity = function() {
|
||||
return {
|
||||
neurons: neurons,
|
||||
connections: connections
|
||||
}
|
||||
}
|
||||
})();
|
||||
+811
@@ -0,0 +1,811 @@
|
||||
/// <reference path="synaptic.ts" />
|
||||
|
||||
import Synaptic = require('./synaptic');
|
||||
import Squash = require('./squash');
|
||||
|
||||
/******************************************************************************************
|
||||
NEURON
|
||||
*******************************************************************************************/
|
||||
|
||||
/* TS CHANGES:
|
||||
|
||||
Now Neuron.connected(neuron) returns null instead of false
|
||||
|
||||
*/
|
||||
|
||||
export class Neuron {
|
||||
optimizable = true;
|
||||
|
||||
ID = Neuron.uid();
|
||||
label = null;
|
||||
connections: Neuron.INeuronConnections = {
|
||||
inputs: {},
|
||||
projected: {},
|
||||
gated: {}
|
||||
};
|
||||
error = {
|
||||
responsibility: 0,
|
||||
projected: 0,
|
||||
gated: 0
|
||||
};
|
||||
trace = {
|
||||
elegibility: {},
|
||||
extended: {},
|
||||
influences: {}
|
||||
};
|
||||
state = 0;
|
||||
old = 0;
|
||||
activation = 0;
|
||||
selfconnection = new Neuron.Connection(this, this, 0); // weight = 0 -> not connected
|
||||
squash = Squash.LOGISTIC;
|
||||
neighboors = {};
|
||||
bias = Math.random() * .2 - .1;
|
||||
derivative = 0;
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
readIncommingConnections(input?: number) {
|
||||
// activation from enviroment (for input neurons)
|
||||
if (typeof input != 'undefined') {
|
||||
this.activation = input;
|
||||
this.derivative = 0;
|
||||
this.bias = 0;
|
||||
return this.activation;
|
||||
}
|
||||
|
||||
// old state
|
||||
this.old = this.state;
|
||||
|
||||
// eq. 15
|
||||
this.state = this.selfconnection.gain * this.selfconnection.weight *
|
||||
this.state + this.bias;
|
||||
|
||||
for (var i in this.connections.inputs) {
|
||||
var theInput = this.connections.inputs[i];
|
||||
this.state += theInput.from.activation * theInput.weight * theInput.gain;
|
||||
}
|
||||
|
||||
// eq. 16
|
||||
this.activation = this.squash(this.state);
|
||||
|
||||
// f'(s)
|
||||
this.derivative = this.squash(this.state, true);
|
||||
}
|
||||
|
||||
updateTraces() {
|
||||
// update traces
|
||||
var influences = [];
|
||||
for (var id in this.trace.extended) {
|
||||
// extended elegibility trace
|
||||
var xtrace = this.trace.extended[id];
|
||||
var neuron = this.neighboors[id];
|
||||
|
||||
// if gated neuron's selfconnection is gated by this unit, the influence keeps track of the neuron's old state
|
||||
var influence = neuron.selfconnection.gater == this ? neuron.old : 0;
|
||||
|
||||
// index runs over all the incoming connections to the gated neuron that are gated by this unit
|
||||
for (var incoming in this.trace.influences[neuron.ID]) { // captures the effect that has an input connection to this unit, on a neuron that is gated by this unit
|
||||
influence += this.trace.influences[neuron.ID][incoming].weight *
|
||||
this.trace.influences[neuron.ID][incoming].from.activation;
|
||||
}
|
||||
influences[neuron.ID] = influence;
|
||||
}
|
||||
|
||||
for (var i in this.connections.inputs) {
|
||||
var theInput = this.connections.inputs[i];
|
||||
|
||||
// elegibility trace - Eq. 17
|
||||
this.trace.elegibility[theInput.ID] = this.selfconnection.gain * this.selfconnection
|
||||
.weight * this.trace.elegibility[theInput.ID] + theInput.gain * theInput.from
|
||||
.activation;
|
||||
|
||||
for (var id in this.trace.extended) {
|
||||
// extended elegibility trace
|
||||
var xtrace = this.trace.extended[id];
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = influences[neuron.ID];
|
||||
|
||||
// eq. 18
|
||||
xtrace[theInput.ID] = neuron.selfconnection.gain * neuron.selfconnection
|
||||
.weight * xtrace[theInput.ID] + this.derivative * this.trace.elegibility[
|
||||
theInput.ID] * influence;
|
||||
}
|
||||
}
|
||||
|
||||
// update gated connection's gains
|
||||
for (var connection in this.connections.gated) {
|
||||
this.connections.gated[connection].gain = this.activation;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// activate the neuron
|
||||
activate(input?: number) : number {
|
||||
this.readIncommingConnections(input);
|
||||
|
||||
this.updateTraces();
|
||||
|
||||
return this.activation;
|
||||
}
|
||||
|
||||
// back-propagate the error
|
||||
propagate(rate: number, target?: number) {
|
||||
// error accumulator
|
||||
var error = 0;
|
||||
|
||||
// whether or not this neuron is in the output layer
|
||||
var isOutput = typeof target != 'undefined' && target != null;
|
||||
|
||||
// output neurons get their error from the enviroment
|
||||
if (isOutput)
|
||||
this.error.responsibility = this.error.projected = target - this.activation; // Eq. 10
|
||||
|
||||
else // the rest of the neuron compute their error responsibilities by backpropagation
|
||||
{
|
||||
// error responsibilities from all the connections projected from this neuron
|
||||
for (var id in this.connections.projected) {
|
||||
var connection = this.connections.projected[id];
|
||||
var neuron = connection.to;
|
||||
// Eq. 21
|
||||
error += neuron.error.responsibility * connection.gain * connection.weight;
|
||||
}
|
||||
|
||||
// projected error responsibility
|
||||
this.error.projected = this.derivative * error;
|
||||
|
||||
error = 0;
|
||||
// error responsibilities from all the connections gated by this neuron
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id]; // gated neuron
|
||||
var influence = neuron.selfconnection.gater == this ? neuron.old : 0; // if gated neuron's selfconnection is gated by this neuron
|
||||
|
||||
// index runs over all the connections to the gated neuron that are gated by this neuron
|
||||
for (var input in this.trace.influences[id]) { // captures the effect that the input connection of this neuron have, on a neuron which its input/s is/are gated by this neuron
|
||||
influence += this.trace.influences[id][input].weight * this.trace.influences[
|
||||
neuron.ID][input].from.activation;
|
||||
}
|
||||
// eq. 22
|
||||
error += neuron.error.responsibility * influence;
|
||||
}
|
||||
|
||||
// gated error responsibility
|
||||
this.error.gated = this.derivative * error;
|
||||
|
||||
// error responsibility - Eq. 23
|
||||
this.error.responsibility = this.error.projected + this.error.gated;
|
||||
}
|
||||
|
||||
// learning rate
|
||||
rate = rate || .1;
|
||||
|
||||
// adjust all the neuron's incoming connections
|
||||
for (var id in this.connections.inputs) {
|
||||
var theInput = this.connections.inputs[id];
|
||||
|
||||
// Eq. 24
|
||||
var gradient = this.error.projected * this.trace.elegibility[theInput.ID];
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id];
|
||||
gradient += neuron.error.responsibility * this.trace.extended[
|
||||
neuron.ID][theInput.ID];
|
||||
}
|
||||
theInput.weight += rate * gradient; // adjust weights - aka learn
|
||||
}
|
||||
|
||||
// adjust bias
|
||||
this.bias += rate * this.error.responsibility;
|
||||
}
|
||||
|
||||
project(neuron, weight?: number): Neuron.Connection {
|
||||
// self-connection
|
||||
if (neuron == this) {
|
||||
this.selfconnection.weight = 1;
|
||||
return this.selfconnection;
|
||||
}
|
||||
|
||||
// check if connection already exists
|
||||
var connected = this.connected(neuron);
|
||||
if (connected && connected.type == "projected") {
|
||||
// update connection
|
||||
if (typeof weight != 'undefined')
|
||||
connected.connection.weight = weight;
|
||||
// return existing connection
|
||||
return connected.connection;
|
||||
} else {
|
||||
// create a new connection
|
||||
var connection = new Neuron.Connection(this, neuron, weight);
|
||||
}
|
||||
|
||||
// reference all the connections and traces
|
||||
this.connections.projected[connection.ID] = connection;
|
||||
this.neighboors[neuron.ID] = neuron;
|
||||
neuron.connections.inputs[connection.ID] = connection;
|
||||
neuron.trace.elegibility[connection.ID] = 0;
|
||||
|
||||
for (var id in neuron.trace.extended) {
|
||||
var trace = neuron.trace.extended[id];
|
||||
trace[connection.ID] = 0;
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
gate(connection) {
|
||||
// add connection to gated list
|
||||
this.connections.gated[connection.ID] = connection;
|
||||
|
||||
var neuron = connection.to;
|
||||
if (!(neuron.ID in this.trace.extended)) {
|
||||
// extended trace
|
||||
this.neighboors[neuron.ID] = neuron;
|
||||
var xtrace = this.trace.extended[neuron.ID] = {};
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
xtrace[input.ID] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// keep track
|
||||
if (neuron.ID in this.trace.influences)
|
||||
this.trace.influences[neuron.ID].push(connection);
|
||||
else
|
||||
this.trace.influences[neuron.ID] = [connection];
|
||||
|
||||
// set gater
|
||||
connection.gater = this;
|
||||
}
|
||||
|
||||
// returns true or false whether the neuron is self-connected or not
|
||||
selfconnected() {
|
||||
return this.selfconnection.weight !== 0;
|
||||
}
|
||||
|
||||
// returns true or false whether the neuron is connected to another neuron (parameter)
|
||||
connected(neuron) {
|
||||
var result: {
|
||||
type: string;
|
||||
connection: Neuron.Connection;
|
||||
} = {
|
||||
type: null,
|
||||
connection: null
|
||||
};
|
||||
|
||||
if (this == neuron) {
|
||||
if (this.selfconnected()) {
|
||||
result.type = 'selfconnection';
|
||||
result.connection = this.selfconnection;
|
||||
return result;
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var type in this.connections) {
|
||||
for (var connection in this.connections[type]) {
|
||||
var connection = this.connections[type][connection];
|
||||
if (connection.to == neuron) {
|
||||
result.type = type;
|
||||
result.connection = connection;
|
||||
return result;
|
||||
} else if (connection.from == neuron) {
|
||||
result.type = type;
|
||||
result.connection = connection;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// clears all the traces (the neuron forgets it's context, but the connections remain intact)
|
||||
clear() {
|
||||
|
||||
for (var trace in this.trace.elegibility)
|
||||
this.trace.elegibility[trace] = 0;
|
||||
|
||||
for (var trace in this.trace.extended)
|
||||
for (var extended in this.trace.extended[trace])
|
||||
this.trace.extended[trace][extended] = 0;
|
||||
|
||||
this.error.responsibility = this.error.projected = this.error.gated = 0;
|
||||
}
|
||||
|
||||
// all the connections are randomized and the traces are cleared
|
||||
reset() {
|
||||
this.clear();
|
||||
|
||||
for (var type in this.connections)
|
||||
for (var connection in this.connections[type])
|
||||
this.connections[type][connection].weight = Math.random() * .2 - .1;
|
||||
this.bias = Math.random() * .2 - .1;
|
||||
|
||||
this.old = this.state = this.activation = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// hardcodes the behaviour of the neuron into an optimized function
|
||||
optimize(optimized, layer): Synaptic.ICompiledParameters {
|
||||
|
||||
optimized = optimized || {};
|
||||
var that = this;
|
||||
var store_activation = [];
|
||||
var store_trace = [];
|
||||
var store_propagation = [];
|
||||
var varID = optimized.memory || 0;
|
||||
var neurons = optimized.neurons || 1;
|
||||
var inputs = optimized.inputs || [];
|
||||
var targets = optimized.targets || [];
|
||||
var outputs = optimized.outputs || [];
|
||||
var variables = optimized.variables || {};
|
||||
var activation_sentences = optimized.activation_sentences || [];
|
||||
var trace_sentences = optimized.trace_sentences || [];
|
||||
var propagation_sentences = optimized.propagation_sentences || [];
|
||||
var layers = optimized.layers || { __count: 0, __neuron: 0 };
|
||||
|
||||
// allocate sentences
|
||||
var allocate = function(store) {
|
||||
var allocated = layer in layers && store[layers.__count];
|
||||
if (!allocated) {
|
||||
layers.__count = store.push([]) - 1;
|
||||
layers[layer] = layers.__count;
|
||||
}
|
||||
}
|
||||
allocate(activation_sentences);
|
||||
allocate(trace_sentences);
|
||||
allocate(propagation_sentences);
|
||||
var currentLayer = layers.__count;
|
||||
|
||||
// get/reserve space in memory by creating a unique ID for a variablel
|
||||
var getVar = function(...args: any[]) {
|
||||
var id;
|
||||
if (args.length == 1) {
|
||||
|
||||
if (args[0] == 'target') {
|
||||
id = 'target_' + targets.length;
|
||||
targets.push(varID);
|
||||
} else
|
||||
id = args[0];
|
||||
if (id in variables)
|
||||
return variables[id];
|
||||
return variables[id] = {
|
||||
value: 0,
|
||||
id: varID++
|
||||
};
|
||||
} else {
|
||||
var extended = args.length > 2;
|
||||
if (extended)
|
||||
var value = args.pop();
|
||||
|
||||
var unit = args.shift();
|
||||
var prop = args.pop();
|
||||
|
||||
if (!extended)
|
||||
var value = unit[prop];
|
||||
|
||||
id = prop + '_';
|
||||
for (var property in args)
|
||||
id += args[property] + '_';
|
||||
id += unit.ID;
|
||||
if (id in variables)
|
||||
return variables[id];
|
||||
|
||||
return variables[id] = {
|
||||
value: value,
|
||||
id: varID++
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// build sentence
|
||||
var buildSentence = function(...args: any[]) {
|
||||
var store = args.pop();
|
||||
var sentence = "";
|
||||
for (var i in args)
|
||||
if (typeof args[i] == 'string')
|
||||
sentence += args[i];
|
||||
else
|
||||
sentence += 'F[' + args[i].id + ']';
|
||||
|
||||
store.push(sentence + ';');
|
||||
}
|
||||
|
||||
// helper to check if an object is empty
|
||||
var isEmpty = function(obj) {
|
||||
for (var prop in obj) {
|
||||
if (obj.hasOwnProperty(prop))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// characteristics of the neuron
|
||||
var noProjections = isEmpty(this.connections.projected);
|
||||
var noGates = isEmpty(this.connections.gated);
|
||||
var isInput = layer == 'input' ? true : isEmpty(this.connections.inputs);
|
||||
var isOutput = layer == 'output' ? true : noProjections && noGates;
|
||||
|
||||
// optimize neuron's behaviour
|
||||
var rate = getVar('rate');
|
||||
var activation = getVar(this, 'activation');
|
||||
if (isInput)
|
||||
inputs.push(activation.id);
|
||||
else {
|
||||
activation_sentences[currentLayer].push(store_activation);
|
||||
trace_sentences[currentLayer].push(store_trace);
|
||||
propagation_sentences[currentLayer].push(store_propagation);
|
||||
var old = getVar(this, 'old');
|
||||
var state = getVar(this, 'state');
|
||||
var bias = getVar(this, 'bias');
|
||||
if (this.selfconnection.gater)
|
||||
var self_gain = getVar(this.selfconnection, 'gain');
|
||||
if (this.selfconnected())
|
||||
var self_weight = getVar(this.selfconnection, 'weight');
|
||||
buildSentence(old, ' = ', state, store_activation);
|
||||
if (this.selfconnected())
|
||||
if (this.selfconnection.gater)
|
||||
buildSentence(state, ' = ', self_gain, ' * ', self_weight, ' * ',
|
||||
state, ' + ', bias, store_activation);
|
||||
else
|
||||
buildSentence(state, ' = ', self_weight, ' * ', state, ' + ',
|
||||
bias, store_activation);
|
||||
else
|
||||
buildSentence(state, ' = ', bias, store_activation);
|
||||
for (var i in this.connections.inputs) {
|
||||
var input = this.connections.inputs[i];
|
||||
var input_activation = getVar(input.from, 'activation');
|
||||
var input_weight = getVar(input, 'weight');
|
||||
if (input.gater)
|
||||
var input_gain = getVar(input, 'gain');
|
||||
if (this.connections.inputs[i].gater)
|
||||
buildSentence(state, ' += ', input_activation, ' * ',
|
||||
input_weight, ' * ', input_gain, store_activation);
|
||||
else
|
||||
buildSentence(state, ' += ', input_activation, ' * ',
|
||||
input_weight, store_activation);
|
||||
}
|
||||
var derivative = getVar(this, 'derivative');
|
||||
switch (this.squash) {
|
||||
case Squash.LOGISTIC:
|
||||
buildSentence(activation, ' = (1 / (1 + Math.exp(-', state, ')))',
|
||||
store_activation);
|
||||
buildSentence(derivative, ' = ', activation, ' * (1 - ',
|
||||
activation, ')', store_activation);
|
||||
break;
|
||||
case Squash.TANH:
|
||||
var eP = getVar('aux');
|
||||
var eN = getVar('aux_2');
|
||||
buildSentence(eP, ' = Math.exp(', state, ')', store_activation);
|
||||
buildSentence(eN, ' = 1 / ', eP, store_activation);
|
||||
buildSentence(activation, ' = (', eP, ' - ', eN, ') / (', eP, ' + ', eN, ')', store_activation);
|
||||
buildSentence(derivative, ' = 1 - (', activation, ' * ', activation, ')', store_activation);
|
||||
break;
|
||||
case Squash.IDENTITY:
|
||||
buildSentence(activation, ' = ', state, store_activation);
|
||||
buildSentence(derivative, ' = 1', store_activation);
|
||||
break;
|
||||
case Squash.HLIM:
|
||||
buildSentence(activation, ' = +(', state, ' > 0)',
|
||||
store_activation);
|
||||
buildSentence(derivative, ' = 1', store_activation);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
var influences = [];
|
||||
for (var id in this.trace.extended) {
|
||||
// calculate extended elegibility traces in advance
|
||||
|
||||
var xtrace = this.trace.extended[id];
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('aux');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
var initialized = false;
|
||||
if (neuron.selfconnection.gater == this) {
|
||||
buildSentence(influence, ' = ', neuron_old, store_trace);
|
||||
initialized = true;
|
||||
}
|
||||
for (var incoming in this.trace.influences[neuron.ID]) {
|
||||
var incoming_weight = getVar(this.trace.influences[neuron.ID]
|
||||
[incoming], 'weight');
|
||||
var incoming_activation = getVar(this.trace.influences[neuron.ID]
|
||||
[incoming].from, 'activation');
|
||||
|
||||
if (initialized)
|
||||
buildSentence(influence, ' += ', incoming_weight, ' * ',
|
||||
incoming_activation, store_trace);
|
||||
else {
|
||||
buildSentence(influence, ' = ', incoming_weight, ' * ',
|
||||
incoming_activation, store_trace);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
influences.push(neuron.ID);
|
||||
buildSentence("influences[" + (influences.length - 1) + "] = ", influence, store_trace);
|
||||
}
|
||||
|
||||
for (var i in this.connections.inputs) {
|
||||
var input = this.connections.inputs[i];
|
||||
if (input.gater)
|
||||
var input_gain = getVar(input, 'gain');
|
||||
var input_activation = getVar(input.from, 'activation');
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
|
||||
.elegibility[input.ID]);
|
||||
if (this.selfconnected()) {
|
||||
if (this.selfconnection.gater) {
|
||||
if (input.gater)
|
||||
buildSentence(trace, ' = ', self_gain, ' * ', self_weight,
|
||||
' * ', trace, ' + ', input_gain, ' * ', input_activation,
|
||||
store_trace);
|
||||
else
|
||||
buildSentence(trace, ' = ', self_gain, ' * ', self_weight,
|
||||
' * ', trace, ' + ', input_activation, store_trace);
|
||||
} else {
|
||||
if (input.gater)
|
||||
buildSentence(trace, ' = ', self_weight, ' * ', trace, ' + ',
|
||||
input_gain, ' * ', input_activation, store_trace);
|
||||
else
|
||||
buildSentence(trace, ' = ', self_weight, ' * ', trace, ' + ',
|
||||
input_activation, store_trace);
|
||||
}
|
||||
} else {
|
||||
if (input.gater)
|
||||
buildSentence(trace, ' = ', input_gain, ' * ', input_activation,
|
||||
store_trace);
|
||||
else
|
||||
buildSentence(trace, ' = ', input_activation, store_trace);
|
||||
}
|
||||
for (var id in this.trace.extended) {
|
||||
// extended elegibility trace
|
||||
var xtrace = this.trace.extended[id];
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('aux');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
|
||||
.elegibility[input.ID]);
|
||||
var xtrace = getVar(this, 'trace', 'extended', neuron.ID, input.ID,
|
||||
this.trace.extended[neuron.ID][input.ID]);
|
||||
if (neuron.selfconnected())
|
||||
var neuron_self_weight = getVar(neuron.selfconnection, 'weight');
|
||||
if (neuron.selfconnection.gater)
|
||||
var neuron_self_gain = getVar(neuron.selfconnection, 'gain');
|
||||
if (neuron.selfconnected())
|
||||
if (neuron.selfconnection.gater)
|
||||
buildSentence(xtrace, ' = ', neuron_self_gain, ' * ',
|
||||
neuron_self_weight, ' * ', xtrace, ' + ', derivative, ' * ',
|
||||
trace, ' * ', "influences[" + influences.indexOf(neuron.ID) + "]", store_trace);
|
||||
else
|
||||
buildSentence(xtrace, ' = ', neuron_self_weight, ' * ',
|
||||
xtrace, ' + ', derivative, ' * ', trace, ' * ',
|
||||
"influences[" + influences.indexOf(neuron.ID) + "]", store_trace);
|
||||
else
|
||||
buildSentence(xtrace, ' = ', derivative, ' * ', trace, ' * ',
|
||||
"influences[" + influences.indexOf(neuron.ID) + "]", store_trace);
|
||||
}
|
||||
}
|
||||
for (var connection in this.connections.gated) {
|
||||
var gated_gain = getVar(this.connections.gated[connection], 'gain');
|
||||
buildSentence(gated_gain, ' = ', activation, store_activation);
|
||||
}
|
||||
}
|
||||
if (!isInput) {
|
||||
var responsibility = getVar(this, 'error', 'responsibility', this.error
|
||||
.responsibility);
|
||||
if (isOutput) {
|
||||
var target = getVar('target');
|
||||
buildSentence(responsibility, ' = ', target, ' - ', activation,
|
||||
store_propagation);
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
|
||||
.elegibility[input.ID]);
|
||||
var input_weight = getVar(input, 'weight');
|
||||
buildSentence(input_weight, ' += ', rate, ' * (', responsibility,
|
||||
' * ', trace, ')', store_propagation);
|
||||
}
|
||||
outputs.push(activation.id);
|
||||
} else {
|
||||
if (!noProjections && !noGates) {
|
||||
var error = getVar('aux');
|
||||
for (var id in this.connections.projected) {
|
||||
var connection = this.connections.projected[id];
|
||||
var neuron = connection.to;
|
||||
var connection_weight = getVar(connection, 'weight');
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
if (connection.gater) {
|
||||
var connection_gain = getVar(connection, 'gain');
|
||||
buildSentence(error, ' += ', neuron_responsibility, ' * ',
|
||||
connection_gain, ' * ', connection_weight,
|
||||
store_propagation);
|
||||
} else
|
||||
buildSentence(error, ' += ', neuron_responsibility, ' * ',
|
||||
connection_weight, store_propagation);
|
||||
}
|
||||
var projected = getVar(this, 'error', 'projected', this.error.projected);
|
||||
buildSentence(projected, ' = ', derivative, ' * ', error,
|
||||
store_propagation);
|
||||
buildSentence(error, ' = 0', store_propagation);
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('aux_2');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
if (neuron.selfconnection.gater == this)
|
||||
buildSentence(influence, ' = ', neuron_old, store_propagation);
|
||||
else
|
||||
buildSentence(influence, ' = 0', store_propagation);
|
||||
for (var influenceInput in this.trace.influences[neuron.ID]) {
|
||||
var connection = this.trace.influences[neuron.ID][influenceInput];
|
||||
var connection_weight = getVar(connection, 'weight');
|
||||
var neuron_activation = getVar(connection.from, 'activation');
|
||||
buildSentence(influence, ' += ', connection_weight, ' * ',
|
||||
neuron_activation, store_propagation);
|
||||
}
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
buildSentence(error, ' += ', neuron_responsibility, ' * ',
|
||||
influence, store_propagation);
|
||||
}
|
||||
var gated = getVar(this, 'error', 'gated', this.error.gated);
|
||||
buildSentence(gated, ' = ', derivative, ' * ', error,
|
||||
store_propagation);
|
||||
buildSentence(responsibility, ' = ', projected, ' + ', gated,
|
||||
store_propagation);
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
var gradient = getVar('aux');
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this
|
||||
.trace.elegibility[input.ID]);
|
||||
buildSentence(gradient, ' = ', projected, ' * ', trace,
|
||||
store_propagation);
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id];
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
var xtrace = getVar(this, 'trace', 'extended', neuron.ID,
|
||||
input.ID, this.trace.extended[neuron.ID][input.ID]);
|
||||
buildSentence(gradient, ' += ', neuron_responsibility, ' * ',
|
||||
xtrace, store_propagation);
|
||||
}
|
||||
var input_weight = getVar(input, 'weight');
|
||||
buildSentence(input_weight, ' += ', rate, ' * ', gradient,
|
||||
store_propagation);
|
||||
}
|
||||
|
||||
} else if (noGates) {
|
||||
buildSentence(responsibility, ' = 0', store_propagation);
|
||||
for (var id in this.connections.projected) {
|
||||
var connection = this.connections.projected[id];
|
||||
var neuron = connection.to;
|
||||
var connection_weight = getVar(connection, 'weight');
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
if (connection.gater) {
|
||||
var connection_gain = getVar(connection, 'gain');
|
||||
buildSentence(responsibility, ' += ', neuron_responsibility,
|
||||
' * ', connection_gain, ' * ', connection_weight,
|
||||
store_propagation);
|
||||
} else
|
||||
buildSentence(responsibility, ' += ', neuron_responsibility,
|
||||
' * ', connection_weight, store_propagation);
|
||||
}
|
||||
buildSentence(responsibility, ' *= ', derivative,
|
||||
store_propagation);
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this
|
||||
.trace.elegibility[input.ID]);
|
||||
var input_weight = getVar(input, 'weight');
|
||||
buildSentence(input_weight, ' += ', rate, ' * (',
|
||||
responsibility, ' * ', trace, ')', store_propagation);
|
||||
}
|
||||
} else if (noProjections) {
|
||||
buildSentence(responsibility, ' = 0', store_propagation);
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('aux');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
if (neuron.selfconnection.gater == this)
|
||||
buildSentence(influence, ' = ', neuron_old, store_propagation);
|
||||
else
|
||||
buildSentence(influence, ' = 0', store_propagation);
|
||||
for (var influenceInput in this.trace.influences[neuron.ID]) {
|
||||
var connection = this.trace.influences[neuron.ID][influenceInput];
|
||||
var connection_weight = getVar(connection, 'weight');
|
||||
var neuron_activation = getVar(connection.from, 'activation');
|
||||
buildSentence(influence, ' += ', connection_weight, ' * ',
|
||||
neuron_activation, store_propagation);
|
||||
}
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
buildSentence(responsibility, ' += ', neuron_responsibility,
|
||||
' * ', influence, store_propagation);
|
||||
}
|
||||
buildSentence(responsibility, ' *= ', derivative,
|
||||
store_propagation);
|
||||
for (var id in this.connections.inputs) {
|
||||
var input = this.connections.inputs[id];
|
||||
var gradient = getVar('aux');
|
||||
buildSentence(gradient, ' = 0', store_propagation);
|
||||
for (var id in this.trace.extended) {
|
||||
var neuron = this.neighboors[id];
|
||||
var neuron_responsibility = getVar(neuron, 'error',
|
||||
'responsibility', neuron.error.responsibility);
|
||||
var xtrace = getVar(this, 'trace', 'extended', neuron.ID,
|
||||
input.ID, this.trace.extended[neuron.ID][input.ID]);
|
||||
buildSentence(gradient, ' += ', neuron_responsibility, ' * ',
|
||||
xtrace, store_propagation);
|
||||
}
|
||||
var input_weight = getVar(input, 'weight');
|
||||
buildSentence(input_weight, ' += ', rate, ' * ', gradient,
|
||||
store_propagation);
|
||||
}
|
||||
}
|
||||
}
|
||||
buildSentence(bias, ' += ', rate, ' * ', responsibility,
|
||||
store_propagation);
|
||||
}
|
||||
return {
|
||||
memory: varID,
|
||||
neurons: neurons + 1,
|
||||
inputs: inputs,
|
||||
outputs: outputs,
|
||||
targets: targets,
|
||||
variables: variables,
|
||||
activation_sentences: activation_sentences,
|
||||
trace_sentences: trace_sentences,
|
||||
propagation_sentences: propagation_sentences,
|
||||
layers: layers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export module Neuron {
|
||||
|
||||
export interface INeuronConnections {
|
||||
inputs: Synaptic.Dictionary<Neuron.Connection>;
|
||||
projected: {};
|
||||
gated: {};
|
||||
}
|
||||
|
||||
export class Connection {
|
||||
ID = Connection.uid();
|
||||
from;
|
||||
to;
|
||||
gain: number = 1;
|
||||
weight: number = 0;
|
||||
gater: any = null;
|
||||
constructor(from, to, weight?: number) {
|
||||
if (!from || !to)
|
||||
throw "Connection Error: Invalid neurons";
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.weight = typeof weight == 'undefined' || isNaN(weight) ? Math.random() * .2 - .1 :
|
||||
weight;
|
||||
}
|
||||
}
|
||||
|
||||
export var neuronQty = 0;
|
||||
export function uid(): number {
|
||||
return neuronQty++;
|
||||
}
|
||||
|
||||
export function quantity() {
|
||||
return {
|
||||
neurons: neuronQty,
|
||||
connections: Connection.connectionQty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export module Neuron.Connection {
|
||||
export var connectionQty = 0;
|
||||
export function uid(): number {
|
||||
return connectionQty++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import Synaptic = require('./synaptic');
|
||||
import Layer = require('./layer');
|
||||
import Squash = require('./squash');
|
||||
import Neuron = require('./neuron');
|
||||
import _Utils = require('./utils');
|
||||
var Utils = _Utils.Utils;
|
||||
|
||||
export class SoftMaxLayer extends Layer.Layer {
|
||||
constructor(size: number, label?: string) {
|
||||
super(size, label);
|
||||
|
||||
this.optimizable = false;
|
||||
|
||||
for (var n = 0; n < this.list.length; n++) {
|
||||
this.list[n].squash = Squash.IDENTITY;
|
||||
}
|
||||
}
|
||||
|
||||
activate(input?: Synaptic.INumericArray): Synaptic.INumericArray {
|
||||
if (this.currentActivation.length != this.list.length)
|
||||
this.currentActivation = new Float64Array(this.list.length);
|
||||
|
||||
var activationIndex = 0;
|
||||
|
||||
var sum = 0;
|
||||
var Amax = null;
|
||||
|
||||
if (typeof input != 'undefined') {
|
||||
if (input.length != this.size)
|
||||
throw "INPUT size and LAYER size must be the same to activate!";
|
||||
|
||||
Utils.softMax(input);
|
||||
|
||||
for (var id in this.list) {
|
||||
this.list[id].readIncommingConnections(input[id]);
|
||||
if (Amax === null || this.list[id].activation > Amax)
|
||||
Amax = this.list[id].activation;
|
||||
}
|
||||
} else {
|
||||
for (var id in this.list) {
|
||||
this.list[id].readIncommingConnections();
|
||||
if (Amax === null || this.list[id].activation > Amax)
|
||||
Amax = this.list[id].activation;
|
||||
}
|
||||
}
|
||||
|
||||
for (var n = 0; n < this.currentActivation.length; n++) {
|
||||
sum += (this.list[n].activation = Math.exp(this.list[n].activation - Amax));
|
||||
}
|
||||
|
||||
for (var n = 0; n < this.currentActivation.length; n++) {
|
||||
// set the activations
|
||||
var x = this.list[n].activation / sum;
|
||||
this.list[n].activation = this.currentActivation[n] = x;
|
||||
|
||||
// set the derivatives
|
||||
|
||||
//x = this.list[n].activation / (sum - this.list[n].activation);
|
||||
|
||||
this.list[n].derivative = x*(1-x);
|
||||
|
||||
this.list[n].updateTraces();
|
||||
}
|
||||
|
||||
return this.currentActivation;
|
||||
}
|
||||
|
||||
static NormalizeConnectionWeights(layerConnection: Layer.Layer.LayerConnection) {
|
||||
var sum = 0;
|
||||
for (var c = 0; c < layerConnection.list.length; c++) {
|
||||
sum += (layerConnection.list[c].weight = Math.exp(layerConnection.list[c].weight));
|
||||
}
|
||||
for (var c = 0; c < layerConnection.list.length; c++) {
|
||||
layerConnection.list[c].weight /= sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import Synaptic = require('./synaptic');
|
||||
|
||||
// squashing functions
|
||||
|
||||
export function LOGISTIC(x: number, derivate?: boolean) {
|
||||
if (derivate) {
|
||||
var fx = LOGISTIC(x);
|
||||
return fx * (1 - fx);
|
||||
}
|
||||
return 1 / (1 + Math.exp(-x));
|
||||
|
||||
}
|
||||
|
||||
export function TANH(x: number, derivate?: boolean) {
|
||||
if (derivate)
|
||||
return 1 - Math.pow(TANH(x), 2);
|
||||
var eP = Math.exp(x);
|
||||
var eN = 1 / eP;
|
||||
return (eP - eN) / (eP + eN);
|
||||
}
|
||||
|
||||
export function IDENTITY(x: number, derivate?: boolean) {
|
||||
return derivate ? 1 : x;
|
||||
}
|
||||
|
||||
export function HLIM(x: number, derivate?: boolean) {
|
||||
return derivate ? 1 : +(x > 0);
|
||||
}
|
||||
|
||||
export function SOFTPLUS(x: number, derivate?: boolean) {
|
||||
if (derivate)
|
||||
return 1 - 1 / (1 + Math.exp(x));
|
||||
return Math.log(1 + Math.exp(x));
|
||||
}
|
||||
|
||||
export function EXP(x: number, derivate?: boolean) {
|
||||
return Math.exp(x);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
var Synaptic = {
|
||||
Neuron: require('./neuron'),
|
||||
Layer: require('./layer'),
|
||||
Network: require('./network'),
|
||||
Trainer: require('./trainer'),
|
||||
Architect: require('./architect')
|
||||
};
|
||||
|
||||
// CommonJS & AMD
|
||||
if (typeof define !== 'undefined' && define.amd)
|
||||
{
|
||||
define([], function(){ return Synaptic });
|
||||
}
|
||||
|
||||
// Node.js
|
||||
if (typeof module !== 'undefined' && module.exports)
|
||||
{
|
||||
module.exports = Synaptic;
|
||||
}
|
||||
|
||||
// Browser
|
||||
if (typeof window == 'object')
|
||||
{
|
||||
(function(){
|
||||
var oldSynaptic = window['synaptic'];
|
||||
Synaptic.ninja = function(){
|
||||
window['synaptic'] = oldSynaptic;
|
||||
return Synaptic;
|
||||
};
|
||||
})();
|
||||
|
||||
window['synaptic'] = Synaptic;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
********************************************************************************************
|
||||
SYNAPTIC
|
||||
********************************************************************************************
|
||||
|
||||
Synaptic is a javascript neural network library for node.js and the browser, its generalized
|
||||
algorithm is architecture-free, so you can build and train basically any type of first order
|
||||
or even second order neural network architectures.
|
||||
|
||||
http://en.wikipedia.org/wiki/Recurrent_neural_network#Second_Order_Recurrent_Neural_Network
|
||||
|
||||
The library includes a few built-in architectures like multilayer perceptrons, multilayer
|
||||
long-short term memory networks (LSTM) or liquid state machines, and a trainer capable of
|
||||
training any given network, and includes built-in training tasks/tests like solving an XOR,
|
||||
passing a Distracted Sequence Recall test or an Embeded Reber Grammar test.
|
||||
|
||||
The algorithm implemented by this library has been taken from Derek D. Monner's paper:
|
||||
|
||||
A generalized LSTM-like training algorithm for second-order recurrent neural networks
|
||||
http://www.overcomplete.net/papers/nn2012.pdf
|
||||
|
||||
There are references to the equations in that paper commented through the source code.
|
||||
|
||||
|
||||
********************************************************************************************/
|
||||
|
||||
|
||||
|
||||
import network = require('./network');
|
||||
import layer = require('./layer');
|
||||
import neuron = require('./neuron');
|
||||
import trainer = require('./trainer');
|
||||
import architect = require('./architect');
|
||||
import squash = require('./squash');
|
||||
import utils = require('./utils');
|
||||
|
||||
declare var window;
|
||||
|
||||
module Synaptic {
|
||||
export interface Dictionary<T> {
|
||||
[id: string] : T;
|
||||
}
|
||||
|
||||
var oldSynaptic = typeof window != "undefined" && window && window['Synaptic'];
|
||||
|
||||
export function ninja() {
|
||||
window['synaptic'] = oldSynaptic;
|
||||
return Synaptic;
|
||||
}
|
||||
|
||||
export interface ICompiledParameters {
|
||||
memory?: any;
|
||||
neurons?: number;
|
||||
inputs?: any[];
|
||||
outputs?: any[];
|
||||
targets?: any[];
|
||||
variables?: any;
|
||||
activation_sentences?: any[];
|
||||
trace_sentences?: any[];
|
||||
propagation_sentences?: any[];
|
||||
layers?: any;
|
||||
}
|
||||
|
||||
export interface INumericArray {
|
||||
[index: number] : number;
|
||||
length : number;
|
||||
}
|
||||
|
||||
export var Neuron = neuron.Neuron;
|
||||
export var Layer = layer.Layer;
|
||||
export var Network = network.Network;
|
||||
export var Trainer = trainer.Trainer;
|
||||
export var Squash = squash;
|
||||
export var Architect = architect;
|
||||
export var Utils = utils.Utils;
|
||||
}
|
||||
|
||||
export = Synaptic;
|
||||
|
||||
if(typeof window != "undefined")
|
||||
window['synaptic'] = Synaptic;
|
||||
+230
-283
@@ -1,31 +1,33 @@
|
||||
// export
|
||||
if (module) module.exports = Trainer;
|
||||
import net = require('./network');
|
||||
|
||||
/*******************************************************************************************
|
||||
TRAINER
|
||||
*******************************************************************************************/
|
||||
|
||||
function Trainer(network, options) {
|
||||
options = options || {};
|
||||
this.network = network;
|
||||
this.rate = options.rate || .2;
|
||||
this.iterations = options.iterations || 100000;
|
||||
this.error = options.error || .005;
|
||||
this.cost = options.cost || null;
|
||||
this.crossValidate = options.crossValidate || null;
|
||||
}
|
||||
export class Trainer {
|
||||
network: net.Network;
|
||||
rate: any = .2;
|
||||
iterations = 100000;
|
||||
error = .005;
|
||||
cost: Trainer.ITrainerCostFn;
|
||||
schedule: any;
|
||||
|
||||
Trainer.prototype = {
|
||||
constructor(network: net.Network, options?: any) {
|
||||
options = options || {};
|
||||
this.network = network;
|
||||
this.rate = options.rate || .2;
|
||||
this.iterations = options.iterations || 100000;
|
||||
this.error = options.error || .005
|
||||
this.cost = options.cost || Trainer.cost.CROSS_ENTROPY;
|
||||
}
|
||||
|
||||
// trains any given set to a network
|
||||
train: function(set, options) {
|
||||
train(set, options) {
|
||||
|
||||
var error = 1;
|
||||
var iterations = bucketSize = 0;
|
||||
var abort = false;
|
||||
var currentRate;
|
||||
var cost = options && options.cost || this.cost || Trainer.cost.MSE;
|
||||
var crossValidate = false, testSet, trainSet;
|
||||
var iterations = 0, bucketSize = 0;
|
||||
var abort_training = false;
|
||||
var input, output, target, currentRate;
|
||||
|
||||
var start = Date.now();
|
||||
|
||||
@@ -48,70 +50,52 @@ Trainer.prototype = {
|
||||
this.cost = options.cost;
|
||||
if (options.schedule)
|
||||
this.schedule = options.schedule;
|
||||
if (options.customLog){
|
||||
if (options.customLog) {
|
||||
// for backward compatibility with code that used customLog
|
||||
console.log('Deprecated: use schedule instead of customLog')
|
||||
this.schedule = options.customLog;
|
||||
}
|
||||
if (this.crossValidate || options.crossValidate) {
|
||||
if(!this.crossValidate) this.crossValidate = {};
|
||||
crossValidate = true;
|
||||
if (options.crossValidate.testSize)
|
||||
this.crossValidate.testSize = options.crossValidate.testSize;
|
||||
if (options.crossValidate.testError)
|
||||
this.crossValidate.testError = options.crossValidate.testError;
|
||||
}
|
||||
}
|
||||
|
||||
currentRate = this.rate;
|
||||
if(Array.isArray(this.rate)) {
|
||||
var bucketSize = Math.floor(this.iterations / this.rate.length);
|
||||
if (Array.isArray(this.rate)) {
|
||||
bucketSize = Math.floor(this.iterations / this.rate.length);
|
||||
}
|
||||
|
||||
if(crossValidate) {
|
||||
var numTrain = Math.ceil((1 - this.crossValidate.testSize) * set.length);
|
||||
trainSet = set.slice(0, numTrain);
|
||||
testSet = set.slice(numTrain);
|
||||
}
|
||||
|
||||
var lastError = 0;
|
||||
while ((!abort && iterations < this.iterations && error > this.error)) {
|
||||
if (crossValidate && error <= this.crossValidate.testError) {
|
||||
break;
|
||||
}
|
||||
|
||||
var currentSetSize = set.length;
|
||||
while (!abort_training && iterations < this.iterations && error > this.error) {
|
||||
error = 0;
|
||||
iterations++;
|
||||
|
||||
if(bucketSize > 0) {
|
||||
if (bucketSize > 0) {
|
||||
var currentBucket = Math.floor(iterations / bucketSize);
|
||||
currentRate = this.rate[currentBucket] || currentRate;
|
||||
}
|
||||
|
||||
if(typeof this.rate === 'function') {
|
||||
currentRate = this.rate(iterations, lastError);
|
||||
currentRate = this.rate[currentBucket];
|
||||
}
|
||||
|
||||
if (crossValidate) {
|
||||
this._trainSet(trainSet, currentRate, cost);
|
||||
error += this.test(testSet).error;
|
||||
currentSetSize = 1;
|
||||
} else {
|
||||
error += this._trainSet(set, currentRate, cost);
|
||||
currentSetSize = set.length;
|
||||
for (var train in set) {
|
||||
input = set[train].input;
|
||||
target = set[train].output;
|
||||
|
||||
output = this.network.activate(input);
|
||||
this.network.propagate(currentRate, target);
|
||||
|
||||
error += this.cost(target, output);
|
||||
}
|
||||
|
||||
// check error
|
||||
error /= currentSetSize;
|
||||
lastError = error;
|
||||
iterations++;
|
||||
error /= set.length;
|
||||
|
||||
if (options) {
|
||||
if (this.schedule && this.schedule.every && iterations %
|
||||
this.schedule.every == 0)
|
||||
abort = this.schedule.do({ error: error, iterations: iterations, rate: currentRate });
|
||||
else if (options.log && iterations % options.log == 0) {
|
||||
console.log('iterations', iterations, 'error', error, 'rate', currentRate);
|
||||
if (this.schedule && this.schedule.every && iterations % this.schedule.every == 0) {
|
||||
|
||||
abort_training = this.schedule.do({
|
||||
error: error,
|
||||
iterations: iterations,
|
||||
rate: currentRate
|
||||
});
|
||||
|
||||
} else if (options.log && iterations % options.log == 0) {
|
||||
console.log('iterations', iterations, 'error', error, 'rate', currentRate, 'T:', target, 'O:', output);
|
||||
};
|
||||
if (options.shuffle)
|
||||
shuffle(set);
|
||||
@@ -122,127 +106,147 @@ Trainer.prototype = {
|
||||
error: error,
|
||||
iterations: iterations,
|
||||
time: Date.now() - start
|
||||
};
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
}
|
||||
|
||||
// trains any given set to a network, using a WebWorker (only for the browser). Returns a Promise of the results.
|
||||
trainAsync: function(set, options) {
|
||||
var train = this.workerTrain.bind(this);
|
||||
return new Promise(function(resolve, reject) {
|
||||
try {
|
||||
train(set, resolve, options, true)
|
||||
} catch(e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
},
|
||||
// trains any given set to a network using a WebWorker
|
||||
workerTrain(set, callback, options) {
|
||||
|
||||
// preforms one training epoch and returns the error (private function used in this.train)
|
||||
_trainSet: function(set, currentRate, costFunction) {
|
||||
var errorSum = 0;
|
||||
for (var train in set) {
|
||||
var input = set[train].input;
|
||||
var target = set[train].output;
|
||||
|
||||
var output = this.network.activate(input);
|
||||
this.network.propagate(currentRate, target);
|
||||
|
||||
errorSum += costFunction(target, output);
|
||||
}
|
||||
return errorSum;
|
||||
},
|
||||
|
||||
// tests a set and returns the error and elapsed time
|
||||
test: function(set, options) {
|
||||
|
||||
var error = 0;
|
||||
var input, output, target;
|
||||
var cost = options && options.cost || this.cost || Trainer.cost.MSE;
|
||||
var that = this;
|
||||
var error = 1;
|
||||
var iterations = 0, bucketSize = 0;
|
||||
var input, output, target, currentRate;
|
||||
var length = set.length;
|
||||
var abort_training = false;
|
||||
|
||||
var start = Date.now();
|
||||
|
||||
for (var test in set) {
|
||||
input = set[test].input;
|
||||
target = set[test].output;
|
||||
output = this.network.activate(input);
|
||||
error += cost(target, output);
|
||||
if (options) {
|
||||
if (options.shuffle) {
|
||||
//+ Jonas Raoni Soares Silva
|
||||
//@ http://jsfromhell.com/array/shuffle [v1.0]
|
||||
function shuffle(o) { //v1.0
|
||||
for (var j, x, i = o.length; i; j = Math.floor(Math.random() *
|
||||
i), x = o[--i], o[i] = o[j], o[j] = x);
|
||||
return o;
|
||||
};
|
||||
}
|
||||
if (options.iterations)
|
||||
this.iterations = options.iterations;
|
||||
if (options.error)
|
||||
this.error = options.error;
|
||||
if (options.rate)
|
||||
this.rate = options.rate;
|
||||
if (options.cost)
|
||||
this.cost = options.cost;
|
||||
if (options.schedule)
|
||||
this.schedule = options.schedule;
|
||||
if (options.customLog)
|
||||
// for backward compatibility with code that used customLog
|
||||
console.log('Deprecated: use schedule instead of customLog')
|
||||
this.schedule = options.customLog;
|
||||
}
|
||||
|
||||
error /= set.length;
|
||||
|
||||
var results = {
|
||||
error: error,
|
||||
time: Date.now() - start
|
||||
};
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
// trains any given set to a network using a WebWorker [deprecated: use trainAsync instead]
|
||||
workerTrain: function(set, callback, options, suppressWarning) {
|
||||
|
||||
if (!suppressWarning) {
|
||||
console.warn('Deprecated: do not use `workerTrain`, use `trainAsync` instead.')
|
||||
// dynamic learning rate
|
||||
currentRate = this.rate;
|
||||
if (Array.isArray(this.rate)) {
|
||||
bucketSize = Math.floor(this.iterations / this.rate.length);
|
||||
}
|
||||
var that = this;
|
||||
|
||||
if (!this.network.optimized)
|
||||
this.network.optimize();
|
||||
// create a worker
|
||||
var worker = this.network.worker();
|
||||
|
||||
// Create a new worker
|
||||
var worker = this.network.worker(this.network.optimized.memory, set, options);
|
||||
// activate the network
|
||||
function activateWorker(input) {
|
||||
worker.postMessage({
|
||||
action: "activate",
|
||||
input: input,
|
||||
memoryBuffer: that.network.optimized.memory
|
||||
}, [that.network.optimized.memory.buffer]);
|
||||
}
|
||||
|
||||
// backpropagate the network
|
||||
function propagateWorker(target) {
|
||||
if (bucketSize > 0) {
|
||||
var currentBucket = Math.floor(iterations / bucketSize);
|
||||
currentRate = this.rate[currentBucket];
|
||||
}
|
||||
worker.postMessage({
|
||||
action: "propagate",
|
||||
target: target,
|
||||
rate: currentRate,
|
||||
memoryBuffer: that.network.optimized.memory
|
||||
}, [that.network.optimized.memory.buffer]);
|
||||
}
|
||||
|
||||
// train the worker
|
||||
worker.onmessage = function(e) {
|
||||
switch(e.data.action) {
|
||||
case 'done':
|
||||
var iterations = e.data.message.iterations;
|
||||
var error = e.data.message.error;
|
||||
var time = e.data.message.time;
|
||||
// give control of the memory back to the network
|
||||
that.network.optimized.ownership(e.data.memoryBuffer);
|
||||
|
||||
that.network.optimized.ownership(e.data.memoryBuffer);
|
||||
if (e.data.action == "propagate") {
|
||||
if (index >= length) {
|
||||
index = 0;
|
||||
iterations++;
|
||||
error /= set.length;
|
||||
|
||||
// Done callback
|
||||
// log
|
||||
if (options) {
|
||||
if (this.schedule && this.schedule.every && iterations % this.schedule.every == 0)
|
||||
abort_training = this.schedule.do({
|
||||
error: error,
|
||||
iterations: iterations
|
||||
});
|
||||
else if (options.log && iterations % options.log == 0) {
|
||||
console.log('iterations', iterations, 'error', error);
|
||||
};
|
||||
if (options.shuffle)
|
||||
shuffle(set);
|
||||
}
|
||||
|
||||
if (!abort_training && iterations < that.iterations && error > that.error) {
|
||||
activateWorker(set[index].input);
|
||||
} else {
|
||||
// callback
|
||||
callback({
|
||||
error: error,
|
||||
iterations: iterations,
|
||||
time: time
|
||||
});
|
||||
|
||||
// Delete the worker and all its associated memory
|
||||
worker.terminate();
|
||||
break;
|
||||
|
||||
case 'log':
|
||||
console.log(e.data.message);
|
||||
|
||||
case 'schedule':
|
||||
if (options && options.schedule && typeof options.schedule.do === 'function') {
|
||||
var scheduled = options.schedule.do
|
||||
scheduled(e.data.message)
|
||||
}
|
||||
break;
|
||||
time: Date.now() - start
|
||||
})
|
||||
}
|
||||
error = 0;
|
||||
} else {
|
||||
activateWorker(set[index].input);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Start the worker
|
||||
worker.postMessage({action: 'startTraining'});
|
||||
},
|
||||
if (e.data.action == "activate") {
|
||||
error += that.cost(set[index].output, e.data.output);
|
||||
propagateWorker(set[index].output);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// kick it
|
||||
var index = 0;
|
||||
var iterations = 0;
|
||||
activateWorker(set[index].input);
|
||||
}
|
||||
|
||||
// trains an XOR to the network
|
||||
XOR: function(options) {
|
||||
XOR(options) {
|
||||
|
||||
if (this.network.inputs() != 2 || this.network.outputs() != 1)
|
||||
throw new Error("Incompatible network (2 inputs, 1 output)");
|
||||
throw "Error: Incompatible network (2 inputs, 1 output)";
|
||||
|
||||
var defaults = {
|
||||
iterations: 100000,
|
||||
log: false,
|
||||
shuffle: true,
|
||||
cost: Trainer.cost.MSE
|
||||
};
|
||||
}
|
||||
|
||||
if (options)
|
||||
for (var i in options)
|
||||
@@ -252,19 +256,19 @@ Trainer.prototype = {
|
||||
input: [0, 0],
|
||||
output: [0]
|
||||
}, {
|
||||
input: [1, 0],
|
||||
output: [1]
|
||||
}, {
|
||||
input: [0, 1],
|
||||
output: [1]
|
||||
}, {
|
||||
input: [1, 1],
|
||||
output: [0]
|
||||
}], defaults);
|
||||
},
|
||||
input: [1, 0],
|
||||
output: [1]
|
||||
}, {
|
||||
input: [0, 1],
|
||||
output: [1]
|
||||
}, {
|
||||
input: [1, 1],
|
||||
output: [0]
|
||||
}], defaults);
|
||||
}
|
||||
|
||||
// trains the network to pass a Distracted Sequence Recall test
|
||||
DSR: function(options) {
|
||||
DSR(options) {
|
||||
options = options || {};
|
||||
|
||||
var targets = options.targets || [2, 4, 7, 8];
|
||||
@@ -276,11 +280,11 @@ Trainer.prototype = {
|
||||
var rate = options.rate || .1;
|
||||
var log = options.log || 0;
|
||||
var schedule = options.schedule || {};
|
||||
var cost = options.cost || this.cost || Trainer.cost.CROSS_ENTROPY;
|
||||
|
||||
var trial, correct, i, j, success;
|
||||
trial = correct = i = j = success = 0;
|
||||
var error = 1,
|
||||
var correct = 0;
|
||||
var i = 0;
|
||||
var success = 0;
|
||||
var trial = i = correct = j = success = 0,
|
||||
error = 1,
|
||||
symbols = targets.length + distractors.length + prompts.length;
|
||||
|
||||
var noRepeat = function(range, avoid) {
|
||||
@@ -290,14 +294,14 @@ Trainer.prototype = {
|
||||
if (number == avoid[i])
|
||||
used = true;
|
||||
return used ? noRepeat(range, avoid) : number;
|
||||
};
|
||||
}
|
||||
|
||||
var equal = function(prediction, output) {
|
||||
for (var i in prediction)
|
||||
if (Math.round(prediction[i]) != output[i])
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
var start = Date.now();
|
||||
|
||||
@@ -354,7 +358,10 @@ Trainer.prototype = {
|
||||
this.network.propagate(rate, output);
|
||||
}
|
||||
|
||||
error += cost(output, prediction);
|
||||
var delta = 0;
|
||||
for (var j in prediction)
|
||||
delta += Math.pow(output[j] - prediction[j], 2);
|
||||
error += delta / this.network.outputs();
|
||||
|
||||
if (distractorsCorrect + targetsCorrect == length)
|
||||
correct++;
|
||||
@@ -373,7 +380,7 @@ Trainer.prototype = {
|
||||
if (log && trial % log == 0)
|
||||
console.log("iterations:", trial, " success:", success, " correct:",
|
||||
correct, " time:", Date.now() - start, " error:", error);
|
||||
if (schedule.do && schedule.every && trial % schedule.every == 0)
|
||||
if (schedule.do && schedule.every && trial % schedule.every == 0) {
|
||||
schedule.do({
|
||||
iterations: trial,
|
||||
success: success,
|
||||
@@ -381,6 +388,8 @@ Trainer.prototype = {
|
||||
time: Date.now() - start,
|
||||
correct: correct
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -389,22 +398,21 @@ Trainer.prototype = {
|
||||
error: error,
|
||||
time: Date.now() - start
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// train the network to learn an Embeded Reber Grammar
|
||||
ERG: function(options) {
|
||||
ERG(options) {
|
||||
|
||||
options = options || {};
|
||||
var iterations = options.iterations || 150000;
|
||||
var criterion = options.error || .05;
|
||||
var rate = options.rate || .1;
|
||||
var log = options.log || 500;
|
||||
var cost = options.cost || this.cost || Trainer.cost.CROSS_ENTROPY;
|
||||
|
||||
// gramar node
|
||||
var Node = function() {
|
||||
this.paths = [];
|
||||
};
|
||||
}
|
||||
Node.prototype = {
|
||||
connect: function(node, value) {
|
||||
this.paths.push({
|
||||
@@ -425,7 +433,7 @@ Trainer.prototype = {
|
||||
return this.paths[i];
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var reberGrammar = function() {
|
||||
|
||||
@@ -434,19 +442,19 @@ Trainer.prototype = {
|
||||
var n1 = (new Node()).connect(output, "E");
|
||||
var n2 = (new Node()).connect(n1, "S");
|
||||
var n3 = (new Node()).connect(n1, "V").connect(n2, "P");
|
||||
var n4 = (new Node()).connect(n2, "X");
|
||||
var n4 = (new Node()).connect(n2, "X")
|
||||
n4.connect(n4, "S");
|
||||
var n5 = (new Node()).connect(n3, "V");
|
||||
var n5 = (new Node()).connect(n3, "V")
|
||||
n5.connect(n5, "T");
|
||||
n2.connect(n5, "X");
|
||||
n2.connect(n5, "X")
|
||||
var n6 = (new Node()).connect(n4, "T").connect(n5, "P");
|
||||
var input = (new Node()).connect(n6, "B");
|
||||
var input = (new Node()).connect(n6, "B")
|
||||
|
||||
return {
|
||||
input: input,
|
||||
output: output
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// build an embeded reber grammar
|
||||
var embededReberGrammar = function() {
|
||||
@@ -466,7 +474,7 @@ Trainer.prototype = {
|
||||
output: output
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
// generate an ERG sequence
|
||||
var generate = function() {
|
||||
@@ -478,7 +486,7 @@ Trainer.prototype = {
|
||||
next = next.node.any();
|
||||
}
|
||||
return str;
|
||||
};
|
||||
}
|
||||
|
||||
// test if a string matches an embeded reber grammar
|
||||
var test = function(str) {
|
||||
@@ -493,7 +501,7 @@ Trainer.prototype = {
|
||||
ch = str.charAt(++i);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
// helper to check if the output and the target vectors match
|
||||
var different = function(array1, array2) {
|
||||
@@ -513,7 +521,7 @@ Trainer.prototype = {
|
||||
}
|
||||
|
||||
return i1 != i2;
|
||||
};
|
||||
}
|
||||
|
||||
var iteration = 0;
|
||||
var error = 1;
|
||||
@@ -524,7 +532,7 @@ Trainer.prototype = {
|
||||
"X": 3,
|
||||
"S": 4,
|
||||
"E": 5
|
||||
};
|
||||
}
|
||||
|
||||
var start = Date.now();
|
||||
while (iteration < iterations && error > criterion) {
|
||||
@@ -558,7 +566,12 @@ Trainer.prototype = {
|
||||
read = sequence.charAt(++i);
|
||||
predict = sequence.charAt(i + 1);
|
||||
|
||||
error += cost(target, output);
|
||||
var delta = 0;
|
||||
for (var k in output)
|
||||
delta += Math.pow(target[k] - output[k], 2)
|
||||
delta /= output.length;
|
||||
|
||||
error += delta;
|
||||
}
|
||||
error /= sequence.length;
|
||||
iteration++;
|
||||
@@ -575,102 +588,36 @@ Trainer.prototype = {
|
||||
test: test,
|
||||
generate: generate
|
||||
}
|
||||
},
|
||||
|
||||
timingTask: function(options){
|
||||
|
||||
if (this.network.inputs() != 2 || this.network.outputs() != 1)
|
||||
throw new Error("Invalid Network: must have 2 inputs and one output");
|
||||
|
||||
if (typeof options == 'undefined')
|
||||
options = {};
|
||||
|
||||
// helper
|
||||
function getSamples (trainingSize, testSize){
|
||||
|
||||
// sample size
|
||||
var size = trainingSize + testSize;
|
||||
|
||||
// generate samples
|
||||
var t = 0;
|
||||
var set = [];
|
||||
for (var i = 0; i < size; i++) {
|
||||
set.push({ input: [0,0], output: [0] });
|
||||
}
|
||||
while(t < size - 20) {
|
||||
var n = Math.round(Math.random() * 20);
|
||||
set[t].input[0] = 1;
|
||||
for (var j = t; j <= t + n; j++){
|
||||
set[j].input[1] = n / 20;
|
||||
set[j].output[0] = 0.5;
|
||||
}
|
||||
t += n;
|
||||
n = Math.round(Math.random() * 20);
|
||||
for (var k = t+1; k <= (t + n) && k < size; k++)
|
||||
set[k].input[1] = set[t].input[1];
|
||||
t += n;
|
||||
}
|
||||
|
||||
// separate samples between train and test sets
|
||||
var trainingSet = []; var testSet = [];
|
||||
for (var l = 0; l < size; l++)
|
||||
(l < trainingSize ? trainingSet : testSet).push(set[l]);
|
||||
|
||||
// return samples
|
||||
return {
|
||||
train: trainingSet,
|
||||
test: testSet
|
||||
}
|
||||
}
|
||||
|
||||
var iterations = options.iterations || 200;
|
||||
var error = options.error || .005;
|
||||
var rate = options.rate || [.03, .02];
|
||||
var log = options.log === false ? false : options.log || 10;
|
||||
var cost = options.cost || this.cost || Trainer.cost.MSE;
|
||||
var trainingSamples = options.trainSamples || 7000;
|
||||
var testSamples = options.trainSamples || 1000;
|
||||
|
||||
// samples for training and testing
|
||||
var samples = getSamples(trainingSamples, testSamples);
|
||||
|
||||
// train
|
||||
var result = this.train(samples.train, {
|
||||
rate: rate,
|
||||
log: log,
|
||||
iterations: iterations,
|
||||
error: error,
|
||||
cost: cost
|
||||
});
|
||||
|
||||
return {
|
||||
train: result,
|
||||
test: this.test(samples.test)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Built-in cost functions
|
||||
Trainer.cost = {
|
||||
// Eq. 9
|
||||
CROSS_ENTROPY: function(target, output)
|
||||
{
|
||||
var crossentropy = 0;
|
||||
for (var i in output)
|
||||
crossentropy -= (target[i] * Math.log(output[i]+1e-15)) + ((1-target[i]) * Math.log((1+1e-15)-output[i])); // +1e-15 is a tiny push away to avoid Math.log(0)
|
||||
return crossentropy;
|
||||
},
|
||||
MSE: function(target, output)
|
||||
{
|
||||
var mse = 0;
|
||||
for (var i in output)
|
||||
mse += Math.pow(target[i] - output[i], 2);
|
||||
return mse / output.length;
|
||||
},
|
||||
BINARY: function(target, output){
|
||||
var misses = 0;
|
||||
for (var i in output)
|
||||
misses += Math.round(target[i] * 2) != Math.round(output[i] * 2);
|
||||
return misses;
|
||||
}
|
||||
}
|
||||
|
||||
export module Trainer {
|
||||
// Built-in cost functions
|
||||
|
||||
export interface ITrainerCostFn {
|
||||
(target, output): number;
|
||||
}
|
||||
|
||||
export var cost = {
|
||||
// Eq. 9
|
||||
CROSS_ENTROPY: function(target, output) {
|
||||
var crossentropy = 0;
|
||||
for (var i in output)
|
||||
crossentropy -= (target[i] * Math.log(output[i] + 1e-15)) + ((1 - target[i]) * Math.log((1 + 1e-15) - output[i])); // +1e-15 is a tiny push away to avoid Math.log(0)
|
||||
return crossentropy;
|
||||
},
|
||||
CROSS_ENTROPY_SOFTMAX: function(target, output) {
|
||||
var crossentropy = 0;
|
||||
for (var i in output)
|
||||
crossentropy -= target[i] * Math.log(output[i] + 1e-15);
|
||||
return crossentropy;
|
||||
},
|
||||
MSE: function(target, output) {
|
||||
var mse = 0;
|
||||
for (var i in output)
|
||||
mse += Math.pow(target[i] - output[i], 2);
|
||||
return mse / output.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
import Synaptic = require('./synaptic');
|
||||
|
||||
export class Utils {
|
||||
|
||||
static transformationMatrixCache: { [index: number] : Float64Array[] } = {};
|
||||
|
||||
static softMax<T extends Synaptic.INumericArray>(outputArray: T): T {
|
||||
// for all i ∈ array
|
||||
// sum = ∑ array[n]^e
|
||||
// i = î^e / sum
|
||||
// where the result ∑ array[0..n] = 1
|
||||
|
||||
if (!outputArray.length) return outputArray;
|
||||
|
||||
var sum = 0;
|
||||
|
||||
var Amax = outputArray[0];
|
||||
for (var i = 0; i < outputArray.length; i++) {
|
||||
if(outputArray[i] < Amax) Amax = outputArray[i];
|
||||
}
|
||||
|
||||
|
||||
// sum = ∑ array[n]^e
|
||||
for (var i = 0; i < outputArray.length; i++) {
|
||||
outputArray[i] = Math.exp(outputArray[i] - Amax);
|
||||
sum += outputArray[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < outputArray.length; i++) outputArray[i] /= sum;
|
||||
|
||||
return outputArray;
|
||||
}
|
||||
|
||||
static softMaxDerivative<T extends Synaptic.INumericArray>(outputArray: T): T {
|
||||
// http://sysmagazine.com/posts/155235/
|
||||
if (!outputArray.length) return outputArray;
|
||||
|
||||
var sum = 0;
|
||||
|
||||
// sum = ∑ array[n]^e
|
||||
for (var i = 0; i < outputArray.length; i++) {
|
||||
outputArray[i] = Math.exp(outputArray[i]);
|
||||
sum += outputArray[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < outputArray.length; i++) {
|
||||
var t = outputArray[i] /= sum;
|
||||
|
||||
outputArray[i] = t * (1 - t);
|
||||
}
|
||||
|
||||
return outputArray;
|
||||
}
|
||||
|
||||
static softMaxReinforcement<T extends Synaptic.INumericArray>(array: T, temperature = 1): T {
|
||||
// Reinforcement learning
|
||||
|
||||
if (!array.length) return array;
|
||||
|
||||
temperature = temperature || 1;
|
||||
|
||||
var sum = 0;
|
||||
|
||||
// sum = ∑ array[n]^e
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
array[i] = Math.exp(array[i] / temperature);
|
||||
sum += array[i];
|
||||
}
|
||||
|
||||
if (sum != 0) {
|
||||
for (var i = 0; i < array.length; i++) array[i] /= sum;
|
||||
} else {
|
||||
var div = 1 / array.length;
|
||||
for (var i = 0; i < array.length; i++) array[i] = div;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
static getCosineSimilarity(arrayA: Synaptic.INumericArray, arrayB: Synaptic.INumericArray): number {
|
||||
// http://en.wikipedia.org/wiki/Cosine_similarity
|
||||
var dotPr = 0;
|
||||
|
||||
var acumA = 0, acumB = 0;
|
||||
|
||||
for (var i = 0; i < arrayA.length; i++) {
|
||||
dotPr += arrayA[i] * arrayB[i];
|
||||
acumA += arrayA[i] * arrayA[i];
|
||||
acumB += arrayB[i] * arrayB[i];
|
||||
}
|
||||
|
||||
return dotPr / (Math.sqrt(acumA) * Math.sqrt(acumB) + .00005);
|
||||
}
|
||||
|
||||
static interpolateArray(output_inputA: Synaptic.INumericArray, inputB: Synaptic.INumericArray, g) {
|
||||
// 3.3.2 focus by location (7)
|
||||
var gInverted = 1 - g;
|
||||
for (var i = 0; i < output_inputA.length; i++)
|
||||
output_inputA[i] = output_inputA[i] * g + gInverted * inputB[i];
|
||||
return output_inputA;
|
||||
}
|
||||
|
||||
// w_sharpWn
|
||||
static sharpArray(output: Synaptic.INumericArray, wn: Synaptic.INumericArray, Y: number) {
|
||||
// 3.3.2 (9)
|
||||
var sum = 0;
|
||||
|
||||
// ∀ a ∈ wn → a = a^Y
|
||||
// sum = ∑ a^Y
|
||||
|
||||
for (var i = 0; i < wn.length; i++) {
|
||||
wn[i] = Math.pow(wn[i], Y);
|
||||
sum += wn[i];
|
||||
}
|
||||
|
||||
// ∀ a ∈ wn → a = a^Y / sum
|
||||
if (sum != 0) {
|
||||
for (var i = 0; i < wn.length; i++) output[i] = wn[i] / sum;
|
||||
} else {
|
||||
var div = 1 / wn.length;
|
||||
for (var i = 0; i < wn.length; i++) output[i] = div;
|
||||
}
|
||||
}
|
||||
|
||||
//wn_shift
|
||||
static scalarShifting(wg: Synaptic.INumericArray, shiftScalar: number) {
|
||||
// w~ 3.3.2 (8)
|
||||
var shiftings = new Float64Array(wg.length);
|
||||
var wn = new Float64Array(wg.length);
|
||||
|
||||
var intPart = shiftScalar | 0;
|
||||
var decimalPart = shiftScalar - intPart;
|
||||
|
||||
shiftings[intPart % shiftings.length] = 1 - decimalPart;
|
||||
shiftings[(intPart + 1) % shiftings.length] = decimalPart;
|
||||
|
||||
|
||||
for (var i = 0; i < wn.length; i++) {
|
||||
var acum = 0;
|
||||
for (var j = 0; j < wn.length; j++) {
|
||||
if ((i - j) < 0)
|
||||
acum += wg[j] * shiftings[shiftings.length - Math.abs(i - j)];
|
||||
else
|
||||
acum += wg[j] * shiftings[(i - j) % shiftings.length];
|
||||
}
|
||||
wn[i] = acum;
|
||||
}
|
||||
|
||||
return wn;
|
||||
}
|
||||
|
||||
static normalizeShift(shift: Float64Array) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < shift.length; i++) {
|
||||
sum += shift[i];
|
||||
}
|
||||
for (var j = 0; j < shift.length; j++) {
|
||||
shift[j] /= sum;
|
||||
}
|
||||
}
|
||||
|
||||
static vectorInvertedShifting(wg: Float64Array, shiftings: Synaptic.INumericArray) {
|
||||
// w~ 3.3.2 (8)
|
||||
|
||||
|
||||
var ret = new Float64Array(wg.length);
|
||||
|
||||
var corrimientoIndex = -((shiftings.length - 1) / 2) | 0;
|
||||
|
||||
var circulantMatrix = Utils.transformationMatrixCache[wg.length] || (Utils.transformationMatrixCache[wg.length] = Utils.buildCirculantMatrix(wg.length));
|
||||
|
||||
for (var i = 0; i < wg.length; i++) {
|
||||
for (var x = 0; x < wg.length; x++) {
|
||||
var tmp = 0;
|
||||
|
||||
for (var shift = 0; shift < shiftings.length; shift++) {
|
||||
|
||||
var matRow = i - x + corrimientoIndex + shift;
|
||||
|
||||
while (matRow < 0)
|
||||
matRow += wg.length;
|
||||
|
||||
matRow %= wg.length;
|
||||
|
||||
tmp += wg[circulantMatrix[x][matRow]] * shiftings[shift];
|
||||
}
|
||||
|
||||
ret[i] = tmp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
wg.set(ret);
|
||||
}
|
||||
|
||||
static initRandomSoftmaxArray(array: Float64Array): void {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
array[i] = Math.random();
|
||||
}
|
||||
|
||||
Utils.softMax(array);
|
||||
}
|
||||
|
||||
static buildCirculantMatrix(length: number, offset: number = 0): Float64Array[] {
|
||||
var ret = [];
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var arr = new Float64Array(length);
|
||||
ret.push(arr);
|
||||
for (var n = 0; n < length; n++) {
|
||||
arr[n] = ((i + n) % length);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
Test using mocha, from root directory:
|
||||
Test using gulp, from root directory:
|
||||
|
||||
`mocha test`
|
||||
`gulp test`
|
||||
|
||||
To test the web version, start a web server at the root dir of this repo, then use your OS browser.
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
global.synaptic = require('../dist/synaptic');
|
||||
@@ -1 +0,0 @@
|
||||
global.synaptic = require('../src/synaptic');
|
||||
@@ -1 +0,0 @@
|
||||
[^_]*.js
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
// import
|
||||
|
||||
var assert = require('assert'),
|
||||
Utils = require('../dist/src/utils').Utils;
|
||||
|
||||
|
||||
// utils
|
||||
|
||||
var noRepeat = function(range, avoid) {
|
||||
var number = Math.random() * range | 0;
|
||||
var used = false;
|
||||
for (var i in avoid)
|
||||
if (number == avoid[i])
|
||||
used = true;
|
||||
return used ? noRepeat(range, avoid) : number;
|
||||
};
|
||||
|
||||
var equal = function(prediction, output) {
|
||||
for (var i in prediction)
|
||||
if (Math.round(prediction[i]) != output[i])
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
var generateRandomArray = function(size){
|
||||
var array = [];
|
||||
for (var j = 0; j < size; j++)
|
||||
array.push(Math.random() + .5 | 0);
|
||||
return array;
|
||||
}
|
||||
|
||||
var sumAll = function(arr){
|
||||
var tmp = 0;
|
||||
for (var index = 0; index < arr.length; index++) {
|
||||
tmp += arr[index];
|
||||
}
|
||||
return parseFloat(tmp.toFixed(3));
|
||||
}
|
||||
|
||||
// specs
|
||||
|
||||
|
||||
describe("NTM Utils", function() {
|
||||
|
||||
it('Test equality arrays', function(){
|
||||
var fixedArray = new Float64Array(5);
|
||||
|
||||
fixedArray.set([0,0,0,0,1]);
|
||||
|
||||
assert.deepEqual(fixedArray, [0,0,0,0,1], 'Float64Array vs array');
|
||||
});
|
||||
|
||||
it('Softmax array', function(){
|
||||
var fixedArray = new Float64Array(5);
|
||||
|
||||
fixedArray.set([0,0,0,0,1]);
|
||||
|
||||
assert.equal(sumAll(Utils.softMax(fixedArray)), 1, 'Fixed array equals 1');
|
||||
|
||||
|
||||
fixedArray.set([0,0,0,0,0]);
|
||||
|
||||
assert.equal(sumAll(Utils.softMax(fixedArray)), 1, 'Fixed zero array equals 1');
|
||||
|
||||
fixedArray.set([Math.random()* 3,Math.random() * 6,Math.random() * 4,Math.random() * 10,Math.random()]);
|
||||
|
||||
assert.equal(sumAll(Utils.softMax(fixedArray)), 1, 'Random positive array equals 1');
|
||||
|
||||
fixedArray.set([Math.random()* -9,Math.random() * 6,Math.random() * -4,Math.random() * 10,Math.random()]);
|
||||
|
||||
assert.equal(sumAll(Utils.softMax(fixedArray)), 1, 'Random signed array equals 1');
|
||||
});
|
||||
|
||||
it('Shifting', function(){
|
||||
var fixedArray = new Float64Array(5);
|
||||
|
||||
fixedArray.set([0,0,0,0,1]);
|
||||
Utils.vectorInvertedShifting(fixedArray, [0, 1, 0]);
|
||||
assert.deepEqual(fixedArray, [0,0,0,0,1], 'Unchanged shiftings');
|
||||
|
||||
fixedArray.set([0,0,0,0,1]);
|
||||
Utils.vectorInvertedShifting(fixedArray, [0, 0, 0]);
|
||||
assert.deepEqual(fixedArray, [0,0,0,0,0], 'All zeros in shift');
|
||||
|
||||
fixedArray.set([0,0,0,0,1]);
|
||||
Utils.vectorInvertedShifting(fixedArray, [1, 0, 0]);
|
||||
assert.deepEqual(fixedArray, [1,0,0,0,0], 'Plus one shift');
|
||||
|
||||
fixedArray.set([0,0,0,0,1]);
|
||||
Utils.vectorInvertedShifting(fixedArray, [0, 0, 1]);
|
||||
assert.deepEqual(fixedArray, [0,0,0,1,0], 'Minus one shift');
|
||||
|
||||
fixedArray.set([1,0,0,0,2]);
|
||||
Utils.vectorInvertedShifting(fixedArray, [1, 0, 0]);
|
||||
assert.deepEqual(fixedArray, [2,1,0,0,0], 'Plus one shift, two values');
|
||||
|
||||
fixedArray.set([1,1,1,1,1]);
|
||||
Utils.vectorInvertedShifting(fixedArray, [1, 1, 1]);
|
||||
assert.deepEqual(fixedArray, [3,3,3,3,3], 'Mixing three values');
|
||||
|
||||
fixedArray.set([1,1,1,1,1]);
|
||||
Utils.vectorInvertedShifting(fixedArray, [1, 1, 0]);
|
||||
assert.deepEqual(fixedArray, [2,2,2,2,2], 'Mixing two values');
|
||||
|
||||
fixedArray.set([1,1,1,1,1]);
|
||||
Utils.vectorInvertedShifting(fixedArray, [.5, 1, 0.5]);
|
||||
assert.deepEqual(fixedArray, [2,2,2,2,2], 'Mixing threeº values');
|
||||
|
||||
|
||||
fixedArray.set([1,1,1,1,1]);
|
||||
Utils.vectorInvertedShifting(fixedArray, [.5, .5, 1, .5, .5]);
|
||||
assert.deepEqual(fixedArray, [3,3,3,3,3], 'Mixing five values');
|
||||
|
||||
fixedArray.set([1,2,1,3,1]);
|
||||
Utils.vectorInvertedShifting(fixedArray, [.5, 0, .5]);
|
||||
assert.deepEqual(fixedArray, [1.5,1,2.5,1,2], 'Mixing five values');
|
||||
|
||||
});
|
||||
});
|
||||
+216
-282
@@ -1,58 +1,41 @@
|
||||
// import
|
||||
var chai = require('chai');
|
||||
chai.use(require('chai-stats'));
|
||||
var assert = chai.assert;
|
||||
|
||||
var Perceptron = synaptic.Architect.Perceptron;
|
||||
var LSTM = synaptic.Architect.LSTM;
|
||||
var Layer = synaptic.Layer;
|
||||
var Network = synaptic.Network;
|
||||
var Trainer = synaptic.Trainer;
|
||||
var assert = require('assert'),
|
||||
synaptic = require('../dist/src/synaptic'),
|
||||
softMaxLayer = require('../dist/src/softmaxLayer');
|
||||
|
||||
|
||||
|
||||
var learningRate = .5;
|
||||
var Perceptron = synaptic.Architect.Perceptron,
|
||||
LSTM = synaptic.Architect.LSTM,
|
||||
Layer = synaptic.Layer,
|
||||
Network = synaptic.Network,
|
||||
Trainer = synaptic.Trainer;
|
||||
|
||||
|
||||
// utils
|
||||
|
||||
function noRepeat(range, avoid) {
|
||||
var noRepeat = function (range, avoid) {
|
||||
var number = Math.random() * range | 0;
|
||||
for (var i in avoid) {
|
||||
if (number == avoid[i]) {
|
||||
return noRepeat(range, avoid);
|
||||
}
|
||||
}
|
||||
return number;
|
||||
}
|
||||
var used = false;
|
||||
for (var i in avoid)
|
||||
if (number == avoid[i])
|
||||
used = true;
|
||||
return used ? noRepeat(range, avoid) : number;
|
||||
};
|
||||
|
||||
function equal(prediction, output) {
|
||||
var equal = function (prediction, output) {
|
||||
for (var i in prediction)
|
||||
if (Math.round(prediction[i]) != output[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
function generateRandomArray(size) {
|
||||
var generateRandomArray = function (size) {
|
||||
var array = [];
|
||||
for (var j = 0; j < size; j++)
|
||||
array.push(Math.random() + .5 | 0);
|
||||
return array;
|
||||
}
|
||||
|
||||
function calculateMse(a, b) {
|
||||
var mse = 0;
|
||||
for (var k in a)
|
||||
mse += Math.pow(a[k] - b[k], 2);
|
||||
mse /= a.length;
|
||||
|
||||
return mse;
|
||||
}
|
||||
|
||||
function equalWithError(output, expected, error) {
|
||||
return Math.abs(output - expected) <= error;
|
||||
}
|
||||
|
||||
// specs
|
||||
|
||||
describe('Basic Neural Network', function () {
|
||||
@@ -75,15 +58,15 @@ describe('Basic Neural Network', function () {
|
||||
input: [0, 0],
|
||||
output: [0]
|
||||
}, {
|
||||
input: [0, 1],
|
||||
output: [0]
|
||||
}, {
|
||||
input: [1, 0],
|
||||
output: [0]
|
||||
}, {
|
||||
input: [1, 1],
|
||||
output: [1]
|
||||
}];
|
||||
input: [0, 1],
|
||||
output: [0]
|
||||
}, {
|
||||
input: [1, 0],
|
||||
output: [0]
|
||||
}, {
|
||||
input: [1, 1],
|
||||
output: [1]
|
||||
}];
|
||||
|
||||
trainer.train(trainingSet, {
|
||||
iterations: 1000,
|
||||
@@ -96,7 +79,7 @@ describe('Basic Neural Network', function () {
|
||||
var test01 = Math.round(network.activate([0, 1]));
|
||||
assert.equal(test01, 0, "[0,1] did not output 0");
|
||||
|
||||
var test10 = Math.round(network.activate([1, 0]));
|
||||
var test10 = Math.round(network.activate([0, 1]));
|
||||
assert.equal(test10, 0, "[1,0] did not output 0");
|
||||
|
||||
var test11 = Math.round(network.activate([1, 1]));
|
||||
@@ -121,15 +104,15 @@ describe('Basic Neural Network', function () {
|
||||
input: [0, 0],
|
||||
output: [0]
|
||||
}, {
|
||||
input: [0, 1],
|
||||
output: [1]
|
||||
}, {
|
||||
input: [1, 0],
|
||||
output: [1]
|
||||
}, {
|
||||
input: [1, 1],
|
||||
output: [1]
|
||||
}];
|
||||
input: [0, 1],
|
||||
output: [1]
|
||||
}, {
|
||||
input: [1, 0],
|
||||
output: [1]
|
||||
}, {
|
||||
input: [1, 1],
|
||||
output: [1]
|
||||
}];
|
||||
|
||||
trainer.train(trainingSet, {
|
||||
iterations: 1000,
|
||||
@@ -142,7 +125,7 @@ describe('Basic Neural Network', function () {
|
||||
var test01 = Math.round(network.activate([0, 1]));
|
||||
assert.equal(test01, 1, "[0,1] did not output 1");
|
||||
|
||||
var test10 = Math.round(network.activate([1, 0]));
|
||||
var test10 = Math.round(network.activate([0, 1]));
|
||||
assert.equal(test10, 1, "[1,0] did not output 1");
|
||||
|
||||
var test11 = Math.round(network.activate([1, 1]));
|
||||
@@ -152,7 +135,8 @@ describe('Basic Neural Network', function () {
|
||||
it("trains a NOT gate", function () {
|
||||
|
||||
var inputLayer = new Layer(1),
|
||||
outputLayer = new Layer(1);
|
||||
outputLayer = new Layer(1),
|
||||
network;
|
||||
|
||||
inputLayer.project(outputLayer);
|
||||
|
||||
@@ -166,9 +150,9 @@ describe('Basic Neural Network', function () {
|
||||
input: [0],
|
||||
output: [1]
|
||||
}, {
|
||||
input: [1],
|
||||
output: [0]
|
||||
}];
|
||||
input: [1],
|
||||
output: [0]
|
||||
}];
|
||||
|
||||
trainer.train(trainingSet, {
|
||||
iterations: 1000,
|
||||
@@ -188,112 +172,100 @@ describe("Perceptron - XOR", function () {
|
||||
var perceptron = new Perceptron(2, 3, 1);
|
||||
perceptron.trainer.XOR();
|
||||
|
||||
it("should return near-0 value on [0,0]", function () {
|
||||
assert.isAtMost(perceptron.activate([0, 0]), .49, "[0,0] did not output 0");
|
||||
var test00 = Math.round(perceptron.activate([0, 0]));
|
||||
it("input: [0,0] output: " + test00, function () {
|
||||
|
||||
assert.equal(test00, 0, "[0,0] did not output 0");
|
||||
});
|
||||
|
||||
it("should return near-1 value on [0,1]", function () {
|
||||
assert.isAtLeast(perceptron.activate([0, 1]), .51, "[0,1] did not output 1");
|
||||
var test01 = Math.round(perceptron.activate([0, 1]));
|
||||
it("input: [0,1] output: " + test01, function () {
|
||||
|
||||
assert.equal(test01, 1, "[0,1] did not output 1");
|
||||
});
|
||||
|
||||
it("should return near-1 value on [1,0]", function () {
|
||||
assert.isAtLeast(perceptron.activate([1, 0]), .51, "[1,0] did not output 1");
|
||||
var test10 = Math.round(perceptron.activate([1, 0]));
|
||||
it("input: [1,0] output: " + test10, function () {
|
||||
|
||||
assert.equal(test10, 1, "[1,0] did not output 1");
|
||||
});
|
||||
|
||||
it("should return near-0 value on [1,1]", function () {
|
||||
assert.isAtMost(perceptron.activate([1, 1]), .49, "[1,1] did not output 0");
|
||||
var test11 = Math.round(perceptron.activate([1, 1]));
|
||||
it("input: [1,1] output: " + test11, function () {
|
||||
|
||||
assert.equal(test11, 0, "[1,1] did not output 0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Perceptron - SIN", function () {
|
||||
var mySin = function (x) {
|
||||
return (Math.sin(x) + 1) / 2;
|
||||
};
|
||||
describe("Perceptron - XOR Softmax", function () {
|
||||
|
||||
var sinNetwork = new Perceptron(1, 12, 1);
|
||||
var trainingSet = [];
|
||||
|
||||
while (trainingSet.length < 800) {
|
||||
var inputValue = Math.random() * Math.PI * 2;
|
||||
trainingSet.push({
|
||||
input: [inputValue],
|
||||
output: [mySin(inputValue)]
|
||||
});
|
||||
}
|
||||
|
||||
var results = sinNetwork.trainer.train(trainingSet, {
|
||||
iterations: 2000,
|
||||
log: false,
|
||||
error: 1e-6,
|
||||
cost: Trainer.cost.MSE,
|
||||
var input = new synaptic.Layer(2);
|
||||
var hidden = new softMaxLayer.SoftMaxLayer(5);
|
||||
var output = new softMaxLayer.SoftMaxLayer(2);
|
||||
|
||||
// generate hidden layers
|
||||
softMaxLayer.SoftMaxLayer.NormalizeConnectionWeights(input.project(hidden));
|
||||
softMaxLayer.SoftMaxLayer.NormalizeConnectionWeights(hidden.project(output));
|
||||
|
||||
// set layers of the neural network
|
||||
|
||||
var perceptron = new synaptic.Network({
|
||||
input: input,
|
||||
hidden: [hidden],
|
||||
output: output
|
||||
});
|
||||
|
||||
// trainer for the network
|
||||
perceptron.trainer = new synaptic.Trainer(perceptron);
|
||||
perceptron.optimized = false;
|
||||
|
||||
[0, .5 * Math.PI, 2]
|
||||
.forEach(function (x) {
|
||||
var y = mySin(x);
|
||||
it("should return value around " + y + " when [" + x + "] is on input", function () {
|
||||
// near scalability: abs(expected-actual) < 0.5 * 10**(-decimal)
|
||||
// 0.5 * Math.pow(10, -.15) => 0.35397289219206896
|
||||
assert.almostEqual(sinNetwork.activate([x])[0], y, .15);
|
||||
});
|
||||
});
|
||||
|
||||
var errorResult = results.error;
|
||||
it("Sin error: " + errorResult, function () {
|
||||
assert.isAtMost(errorResult, .001, "Sin error not less than or equal to desired error.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Perceptron - SIN - CrossValidate", function () {
|
||||
|
||||
var mySin = function (x) {
|
||||
return (Math.sin(x) + 1) / 2;
|
||||
};
|
||||
|
||||
var sinNetwork = new Perceptron(1, 12, 1);
|
||||
|
||||
var trainingSet = Array.apply(null, Array(800)).map(function () {
|
||||
var inputValue = Math.random() * Math.PI * 2;
|
||||
return {
|
||||
input: [inputValue],
|
||||
output: [mySin(inputValue)]
|
||||
};
|
||||
});
|
||||
|
||||
var results = sinNetwork.trainer.train(trainingSet, {
|
||||
iterations: 2000,
|
||||
log: false,
|
||||
error: 1e-6,
|
||||
cost: Trainer.cost.MSE,
|
||||
crossValidate: {
|
||||
testSize: .3,
|
||||
testError: 1e-6
|
||||
var trainingSet = [
|
||||
{
|
||||
input: [0, 0],
|
||||
output: [0, 1]
|
||||
}, {
|
||||
input: [1, 1],
|
||||
output: [0, 1]
|
||||
},
|
||||
{
|
||||
input: [0, 1],
|
||||
output: [1, 0]
|
||||
}, {
|
||||
input: [1, 0],
|
||||
output: [1, 0]
|
||||
}
|
||||
];
|
||||
|
||||
perceptron.trainer.train(trainingSet, {
|
||||
log: 5000,
|
||||
cost: synaptic.Trainer.cost.CROSS_ENTROPY_SOFTMAX,
|
||||
error: 0.001,
|
||||
iterations: 100000,
|
||||
rate: 0.1
|
||||
});
|
||||
|
||||
var test0 = sinNetwork.activate([0])[0];
|
||||
var expected0 = mySin(0);
|
||||
it("input: [0] output: " + test0 + ", expected: " + expected0, function () {
|
||||
assert.isAtMost(Math.abs(test0 - expected0), .035, "[0] did not output " + expected0);
|
||||
var test00 = Math.round(perceptron.activate([0, 0])[0]);
|
||||
|
||||
it("input: [0,0] output: " + test00, function () {
|
||||
assert.equal(test00, 0, "[0,0] did not output 0");
|
||||
});
|
||||
|
||||
var test05PI = sinNetwork.activate([.5 * Math.PI])[0];
|
||||
var expected05PI = mySin(.5 * Math.PI);
|
||||
it("input: [0.5*Math.PI] output: " + test05PI + ", expected: " + expected05PI, function () {
|
||||
assert.isAtMost(Math.abs(test05PI - expected05PI), .035, "[0.5*Math.PI] did not output " + expected05PI);
|
||||
var test01 = Math.round(perceptron.activate([0, 1])[0]);
|
||||
it("input: [0,1] output: " + test01, function () {
|
||||
assert.equal(test01, 1, "[0,1] did not output 1");
|
||||
});
|
||||
|
||||
var test2 = sinNetwork.activate([2])[0];
|
||||
var expected2 = mySin(2);
|
||||
it("input: [2] output: " + test2 + ", expected: " + expected2, function () {
|
||||
var eq = equalWithError(test2, expected2, .035);
|
||||
assert.equal(eq, true, "[2] did not output " + expected2);
|
||||
var test10 = Math.round(perceptron.activate([1, 0])[0]);
|
||||
it("input: [1,0] output: " + test10, function () {
|
||||
assert.equal(test10, 1, "[1,0] did not output 1");
|
||||
});
|
||||
|
||||
var errorResult = results.error;
|
||||
it("CrossValidation error: " + errorResult, function () {
|
||||
var lessThanOrEqualError = errorResult <= .001;
|
||||
assert.equal(lessThanOrEqualError, true, "CrossValidation error not less than or equal to desired error.");
|
||||
var test11 = Math.round(perceptron.activate([1, 1])[0]);
|
||||
it("input: [1,1] output: " + test11, function () {
|
||||
assert.equal(test11, 0, "[1,1] did not output 0");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -394,107 +366,113 @@ describe("LSTM - Discrete Sequence Recall", function () {
|
||||
}
|
||||
});
|
||||
|
||||
describe("LSTM - Timing Task", function () {
|
||||
var network = new LSTM(2, 7, 1);
|
||||
var result = network.trainer.timingTask({
|
||||
log: false,
|
||||
trainSamples: 4000,
|
||||
testSamples: 500
|
||||
});
|
||||
|
||||
it("should complete the training in less than 200 iterations", function () {
|
||||
assert(result.train.iterations <= 200);
|
||||
});
|
||||
|
||||
it("should pass the test with an error smaller than 0.05", function () {
|
||||
assert(result.test.error < .05);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Optimized and Unoptimized Networks Equivalency", function () {
|
||||
var optimized = new Perceptron(10, 15, 5);
|
||||
|
||||
var optimized;
|
||||
var unoptimized;
|
||||
beforeEach(function () {
|
||||
optimized = new LSTM(2, 1, 1);
|
||||
unoptimized = optimized.clone();
|
||||
unoptimized.setOptimize(false);
|
||||
});
|
||||
var unoptimized = optimized.clone();
|
||||
unoptimized.setOptimize(false);
|
||||
|
||||
var learningRate = .5;
|
||||
var iterations = 1000;
|
||||
|
||||
it('should produce the same output for both networks', function () {
|
||||
this.timeout(30000);
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
var input = generateRandomArray(2);
|
||||
var target = generateRandomArray(1);
|
||||
optimized.activate(input);
|
||||
unoptimized.activate(input);
|
||||
optimized.propagate(learningRate, target);
|
||||
unoptimized.propagate(learningRate, target);
|
||||
}
|
||||
var mse = calculateMse(optimized.activate(input), unoptimized.activate(input));
|
||||
assert.isAtMost(mse, 1e-9, 'output should be same for both networks after ' + i + ' iterations');
|
||||
});
|
||||
for (var i = 1; i <= iterations; i++) {
|
||||
//random input
|
||||
var input = generateRandomArray(10);
|
||||
|
||||
// activate networks
|
||||
var output1 = optimized.activate(input);
|
||||
var output2 = unoptimized.activate(input);
|
||||
|
||||
if (i % 100 == 0)
|
||||
it(' same output for both networks after ' + i + ' iterations', function () {
|
||||
var diff = false;
|
||||
for (var k in output1)
|
||||
if (output1[k] - output2[k] != 0)
|
||||
diff = true;
|
||||
assert(!diff);
|
||||
});
|
||||
|
||||
// random target
|
||||
var target = generateRandomArray(5);
|
||||
|
||||
// propagate networks
|
||||
optimized.propagate(learningRate, target);
|
||||
unoptimized.propagate(learningRate, target);
|
||||
}
|
||||
});
|
||||
|
||||
describe("toJSON/fromJSON Networks Equivalency", function () {
|
||||
var original;
|
||||
var imported;
|
||||
beforeEach(function () {
|
||||
original = new LSTM(10, 5, 5);
|
||||
imported = Network.fromJSON(original.toJSON());
|
||||
});
|
||||
var original = new Perceptron(10, 15, 5);
|
||||
|
||||
it('should produce the same output for both networks', function () {
|
||||
this.timeout(30000);
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
var input = generateRandomArray(10);
|
||||
var output1 = original.activate(input);
|
||||
var output2 = imported.activate(input);
|
||||
var exported = original.toJSON();
|
||||
var imported = Network.fromJSON(exported);
|
||||
|
||||
var target = generateRandomArray(5);
|
||||
var learningRate = .5;
|
||||
var iterations = 1000;
|
||||
|
||||
// propagate networks
|
||||
original.propagate(learningRate, target);
|
||||
imported.propagate(learningRate, target);
|
||||
for (var i = 1; i <= iterations; i++) {
|
||||
//random input
|
||||
var input = generateRandomArray(10);
|
||||
|
||||
assert.isAtMost(calculateMse(output1, output2), 1e-10,
|
||||
'output should be same for both networks after ' + i + ' iterations');
|
||||
}
|
||||
});
|
||||
// activate networks
|
||||
var output1 = original.activate(input);
|
||||
var output2 = imported.activate(input);
|
||||
|
||||
if (i % 100 == 0)
|
||||
it(' same output for both networks after ' + i + ' iterations', function () {
|
||||
var diff = false;
|
||||
for (var k in output1)
|
||||
if (output1[k] - output2[k] != 0)
|
||||
diff = true;
|
||||
assert(!diff);
|
||||
});
|
||||
|
||||
// random target
|
||||
var target = generateRandomArray(5);
|
||||
|
||||
// propagate networks
|
||||
original.propagate(learningRate, target);
|
||||
imported.propagate(learningRate, target);
|
||||
}
|
||||
});
|
||||
|
||||
describe("Cloned Networks Equivalency", function () {
|
||||
var original;
|
||||
var cloned;
|
||||
beforeEach(function () {
|
||||
original = new LSTM(10, 5, 5);
|
||||
cloned = Network.fromJSON(original.toJSON());
|
||||
});
|
||||
var original = new Perceptron(10, 15, 5);
|
||||
var cloned = original.clone();
|
||||
|
||||
it('should produce the same output for both networks', function () {
|
||||
this.timeout(30000);
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
var input = generateRandomArray(10);
|
||||
var output1 = original.activate(input);
|
||||
var output2 = cloned.activate(input);
|
||||
var learningRate = .5;
|
||||
var iterations = 1000;
|
||||
|
||||
var target = generateRandomArray(5);
|
||||
for (var i = 1; i <= iterations; i++) {
|
||||
//random input
|
||||
var input = generateRandomArray(10);
|
||||
|
||||
// propagate networks
|
||||
original.propagate(learningRate, target);
|
||||
cloned.propagate(learningRate, target);
|
||||
// activate networks
|
||||
var output1 = original.activate(input);
|
||||
var output2 = cloned.activate(input);
|
||||
|
||||
assert.isAtMost(calculateMse(output1, output2), 1e-10,
|
||||
'output should be same for both networks after ' + i + ' iterations');
|
||||
}
|
||||
});
|
||||
if (i % 100 == 0)
|
||||
it(' same output for both networks after ' + i + ' iterations', function () {
|
||||
var diff = false;
|
||||
for (var k in output1)
|
||||
if (output1[k] - output2[k] != 0)
|
||||
diff = true;
|
||||
assert(!diff);
|
||||
});
|
||||
|
||||
// random target
|
||||
var target = generateRandomArray(5);
|
||||
|
||||
// propagate networks
|
||||
original.propagate(learningRate, target);
|
||||
cloned.propagate(learningRate, target);
|
||||
}
|
||||
});
|
||||
|
||||
describe("Scheduled Tasks", function () {
|
||||
describe("Manual Override", function () {
|
||||
var perceptron = new Perceptron(2, 3, 1);
|
||||
|
||||
it('should stop training at 3000 iterations', function () {
|
||||
it('iterations ended at full 3000', function () {
|
||||
var final_stats = perceptron.trainer.XOR({
|
||||
iterations: 3000,
|
||||
rate: 0.000001,
|
||||
@@ -502,14 +480,16 @@ describe("Scheduled Tasks", function () {
|
||||
schedule: {
|
||||
every: 1000,
|
||||
do: function (data) {
|
||||
return data.iterations == 20000;
|
||||
if (data.iterations == 20000) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
assert.equal(final_stats.iterations, 3000)
|
||||
});
|
||||
|
||||
it('should abort the training at 2000 iterations', function () {
|
||||
it('iterations ended at 2000, not full 3000', function () {
|
||||
var final_stats = perceptron.trainer.XOR({
|
||||
iterations: 3000,
|
||||
rate: 0.000001,
|
||||
@@ -517,86 +497,40 @@ describe("Scheduled Tasks", function () {
|
||||
schedule: {
|
||||
every: 1000,
|
||||
do: function (data) {
|
||||
return data.iterations == 2000;
|
||||
if (data.iterations == 2000) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
assert.equal(final_stats.iterations, 2000)
|
||||
});
|
||||
|
||||
it('should work even if schedule.do() returns no value', function () {
|
||||
it('training works even when schedule() has no return value', function () {
|
||||
var final_stats = perceptron.trainer.XOR({
|
||||
iterations: 3000,
|
||||
rate: 0.000001,
|
||||
error: 0.000001,
|
||||
schedule: {
|
||||
every: 1000,
|
||||
do: function (data) {}
|
||||
do: function (data) { }
|
||||
}
|
||||
});
|
||||
assert.equal(final_stats.iterations, 3000)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("Rate Callback Check", function () {
|
||||
var perceptron = new Perceptron(2, 3, 1);
|
||||
|
||||
it('should switch rate from 0.01 to 0.005 after 1000 iterations', function () {
|
||||
it('using depreciated customLog still works', function () {
|
||||
var counter = 0;
|
||||
var final_stats = perceptron.trainer.XOR({
|
||||
iterations: 2000,
|
||||
rate: function (iterations, error) {
|
||||
return iterations < 1000 ? 0.01 : 0.005
|
||||
},
|
||||
iterations: 3000,
|
||||
rate: 0.000001,
|
||||
error: 0.000001,
|
||||
schedule: {
|
||||
every: 1,
|
||||
do: function (data) {
|
||||
switch (data.iterations) {
|
||||
case 1:
|
||||
case 500:
|
||||
case 999:
|
||||
assert.equal(data.rate, 0.01);
|
||||
break;
|
||||
|
||||
case 1000:
|
||||
case 1500:
|
||||
case 2000:
|
||||
assert.equal(data.rate, 0.005);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Rate Array Check", function () {
|
||||
var perceptron = new Perceptron(2, 3, 1);
|
||||
|
||||
it('should switch rate from 0.01 to 0.005 after 1000 iterations', function () {
|
||||
var final_stats = perceptron.trainer.XOR({
|
||||
iterations: 2000,
|
||||
rate: [0.01, 0.005],
|
||||
error: 0.000001,
|
||||
schedule: {
|
||||
every: 1,
|
||||
do: function (data) {
|
||||
switch (data.iterations) {
|
||||
case 1:
|
||||
case 500:
|
||||
case 999:
|
||||
assert.equal(data.rate, 0.01);
|
||||
break;
|
||||
|
||||
case 1000:
|
||||
case 1500:
|
||||
case 2000:
|
||||
assert.equal(data.rate, 0.005);
|
||||
break;
|
||||
}
|
||||
}
|
||||
customLog: {
|
||||
every: 1000,
|
||||
do: function (data) { counter++; }
|
||||
}
|
||||
});
|
||||
assert.equal(counter, 3)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>test</title>
|
||||
<script src="../dist/synaptic.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
var Architect = synaptic.Architect;
|
||||
|
||||
var myPerceptron = new Architect.Perceptron(2,3,1);
|
||||
document.body.innerHTML += JSON.stringify(myPerceptron.trainer.XOR())+'<br>'
|
||||
document.body.innerHTML += JSON.stringify(myPerceptron.trainer.XOR())+'<br>'
|
||||
|
||||
if(document.body.innerHTML.length > 0){
|
||||
document.body.innerHTML += '<br>if you see this, then synaptic works'
|
||||
}
|
||||
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"preserveConstEnums": true,
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"target": "ES5",
|
||||
"outDir": "./"
|
||||
},
|
||||
"files": [
|
||||
"src/synaptic.ts",
|
||||
"src/squash.ts",
|
||||
"src/neuron.ts",
|
||||
"src/layer.ts",
|
||||
"src/softmaxLayer.ts",
|
||||
"src/network.ts",
|
||||
"src/trainer.ts",
|
||||
"src/utils.ts",
|
||||
"src/architect.ts",
|
||||
"src/architect/Perceptron.ts",
|
||||
"src/architect/LSTM.ts",
|
||||
"src/architect/Liquid.ts",
|
||||
"src/architect/Hopfield.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
var webpack = require('webpack')
|
||||
var license = require('./prebuild.js')
|
||||
module.exports = {
|
||||
context: __dirname,
|
||||
entry: {
|
||||
synaptic: './src/synaptic.js',
|
||||
'synaptic.min': './src/synaptic.js'
|
||||
},
|
||||
output: {
|
||||
path: 'dist',
|
||||
filename: '[name].js',
|
||||
},
|
||||
plugins: [
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new webpack.BannerPlugin(license())
|
||||
]
|
||||
}
|
||||
Referência em uma Nova Issue
Bloquear um usuário