## Neural network
##
## It is a basic neural network with three layers (input, hidden and output).
## The network is randomly initialized, it is possible to train it by passing it
## the expected result for some input.
class_name NeuralNetwork
var input_nodes: int
var hidden_nodes: int
var output_nodes: int
var weights_input_hidden: Matrix
var weights_hidden_output: Matrix
var bias_hidden: Matrix
var bias_output: Matrix
var learning_rate: float
var activation_function: Callable
var activation_dfunction: Callable
func _init(_input_nodes: int,_hidden_nodes: int,_output_nodes: int):
input_nodes = _input_nodes;
hidden_nodes = _hidden_nodes;
output_nodes = _output_nodes;
weights_input_hidden = Matrix.rand(Matrix.new(hidden_nodes, input_nodes))
weights_hidden_output = Matrix.rand(Matrix.new(output_nodes, hidden_nodes));
bias_hidden = Matrix.rand(Matrix.new(hidden_nodes, 1));
bias_output = Matrix.rand(Matrix.new(output_nodes, 1));
set_learning_rate()
set_activation_function()
func set_learning_rate(_learning_rate: float = 0.1) -> void:
learning_rate = _learning_rate
func set_activation_function(callback: Callable = Callable(Activation, "sigmoid"), dcallback: Callable = Callable(Activation, "dsigmoid")) -> void:
activation_function = callback
activation_dfunction = dcallback
func predict(input_array: Array[float]) -> Array[float]:
var inputs := Matrix.from_array(input_array)
var hidden := Matrix.product(weights_input_hidden, inputs)
hidden = Matrix.add(hidden, bias_hidden)
hidden = Matrix.map(hidden, activation_function)
var output = Matrix.product(weights_hidden_output, hidden)
output = Matrix.add(output, bias_output)
output = Matrix.map(output, activation_function)
return Matrix.to_array(output)
func train(input_array: Array[float], target_array: Array[float]):
var inputs := Matrix.from_array(input_array)
var targets := Matrix.from_array(target_array)
var hidden := Matrix.product(weights_input_hidden, inputs);
hidden = Matrix.add(hidden, bias_hidden)
hidden = Matrix.map(hidden, activation_function)
var outputs := Matrix.product(weights_hidden_output, hidden)
outputs = Matrix.add(outputs, bias_output)
outputs = Matrix.map(outputs, activation_function)
var output_errors = Matrix.subtract(targets, outputs)
var gradients = Matrix.map(outputs, activation_dfunction)
gradients = Matrix.multiply(gradients, output_errors)
gradients = Matrix.scalar(gradients, learning_rate)
var hidden_t = Matrix.transpose(hidden)
var weight_ho_deltas = Matrix.product(gradients, hidden_t)
weights_hidden_output = Matrix.add(weights_hidden_output, weight_ho_deltas)
bias_output = Matrix.add(bias_output, gradients)
var weights_hidden_output_t = Matrix.transpose(weights_hidden_output)
var hidden_errors = Matrix.product(weights_hidden_output_t, output_errors)
var hidden_gradient = Matrix.map(hidden, activation_dfunction)
hidden_gradient = Matrix.multiply(hidden_gradient, hidden_errors)
hidden_gradient = Matrix.scalar(hidden_gradient, learning_rate)
var inputs_t = Matrix.transpose(inputs)
var weight_ih_deltas = Matrix.product(hidden_gradient, inputs_t)
weights_input_hidden = Matrix.add(weights_input_hidden, weight_ih_deltas)
bias_hidden = Matrix.add(bias_hidden, hidden_gradient)
static func reproduce(a: NeuralNetwork, b: NeuralNetwork) -> NeuralNetwork:
var result = NeuralNetwork.new(a.input_nodes, a.hidden_nodes, a.output_nodes)
result.weights_input_hidden = Matrix.random(a.weights_input_hidden, b.weights_input_hidden)
result.weights_hidden_output = Matrix.random(a.weights_hidden_output, b.weights_hidden_output)
result.bias_hidden = Matrix.random(a.bias_hidden, b.bias_hidden)
result.bias_output = Matrix.random(a.bias_output, b.bias_output)
return result
static func mutate(nn: NeuralNetwork, callback: Callable) -> NeuralNetwork:
var result = NeuralNetwork.new(nn.input_nodes, nn.hidden_nodes, nn.output_nodes)
result.weights_input_hidden = Matrix.map(nn.weights_input_hidden, callback)
result.weights_hidden_output = Matrix.map(nn.weights_hidden_output, callback)
result.bias_hidden = Matrix.map(nn.bias_hidden, callback)
result.bias_output = Matrix.map(nn.bias_output, callback)
return result
static func copy(nn : NeuralNetwork) -> NeuralNetwork:
var result = NeuralNetwork.new(nn.input_nodes, nn.hidden_nodes, nn.output_nodes)
result.weights_input_hidden = Matrix.copy(nn.weights_input_hidden)
result.weights_hidden_output = Matrix.copy(nn.weights_hidden_output)
result.bias_hidden = Matrix.copy(nn.bias_hidden)
result.bias_output = Matrix.copy(nn.bias_output)
return result