godot_projects/02_GraduallyDescentIntoTheSingularity/MLP.gd
2023-05-14 20:45:24 +02:00

113 lines
3.1 KiB
GDScript

extends Node
class_name MLP
@export var weights: Array[Array] = []
@export var biases: Array[Array] = []
@export var learning_rate: float = 0.01
func _init(sizes: Array = [3,2]) -> void:
randomize()
weights = []
biases = []
for i in range(sizes.size() - 1):
weights.append([])
biases.append([])
for _j in range(sizes[i]):
weights[i].append([])
for _k in range(sizes[i + 1]):
weights[i][_j].append(randf() * 2 - 1)
biases[i].append(randf() * 2 - 1)
# Feedforward: compute the output of the MLP for a given input.
func feedforward(input: Array) -> Array:
var a = input
for i in range(weights.size()):
var dp = dot_product(a, weights[i])
for j in range(a.size()):
a[j] = sigmoid(dp[j] + + biases[i][j])
return a
# Backpropagation: update the weights and biases based on the input and target output.
func backpropagate(input: Array, target: Array) -> void:
var nabla_b: Array = []
var nabla_w: Array = []
# Feedforward
var activation:Array[float] = input
var activations: Array[Array] = [input] # List to store all the activations, layer by layer
var zs: Array[Array] = [] # List to store all the z vectors, layer by layer
var z: int = 0
var sp: float = 0.0
var delta: Array = []
for i in range(weights.size()):
var dp = dot_product(activation, weights[i])
var zs_i:Array[float] = []
var activations_i: Array[float] = []
for j in range(dp.size()):
z = dp[j] + biases[i][j]
zs_i.append(z)
activations_i.append(sigmoid(z))
activation = activations_i
zs.append(zs_i)
activations.append(activations_i)
# Backward pass
sp = sigmoid_prime(zs[zs.size() - 1])
for cd in cost_derivative(activations[activations.size() - 1], target):
delta.append(cd * sp)
nabla_b.append(delta)
nabla_w.append(dot_product(delta, activations[activations.size() - 2].transpose()))
for l in range(2, weights.size() + 1):
z = zs[zs.size() - l]
sp = sigmoid_prime(z)
delta = []
for dp in dot_product(weights[weights.size() - l + 1].transpose(), delta):
delta.append(dp * sp)
nabla_b.append(delta)
nabla_w.append(dot_product(delta, activations[activations.size() - l - 1].transpose()))
# Update weights and biases
for i in range(weights.size()):
weights[i] -= learning_rate * nabla_w[nabla_w.size() - i - 1]
biases[i] -= learning_rate * nabla_b[nabla_b.size() - i - 1]
func sigmoid(x: float) -> float:
return 1.0 / (1.0 + exp(-x))
func sigmoid_prime(x: float) -> float:
return sigmoid(x) * (1 - sigmoid(x))
func cost_derivative(output_activations: Array, y: Array) -> Array:
var output: Array = []
for i in range(0, output_activations.size()):
output[i] = output_activations[i] - y[i]
return output
func dot_product(a: Array, b: Array) -> Array:
var result: Array = []
for i in range(a.size()):
var sum: float = 0.0
for j in range(a[i].size()):
sum += a[i][j] * b[j]
result.append(sum)
return result
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass