automata

package module
v0.0.0-...-b61d9fe Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2017 License: Apache-2.0 Imports: 3 Imported by: 0

README

GoDoc Build Status Coverage Status

Disclaimer: This project is currently a work in progress. As such, the API has not been finalised and breaking changes are frequent.

This project can be used to create neural networks with varying architectures. It has built-in support for LSTMs, Perceptrons and Hopfield networks. You can check the tests or GoDoc to find out more.

Aims

The aim of this project is to implement a generalised neural network library in pure Go.

The goal is to allow for people to experiment and discover new ways to design neural networks. As such, the focus is on:

  • Clarity: People with very basic knowledge on ANNs should be able to use this library.
  • Extensibility: It should be easy to add new layers / activation functions / etc. With this in mind, this project is based off this excellent paper.

Whilst the following are considered, they are not the core goals of this project. If they contend with the core goals of this project, they will lose out:

  • Speed: Parallelisation is not built-in or considered upfront.
  • Memory consumption: Neurons are modelled as actual structs rather than matrix math which results in much higher memory overheads.

Documentation

Index

Constants

View Source
const (
	GateTypeInput = iota
	GateTypeOutput
	GateTypeOneToOne
)

Variables

This section is empty.

Functions

This section is empty.

Types

type BinaryCost

type BinaryCost struct{}

BinaryCost implement the binary (Zero-One Loss) function

func (*BinaryCost) Cost

func (c *BinaryCost) Cost(target, output []float64) float64

Cost of the given output

type ConnID

type ConnID int64

type Connection

type Connection struct {
	ID     ConnID
	From   *Neuron
	To     *Neuron
	Gater  *Neuron
	Weight float64
	Gain   float64
}

Connection represents a connection between two neurons.

func NewConnection

func NewConnection(from, to *Neuron, weight *float64) *Connection

type Coster

type Coster interface {
	// Cost of the given output. The length of target and output will always be the same.
	Cost(target, output []float64) (cost float64)
}

Coster defines a cost function for use with the Trainer.

type CrossEntropyCost

type CrossEntropyCost struct{}

CrossEntropyCost implement the cross entropy function (Eq. 9)

func (*CrossEntropyCost) Cost

func (c *CrossEntropyCost) Cost(target, output []float64) (cost float64)

Cost of the given output

type GateType

type GateType int

type Hopfield

type Hopfield struct {
	Network
}

func NewHopfieldNetwork

func NewHopfieldNetwork(table *LookupTable, size int) *Hopfield

NewHopfieldNetwork creates a new Hopfield network. 'size' dictates the number of input neurons and the number of output neurons. Generally this value should match the number of boolean options in your network e.g. 1 neuron per pixel if recalling images.

func (*Hopfield) Activate

func (h *Hopfield) Activate(input []float64) ([]float64, error)

Activate activates the network with the given input. The output values will always be either 0 or 1 to reflect the semantics of hopfield networks.

type Layer

type Layer struct {
	List        []*Neuron
	ConnectedTo []LayerConnection
	LookupTable *LookupTable
}

Layer represents a group of neurons which activate together.

func NewLayer

func NewLayer(table *LookupTable, size int) Layer

func (*Layer) Activate

func (l *Layer) Activate(inputs []float64) ([]float64, error)

Activate all neurons in the layer.

func (*Layer) Gate

func (l *Layer) Gate(conn *LayerConnection, gateType GateType) error

Gate a connection between two layers.

func (*Layer) Project

func (l *Layer) Project(toLayer *Layer, ltype LayerType) *LayerConnection

Project a connection from this layer to another one.

func (*Layer) Propagate

func (l *Layer) Propagate(rate float64, target []float64) error

Propagate an error on all neurons in this layer.

func (*Layer) SetBias

func (l *Layer) SetBias(bias float64)

SetBias sets the bias of all neurons in this layer to the given value.

type LayerConnection

type LayerConnection struct {
	From        *Layer
	To          *Layer
	Type        LayerType
	Connections map[ConnID]*Connection
	List        []*Connection
}

LayerConnection represents a connection between two layers.

func NewLayerConnection

func NewLayerConnection(from, to *Layer, ltype LayerType) LayerConnection

type LayerType

