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