diff --git a/Rakefile b/Rakefile index 114dbad..d24b403 100644 --- a/Rakefile +++ b/Rakefile @@ -78,7 +78,7 @@ namespace :test do MOCHA = "./node_modules/.bin/mocha" COVERALLS = "./node_modules/.bin/coveralls" - OPTS = "--compilers coffee:coffee-script/register --ui exports --bail" + OPTS = "--compilers coffee:coffee-script/register --ui exports --timeout 5000 --bail" desc "Run code coverage, mocha with Blanket.js" task :coverage do diff --git a/package.json b/package.json index de968a7..915b1f7 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ }, "dependencies": { "async": "~0.9.0", + "brain": "^0.7.0", + "chance": "^0.6.1", "d3": "~3.4.13", "d3-tip": "Caged/d3-tip", "director": "~1.2.3", diff --git a/test/neural.coffee b/test/neural.coffee new file mode 100644 index 0000000..d926811 --- /dev/null +++ b/test/neural.coffee @@ -0,0 +1,59 @@ +{ assert } = require 'chai' +Chance = require 'chance' +brain = require 'brain' +_ = require 'lodash' +d3 = require 'd3' + +chance = new Chance() + +module.exports = + + 'neural net - detect sine chart type': (done) -> + fns = + # A "rough" sine wave. + sine: (x) -> Math.sin chance.floating 'min': Math.max(x - 1, 0), 'max': x + 1 + # A linear function. + linear: (x) -> ( chance.floating 'min': Math.max(x - 1, 0), 'max': x + 1 ) + 2 + + # Generate sample data points. + samples = 10 + data = _.map [ 0 ... 10 ], (i) -> + fn = fns[ [ 'linear', 'sine' ][ sine = +(i < (samples / 2)) ] ] + points = ( fn(j) for j in [ 0 ... 100 ] ) + { points, sine } + + # Scale the data to just 6 points. + for chart in data then do (chart) -> + step = Math.round (len = chart.points.length) / 5 + chart.points = ( chart.points[ Math.min(i, len - 1) ] for i in _.range(0, len + step, step) ) + + # Scale the values for each chart. + for chart in data then do (chart) -> + scale = d3.scale.linear().range([ 0, 1 ]).domain([ d3.min(chart.points), d3.max(chart.points) ]) + chart.points = _.map chart.points, scale + + # Split the dataset. + training = data[ 1...9 ] + testing = [ data[0] ].concat data[9] + + net = new brain.NeuralNetwork() + + keys = [ 0, 0.2, 0.4, 0.6, 0.8, 1 ] + input = for chart in training then do (chart) -> + 'input': _.zipObject keys, chart.points + 'output': + 'sine': chart.sine + + net.train input, + 'errorThresh': 0.005 # error threshold to reach + 'iterations': 5e4 # maximum training iterations + 'log': no # console.log() progress periodically + 'logPeriod': 1e3 # number of iterations between logging + 'learningRate': 0.2 # learning rate + + # Now test it. + for sample in testing + out = net.run _.zipObject keys, sample.points + assert sample.sine is Math.round(out.sine), "Network says #{out.sine} but sample says #{sample.sine}" + + do done \ No newline at end of file