type LayerType int
const (
	LayerTypeAuto LayerType = iota
	LayerTypeAllToAll
	LayerTypeOneToOne
	LayerTypeAllToElse
)

type LookupTable

type LookupTable struct {
	Neurons     []*Neuron
	Connections []*Connection
}

LookupTable stores mappings of:

  • Neuron IDs to Neurons
  • Connection IDs to Connections

It can be thought of as a global hash map, but is implemented slightly differently for performance reasons.

Rationale: Neurons need references to other neurons/connections (e.g. neighbours). The simplest way to do this is to store a map[ID]*Thing in the Neuron struct itself. This ends up being slow because this is called a lot and each time incurs hashing overheads. It would be better if this was done as a slice, especially since network topologies don't tend to change at runtime so there are no resizing overheads. This means the IDs would be indexes. LookupTable is a massive global slice which provides Neurons/Layers/Connections access to each other via their ID. It's not a true global variable as it is dependency injected at the point of use, allowing the ability of running multiple disconnected networks without sharing the same ID space. Sharing the same LookupTable for all neurons in a network also lowers memory usage from O(n) to O(1), as each neuron is not having to store its own mini lookup table.

func (*LookupTable) GetConnection

func (t *LookupTable) GetConnection(id ConnID) *Connection

GetConnection from the lookup table. Returns nil if the ID does not exist in the table.

func (*LookupTable) GetNeuron

func (t *LookupTable) GetNeuron(id NeuronID) *Neuron

GetNeuron from the lookup table. Returns nil if the ID does not exist in the table.

func (*LookupTable) SetConnection

func (t *LookupTable) SetConnection(conn *Connection) ConnID

SetConnection in the lookup table. Returns the ID for this connection.

func (*LookupTable) SetConnectionWithID

func (t *LookupTable) SetConnectionWithID(id ConnID, conn *Connection)

SetConnectionWithID in the lookup table. If the ID is already associated with a Connection then it is replaced.

func (*LookupTable) SetNeuron

func (t *LookupTable) SetNeuron(neuron *Neuron) NeuronID

SetNeuron in the lookup table. Returns the ID for this neuron.

type MeanSquaredErrorCost

type MeanSquaredErrorCost struct{}

MeanSquaredErrorCost implements the MSE cost function.

func (*MeanSquaredErrorCost) Cost

func (c *MeanSquaredErrorCost) Cost(target, output []float64) (cost float64)

Cost of the given output.

type Network

type Network struct {
	Input  *Layer
	Hidden []Layer
	Output *Layer
}

Network represents an arbitrary artificial neural network.

func NewLSTM

func NewLSTM(table *LookupTable, inputSize int, memoryBlocks []int, outputSize int) *Network

NewLSTM creates a new Long-Short Term Memory network. 'inputSize' is the number of neurons in the input layer. 'outputSize' is the number of neurons in the output layer. The length of 'memoryBlocks' determines how many memory blocks the network will have, whilst the elements determine the number of memory cells in that block.

func NewPerceptronNetwork

func NewPerceptronNetwork(table *LookupTable, sizesOfLayers []int) (*Network, error)

NewPerceptronNetwork creates a new Perceptron with the number of layers equal to the length of the provided slice. The elements in the slice determine the size of each layer. The first element is the input layer. The last element is the output layer. The inbetween elements are hidden layers. The length of the slice must be at least 3 to accomodate an input, hidden and output layer. For example:

// create a network with:
//  - 2 neurons on the input layer
//  - 3 neurons on the first hidden layer
//  - 4 neurons on the second hidden layer
//  - 1 neuron on the output layer
perceptron := automata.NewPerceptron([]int{2,3,4,1})

func (*Network) Activate

func (n *Network) Activate(input []float64) ([]float64, error)

Activate the network with the given neuron, feeding forward to produce an output.

func (*Network) Gate

func (n *Network) Gate(conn *LayerConnection, gateType GateType)

func (*Network) ProjectLayer

func (n *Network) ProjectLayer(layer *Layer, ltype LayerType)

func (*Network) ProjectNetwork

func (n *Network) ProjectNetwork(network *Network, ltype LayerType)

func (*Network) Propagate

func (n *Network) Propagate(rate float64, target []float64) error

Propagate the error through the entire network.

