Last active
July 20, 2016 14:25
-
-
Save unscriptable/77c98c87037d4f502d51 to your computer and use it in GitHub Desktop.
Just playing with neurons
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Just playing with neurons. Neurons must be connected to each other with | |
* a synapse, which provides the "strength" of the signal of the upstream | |
* neuron. | |
* A few months back, I had the idea that neurons were just like functions. | |
* The inputs are the signals from other neurons that have been modified | |
* via synapses. The output (action potential) is simply the return value. | |
* | |
* A few weeks ago, I realized that functions weren't the right model *at all*. | |
* Neural networks are more like streams. This implementation relies on | |
* mutable neuron and synapse parameters since the stream can't be modified | |
* once it's composed. (I'm working on an immutable version.) | |
*/ | |
import most from 'most' | |
/** | |
* Simple neuron model with threshold and decay, but no refractory time. | |
* Params may be varied at any time. | |
* @param {(number, number) => number} receive - receive a signal | |
* @param {(number, number, number) => number} decay - decay value over time | |
* @param {Object} params | |
* @param {Number} [params.threshold=1] value needed to propagate signal. | |
* @param {Number} [params.decay=0] decay of value (per second). | |
*/ | |
const neuron = (receive, decay, elapsed) => params => { | |
let value = 0 | |
let time = new Date() | |
return input => { | |
let [ timeDiff, time ] = elapsed(time) | |
const decayRate = params.decay || 0 | |
const threshold = 'threshold' in params ? params.threshold : 1 | |
let [ value, fired ] = isFired(receive(decay(value, decayRate, timeDiff), input)), threshold) | |
return fired | |
} | |
} | |
/** | |
* Simple synapse model that provides a scalar value to a neuron. | |
* Params may be varied at any time. | |
* @param {Object} params | |
* @param {Number} [params.scale=1] | |
*/ | |
const synapse = params => () => | |
'scale' in params ? params.scale : 1 | |
// Calculate the elapsed time since a previous time. | |
const elapsed = prev => { | |
const now = new Date() | |
return [ now - prev, now ] | |
} | |
// Adjust a neuron's value in response to an input signal. | |
const receive = (value, input) => | |
value + input | |
// Decay a neuron's value at a given rate and elapsed time. | |
// Assumes rate is units/sec. | |
const decay = (value, rate, timediff) => | |
Math.max(0, value - value * timediff / 1000 * rate) | |
// Determines if a neuron fires by comparing against a threshold. | |
const isFired = (value, threshold) => | |
value >= threshold | |
? [ 0, true ] | |
: [ value, false ] | |
// ---------------------------------------- | |
/** Inputs would normally come from all kinds of sources, including periodic | |
* ones, but I'm just showing a very simple periodic one here. The network | |
* is extremely simple, too. Just a single neuron with a threshold exactly | |
* twice the input signal. The result is a neuron that "fires" every | |
* other second. | |
*/ | |
most.periodic(1000) | |
.tap(() => console.log('.')) | |
.map(synapse({ scale: 1 })) // could also be .constant(1) | |
.filter(neuron({ threshold: 2, decay: 0 })) | |
.forEach(x => console.log('-->', x)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment