├── package.json ├── LICENSE ├── README.md ├── tests └── test-perceptron.js └── perceptron.js /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "perceptron", 3 | "version": "0.0.1", 4 | "description": "A simple perceptron written in Javascript", 5 | "main": "perceptron.js", 6 | "scripts": { 7 | "test": "node tests/*.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/chesles/perceptron.git" 12 | }, 13 | "author": "chesles ", 14 | "license": "MIT", 15 | "readmeFilename": "README.md", 16 | "directories": { 17 | "test": "tests" 18 | }, 19 | "dependencies": {}, 20 | "devDependencies": { 21 | "specify": "~1.1.2" 22 | }, 23 | "keywords": [ 24 | "machine learning", 25 | "perceptron" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 John Chesley 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Perceptron. 2 | 3 | This is an implementation of the [perceptron learning algorithm](http://en.wikipedia.org/wiki/Perceptron#Learning_algorithm) for node.js. 4 | 5 | ## Installing it. 6 | 7 | npm install perceptron 8 | 9 | ## Using it. 10 | 11 | Let's teach a perceptron to implement a boolean AND function: 12 | 13 | ``` 14 | var perceptron = require('perceptron') 15 | 16 | var and = perceptron() 17 | 18 | and.train([1, 1], 1) 19 | and.train([0, 1], 0) 20 | and.train([1, 0], 0) 21 | and.train([0, 0], 0) 22 | 23 | // practice makes perfect (we hope...) 24 | while(!and.retrain()) {} 25 | 26 | and.perceive([1, 1]) // => 1 27 | and.perceive([0, 1]) // => 0 28 | and.perceive([1, 0]) // => 0 29 | and.perceive([0, 0]) // => 0 30 | ``` 31 | 32 | The perceptron starts with random weights if you don't provide any defaults. 33 | Weights are adjusted according to the delta rule each time you call `train` and 34 | the current weights give the wrong answer. Since this adjustment can cause the 35 | perceptron to 'unlearn' previously learned inputs, `retrain` iterates over all 36 | previous inputs, calling `train` again. Both `train` and `retrain` return a 37 | boolean success value, indicating if the input(s) were learned. 38 | -------------------------------------------------------------------------------- /tests/test-perceptron.js: -------------------------------------------------------------------------------- 1 | var specify = require('specify') 2 | , perceptron = require('../perceptron') 3 | 4 | specify('basic training', function(test) { 5 | var opts = { 6 | weights: [0, 0, 0, 0], 7 | learningrate: 1 8 | } 9 | var p = perceptron(opts) 10 | 11 | p.train([0, 0, 1], 0) 12 | // weights should have remained the same 13 | test.equal(p.weights[0], opts.weights[0]) 14 | test.equal(p.weights[1], opts.weights[1]) 15 | test.equal(p.weights[2], opts.weights[2]) 16 | 17 | // weights should change now 18 | p.train([1, 1, 1], 1) 19 | test.equal(p.weights[0], opts.weights[0] + 1) 20 | test.equal(p.weights[1], opts.weights[1] + 1) 21 | test.equal(p.weights[2], opts.weights[2] + 1) 22 | 23 | p.train([1, 0, 1], 1) 24 | p.train([0, 1, 1], 0) 25 | 26 | // with this data set only one round of training is needed 27 | test.equal(p.perceive([0, 0, 1]), 0) 28 | test.equal(p.perceive([1, 1, 1]), 1) 29 | test.equal(p.perceive([1, 0, 1]), 1) 30 | test.equal(p.perceive([0, 1, 1]), 0) 31 | }) 32 | 33 | specify('AND perceptron', function(test) { 34 | var and = perceptron() //{weights: [0, 0, -0.3]}) 35 | and.train([1,1], 1) 36 | and.train([0,0], 0) 37 | and.train([1,0], 0) 38 | and.train([0,1], 0) 39 | var count = 0 40 | while(!and.retrain()) { 41 | count++ 42 | } 43 | // retraining shouldn't take more than a few iterations, really 44 | test.ok(count < 20) 45 | test.equal(and.perceive([1,1]), 1) 46 | test.equal(and.perceive([0,1]), 0) 47 | test.equal(and.perceive([1,0]), 0) 48 | test.equal(and.perceive([0,0]), 0) 49 | }) 50 | 51 | specify.run() 52 | -------------------------------------------------------------------------------- /perceptron.js: -------------------------------------------------------------------------------- 1 | function Perceptron(opts) { 2 | if (this === global) return new Perceptron(opts); 3 | 4 | if (!opts) opts = {} 5 | 6 | var debug = 'debug' in opts ? opts.debug : false; 7 | 8 | var weights = 'weights' in opts 9 | ? opts.weights.slice() 10 | : [] 11 | 12 | var threshold = 'threshold' in opts 13 | ? opts.threshold 14 | : 1 15 | 16 | var learningrate; 17 | if (!('learningrate' in opts)) { 18 | learningrate = 0.1 19 | } 20 | else { 21 | learningrate = opts.learningrate 22 | } 23 | 24 | var data = [] 25 | 26 | var api = { 27 | weights: weights, 28 | retrain: function() { 29 | var length = data.length 30 | var success = true 31 | for(var i=0; i training %s, expecting: %s got: %s', inputs, expected, result) 50 | 51 | if (result == expected) { 52 | return true 53 | } 54 | else { 55 | if (debug) console.log('> adjusting weights...', weights, inputs); 56 | for(var i=0; i < weights.length; i++) { 57 | var input = (i == inputs.length) 58 | ? threshold 59 | : inputs[i] 60 | api.adjust(result, expected, input, i) 61 | } 62 | if (debug) console.log(' -> weights:', weights) 63 | return false 64 | } 65 | }, 66 | 67 | adjust: function(result, expected, input, index) { 68 | var d = api.delta(result, expected, input, learningrate); 69 | weights[index] += d; 70 | if (isNaN(weights[index])) throw new Error('weights['+index+'] went to NaN!!') 71 | }, 72 | 73 | delta: function(actual, expected, input, learnrate) { 74 | return (expected - actual) * learnrate * input 75 | }, 76 | 77 | perceive: function(inputs, net) { 78 | var result = 0 79 | for(var i=0; i 0 ? 1 : 0 86 | }, 87 | } 88 | 89 | return api; 90 | } 91 | 92 | module.exports = Perceptron 93 | --------------------------------------------------------------------------------