This is subject to the Vanishing Gradient problem. As errors propagate, they will be multiplied by the weights which are typically a fraction between 0-1. This will rapidly diminish the error value as the error continues to propagate, and can be exacerbated depending on the squashing function used, making earlier layers difficult to train.

type Networker

type Networker interface {
	Activate(input []float64) ([]float64, error)
	Propagate(rate float64, target []float64) error
}

Networker represents an abstraction of a Network, which is used primarily with the Trainer to accommodate different Network structs such as Hopfield.

type Neuron

type Neuron struct {
	ID         NeuronID
	Old        float64
	State      float64
	Derivative float64
	Activation float64
	Self       *Connection
	Squash     Squasher
	Bias       float64
	Neighbours []NeuronID

	Inputs    []ConnID
	Projected []ConnID
	Gated     []ConnID

	ErrorResponsibility float64
	ErrorProjected      float64
	ErrorGated          float64

	TraceEligibility []float64 // Efficient form of map[ConnID]float64
	TraceExtended    map[NeuronID]map[ConnID]float64
	TraceInfluences  map[NeuronID][]ConnID

	LookupTable *LookupTable
}

Neuron represents a base unit of work in a neural network.

func NewNeuron

func NewNeuron(table *LookupTable) *Neuron

func (*Neuron) Activate

func (n *Neuron) Activate(input *float64) float64

Activate this neuron with an optional input.

The logic in this function is based on "A generalized LSTM-like training algorithm for second-order recurrent neural networks" See: http://www.overcomplete.net/papers/nn2012.pdf

func (*Neuron) ConnectionForNeuron

func (n *Neuron) ConnectionForNeuron(target *Neuron) *Connection

ConnectionForNeuron returns the connection between the two neurons or nil if there is no connection.

func (*Neuron) Gate

func (n *Neuron) Gate(conn *Connection)

func (*Neuron) Project

func (n *Neuron) Project(targetNeuron *Neuron, weight *float64) *Connection

func (*Neuron) Propagate

func (n *Neuron) Propagate(rate float64, target *float64)

Propagate an error through this neuron. 'rate' is the learning rate for this neuron, target is set if this neuron forms part of an output layer, otherwise is nil.

The logic in this function is based on "A generalized LSTM-like training algorithm for second-order recurrent neural networks" See: http://www.overcomplete.net/papers/nn2012.pdf

type NeuronID

type NeuronID int64

type SquashIdentity

type SquashIdentity struct{}

SquashIdentity implements the identity function.

func (*SquashIdentity) Squash

func (s *SquashIdentity) Squash(x float64, derivate bool) float64

Squash x.

type SquashLogistic

type SquashLogistic struct{}

SquashLogistic implements the logistic function.

func (*SquashLogistic) Squash

func (s *SquashLogistic) Squash(x float64, derivate bool) float64

Squash x.

type SquashRelu

type SquashRelu struct{}

SquashRelu implements the ReLU activation function, which is not subject to the Vanishing Gradient problem . https://en.wikipedia.org/wiki/Rectifier_(neural_networks)

func (*SquashRelu) Squash

func (s *SquashRelu) Squash(x float64, derivate bool) float64

Squash x.

type SquashTanh

type SquashTanh struct{}

SquashTanh implements tanh function.

func (*SquashTanh) Squash

func (s *SquashTanh) Squash(x float64, derivate bool) float64

Squash x.

type Squasher

type Squasher interface {
	// Squash an input x into a suitable output. If 'derivate' is true, the derivative should
	// be returned.
	Squash(x float64, derivate bool) float64
}

Squasher implements a squashing function which can be used as an activation function. Squashing functions modify inputs allowing neurons to model non-linear relationships. This typically involves clamping/bounding the output at one or both ends.

These functions can be very sensitive to the weights which are applied to them which can make training difficult because you need to discover the precise weightings which will squash the input in the way you want. Some functions can exacerbate the Vanishing Gradient problem.

Common squash functions are already implemented.

type TrainSet

type TrainSet struct {
	Input  []float64
	Output []float64
}

type Trainer

type Trainer struct {
	Network      Networker
	LearnRate    float64
	Iterations   int
	MaxErrorRate float64
	CostFunction Coster
}

func (*Trainer) Train

func (t *Trainer) Train(trainingSet []TrainSet) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL