-
Notifications
You must be signed in to change notification settings - Fork 0
Training and Diagnostics
When a model fails, change one assumption at a time. First prove that data shapes and targets are correct; then verify the forward path; only then tune optimization. This page turns that order into a repeatable workflow and points to the nuNN code that defines each metric and update.

- Establish a simple baseline.
- Inspect one input, target, and prediction end to end.
- Overfit a tiny subset.
- Train on the full training set.
- Evaluate with updates disabled on held-out data.
- Save, reload into a fresh object, and compare outputs.
- Only after correctness is established, compare speed, backend, or optimizer.
A model that cannot overfit a few samples usually has a shape, target, preprocessing, loss, or update-path problem. More epochs rarely repair one of those.
| Check | Typical failure |
|---|---|
| input width equals model input size | wrong flattening, missing feature, stale topology |
| target width equals output size | scalar class used where one-hot vector is required |
| values use the intended scale | bytes left in [0,255], mixed physical units |
| training and inference apply identical preprocessing | OCR drawing resampled differently from MNIST |
| sequence state resets at real boundaries | context leaks across independent samples |
| terminal RL transitions are marked | target incorrectly bootstraps beyond episode end |
Print dimensions and a few values before inspecting gradients.
The feedforward loss definitions are in nu_costfuncs.h, nu_costfuncs.cc, and nu_mlpmatrixnn.cc.
There is one historical scaling difference to remember:
| Method | Current value |
|---|---|
MlpNN::calcMSE / common cf::calcMSE
|
0.5 * sum((output - target)^2) |
MlpMatrixNN::calcMSE |
sum((output - target)^2) / output_size |
both calcCrossEntropy implementations |
mean binary cross-entropy over outputs |
Do not compare raw MSE numbers or early-stopping thresholds between MlpNN and MlpMatrixNN without accounting for this scaling. Within one fixed model and output size, the curve remains useful.
For MLP classification, CrossEntropy means binary cross-entropy per sigmoid output, not categorical softmax. The constructors reject cross-entropy with a non-sigmoid output layer.
| Shape | Likely interpretation | First checks |
|---|---|---|
| smooth fall, then plateau | normal convergence or capacity limit | held-out metric; then epochs or learning rate |
| oscillation | updates are too aggressive or data order is influential | lower learning rate; inspect scaling; use batches |
| rapid explosion or non-finite value | unstable update, input scale, or target mismatch | learning rate, normalization, gradient clip |
| flat from the first update | no useful gradient or training path not called | target, activation, loss, backPropagate() / trainBatch()
|
| training improves, held-out metric stalls | overfitting or distribution mismatch | split integrity, preprocessing, capacity |
| good before save, bad after load | persistence or file-selection problem | fresh-object round trip and exact filename |
| RL batch loss falls but returns do not improve | Q fit does not imply policy improvement | rewards, termination, exploration, state encoding |
A single final scalar hides whether training was stable. Record at least epoch, training loss, held-out metric, and elapsed time.
A gradient update has the form:
parameters <- parameters - learning_rate * gradient
nuNN implementations may express the stored delta with the opposite sign and add the update; the effective direction is still loss-reducing.
- Too small: the curve falls slowly or appears flat on a short run.
- Too large: the curve oscillates, diverges, or becomes non-finite.
- Changing activation, batch size, or optimizer can change the useful range.
Reduce the learning rate before adding complexity when a previously working topology becomes unstable.
MlpNN and SGD-mode MlpMatrixNN can add momentum:
update_t = learning_rate * descent_direction
+ momentum * update_(t-1)
parameter <- parameter + update_t
Momentum can accelerate a persistent direction and damp alternating gradients. It can also amplify an excessive learning rate.
The Eigen path in MlpMatrixNN additionally supports Adam:
net.setOptimizer(
nu::MlpMatrixNN::Optimizer::Adam,
0.9,
0.999,
1e-8
);Calling setOptimizer() resets the stored moments and step counter. JSON persistence records whether the model uses SGD or Adam; moment tensors themselves are not part of the current model format. The OpenCL backend always performs SGD.
| Batch size | Effect |
|---|---|
| 1 | maximum update noise, simple sample-level inspection |
| small batch | smoother estimate with frequent updates |
| large batch | stable matrix work but fewer parameter updates per epoch |
MlpMatrixNN::trainBatch(inputs, targets) averages gradients over the batch. Keep the two containers non-empty and equally sized, and flush a final partial batch.
When comparing batch sizes, decide whether to hold epochs, examples seen, or parameter-update count constant. They are different experimental budgets.
NNTrainer::runTraining trains complete epochs and compares the mean callback cost for the epoch with minErr:
nu::MlpTrainer trainer(net, 20000, 0.01);
size_t completedEpochs = trainer.runTraining(
samples,
[](nu::MlpNN& model, const auto& target) {
return model.calcMSE(target);
}
);A negative minErr disables threshold stopping. The optional p2use argument clamps to [0,1] and selects the leading fraction of the supplied iteration order; it is not a randomized validation split.
The progress callback runs after the current sample update and can stop training by returning true. Its behavior is covered by test_trainer.cc.
| Task | Output | Loss |
|---|---|---|
| unbounded regression | linear | MSE |
| bounded scalar reconstruction | depends on data range | MSE or model-specific loss |
| binary / independent labels | sigmoid | binary cross-entropy or MSE |
| one-of-N in MLP | N sigmoid outputs, choose argmax | binary cross-entropy or MSE |
| recurrent classification | softmax RnnOutput
|
cross-entropy |
| DQN action values | linear | Bellman-target MSE |
ReLU and Leaky ReLU are useful hidden activations, but a linear output is necessary when targets can be negative or unbounded. In an MLP, cross-entropy requires sigmoid output by API contract.
MlpMatrixNN exposes the resolved backend through getBackend():
auto backend = net.getBackend();Auto is a request to choose, not a backend that remains unresolved. After construction it reports Eigen or OpenCL.
For a backend parity check:
- create two networks with the same topology;
- copy identical weights and biases with
getLayerW/B()andsetLayerW/B(); - feed the same input;
- compare outputs within a floating-point tolerance.
CPU and GPU runs need not be bit-identical. Large disagreement suggests a synchronization, layout, or runtime issue.
Use streams and a fresh object:
std::stringstream json;
trained.toJson(json);
nu::MlpNN loaded;
loaded.loadJson(json);
trained.setInputVector(sample);
trained.feedForward();
loaded.setInputVector(sample);
loaded.feedForward();
nu::Vector expected;
nu::Vector actual;
trained.copyOutputVector(expected);
loaded.copyOutputVector(actual);Compare topology, activations, loss choice, and every output within a small tolerance. The repository tests this path for MlpNN and MlpMatrixNN.
For a file-based check, also log the absolute filename loaded by the application. Many apparent serialization bugs are stale-file bugs.
- call
resetState()before every independent sequence; - compare teacher-forced one-step metrics with autoregressive rollout;
- record truncation length and gradient clip;
- inspect whether loss is averaged across the whole sequence.
- calculate every channel and length transition before constructing the FC head;
- confirm channel-major flat layout;
- remember that valid convolution shrinks length and pooling discards incomplete remainder windows.
- verify token IDs and fixed context length;
- require
dModel % numHeads == 0; - ensure next-token targets are shifted exactly once;
- compare loss with generated samples at a documented temperature.
- standardize before distance- or variance-based methods;
- use the metric defined by the objective;
- inspect centers, components, reconstructions, or map weights, not only one scalar.
- separate episodic return, success rate, and neural batch loss;
- enforce an episode step limit;
- evaluate greedily;
- run multiple seeds and record the exploration schedule.
For every result, save:
model and topology
activation and loss
optimizer, learning rate, momentum
batch size / BPTT window / replay settings
training and evaluation sample counts
preprocessing
random seed when exposed
epochs or environment steps
training loss and held-out metric
model filename and source revision
That record is usually enough to reproduce a surprising curve or discover the missing difference between two runs.
Use Implementation Map to open the exact training source, MNIST and OCR for a full supervised pipeline, or Examples Gallery for controlled experiments.