neuralnet/network.go

303 lines
7.1 KiB
Go
Raw Permalink Normal View History

package nn
import (
"gonum.org/v1/gonum/mat"
"math/rand"
"time"
)
type Network struct {
layers []uint // number of neurons of each layer
fed bool // whether the network has the state fed by a forward pass
neurons []*mat.Dense // neuron value vector for each layer (size=L)
biases []*mat.Dense // neuron bias vector for each layer (size=L-1)
weights []*mat.Dense // weights between each 2-layers (size=L-1)
}
const MaxLayerCount = 255
const LearningRate = 1
// Empty creates a new network where each argument is the number of neurons of a layer
// each param represents a layer, including input/output
func Empty(_layers ...uint) (*Network, error) {
var L = len(_layers)
// check size
if L < 2 {
return nil, ErrMissingLayers
} else if L > MaxLayerCount {
return nil, ErrTooMuchLayers
}
net := &Network{
layers: _layers,
fed: false,
neurons: make([]*mat.Dense, 0),
biases: make([]*mat.Dense, 0),
weights: make([]*mat.Dense, 0),
}
for i, layer := range _layers {
// check neuron count
if layer < 1 {
return nil, ErrEmptyLayer
}
// create neurons
net.neurons = append(net.neurons, mat.NewDense(int(layer), 1, nil))
// do not create weights nor biases for first layer
// (no previous layer to bound to)
if i == 0 {
continue
}
// create biases
biases := make([]float64, 0, layer+1)
for b := uint(0); b < layer; b++ {
rand.Seed(time.Now().UnixNano())
biases = append(biases, rand.Float64())
}
2018-10-11 21:26:36 +00:00
biasesVec := mat.NewDense(int(layer), 1, biases)
net.biases = append(net.biases, biasesVec)
rows, cols := int(layer), int(_layers[i-1])
weights := make([]float64, 0, rows*cols+1)
for v := 0; v < rows*cols; v++ {
rand.Seed(time.Now().UnixNano())
weights = append(weights, rand.Float64())
}
weightsMat := mat.NewDense(rows, cols, weights)
net.weights = append(net.weights, weightsMat)
}
return net, nil
}
// reset neuron values
func (net *Network) reset() {
net.fed = false
for i, _ := range net.neurons {
net.neurons[i] = mat.NewDense(int(net.layers[i]), 1, nil)
}
}
2018-10-11 21:26:36 +00:00
// forward processes a forward propagation from an input vector
// and lets the network in the final processing state
2018-10-11 21:26:36 +00:00
func (net *Network) forward(_input ...float64) error {
// check input size
if len(_input) < net.neurons[0].ColView(0).Len() {
2018-10-11 21:26:36 +00:00
return ErrMissingInput
}
// reset neuron values
net.reset()
// forward input to first layer
for n, l := 0, net.neurons[0].ColView(0).Len(); n < l; n++ {
net.neurons[0].Set(n, 0, _input[n])
}
// process each layer from the previous one
for l, ll := 1, len(net.layers); l < ll; l++ {
// Z = w^l . a^(l-1) + b^l
z := net.neurons[l]
a := net.neurons[l-1] // neurons of previous layer
w := net.weights[l-1] // shifted by 1 because no weights between layers -1 and 0
b := net.biases[l-1] // same shift as weights
z.Mul(w, a)
z.Add(z, b)
z.Apply(sigmoid, z)
}
net.fed = true
2018-10-11 21:26:36 +00:00
return nil
}
// Cost returns the cost from the given output
func (net *Network) Cost(_expect ...float64) (float64, error) {
2018-10-11 21:26:36 +00:00
costVec, err := net.costVec(_expect...)
if err != nil {
return 0, err
}
return mat.Sum(costVec), nil
}
// costVec returns the cost derivative for each output (as a vector)
// from the given _expect data
func (net *Network) costVec(_expect ...float64) (*mat.Dense, error) {
if !net.fed {
return nil, ErrNoState
}
out := net.neurons[len(net.neurons)-1]
// check output size
2018-10-11 21:26:36 +00:00
if len(_expect) < out.ColView(0).Len() {
return nil, ErrMissingOutput
}
2018-10-11 21:26:36 +00:00
// build expect vector
expect := mat.NewDense(len(_expect), 1, _expect)
2018-10-11 21:26:36 +00:00
// process cost = 1/2 * learningRate * (out - expect)^2
cost := new(mat.Dense)
cost.Sub(out, expect) // out - expect
cost.MulElem(cost, cost) // (out - expect)^2
cost.Mul(cost, mat.NewDense(1, 1, []float64{0.5 * LearningRate})) // 1/2 *learningRate * (out - expect)^2
2018-10-11 21:26:36 +00:00
return cost, nil
}
2018-10-11 21:26:36 +00:00
// errorVec returns the cost derivative (also called ERROR) for each
// output (as a vector) from the given _expect data
func (net *Network) errorVec(_expect ...float64) (*mat.Dense, error) {
if !net.fed {
return nil, ErrNoState
}
outLayer := net.neurons[len(net.neurons)-1]
// check output size
2018-10-11 21:26:36 +00:00
if len(_expect) < outLayer.ColView(0).Len() {
return nil, ErrMissingOutput
}
2018-10-11 21:26:36 +00:00
// build expect vector
expect := mat.NewDense(len(_expect), 1, _expect)
2018-10-11 21:26:36 +00:00
// calc cost derivative = 2 * learningRate * (expect - out)
cost := new(mat.Dense)
cost.Sub(expect, outLayer)
cost.Mul(cost, mat.NewDense(1, 1, []float64{2 * LearningRate}))
2018-10-11 21:26:36 +00:00
// return diff (derivative of cost)
return cost, nil
}
2018-10-11 21:26:36 +00:00
// backward processes the backpropagation from the current network state
// and the expected data : _expect
2018-10-11 21:26:36 +00:00
func (net *Network) backward(_expect ...float64) error {
out := net.neurons[len(net.neurons)-1]
2018-10-11 21:26:36 +00:00
// 1. fail on no state (no forward pass applied first)
if !net.fed {
return ErrNoState
}
2018-10-11 21:26:36 +00:00
// fail on invalid _expect size
if len(_expect) != out.ColView(0).Len() {
return ErrMissingOutput
}
2018-10-11 21:26:36 +00:00
// calc ERROR = 0.5 * learningRate * (expect - out)
// *it is in fact the cost derivative
errors, err := net.errorVec(_expect...)
if err != nil {
return err
}
2018-10-11 21:26:36 +00:00
// FOR EACH LAYER (from last to 1)
for l := len(net.layers) - 1; l > 0; l-- {
neurons := net.neurons[l]
previous := net.neurons[l-1]
weights := net.weights[l-1] // from l-1 to l
biases := net.biases[l-1] // at l
2018-10-11 21:26:36 +00:00
// calc GRADIENTS = sigmoid'( neuron[l-1] )
gradients := new(mat.Dense)
gradients.Apply(derivateSigmoid, neurons)
gradients.MulElem(gradients, errors)
gradients.Mul(gradients, mat.NewDense(1, 1, []float64{LearningRate}))
2018-10-11 21:26:36 +00:00
// calc WEIGHTS DELTAS = gradients . previous^T
wdeltas := new(mat.Dense)
wdeltas.Mul(gradients, previous.T())
2018-10-11 21:26:36 +00:00
// update weights
weights.Add(weights, wdeltas)
2018-10-11 21:26:36 +00:00
// adjust biases
biases.Add(biases, gradients)
2018-10-11 21:26:36 +00:00
// update ERRORS
previousErrors := new(mat.Dense)
previousErrors.Clone(errors)
errors.Reset()
errors.Mul(weights.T(), previousErrors)
}
2018-10-11 21:26:36 +00:00
return nil
2018-10-11 21:26:36 +00:00
}
2018-10-11 21:26:36 +00:00
// Guess uses the trained network to guess output from an input
func (net *Network) Guess(_input ...float64) ([]float64, error) {
2018-10-11 21:26:36 +00:00
// process feed forward
err := net.forward(_input...)
if err != nil {
return nil, err
}
2018-10-11 21:26:36 +00:00
// extract output
return net.Output()
}
// Train uses the trained network to train with the _input and tries to learn
// to guess the _expect instead
func (net *Network) Train(_input []float64, _expect []float64) error {
out := net.neurons[len(net.neurons)-1]
2018-10-11 21:26:36 +00:00
// check output size
if len(_expect) != out.ColView(0).Len() {
return ErrMissingOutput
}
2018-10-11 21:26:36 +00:00
// process guess subroutine
_, err := net.Guess(_input...)
if err != nil {
return err
}
2018-10-11 21:26:36 +00:00
// process backward propagation
return net.backward(_expect...)
}
// Output returns the output data (only if the network has been fed)
func (net Network) Output() ([]float64, error) {
if !net.fed {
return nil, ErrNoState
}
out := net.neurons[len(net.neurons)-1]
2018-10-11 21:26:36 +00:00
output := make([]float64, 0, net.layers[len(net.layers)-1])
for n, l := 0, out.ColView(0).Len(); n < l; n++ {
output = append(output, out.At(n, 0))
}
return output, nil
}