/** * limbic_trainer.ts – LimbicTrainer: gradient-based training for the LimbicModel. * * Trains the affect head to predict affect deltas and reward from * (experience embedding, current state) pairs, using full-batch gradient * descent with AdamW. When a GPUDevice is supplied the optimiser step runs on * the GPU via the shared WEIGHT_UPDATE_WGSL kernel (real WebGPU training); with * no device it uses a numerically-equivalent CPU AdamW so training works * everywhere (CI, Node without @webgpu/node, etc.). * * The objective is MSE (regression), not the cross-entropy used by the * language-model {@link MambaTrainer} — a limbic experience has continuous * targets, not a next-token distribution. */ import type { LimbicModel } from "./limbic_model.js"; /** One training example: an experience and the affect change it should produce. */ export interface LimbicSample { /** Experience embedding (length = model.inputDim). */ input: ArrayLike; /** Affective state at the time of the experience (length = model.stateDim). */ state: ArrayLike; /** Observed affect delta target in (-1, 1) per state dim (length = model.stateDim). */ deltaTarget: ArrayLike; /** Observed scalar reward for the experience. */ reward: number; } export interface LimbicTrainOptions { learningRate?: number; epochs?: number; weightDecay?: number; beta1?: number; beta2?: number; eps?: number; /** Max global gradient L2 norm before the optimiser step. Default 1.0. */ maxGradNorm?: number; /** Per-epoch hook. `gradNorm` is the pre-clip gradient L2 norm this epoch (the * instability early-warning) — computed CPU-side so it's always available, unlike * the GPU trainer where it's opt-in. */ onEpochEnd?: ((epoch: number, loss: number, gradNorm?: number) => void) | null; } export declare class LimbicTrainer { readonly model: LimbicModel; readonly device: GPUDevice | null; private _moments; private _step; private readonly _adamwPipeline; constructor(model: LimbicModel, device?: GPUDevice | null); /** Whether the optimiser step runs on the GPU. */ get gpuTraining(): boolean; private _initMoments; /** * Train on a batch of samples for `epochs` passes. Returns the per-epoch mean * loss (monotonically decreasing on a learnable mapping). Full-batch: grads * accumulate across the whole sequence (recurrent hidden carried, reset per * epoch), are averaged, clipped, then applied once per epoch. */ train(samples: LimbicSample[], opts?: LimbicTrainOptions): Promise; /** Mean MSE loss over samples (no weight update). Hidden resets per sample. */ evaluate(samples: LimbicSample[]): number; /** Clip grads to `maxNorm` in place; returns the PRE-clip L2 norm (the signal). */ private _clipGradients; private _adamwStepCpu; /** AdamW on the GPU via the shared WEIGHT_UPDATE_WGSL kernel. Awaited per step. */ private _adamwStepGpu; } //# sourceMappingURL=limbic_trainer.d.ts.map