chore: remove unused prototypes/ and spike/ dirs
This commit is contained in:
@@ -1,111 +0,0 @@
|
|||||||
## ga_spike.nim — throwaway GA neuroevolution spike: evolve ANN to predict sin(x)
|
|
||||||
## Validates: population init, fitness eval, truncation selection, gaussian
|
|
||||||
## mutation on all weights, single-elite preservation, champion tracking.
|
|
||||||
## Issue #66.
|
|
||||||
|
|
||||||
import std/[math, random, algorithm, strformat]
|
|
||||||
|
|
||||||
const
|
|
||||||
InputDim = 10 # sliding window of 10 past sin values
|
|
||||||
HiddenDim = 4
|
|
||||||
OutputDim = 1
|
|
||||||
NumWeights = (InputDim * HiddenDim + HiddenDim) + # W1 + b1
|
|
||||||
(HiddenDim * OutputDim + OutputDim) # W2 + b2 = 49
|
|
||||||
PopSize = 200
|
|
||||||
Generations = 200
|
|
||||||
TopFrac = 0.2 # keep top 20%
|
|
||||||
Sigma = 0.01 # gaussian mutation sigma on ALL weights
|
|
||||||
EvalPoints = 50 # fitness eval sample size
|
|
||||||
|
|
||||||
type Individual = object
|
|
||||||
weights: seq[float64]
|
|
||||||
fitness: float64
|
|
||||||
|
|
||||||
proc forward(w: seq[float64]; input: array[InputDim, float64]): float64 =
|
|
||||||
## 10->4->1 tanh ANN, flat weight layout: W1[40], b1[4], W2[4], b2[1]
|
|
||||||
var hidden: array[HiddenDim, float64]
|
|
||||||
for h in 0..<HiddenDim:
|
|
||||||
var s = w[InputDim * HiddenDim + h] # b1[h]
|
|
||||||
for i in 0..<InputDim:
|
|
||||||
s += w[h * InputDim + i] * input[i] # W1[h,i]
|
|
||||||
hidden[h] = tanh(s)
|
|
||||||
var s = w[InputDim * HiddenDim + HiddenDim + HiddenDim] # b2[0]
|
|
||||||
for h in 0..<HiddenDim:
|
|
||||||
s += w[InputDim * HiddenDim + HiddenDim + h] * hidden[h] # W2[h]
|
|
||||||
result = tanh(s)
|
|
||||||
|
|
||||||
proc evaluate(ind: var Individual; rng: var Rand) =
|
|
||||||
## Fitness = -MSE on EvalPoints random samples of sin prediction.
|
|
||||||
var mse = 0.0
|
|
||||||
for _ in 0..<EvalPoints:
|
|
||||||
let x = rng.rand(0.0 .. 20.0 * PI)
|
|
||||||
var input: array[InputDim, float64]
|
|
||||||
for i in 0..<InputDim:
|
|
||||||
input[i] = sin(x - float64(InputDim - 1 - i))
|
|
||||||
let target = sin(x)
|
|
||||||
let pred = forward(ind.weights, input)
|
|
||||||
mse += (pred - target) * (pred - target)
|
|
||||||
ind.fitness = -mse / EvalPoints.float64
|
|
||||||
|
|
||||||
proc mutate(ind: var Individual; rng: var Rand) =
|
|
||||||
## Additive gaussian on ALL weights, per GA-parameters research.
|
|
||||||
for i in 0..<ind.weights.len:
|
|
||||||
ind.weights[i] += rng.gauss(0.0, Sigma)
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
var rng = initRand(42)
|
|
||||||
|
|
||||||
# Init population: random weights in [-0.5, 0.5]
|
|
||||||
var pop = newSeq[Individual](PopSize)
|
|
||||||
for i in 0..<PopSize:
|
|
||||||
pop[i].weights = newSeq[float64](NumWeights)
|
|
||||||
for j in 0..<NumWeights:
|
|
||||||
pop[i].weights[j] = rng.rand(-0.5 .. 0.5)
|
|
||||||
|
|
||||||
echo &"GA spike: {PopSize} individuals, {NumWeights} weights, {Generations} gens, sigma={Sigma}"
|
|
||||||
|
|
||||||
for gen in 0..<Generations:
|
|
||||||
# Evaluate
|
|
||||||
for i in 0..<PopSize:
|
|
||||||
pop[i].evaluate(rng)
|
|
||||||
|
|
||||||
# Sort descending by fitness (higher = better)
|
|
||||||
pop.sort(proc(a, b: Individual): int = cmp(b.fitness, a.fitness))
|
|
||||||
|
|
||||||
if gen mod 20 == 0 or gen == Generations - 1:
|
|
||||||
echo &" gen {gen:3d} best_fitness={pop[0].fitness:.6f} (MSE={-pop[0].fitness:.6f})"
|
|
||||||
|
|
||||||
# Selection: keep top 20%
|
|
||||||
let nParents = max(1, int(PopSize.float64 * TopFrac))
|
|
||||||
|
|
||||||
# Next generation: elite(1) + mutated children from parents
|
|
||||||
var next = newSeq[Individual](PopSize)
|
|
||||||
next[0] = pop[0] # single elite, unchanged
|
|
||||||
for i in 1..<PopSize:
|
|
||||||
let parent = rng.rand(0..<nParents)
|
|
||||||
next[i].weights = pop[parent].weights # clone parent
|
|
||||||
next[i].mutate(rng)
|
|
||||||
pop = next
|
|
||||||
|
|
||||||
# Final evaluation of champion
|
|
||||||
pop[0].evaluate(rng)
|
|
||||||
echo &"\nChampion fitness: {pop[0].fitness:.6f} (MSE={-pop[0].fitness:.6f})"
|
|
||||||
|
|
||||||
# Print predictions vs actual
|
|
||||||
echo "\nPredictions (sample):"
|
|
||||||
var totalErr = 0.0
|
|
||||||
let nSamples = 20
|
|
||||||
for i in 0..<nSamples:
|
|
||||||
let x = float64(i) * 1.0
|
|
||||||
var input: array[InputDim, float64]
|
|
||||||
for j in 0..<InputDim:
|
|
||||||
input[j] = sin(x - float64(InputDim - 1 - j))
|
|
||||||
let target = sin(x)
|
|
||||||
let pred = forward(pop[0].weights, input)
|
|
||||||
totalErr += abs(pred - target)
|
|
||||||
echo &" x={x:5.1f} sin(x)={target:+.4f} pred={pred:+.4f} err={abs(pred-target):.4f}"
|
|
||||||
|
|
||||||
let avgErr = totalErr / nSamples.float64
|
|
||||||
echo &"\nAverage absolute error: {avgErr:.4f}"
|
|
||||||
assert avgErr < 0.1, &"Champion avg error {avgErr:.4f} >= 0.1 — evolution did not converge"
|
|
||||||
echo "PASS: assert avgErr < 0.1"
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# Package
|
|
||||||
version = "0.1.0"
|
|
||||||
author = "SirStone"
|
|
||||||
description = "Arraymancer viability spike — XOR perceptron"
|
|
||||||
license = "MIT"
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
requires "nim >= 2.0.0"
|
|
||||||
requires "arraymancer >= 0.7.0"
|
|
||||||
Binary file not shown.
@@ -1,63 +0,0 @@
|
|||||||
import arraymancer
|
|
||||||
import std/[strformat, times]
|
|
||||||
|
|
||||||
# XOR dataset — [4,2] inputs, [4,1] targets
|
|
||||||
let xData = [[0.0'f32, 0.0'f32],
|
|
||||||
[0.0'f32, 1.0'f32],
|
|
||||||
[1.0'f32, 0.0'f32],
|
|
||||||
[1.0'f32, 1.0'f32]].toTensor()
|
|
||||||
|
|
||||||
let yData = [[0.0'f32], [1.0'f32], [1.0'f32], [0.0'f32]].toTensor()
|
|
||||||
|
|
||||||
var ctx = newContext Tensor[float32]
|
|
||||||
|
|
||||||
let l1 = ctx.init(Linear[float32], 2, 4)
|
|
||||||
let l2 = ctx.init(Linear[float32], 4, 1)
|
|
||||||
|
|
||||||
type XorNet = object
|
|
||||||
l1: Linear[float32]
|
|
||||||
l2: Linear[float32]
|
|
||||||
|
|
||||||
var net = XorNet(l1: l1, l2: l2)
|
|
||||||
var adam = optimizer(net, Adam, learning_rate = 0.01'f32)
|
|
||||||
|
|
||||||
let t0 = cpuTime()
|
|
||||||
|
|
||||||
for epoch in 1..1000:
|
|
||||||
let xVar = ctx.variable(xData)
|
|
||||||
let yVar = ctx.variable(yData)
|
|
||||||
|
|
||||||
# Forward
|
|
||||||
let h = l1.forward(xVar).sigmoid()
|
|
||||||
let pred = l2.forward(h).sigmoid()
|
|
||||||
|
|
||||||
# MSE loss: mean((pred - y)^2)
|
|
||||||
let diff = pred - yVar
|
|
||||||
let loss = mean(diff *. diff)
|
|
||||||
|
|
||||||
if epoch mod 200 == 0:
|
|
||||||
echo &"Epoch {epoch:4d} loss = {loss.value[0]:.6f}"
|
|
||||||
|
|
||||||
loss.backprop()
|
|
||||||
adam.update()
|
|
||||||
|
|
||||||
let trainTime = cpuTime() - t0
|
|
||||||
|
|
||||||
# Inference (no grad)
|
|
||||||
echo ""
|
|
||||||
echo "Final predictions:"
|
|
||||||
let xVar = ctx.variable(xData)
|
|
||||||
let hInf = l1.forward(xVar).sigmoid()
|
|
||||||
let preds = l2.forward(hInf).sigmoid()
|
|
||||||
|
|
||||||
var allCorrect = true
|
|
||||||
for i in 0..<4:
|
|
||||||
let p = preds.value[i, 0]
|
|
||||||
let rounded = if p >= 0.5'f32: 1 else: 0
|
|
||||||
let expected = [0,1,1,0][i]
|
|
||||||
if rounded != expected: allCorrect = false
|
|
||||||
echo &" [{xData[i,0].int},{xData[i,1].int}] -> {p:.4f} (rounded: {rounded}, expected: {expected})"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo &"All correct: {allCorrect}"
|
|
||||||
echo &"Training time: {trainTime:.2f}s"
|
|
||||||
Reference in New Issue
Block a user