Skip to content

Layers, Losses, Optimizers

The Layer interface

Every layer implements the same interface:

type Layer interface {
    Init(inputCols int, rng *rand.Rand) (outputCols int, err error)
    Forward(input *Matrix) (*Matrix, error)
    Backward(gradOutput *Matrix) (*Matrix, error)
    Grads() (*Matrix, []Float)
    Params() (*Matrix, []Float)
    SetParams(weights *Matrix, bias []Float) error
}

Init is called by Sequential.Compile, which threads the column count through the stack — that is why NewDense(8) only names its output width. Layers reuse their forward/backward scratch buffers across training steps, so GC stays out of the training loop (a full MLP step runs in ~29 allocations); Predict always returns freshly allocated results.

Layers

Layer Constructor Notes
Dense layer.NewDense(outCols) Fully connected; Glorot/He-style initialization
Embedding layer.NewEmbedding(vocabSize, dim) Input rows are integer token ids stored in Float; looked-up vectors concatenate across the row
Conv2D layer.NewConv2D(outC, kernel, stride, pad) im2col + the Dot kernel; input shape comes from CompileImage
MaxPool2D layer.NewMaxPool2D(size) Input shape comes from CompileImage
BatchNorm layer.NewBatchNorm() Running statistics are saved with the model
LayerNorm layer.NewLayerNorm() Per-row normalization
Dropout layer.NewDropout(rate) Active only during training

Conv2D and MaxPool2D treat each row as a channel-major image: index = (channel*height + y)*width + x. Dropout and BatchNorm switch automatically between training behavior (inside Fit/FitStep) and inference behavior (inside Predict).

Embedding keeps the matrix-only API: each input row is a token-id sequence (build it safely with tensai.NewMatrixFromInts, which verifies every id survives the float32 conversion exactly), and the layer concatenates the looked-up embedding vectors across columns. For example, Compile(4, ...) plus NewEmbedding(vocab, 8) turns an Mx4 token-id matrix into an Mx32 dense feature matrix that can feed LayerNorm, GELU, and Dense.

Activations

Activations are layers too — add them like any other:

Activation Usage
ReLU &layer.ReLU{}
LeakyReLU layer.NewLeakyReLU(0.01)
GELU &layer.GELU{} (vectorized erf in the SIMD build)
Sigmoid &layer.Sigmoid{}
Tanh &layer.Tanh{}
Softmax &layer.Softmax{} (usually you want SoftmaxCrossEntropy instead)

Loss functions

Loss For Targets
loss.MeanSquaredError{} Regression Same shape as the prediction
loss.SoftmaxCrossEntropy{} Multi-class classification Mx1 matrix of class indices
loss.BinaryCrossEntropy{} Binary targets Same shape as the prediction

Softmax is applied inside SoftmaxCrossEntropy (with the row maximum subtracted for numerical stability), so the model ends with a plain Dense and Predict returns raw logits — use argmax for the class.

Optimizers

Optimizer Constructor
Momentum SGD optim.NewSGD(lr, momentum)
Adam optim.NewAdam(lr)
AdamW optim.NewAdamW(lr, weightDecay) — decoupled weight decay

An Optimizer is just the rule's configuration: the model asks it for one optim.Updater per parameter pair, and the updater carries that pair's state (momentum buffers, Adam moments, step count). A custom optimizer therefore implements two small methods, New() Updater and the updater's Step. The Adam/AdamW and SGD parameter updates are AVX2-vectorized kernels in the SIMD build.

k-NN baseline

knn.New(k) is a no-training baseline classifier — Fit just stores the data, and Predict builds the distance matrix on the same SIMD matmul kernel. On the MNIST 5000-sample subset it scores ~91% against ~92% for the MLP and ~95% for the CNN — a useful sanity floor next to the networks.