fix(SNNBot): average matured snapshot targets instead of multiple updates
Multiple superSpikeUpdate calls per EVALUATE caused oscillation by effectively multiplying the learning rate. Now: accumulate retroTarget via circular mean across all matured snapshots, apply one update with the averaged target and most recent snapshot's SNN state.
This commit is contained in:
@@ -464,9 +464,16 @@ method run*(bot: SNNBot) =
|
|||||||
# Retroactive would-have-hit error signal: find the most recently matured snapshot.
|
# Retroactive would-have-hit error signal: find the most recently matured snapshot.
|
||||||
# A snapshot matures when currentTick >= snapshotTick + ceil(distance / BULLET_SPEED).
|
# A snapshot matures when currentTick >= snapshotTick + ceil(distance / BULLET_SPEED).
|
||||||
# Look up enemy position at impact tick from ring buffer (offset from most-recent write).
|
# Look up enemy position at impact tick from ring buffer (offset from most-recent write).
|
||||||
var retroTarget = bot.lastRelBearing # fallback; overwritten by each matured snapshot (last one used for logging)
|
var retroTarget = bot.lastRelBearing # fallback; overwritten after averaging (used for logging)
|
||||||
var hasMatured = false
|
var hasMatured = false
|
||||||
var keepIdx = 0 # first non-matured snapshot to keep
|
var keepIdx = 0 # first non-matured snapshot to keep
|
||||||
|
# Accumulate retroTarget circular components across all matured snapshots.
|
||||||
|
var sumSin = 0.0; var sumCos = 0.0
|
||||||
|
var matureCount = 0
|
||||||
|
# Most-recent matured snapshot's SNN state (most relevant for weight update).
|
||||||
|
var bestSpikes: array[N_HID, float]
|
||||||
|
var bestVSnap: array[N_HID, float]
|
||||||
|
var bestPreTrace: array[N_IN, float]
|
||||||
for i in 0 ..< bot.snapshots.len:
|
for i in 0 ..< bot.snapshots.len:
|
||||||
let snap = bot.snapshots[i]
|
let snap = bot.snapshots[i]
|
||||||
let travelTicks = int(ceil(snap.distance / BULLET_SPEED))
|
let travelTicks = int(ceil(snap.distance / BULLET_SPEED))
|
||||||
@@ -483,13 +490,23 @@ method run*(bot: SNNBot) =
|
|||||||
let ex = bot.posBuf[foundSlot].x
|
let ex = bot.posBuf[foundSlot].x
|
||||||
let ey = bot.posBuf[foundSlot].y
|
let ey = bot.posBuf[foundSlot].y
|
||||||
let absBearing = directionTo(snap.botX, snap.botY, ex, ey)
|
let absBearing = directionTo(snap.botX, snap.botY, ex, ey)
|
||||||
retroTarget = normalizeRelativeAngle(absBearing - snap.gunHeading)
|
let rt = normalizeRelativeAngle(absBearing - snap.gunHeading)
|
||||||
# Update weights for every matured snapshot immediately; earlier ones were previously discarded.
|
# Accumulate circular mean components
|
||||||
bot.snn.superSpikeUpdate(snap.spikes, snap.vSnap, snap.preTrace, retroTarget)
|
sumSin += sin(degToRad(rt))
|
||||||
|
sumCos += cos(degToRad(rt))
|
||||||
|
inc matureCount
|
||||||
|
# Keep most-recent (highest index) snapshot's SNN state
|
||||||
|
bestSpikes = snap.spikes
|
||||||
|
bestVSnap = snap.vSnap
|
||||||
|
bestPreTrace = snap.preTrace
|
||||||
hasMatured = true
|
hasMatured = true
|
||||||
keepIdx = i + 1 # discard matured snapshots up to and including this one
|
keepIdx = i + 1 # discard matured snapshots up to and including this one
|
||||||
else:
|
else:
|
||||||
break # snapshots are in order; stop at first non-matured
|
break # snapshots are in order; stop at first non-matured
|
||||||
|
# One update per EVALUATE using circular-mean target — stable learning rate regardless of snapshot count.
|
||||||
|
if matureCount > 0:
|
||||||
|
retroTarget = arctan2(sumSin, sumCos) * 180.0 / PI
|
||||||
|
bot.snn.superSpikeUpdate(bestSpikes, bestVSnap, bestPreTrace, retroTarget)
|
||||||
# Discard matured snapshots; immature ones are preserved automatically (keepIdx stays 0
|
# Discard matured snapshots; immature ones are preserved automatically (keepIdx stays 0
|
||||||
# or points past the last matured entry; the rest of bot.snapshots is kept intact).
|
# or points past the last matured entry; the rest of bot.snapshots is kept intact).
|
||||||
if keepIdx > 0:
|
if keepIdx > 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user