├── .gitignore ├── go_neural.go ├── go_neural_test.go ├── activation_function_test.go ├── activation_func.go ├── learn ├── evaluation.go ├── learn.go └── learn_test.go ├── layer_test.go ├── neuron_test.go ├── synapse.go ├── layer.go ├── enter.go ├── Makefile ├── engine ├── engine_test.go └── engine.go ├── network_test.go ├── neuron.go ├── persist ├── persist_test.go └── persist.go ├── README.md └── network.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.test 2 | -------------------------------------------------------------------------------- /go_neural.go: -------------------------------------------------------------------------------- 1 | package neural 2 | -------------------------------------------------------------------------------- /go_neural_test.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | import ( 4 | . "launchpad.net/gocheck" 5 | "testing" 6 | ) 7 | 8 | func Test(t *testing.T) { TestingT(t) } 9 | 10 | type SuiteT struct{} 11 | 12 | func (s *SuiteT) SetUpTest(c *C) {} 13 | 14 | var _ = Suite(&SuiteT{}) 15 | -------------------------------------------------------------------------------- /activation_function_test.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | import ( 4 | . "launchpad.net/gocheck" 5 | ) 6 | 7 | func (s *SuiteT) TestLogisticFunc(c *C) { 8 | f := NewLogisticFunc(1) 9 | 10 | c.Assert(f(0), Equals, 0.5) 11 | c.Assert(1-f(6) > 0, Equals, true) 12 | c.Assert(1-f(6) < 0.1, Equals, true) 13 | } 14 | -------------------------------------------------------------------------------- /activation_func.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | import "math" 4 | 5 | type ActivationFunction func(float64) float64 6 | 7 | func NewLogisticFunc(a float64) ActivationFunction { 8 | return func(x float64) float64 { 9 | return LogisticFunc(x, a) 10 | } 11 | } 12 | func LogisticFunc(x, a float64) float64 { 13 | return 1 / (1 + math.Exp(-a*x)) 14 | } 15 | -------------------------------------------------------------------------------- /learn/evaluation.go: -------------------------------------------------------------------------------- 1 | package learn 2 | 3 | import ( 4 | "github.com/NOX73/go-neural" 5 | "math" 6 | ) 7 | 8 | // Math Evaluation with Least squares method. 9 | func Evaluation(n *neural.Network, in, ideal []float64) float64 { 10 | out := n.Calculate(in) 11 | 12 | var e float64 13 | for i, _ := range out { 14 | e += math.Pow(out[i]-ideal[i], 2) 15 | } 16 | 17 | return e / 2 18 | } 19 | -------------------------------------------------------------------------------- /layer_test.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | import ( 4 | . "launchpad.net/gocheck" 5 | ) 6 | 7 | func (s *SuiteT) TestLayer(c *C) { 8 | l := NewLayer(5) 9 | c.Assert(l.Neurons, HasLen, 5) 10 | } 11 | 12 | func (s *SuiteT) TestConnectToLayer(c *C) { 13 | count := 5 14 | l := NewLayer(count) 15 | l2 := NewLayer(count) 16 | 17 | l.ConnectTo(l2) 18 | 19 | for _, n := range l.Neurons { 20 | c.Assert(n.OutSynapses, HasLen, count) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /neuron_test.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | import ( 4 | . "launchpad.net/gocheck" 5 | ) 6 | 7 | func (s *SuiteT) TestAttachNeurons(c *C) { 8 | n := NewNeuron() 9 | n2 := NewNeuron() 10 | w := 0.5 11 | 12 | n.SynapseTo(n2, w) 13 | 14 | c.Assert(n.OutSynapses[0].Weight, Equals, w) 15 | } 16 | 17 | func (s *SuiteT) TestInputsSynapses(c *C) { 18 | n := NewNeuron() 19 | 20 | NewSynapseFromTo(NewNeuron(), n, 0.1) 21 | NewSynapseFromTo(NewNeuron(), n, 0.1) 22 | NewSynapseFromTo(NewNeuron(), n, 0.1) 23 | 24 | c.Assert(n.InSynapses, HasLen, 3) 25 | } 26 | -------------------------------------------------------------------------------- /synapse.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | func NewSynapse(weight float64) *Synapse { 4 | return &Synapse{Weight: weight} 5 | } 6 | 7 | func NewSynapseFromTo(from, to *Neuron, weight float64) *Synapse { 8 | syn := NewSynapse(weight) 9 | 10 | from.OutSynapses = append(from.OutSynapses, syn) 11 | to.InSynapses = append(to.InSynapses, syn) 12 | 13 | return syn 14 | } 15 | 16 | type Synapse struct { 17 | Weight float64 18 | In float64 `json:"-"` 19 | Out float64 `json:"-"` 20 | } 21 | 22 | func (s *Synapse) Signal(value float64) { 23 | s.In = value 24 | s.Out = s.In * s.Weight 25 | } 26 | -------------------------------------------------------------------------------- /layer.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | func NewLayer(neurons int) *Layer { 4 | l := &Layer{} 5 | l.init(neurons) 6 | return l 7 | } 8 | 9 | type Layer struct { 10 | Neurons []*Neuron 11 | } 12 | 13 | func (l *Layer) ConnectTo(layer *Layer) { 14 | for _, n := range l.Neurons { 15 | for _, toN := range layer.Neurons { 16 | n.SynapseTo(toN, 0) 17 | } 18 | } 19 | } 20 | 21 | func (l *Layer) init(neurons int) { 22 | for ; neurons > 0; neurons-- { 23 | l.addNeuron() 24 | } 25 | } 26 | 27 | func (l *Layer) addNeuron() { 28 | n := NewNeuron() 29 | l.Neurons = append(l.Neurons, n) 30 | } 31 | 32 | func (l *Layer) Calculate() { 33 | for _, n := range l.Neurons { 34 | n.Calculate() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /enter.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | type Enter struct { 4 | OutSynapses []*Synapse 5 | Input float64 `json:"-"` 6 | } 7 | 8 | func NewEnter() *Enter { 9 | return &Enter{} 10 | } 11 | 12 | func (e *Enter) SynapseTo(nTo *Neuron, weight float64) { 13 | syn := NewSynapse(weight) 14 | 15 | e.OutSynapses = append(e.OutSynapses, syn) 16 | nTo.InSynapses = append(nTo.InSynapses, syn) 17 | } 18 | 19 | func (e *Enter) SetInput(val float64) { 20 | e.Input = val 21 | } 22 | 23 | func (e *Enter) ConnectTo(layer *Layer) { 24 | for _, n := range layer.Neurons { 25 | e.SynapseTo(n, 0) 26 | } 27 | } 28 | 29 | func (e *Enter) Signal() { 30 | for _, s := range e.OutSynapses { 31 | s.Signal(e.Input) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: test_neural test_persist test_learn test_engine 2 | 3 | test_persist: 4 | @( go test ./persist/ ) 5 | 6 | test_neural: 7 | @( go test ) 8 | 9 | test_learn: 10 | @( go test ./learn/ ) 11 | 12 | test_engine: 13 | @( go test ./engine/ ) 14 | 15 | goget: 16 | @( \ 17 | go get github.com/NOX73/go-neural; \ 18 | go get github.com/NOX73/go-neural/persist; \ 19 | go get github.com/NOX73/go-neural/learn; \ 20 | go get github.com/NOX73/go-neural/engine; \ 21 | ) 22 | 23 | gogetu: 24 | @( \ 25 | go get -u github.com/NOX73/go-neural; \ 26 | go get -u github.com/NOX73/go-neural/persist; \ 27 | go get -u github.com/NOX73/go-neural/learn; \ 28 | go get -u github.com/NOX73/go-neural/engine; \ 29 | ) 30 | -------------------------------------------------------------------------------- /engine/engine_test.go: -------------------------------------------------------------------------------- 1 | package engine 2 | 3 | import ( 4 | "github.com/NOX73/go-neural" 5 | "github.com/NOX73/go-neural/persist" 6 | "github.com/stretchr/testify/assert" 7 | "testing" 8 | ) 9 | 10 | func TestBasic(t *testing.T) { 11 | network := neural.NewNetwork(2, []int{2, 2}) 12 | engine := New(network) 13 | engine.Start() 14 | 15 | engine.Learn([]float64{1, 2}, []float64{3, 3}, 0.1) 16 | 17 | out := engine.Calculate([]float64{1, 2}) 18 | 19 | assert.Equal(t, len(out), 2) 20 | } 21 | 22 | func TestDump(t *testing.T) { 23 | network := neural.NewNetwork(3, []int{3, 3}) 24 | engine := New(network) 25 | engine.Start() 26 | 27 | dump := persist.ToDump(network) 28 | dumpEng := engine.Dump() 29 | 30 | assert.Equal(t, dump.Enters, dumpEng.Enters) 31 | } 32 | -------------------------------------------------------------------------------- /network_test.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | import ( 4 | . "launchpad.net/gocheck" 5 | //"log" 6 | ) 7 | 8 | func (s *SuiteT) TestCreateNewNetwork(c *C) { 9 | net := NewNetwork(10, []int{5, 6, 2}) 10 | 11 | c.Assert(net.Enters, HasLen, 10) 12 | c.Assert(net.Enters[0].OutSynapses, HasLen, 5) 13 | 14 | c.Assert(net.Layers, HasLen, 3) 15 | c.Assert(net.Layers[0].Neurons, HasLen, 5) 16 | 17 | c.Assert(net.Layers[0].Neurons[0].OutSynapses, HasLen, 6) 18 | c.Assert(net.Layers[1].Neurons[0].OutSynapses, HasLen, 2) 19 | c.Assert(net.Layers[2].Neurons[0].OutSynapses, HasLen, 0) 20 | } 21 | 22 | func (s *SuiteT) TestCalculcateNetwork(c *C) { 23 | 24 | net := NewNetwork(5, []int{5, 6, 1}) 25 | 26 | out := net.Calculate([]float64{0, 0, 0, 0, 0}) 27 | 28 | c.Assert(out[0], Equals, 0.5) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /neuron.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | type Neuron struct { 4 | OutSynapses []*Synapse 5 | InSynapses []*Synapse `json:"-"` 6 | ActivationFunction ActivationFunction `json:"-"` 7 | Out float64 `json:"-"` 8 | } 9 | 10 | func NewNeuron() *Neuron { 11 | return &Neuron{} 12 | } 13 | 14 | func (n *Neuron) SynapseTo(nTo *Neuron, weight float64) { 15 | NewSynapseFromTo(n, nTo, weight) 16 | } 17 | 18 | func (n *Neuron) SetActivationFunction(aFunc ActivationFunction) { 19 | n.ActivationFunction = aFunc 20 | } 21 | 22 | func (n *Neuron) Calculate() { 23 | var sum float64 24 | for _, s := range n.InSynapses { 25 | sum += s.Out 26 | } 27 | 28 | n.Out = n.ActivationFunction(sum) 29 | 30 | for _, s := range n.OutSynapses { 31 | s.Signal(n.Out) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /persist/persist_test.go: -------------------------------------------------------------------------------- 1 | package persist 2 | 3 | import ( 4 | "github.com/NOX73/go-neural" 5 | . "launchpad.net/gocheck" 6 | "testing" 7 | ) 8 | 9 | func Test(t *testing.T) { TestingT(t) } 10 | 11 | type SuiteT struct{} 12 | 13 | func (s *SuiteT) SetUpTest(c *C) {} 14 | 15 | var _ = Suite(&SuiteT{}) 16 | 17 | func (s *SuiteT) TestPersist(c *C) { 18 | n := neural.NewNetwork(5, []int{5, 5, 5}) 19 | n.RandomizeSynapses() 20 | 21 | path := "/tmp/network.json" 22 | ToFile(path, n) 23 | 24 | n2 := FromFile(path) 25 | 26 | c.Assert(n2.Enters, HasLen, len(n.Enters)) 27 | c.Assert(n2.Layers, HasLen, len(n.Layers)) 28 | 29 | for i, l := range n2.Layers { 30 | for j, nr := range l.Neurons { 31 | for h, s := range nr.OutSynapses { 32 | c.Assert(s.Weight, Equals, n.Layers[i].Neurons[j].OutSynapses[h].Weight) 33 | } 34 | } 35 | } 36 | 37 | in := []float64{0.5, 0.5, 0.5, 0.5, 0.5} 38 | out := n.Calculate(in) 39 | out2 := n2.Calculate(in) 40 | 41 | for i, o := range out2 { 42 | c.Assert(o, Equals, out[i]) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /learn/learn.go: -------------------------------------------------------------------------------- 1 | package learn 2 | 3 | import ( 4 | "github.com/NOX73/go-neural" 5 | ) 6 | 7 | type Deltas [][]float64 8 | 9 | type Sample struct { 10 | In []float64 11 | Ideal []float64 12 | } 13 | 14 | func Learn(n *neural.Network, in, ideal []float64, speed float64) { 15 | Backpropagation(n, in, ideal, speed) 16 | } 17 | 18 | func Backpropagation(n *neural.Network, in, ideal []float64, speed float64) { 19 | n.Calculate(in) 20 | 21 | deltas := make([][]float64, len(n.Layers)) 22 | 23 | last := len(n.Layers) - 1 24 | l := n.Layers[last] 25 | deltas[last] = make([]float64, len(l.Neurons)) 26 | for i, n := range l.Neurons { 27 | deltas[last][i] = n.Out * (1 - n.Out) * (ideal[i] - n.Out) 28 | } 29 | 30 | for i := last - 1; i >= 0; i-- { 31 | l := n.Layers[i] 32 | deltas[i] = make([]float64, len(l.Neurons)) 33 | for j, n := range l.Neurons { 34 | 35 | var sum float64 = 0 36 | for k, s := range n.OutSynapses { 37 | sum += s.Weight * deltas[i+1][k] 38 | } 39 | 40 | deltas[i][j] = n.Out * (1 - n.Out) * sum 41 | } 42 | } 43 | 44 | for i, l := range n.Layers { 45 | for j, n := range l.Neurons { 46 | for _, s := range n.InSynapses { 47 | s.Weight += speed * deltas[i][j] * s.In 48 | } 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-neural 2 | ============== 3 | 4 | # Install 5 | 6 | ``` 7 | go get github.com/NOX73/go-neural 8 | go get github.com/NOX73/go-neural/persist 9 | go get github.com/NOX73/go-neural/learn 10 | ``` 11 | 12 | # Neural Network 13 | 14 | Create new network: 15 | 16 | ```go 17 | 18 | import "github.com/NOX73/go-neural" 19 | 20 | //... 21 | 22 | // Network has 9 enters and 3 layers 23 | // ( 9 neurons, 9 neurons and 4 neurons). 24 | // Last layer is network output. 25 | n := neural.NewNetwork(9, []int{9,9,4}) 26 | // Randomize sypaseses weights 27 | n.RandomizeSynapses() 28 | 29 | result := n.Calculate([]float64{0,1,0,1,1,1,0,1,0}) 30 | 31 | ``` 32 | 33 | # Persist network 34 | 35 | Save to file: 36 | 37 | ```go 38 | import "github.com/NOX73/go-neural/persist" 39 | 40 | persist.ToFile("/path/to/file.json", network) 41 | ``` 42 | 43 | Load from file: 44 | 45 | ```go 46 | import "github.com/NOX73/go-neural/persist" 47 | 48 | network := persist.FromFile("/path/to/file.json") 49 | ``` 50 | 51 | # Learning 52 | 53 | ```go 54 | import "github.com/NOX73/go-neural/learn" 55 | 56 | var input, idealOutput []float64 57 | // Learning speed [0..1] 58 | var speed float64 59 | 60 | learn.Learn(network, in, idealOut, speed) 61 | ``` 62 | 63 | You can get estimate of learning quality: 64 | 65 | ```go 66 | e := learn.Evaluation(network, in, idealOut) 67 | ``` 68 | 69 | # Engine 70 | 71 | For concurrent learn, calculate & dump neural network. 72 | 73 | ```go 74 | network := neural.NewNetwork(2, []int{2, 2}) 75 | engine := New(network) 76 | engine.Start() 77 | 78 | engine.Learn([]float64{1, 2}, []float64{3, 3}, 0.1) 79 | 80 | out := engine.Calculate([]float64{1, 2}) 81 | ``` 82 | 83 | # Live example 84 | 85 | Dirty live example: [https://github.com/NOX73/go-neural-play] 86 | 87 | -------------------------------------------------------------------------------- /persist/persist.go: -------------------------------------------------------------------------------- 1 | package persist 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/NOX73/go-neural" 6 | "io/ioutil" 7 | ) 8 | 9 | type Weights [][][]float64 10 | type NetworkDump struct { 11 | Enters int 12 | Weights Weights 13 | } 14 | 15 | func DumpFromFile(path string) *NetworkDump { 16 | b, err := ioutil.ReadFile(path) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | dump := &NetworkDump{} 22 | err = json.Unmarshal(b, dump) 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | return dump 28 | } 29 | 30 | func FromFile(path string) *neural.Network { 31 | dump := DumpFromFile(path) 32 | n := FromDump(dump) 33 | return n 34 | } 35 | 36 | func ToFile(path string, n *neural.Network) { 37 | dump := ToDump(n) 38 | 39 | DumpToFile(path, dump) 40 | } 41 | 42 | func DumpToFile(path string, dump *NetworkDump) { 43 | j, _ := json.Marshal(dump) 44 | 45 | err := ioutil.WriteFile(path, j, 0644) 46 | if err != nil { 47 | panic(err) 48 | } 49 | } 50 | 51 | func ToDump(n *neural.Network) *NetworkDump { 52 | 53 | dump := &NetworkDump{Enters: len(n.Enters), Weights: make([][][]float64, len(n.Layers))} 54 | 55 | for i, l := range n.Layers { 56 | dump.Weights[i] = make([][]float64, len(l.Neurons)) 57 | for j, n := range l.Neurons { 58 | dump.Weights[i][j] = make([]float64, len(n.InSynapses)) 59 | for k, s := range n.InSynapses { 60 | dump.Weights[i][j][k] = s.Weight 61 | } 62 | } 63 | } 64 | 65 | return dump 66 | } 67 | 68 | func FromDump(dump *NetworkDump) *neural.Network { 69 | layers := make([]int, len(dump.Weights)) 70 | for i, layer := range dump.Weights { 71 | layers[i] = len(layer) 72 | } 73 | 74 | n := neural.NewNetwork(dump.Enters, layers) 75 | 76 | for i, l := range n.Layers { 77 | for j, n := range l.Neurons { 78 | for k, s := range n.InSynapses { 79 | s.Weight = dump.Weights[i][j][k] 80 | } 81 | } 82 | } 83 | 84 | return n 85 | } 86 | -------------------------------------------------------------------------------- /learn/learn_test.go: -------------------------------------------------------------------------------- 1 | package learn 2 | 3 | import ( 4 | "github.com/NOX73/go-neural" 5 | . "launchpad.net/gocheck" 6 | "math/rand" 7 | "testing" 8 | ) 9 | 10 | type lessThenChecker struct { 11 | *CheckerInfo 12 | } 13 | 14 | var LessThen Checker = &lessThenChecker{ 15 | &CheckerInfo{Name: "LessThen", Params: []string{"a", "b"}}, 16 | } 17 | 18 | func (checker *lessThenChecker) Check(params []interface{}, names []string) (result bool, error string) { 19 | a, _ := params[0].(float64) 20 | b, _ := params[1].(float64) 21 | 22 | return a < b, "" 23 | } 24 | 25 | type moreThenChecker struct { 26 | *CheckerInfo 27 | } 28 | 29 | var MoreThen Checker = &moreThenChecker{ 30 | &CheckerInfo{Name: "MoreThen", Params: []string{"a", "b"}}, 31 | } 32 | 33 | func (checker *moreThenChecker) Check(params []interface{}, names []string) (result bool, error string) { 34 | a, _ := params[0].(float64) 35 | b, _ := params[1].(float64) 36 | 37 | return a > b, "" 38 | } 39 | 40 | func Test(t *testing.T) { TestingT(t) } 41 | 42 | type SuiteT struct{} 43 | 44 | func (s *SuiteT) SetUpTest(c *C) {} 45 | 46 | var _ = Suite(&SuiteT{}) 47 | 48 | func (s *SuiteT) TestLearn(c *C) { 49 | 50 | n := neural.NewNetwork(2, []int{5, 5, 2}) 51 | n.RandomizeSynapses() 52 | 53 | for i := 0; i < 1000000; i++ { 54 | in := []float64{rand.Float64() * 10, rand.Float64() * 10} 55 | var ideal []float64 56 | if in[0] > in[1] { 57 | ideal = []float64{1, 0} 58 | } else { 59 | ideal = []float64{0, 1} 60 | } 61 | Learn(n, in, ideal, 0.1) 62 | } 63 | 64 | count := 1000 65 | success := 0 66 | for i := 0; i < count; i++ { 67 | in := []float64{rand.Float64() * 10, rand.Float64() * 10} 68 | res := n.Calculate(in) 69 | 70 | if in[0] > in[1] && res[0] > res[1] { 71 | success++ 72 | } 73 | if in[0] < in[1] && res[0] < res[1] { 74 | success++ 75 | } 76 | } 77 | 78 | // Success persente should be more then 90% 79 | successPersents := float64(success) / float64(count) 80 | c.Assert(successPersents, MoreThen, 0.99) 81 | 82 | } 83 | -------------------------------------------------------------------------------- /engine/engine.go: -------------------------------------------------------------------------------- 1 | package engine 2 | 3 | import ( 4 | "github.com/NOX73/go-neural" 5 | "github.com/NOX73/go-neural/learn" 6 | "github.com/NOX73/go-neural/persist" 7 | ) 8 | 9 | const ( 10 | learnChannelCapacity = 5 11 | calcChannelCapacity = 5 12 | dumpChannelCapacity = 5 13 | ) 14 | 15 | type Engine interface { 16 | Start() 17 | Learn(in, ideal []float64, speed float64) 18 | Calculate([]float64) []float64 19 | Dump() *persist.NetworkDump 20 | } 21 | 22 | type engine struct { 23 | Network *neural.Network 24 | LearnChannel chan *request 25 | CalculateChannel chan *request 26 | DumpChannel chan *request 27 | } 28 | 29 | type request []interface{} 30 | 31 | func New(n *neural.Network) Engine { 32 | e := &engine{ 33 | Network: n, 34 | LearnChannel: make(chan *request, learnChannelCapacity), 35 | CalculateChannel: make(chan *request, calcChannelCapacity), 36 | DumpChannel: make(chan *request, dumpChannelCapacity), 37 | } 38 | 39 | return e 40 | } 41 | 42 | func (e *engine) Start() { 43 | go e.loop() 44 | } 45 | 46 | func (e *engine) Learn(in, ideal []float64, speed float64) { 47 | e.LearnChannel <- &request{&in, &ideal, speed} 48 | } 49 | 50 | func (e *engine) Calculate(in []float64) []float64 { 51 | resp := make(chan *[]float64, 1) 52 | e.CalculateChannel <- &request{&in, resp} 53 | return *(<-resp) 54 | } 55 | 56 | func (e *engine) Dump() *persist.NetworkDump { 57 | resp := make(chan *persist.NetworkDump, 1) 58 | e.DumpChannel <- &request{resp} 59 | return <-resp 60 | } 61 | 62 | func (e *engine) loop() { 63 | for { 64 | 65 | select { 66 | case r := <-e.CalculateChannel: 67 | e.calculate(r) 68 | case r := <-e.DumpChannel: 69 | e.dump(r) 70 | default: 71 | } 72 | 73 | select { 74 | case r := <-e.DumpChannel: 75 | e.dump(r) 76 | case r := <-e.CalculateChannel: 77 | e.calculate(r) 78 | case r := <-e.LearnChannel: 79 | e.learn(r) 80 | } 81 | 82 | } 83 | } 84 | 85 | func (e *engine) learn(req *request) { 86 | r := *req 87 | 88 | in := r[0].(*[]float64) 89 | ideal := r[1].(*[]float64) 90 | speed := r[2].(float64) 91 | learn.Learn(e.Network, *in, *ideal, speed) 92 | } 93 | 94 | func (e *engine) calculate(req *request) { 95 | r := *req 96 | 97 | in := r[0].(*[]float64) 98 | resp := r[1].(chan *[]float64) 99 | 100 | res := e.Network.Calculate(*in) 101 | resp <- &(res) 102 | } 103 | 104 | func (e *engine) dump(req *request) { 105 | r := *req 106 | resp := r[0].(chan *persist.NetworkDump) 107 | 108 | resp <- persist.ToDump(e.Network) 109 | } 110 | -------------------------------------------------------------------------------- /network.go: -------------------------------------------------------------------------------- 1 | package neural 2 | 3 | import "fmt" 4 | import "math/rand" 5 | 6 | func NewNetwork(in int, layers []int) *Network { 7 | n := &Network{ 8 | Enters: make([]*Enter, 0, in), 9 | Layers: make([]*Layer, 0, len(layers)), 10 | } 11 | n.init(in, layers, NewLogisticFunc(1)) 12 | return n 13 | } 14 | 15 | type Network struct { 16 | Enters []*Enter 17 | Layers []*Layer 18 | Out []float64 `json:"-"` 19 | } 20 | 21 | func (n *Network) init(in int, layers []int, aFunc ActivationFunction) { 22 | n.initLayers(layers) 23 | n.initEnters(in) 24 | n.ConnectLayers() 25 | n.ConnectEnters() 26 | n.SetActivationFunction(aFunc) 27 | } 28 | 29 | func (n *Network) initLayers(layers []int) { 30 | for _, count := range layers { 31 | layer := NewLayer(count) 32 | n.Layers = append(n.Layers, layer) 33 | } 34 | } 35 | 36 | func (n *Network) initEnters(in int) { 37 | for ; in > 0; in-- { 38 | e := NewEnter() 39 | n.Enters = append(n.Enters, e) 40 | } 41 | } 42 | 43 | func (n *Network) ConnectLayers() { 44 | for i := len(n.Layers) - 1; i > 0; i-- { 45 | n.Layers[i-1].ConnectTo(n.Layers[i]) 46 | } 47 | } 48 | 49 | func (n *Network) ConnectEnters() { 50 | for _, e := range n.Enters { 51 | e.ConnectTo(n.Layers[0]) 52 | } 53 | } 54 | 55 | func (n *Network) SetActivationFunction(aFunc ActivationFunction) { 56 | for _, l := range n.Layers { 57 | for _, n := range l.Neurons { 58 | n.SetActivationFunction(aFunc) 59 | } 60 | } 61 | } 62 | 63 | func (n *Network) setEnters(v *[]float64) { 64 | values := *v 65 | if len(values) != len(n.Enters) { 66 | panic(fmt.Sprint("Enters count ( ", len(n.Enters), " ) != count of elements in SetEnters function argument ( ", len(values), " ) .")) 67 | } 68 | 69 | for i, e := range n.Enters { 70 | e.Input = values[i] 71 | } 72 | 73 | } 74 | 75 | func (n *Network) sendEnters() { 76 | for _, e := range n.Enters { 77 | e.Signal() 78 | } 79 | } 80 | 81 | func (n *Network) calculateLayers() { 82 | for _, l := range n.Layers { 83 | l.Calculate() 84 | } 85 | } 86 | 87 | func (n *Network) generateOut() { 88 | outL := n.Layers[len(n.Layers)-1] 89 | n.Out = make([]float64, len(outL.Neurons)) 90 | 91 | for i, neuron := range outL.Neurons { 92 | n.Out[i] = neuron.Out 93 | } 94 | } 95 | 96 | func (n *Network) Calculate(enters []float64) []float64 { 97 | n.setEnters(&enters) 98 | n.sendEnters() 99 | n.calculateLayers() 100 | n.generateOut() 101 | 102 | return n.Out 103 | } 104 | 105 | func (n *Network) RandomizeSynapses() { 106 | for _, l := range n.Layers { 107 | for _, n := range l.Neurons { 108 | for _, s := range n.InSynapses { 109 | s.Weight = 2 * (rand.Float64() - 0.5) 110 | } 111 | } 112 | } 113 | } 114 | --------------------------------------------------------------------------------