-
Notifications
You must be signed in to change notification settings - Fork 8
/
gomind.go
161 lines (139 loc) · 6.47 KB
/
gomind.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Package gomind for a simple Multi Layer Perceptron (MLP) Feed Forward Artificial Neural Network library.
package gomind
import (
"errors"
"fmt"
"math/rand"
"time"
"github.com/surenderthakran/gomind/activation"
"github.com/surenderthakran/gomind/layer"
"github.com/surenderthakran/gomind/network"
)
const (
defaultActivationFunction = "LINEAR"
)
// NeuralNetworkInterface defines methods used by gomind from network.NeuralNetwork's type.
type NeuralNetworkInterface interface {
CalculateOutput(input []float64) []float64
LastOutput() []float64
HiddenLayer() *layer.Layer
OutputLayer() *layer.Layer
CalculateNewOutputLayerWeights(outputs, targetOutputs []float64) error
CalculateNewHiddenLayerWeights() error
UpdateWeights()
CalculateError(targetOutput []float64) (float64, error)
}
// Model type defines the neural network's architecture and metadata.
type Model struct {
numberOfInputs int
numberOfHiddenNeurons int
hiddenLayerActivationFunctionName string
numberOfOutputs int
outputLayerActivationFunctionName string
learningRate float64
network NeuralNetworkInterface
}
// ModelConfiguration type defines the network configuration template filled by external code while creating a new model.
type ModelConfiguration struct {
NumberOfInputs int // mandatory
NumberOfOutputs int // mandatory
NumberOfHiddenLayerNeurons int
LearningRate float64
HiddenLayerActivationFunctionName string
OutputLayerActivationFunctionName string
}
// LearnSample function trains the neural network using the given input/output sample.
func (model *Model) LearnSample(input, output []float64) error {
outputs := model.network.CalculateOutput(input)
if err := model.network.CalculateNewOutputLayerWeights(outputs, output); err != nil {
return err
}
if err := model.network.CalculateNewHiddenLayerWeights(); err != nil {
return err
}
model.network.UpdateWeights()
return nil
}
// LastOutput function returns the last output of the network.
func (model *Model) LastOutput() []float64 {
return model.network.LastOutput()
}
// CalculateError function generates the error value for the given target output against the network's last output.
func (model *Model) CalculateError(targetOutput []float64) (float64, error) {
return model.network.CalculateError(targetOutput)
}
// Describe function prints the current state of the neural network and its components.
func (model *Model) Describe(showNeurons bool) {
fmt.Println(fmt.Sprintf("Input Layer: (No of nodes: %v)", model.numberOfInputs))
fmt.Println(fmt.Sprintf("Hidden Layer: (No of neurons: %v, Activation Function: %v)", len(model.network.HiddenLayer().Neurons()), model.network.HiddenLayer().Activation().Name()))
if showNeurons == true {
model.network.HiddenLayer().Describe()
}
fmt.Println(fmt.Sprintf("Output Layer: (No of neurons: %v, Activation Function: %v))", len(model.network.OutputLayer().Neurons()), model.network.OutputLayer().Activation().Name()))
if showNeurons == true {
model.network.OutputLayer().Describe()
}
}
// estimateIdealNumberOfHiddenLayerNeurons function attempts to estimate the ideal number of neural networks in the hidden layer
// of the network for a given number of inputs and outputs.
func estimateIdealNumberOfHiddenLayerNeurons(numberOfInputs, numberOfOutputs int) int {
var possibleResults []int
twoThirdRule := ((numberOfInputs * 2) / 3) + numberOfOutputs
possibleResults = append(possibleResults, twoThirdRule)
if len(possibleResults) == 1 && possibleResults[0] < 2*numberOfInputs {
if numberOfInputs < numberOfOutputs && numberOfInputs <= possibleResults[0] && possibleResults[0] <= numberOfOutputs {
return possibleResults[0]
} else if numberOfOutputs < numberOfInputs && numberOfOutputs <= possibleResults[0] && possibleResults[0] <= numberOfInputs {
return possibleResults[0]
} else if numberOfOutputs == numberOfInputs {
return possibleResults[0]
}
}
return numberOfInputs
}
// New is used to create a new GoMind network model.
func New(configuration *ModelConfiguration) (*Model, error) {
fmt.Println("Initializing new Neural Network!")
// setting timestamp as seed for random number generator.
rand.Seed(time.Now().UnixNano())
model := &Model{}
if configuration.NumberOfInputs == 0 {
return nil, errors.New("NumberOfInputs field in ModelConfiguration is a mandatory field which cannot be zero.")
}
model.numberOfInputs = configuration.NumberOfInputs
if configuration.NumberOfOutputs == 0 {
return nil, errors.New("NumberOfOutputs field in ModelConfiguration is a mandatory field which cannot be zero.")
}
model.numberOfOutputs = configuration.NumberOfOutputs
if configuration.LearningRate < 0 || configuration.LearningRate > 1 {
return nil, errors.New("LearningRate cannot be less than 0 or greater than 1.")
} else if configuration.LearningRate == 0 {
model.learningRate = 0.5
fmt.Println("Using default learning rate 0.5")
} else {
model.learningRate = configuration.LearningRate
}
model.numberOfHiddenNeurons = configuration.NumberOfHiddenLayerNeurons
if model.numberOfHiddenNeurons == 0 {
model.numberOfHiddenNeurons = estimateIdealNumberOfHiddenLayerNeurons(model.numberOfInputs, model.numberOfOutputs)
fmt.Println("Estimated Ideal Number Of Hidden Layer Neurons: ", model.numberOfHiddenNeurons)
}
model.hiddenLayerActivationFunctionName = activation.ValidFunction(configuration.HiddenLayerActivationFunctionName)
if model.hiddenLayerActivationFunctionName == "" {
model.hiddenLayerActivationFunctionName = defaultActivationFunction
fmt.Println("Estimated Ideal Activation Function for Hidden Layer Neurons: ", model.hiddenLayerActivationFunctionName)
}
model.outputLayerActivationFunctionName = activation.ValidFunction(configuration.OutputLayerActivationFunctionName)
if model.outputLayerActivationFunctionName == "" {
model.outputLayerActivationFunctionName = defaultActivationFunction
fmt.Println("Estimated Ideal Activation Function for Output Layer Neurons: ", model.outputLayerActivationFunctionName)
}
neuralNetwork, err := network.New(model.numberOfInputs, model.numberOfHiddenNeurons, model.numberOfOutputs, model.learningRate, model.hiddenLayerActivationFunctionName, model.outputLayerActivationFunctionName)
if err != nil {
return nil, fmt.Errorf("error creating a neural network: %v", err)
}
model.network = neuralNetwork
fmt.Println("Successfully created the following model:")
model.Describe(false)
return model, nil
}