8.12.3.8. Anomaly Detection On-Device Training Library
8.12.3.8.1. Table of Contents
8.12.3.8.2. 1. Overview
The anomaly_detection_odt library is a task-specific library built on top of the core ondevice_training_lib. It provides a complete anomaly detection workflow for autoencoder-based models running on TI microcontrollers.
While the core library handles the raw neural network operations (forward, backward, weight update), this library manages everything else needed for a working anomaly detection system:
A state machine with three operating modes (training, threshold calculation, inference)
Training orchestration with epoch structure, train/validate phase switching, and batch management
EMA-smoothed early stopping with configurable patience and improvement delta
Threshold calculation using percentile-based or Gaussian-based methods
Majority voting inference with sliding window to reduce false positives
Rolling statistics for runtime monitoring
8.12.3.8.2.1. Where it fits
┌─────────────────────────────────────────┐
│ Application Code │
│ (application_main.c) │ ← Calls this library's API
├─────────────────────────────────────────┤
│ Anomaly Detection ODT │
│ (anomaly_detection_odt) │ ← THIS LIBRARY: state machine, training loop
├─────────────────────────────────────────┤
│ Core Training Library │
│ (ondevice_training_lib) │ ← Forward, backward, SGD, loss
├─────────────────────────────────────────┤
│ Model Configuration │
│ (trainable_model_config.h/.c) │ ← Layer definitions, weights, buffers
└─────────────────────────────────────────┘
8.12.3.8.2.2. Source files
File |
Location |
Purpose |
|---|---|---|
|
|
API declarations, all data structures, mode/phase enums |
|
|
All function implementations |
8.12.3.8.3. 2. State Machine
The library operates as a state machine with three modes. The application drives transitions by calling the appropriate API functions.
┌──────────────────────────┐ StartTraining() ┌──────────────────────────┐
│ │ ──────────────────────►│ │
│ MODE_INFERENCE │ │ MODE_TRAINING │
│ │ │ │
│ Detect anomalies using │ │ ┌──────────────────┐ │
│ trained model + │ │ │ PHASE_TRAIN │ │
│ majority voting │ │ │ forward+backward│ │
│ │ │ │ weight updates │ │
└──────────────────────────┘ │ └────────┬─────────┘ │
▲ │ │ epoch done │
│ │ ▼ │
│ │ ┌──────────────────┐ │
│ │ │ PHASE_VALIDATE │ │
│ │ │ forward only │ │
│ │ │ EMA + early stop│ │
│ │ └──────────────────┘ │
│ │ │
│ └────────────┬─────────────┘
│ │
│ │ StopTraining()
│ │ or early stopping
│ ▼
│ ┌──────────────────────────┐
│ Auto-transition after │ │
│ threshold_samples collected │ MODE_THRESHOLD_CALC │
◄───────────────────────────────────────│ │
│ Collect normal errors, │
│ compute threshold │
└──────────────────────────┘
Transitions:
From |
To |
Trigger |
|---|---|---|
|
|
|
|
|
|
|
|
Automatic, after |
Sub-states within MODE_TRAINING:
Training alternates between two phases within each epoch:
PHASE_TRAIN— forward + backward pass, weight updatesPHASE_VALIDATE— forward pass only, loss evaluation, early stopping check
One epoch = batches_per_epoch training batches + 1 validation batch.
8.12.3.8.4. 3. Data Structures
8.12.3.8.4.1. 3.1 AnomalyDetectionConfig_t — User Configuration
This structure is provided by the application at initialization time. It contains all configurable hyperparameters.
Field |
Type |
Example |
Description |
|---|---|---|---|
|
|
0.0001 |
SGD learning rate. Passed to |
|
|
10 |
Epochs without improvement before early stopping. Must be > 0 |
|
|
0.5 |
Minimum decrease in EMA validation loss to count as improvement. Prevents tiny fluctuations from resetting the patience counter |
|
|
0.5 |
EMA smoothing factor for validation loss. Range: (0, 1]. Higher = more reactive to recent values. Lower = more smoothing |
|
|
10 |
Number of training batches per epoch. Total training samples per epoch = |
|
|
6.98 |
Initial anomaly threshold from PC-side training ( |
|
|
0.99 |
Percentile for threshold calculation (e.g., 0.99 = 99th percentile). Range: [0, 1] |
|
|
4.0 |
Number of standard deviations above mean for Gaussian threshold. Higher = fewer false positives but may miss subtle anomalies |
|
|
1 |
Threshold method selector: 1 = percentile, 0 = Gaussian |
|
|
500 |
Number of normal samples to collect for threshold calculation. Max value limited to 1000 for now |
|
|
1 |
Sliding window size for majority voting. 1 = no voting (each frame decides independently). Range: 1–10 |
|
|
100 |
Frames per statistics reporting window. After this many frames, stats are logged and reset |
Validation at init: All fields are validated. Invalid values cause AnomalyDetection_Init to return -1 with an error log.
8.12.3.8.4.2. 3.2 AnomalyDetectionContext_t — Internal State
This is the main context structure that holds all internal state. The application creates one instance and passes it to every API call. Key groups of fields:
Model context:
model_ctx— theModelContext_tfromondevice_training_lib, containing layers, weights, buffers
Mode and phase:
current_mode—MODE_INFERENCE,MODE_TRAINING, orMODE_THRESHOLD_CALCcurrent_phase—PHASE_TRAINorPHASE_VALIDATE(meaningful only in training mode)
Training counters:
train_sample_count— samples accumulated in current training batchval_sample_count— samples accumulated in current validation batchepoch_num— total epochs completedbatch_num_curr_epoch— training batches completed in current epoch
Loss tracking:
train_loss_sum— accumulated training loss for current epochval_loss_sum— accumulated validation loss for current batchbest_val_loss— best EMA validation loss achieved (initialized to 1e10)best_epoch_num— epoch where best validation loss was achieved
Early stopping:
no_improvement_count— consecutive epochs without improvementema_val_loss— EMA-smoothed validation lossema_initialized— flag indicating whether EMA has been initialized (first epoch sets it directly, subsequent epochs apply the EMA formula)
Threshold calculation:
threshold_frame_count— frames collected so farthreshold_mean_sum— running sum of errors for mean calculationthreshold_errors[1000]— buffer storing reconstruction errorsthreshold— the active anomaly detection threshold
Inference:
inference_error_window[10]— circular buffer of recent reconstruction errorsinference_window_index— current write positioninference_window_filled— flag indicating the window has wrapped at least oncestats_frame_count,stats_anomaly_count,stats_normal_count— rolling statistics
8.12.3.8.4.3. 3.3 Result and Metrics Structures
AnomalyDetectionResult_t — returned by ProcessInferenceFrame:
Field |
Type |
Description |
|---|---|---|
|
|
0 = normal, 1 = anomaly (after majority voting) |
|
|
Raw MSE reconstruction error for this frame |
|
|
Current threshold used for the decision |
AnomalyDetectionTrainingResult_t — returned by StopTraining:
Field |
Type |
Description |
|---|---|---|
|
|
Average training loss from last epoch |
|
|
Average validation loss from last epoch |
|
|
Best EMA validation loss achieved during training |
|
|
Total epochs completed |
|
enum |
|
AnomalyDetectionMetrics_t — returned by GetMetrics:
Field |
Type |
Description |
|---|---|---|
|
|
Average training loss so far in current epoch |
|
|
Average validation loss so far in current batch |
|
|
Best EMA validation loss |
|
|
Current epoch number |
|
|
Epochs without improvement |
|
|
Frames in current statistics window |
|
|
Anomalies in current statistics window |
|
|
Normal frames in current statistics window |
|
|
Anomaly percentage in current window |
|
|
Normal percentage in current window |
|
|
Current anomaly detection threshold |
8.12.3.8.5. 4. API Reference
8.12.3.8.5.1. 4.1 AnomalyDetection_Init
int AnomalyDetection_Init(AnomalyDetectionContext_t* ctx, AnomalyDetectionConfig_t* config);
Initializes the anomaly detection system. Must be called once before any other function. Validates all configuration fields, sets up the underlying neural network model via ODT_Init(), and starts the system in MODE_INFERENCE with the initial threshold from config->initial_threshold.
The config struct is copied internally — the caller can reuse or free it after this call.
Returns: 0 on success, -1 on failure.
Common errors (returns -1):
NULL
ctxorconfiglearning_rate<= 0,patience== 0,batches_per_epoch== 0ema_alphanot in (0, 1]threshold_samplesnot in 1–1000,inference_window_sizenot in 1–10ODT_Initfailure (model config mismatch — see ondevice_training_lib)
Example:
AnomalyDetectionContext_t ad_ctx;
AnomalyDetectionConfig_t config = {
.learning_rate = 0.0001f,
.patience = 10,
.min_improvement_delta = 0.5f,
.ema_alpha = 0.5f,
.batches_per_epoch = 10,
.initial_threshold = RECONSTRUCTION_ERROR_THRESHOLD,
.threshold_percentile = 0.99f,
.gaussian_std_multiplier = 4.0f,
.use_percentile_threshold = 1,
.threshold_samples = 500,
.inference_window_size = 1,
.stats_window_size = 100
};
int status = AnomalyDetection_Init(&ad_ctx, &config);
if (status != 0) {
// Handle error — check console for specific validation failure
}
8.12.3.8.5.2. 4.2 AnomalyDetection_StartTraining
int AnomalyDetection_StartTraining(AnomalyDetectionContext_t* ctx);
Transitions from MODE_INFERENCE to MODE_TRAINING. Resets all training state (counters, losses, early stopping) and saves the current weights as the initial “best” checkpoint. After this call, the system expects training samples via ProcessTrainingFrame.
Precondition: Must be in MODE_INFERENCE. Returns -1 if called from any other mode.
Returns: 0 on success, -1 on error.
8.12.3.8.5.3. 4.3 AnomalyDetection_StopTraining
int AnomalyDetection_StopTraining(AnomalyDetectionContext_t* ctx, AnomalyDetectionTrainingResult_t* result);
Ends training, restores the best weights (from the epoch with lowest validation loss, not the last epoch), and transitions to MODE_THRESHOLD_CALC. The result struct is filled with a training summary including final losses, best loss, epochs trained, and whether training was early-stopped or user-stopped.
Precondition: Must be in MODE_TRAINING. Returns -1 if called from any other mode.
Returns: 0 on success, -1 on error.
This function is also called automatically by ProcessTrainingFrame when early stopping triggers. You can call it manually at any time to stop training early (e.g., after a fixed number of epochs).
8.12.3.8.5.4. 4.4 AnomalyDetection_ProcessTrainingFrame
void AnomalyDetection_ProcessTrainingFrame(AnomalyDetectionContext_t* ctx, float* frozen_output, float* target);
The main training function. Call it once per sample — the library handles everything internally: batching, epoch transitions, train/validate phase switching, EMA-smoothed loss tracking, weight checkpointing, and early stopping.
Parameters:
Parameter |
Size |
Description |
|---|---|---|
|
|
Output from the frozen model for this sample. This is the input to the trainable model. |
|
|
Ground truth that the model tries to reconstruct. For autoencoders, this is the original feature input — the model learns to reconstruct the input features from the frozen model’s output. |
Precondition: Must be in MODE_TRAINING.
How it works:
The library internally alternates between two phases each epoch. You don’t need to manage this — just keep calling ProcessTrainingFrame with your data:
Training phase: Runs forward + backward pass, updates weights. After
batches_per_epoch × TRAIN_BATCH_SIZEsamples, automatically switches to validation.Validation phase: Runs forward pass only (no weight updates), computes EMA-smoothed loss, checks for improvement, and saves best weights if the model improved. After
VAL_BATCH_SIZEsamples, increments the epoch counter and switches back to training.
Early stopping: If validation loss hasn’t improved for patience consecutive epochs, the library automatically calls StopTraining
Epoch structure:
Epoch N:
├── Training: batches_per_epoch × TRAIN_BATCH_SIZE samples (weights updated)
└── Validation: VAL_BATCH_SIZE samples (loss evaluated)
→ EMA smoothing → improvement check → save best weights
Epoch N+1: (repeats until early stopping or manual stop)
What to keep in mind:
The application is responsible for feeding data in the right order — training samples during the training phase, validation samples during the validation phase. Get the current phase to know whether to pass training or validation data.
Pass
frozen_outputas the input and the original feature input astarget.Early stopping can trigger at any epoch. After it triggers, the mode changes to
MODE_THRESHOLD_CALC— subsequent calls toProcessTrainingFramewill be rejected.
8.12.3.8.5.5. 4.5 AnomalyDetection_ProcessThresholdFrame
void AnomalyDetection_ProcessThresholdFrame(AnomalyDetectionContext_t* ctx, float* frozen_output, float* target);
Collects reconstruction errors from normal samples to compute the anomaly detection threshold. Call it repeatedly with normal (non-anomalous) data. After threshold_samples have been collected, the next call triggers threshold calculation and automatically transitions to MODE_INFERENCE.
Precondition: Must be in MODE_THRESHOLD_CALC.
Only feed normal (non-anomalous) samples. The threshold characterizes the distribution of normal reconstruction errors — feeding anomalous data here will produce an incorrect threshold.
How it works internally:
Each call runs a forward pass, computes the reconstruction error, and stores it
After collecting
threshold_sampleserrors, it computes the threshold using the configured method (percentile or Gaussian — see Section 6 for details)Both methods are always computed and logged for comparison, regardless of which one is selected
The system then auto-transitions to
MODE_INFERENCE— no additional call neededThe threshold is stored in
AnomalyDetectionContext_t.thresholdand used automatically during inference
Example console output:
Threshold Calculation Results:
Mean error: 14
Std error: 6
Gaussian (mean + 4*std): 39
95th percentile: 25
99th percentile: 25
Configured percentile (0): 25
Method used: Percentile
FINAL THRESHOLD: 25
8.12.3.8.5.6. 4.6 AnomalyDetection_ProcessInferenceFrame
void AnomalyDetection_ProcessInferenceFrame(AnomalyDetectionContext_t* ctx, float* frozen_output, float* target, AnomalyDetectionResult_t* result);
Performs real-time anomaly detection on a single frame. Computes the reconstruction error, applies majority voting over a sliding window, and returns the anomaly decision in result.
Precondition: Must be in MODE_INFERENCE.
Output (AnomalyDetectionResult_t):
is_anomaly— 0 (normal) or 1 (anomaly), after majority votingreconstruction_error— raw MSE error for this framethreshold— the threshold used for the decision
Majority voting: When inference_window_size = 1, each frame is classified independently. When > 1 (e.g., 5), the library maintains a sliding window of recent errors. An anomaly is declared only if more than half the window exceeds the threshold. This reduces false positives from isolated spikes.
Window = [12.3, 8.1, 45.2, 7.5, 9.0], threshold = 25.0
Exceeds threshold: 1 out of 5 → Normal (not majority)
Window = [45.2, 38.1, 42.0, 7.5, 31.0], threshold = 25.0
Exceeds threshold: 4 out of 5 → Anomaly (majority)
Rolling statistics: The library tracks anomaly/normal counts over a configurable window (stats_window_size). Statistics are logged to console periodically and then reset. Use GetMetrics to read them programmatically.
8.12.3.8.5.7. 4.7 AnomalyDetection_ForwardPass
int AnomalyDetection_ForwardPass(AnomalyDetectionContext_t* ctx, float* frozen_output, float* target, float* output, float* error);
Utility function that runs a forward pass and computes the reconstruction error, without any side effects — no weight updates, no state changes, no threshold or voting logic. Useful for golden vector validation and manual testing.
Returns: 0 on success, -1 on error (NULL parameters).
Can be called in any mode.
Example (golden vector validation):
float output[FINAL_OUTPUT_SIZE];
float error;
AnomalyDetection_ForwardPass(&ad_ctx, frozen_output, target, output, &error);
// Compare output against golden_output element by element
int matched = 0, not_matched = 0;
for (int i = 0; i < FINAL_OUTPUT_SIZE; i++) {
if (fabsf(output[i] - golden_output[i]) < 1e-3f) {
matched++;
} else {
not_matched++;
}
}
8.12.3.8.5.8. 4.8 AnomalyDetection_GetMode / AnomalyDetection_GetMetrics
int AnomalyDetection_GetMode(AnomalyDetectionContext_t* ctx, AnomalyDetectionTrainingMode_t* mode);
void AnomalyDetection_GetMetrics(AnomalyDetectionContext_t* ctx, AnomalyDetectionMetrics_t* metrics);
GetMode: Returns the current operating mode (MODE_INFERENCE, MODE_TRAINING, or MODE_THRESHOLD_CALC).
GetMetrics: Fills the metrics structure with current state. Training metrics (losses, epoch) are meaningful in MODE_TRAINING. Inference metrics (frame counts, anomaly percentage) are meaningful in MODE_INFERENCE.
8.12.3.8.6. 5. Training Features — Detailed Explanation
8.12.3.8.6.1. 5.1 EMA-Smoothed Validation Loss
With small validation datasets (e.g., 6 samples), the raw validation loss can fluctuate significantly from epoch to epoch. EMA smoothing reduces this noise.
Formula:
First epoch: ema = raw_val_loss (initialize)
Subsequent: ema = alpha × raw_val_loss + (1 - alpha) × previous_ema
Effect of ema_alpha:
Alpha |
Behavior |
|---|---|
1.0 |
No smoothing — EMA equals the raw value |
0.5 |
Equal weight to current and history — moderate smoothing |
0.2 |
Strong smoothing — slow to react to changes |
8.12.3.8.6.2. 5.2 Early Stopping
Early stopping prevents overfitting by monitoring the EMA validation loss and stopping training when it stops improving.
How it works:
After each validation epoch, check:
ema_val_loss < best_val_loss - min_improvement_deltaIf yes: reset
no_improvement_countto 0, save best weightsIf no: increment
no_improvement_countIf
no_improvement_count >= patience: trigger early stopping
The min_improvement_delta parameter prevents tiny improvements from resetting the patience counter. For example, with delta = 0.5, a loss decrease from 100.0 to 99.9 does NOT count as improvement, but 100.0 to 99.4 does.
8.12.3.8.6.3. 5.3 Best Weight Checkpointing
Every time a new best validation loss is achieved, the current weights are saved via ODT_SaveBestWeights. When training ends (either by early stopping or manual stop), the best weights are restored via ODT_LoadBestWeights.
This means the final model uses the weights from the best epoch, not the last epoch.
8.12.3.8.7. 6. Threshold Calculation — Detailed Explanation
After training, the model’s notion of “normal” has changed, so the anomaly detection threshold must be recalculated using the updated model.
8.12.3.8.7.1. Process
Feed
threshold_samplesnormal samples through the trained modelCollect each sample’s reconstruction error in
threshold_errors[]After all samples collected, compute the threshold using one of two methods
8.12.3.8.7.2. Percentile method
Sort all errors in ascending order. Pick the error at the configured percentile index.
sorted_errors = [3.2, 5.1, 7.0, 8.4, 9.1, 12.3, ..., 25.1]
99th percentile index = 0.99 × 500 = 495
threshold = sorted_errors[495]
This method directly says: “only 1% of normal samples have error above this threshold.”
8.12.3.8.7.3. Gaussian method
Assume errors follow a normal distribution. Set threshold at mean + k × std.
mean = sum(errors) / N
std = sqrt(sum((error - mean)²) / N)
threshold = mean + gaussian_std_multiplier × std
With gaussian_std_multiplier = 4, this covers 99.997% of a normal distribution.
8.12.3.8.7.4. Which method to choose
Method |
Pros |
Cons |
|---|---|---|
Percentile |
No distribution assumption, robust to outliers |
Needs enough samples for stable percentile estimate |
Gaussian |
Works with fewer samples, mathematically grounded |
Assumes normal distribution, sensitive to outliers |
Set use_percentile_threshold = 1 for percentile (default) or 0 for Gaussian. Both are computed and logged regardless of selection, so you can compare them.
8.12.3.8.8. 7. Logging System
Same pattern as the core library. Uses AD_LOG macro:
DEBUG build: compiles to
printfDEBUG + interrupt safe: wraps in
DINT/EINTRelease build: compiles to nothing (zero overhead)
Key log messages during normal operation:
Epoch 0: Train Loss = 5571
Epoch 0: Val Loss = 6109 (raw), 6109 (smoothed) Best model, weights saved!
Epoch 1: Train Loss = 5547
Epoch 1: Val Loss = 5818 (raw), 5963 (smoothed) Best model, weights saved!
...
Epoch 121: Val Loss = 13 (raw), 16 (smoothed) No improvement (10/10)
EARLY STOPPING TRIGGERED
8.12.3.8.9. 8. API Quick Reference
Function |
Mode Required |
Purpose |
|---|---|---|
|
Any |
Initialize system, validate config, reset state |
|
INFERENCE |
Transition to TRAINING mode |
|
TRAINING |
End training, load best weights, transition to THRESHOLD_CALC |
|
TRAINING |
Process one training/validation sample |
|
THRESHOLD_CALC |
Collect error for threshold calculation |
|
INFERENCE |
Detect anomaly with majority voting |
|
Any |
Forward pass + MSE error (no side effects) |
|
Any |
Query current mode |
|
Any |
Get training/inference statistics |
8.12.3.8.10. 9. Usage Examples
This section walks through how to use the library with simple, self-contained examples. Each example builds on the previous one. All examples assume:
The model artifacts (
trainable_model_config.h/.c,mod.a) are already generatedTraining data (
ondevice_training_data.h/.c) is availableA
prepare_frozen_output()function exists that takes a raw sample, runs feature extraction + frozen model, and fillsfrozen_output[]andmodel_input[](the feature-space input before the frozen model)
For a complete working implementation, see the fan blade example application.
8.12.3.8.10.1. 9.1 Minimal API Patterns
These snippets show the bare-minimum API calls for each operation — no error handling, no prints, just the essential calls.
Initialize:
AnomalyDetectionContext_t ctx;
AnomalyDetectionConfig_t config = { /* fill fields */ };
AnomalyDetection_Init(&ctx, &config);
Run inference on one sample:
AnomalyDetectionResult_t result;
AnomalyDetection_ProcessInferenceFrame(&ctx, frozen_output, model_input, &result);
// result.is_anomaly → 0 or 1
Train (full loop until early stopping):
AnomalyDetection_StartTraining(&ctx);
AnomalyDetectionTrainingMode_t mode;
AnomalyDetection_GetMode(&ctx, &mode);
while (mode == MODE_TRAINING) {
if (ctx.current_phase == PHASE_TRAIN)
prepare_frozen_output(train_data[i++], frozen_output, model_input);
else
prepare_frozen_output(val_data[j++], frozen_output, model_input);
AnomalyDetection_ProcessTrainingFrame(&ctx, frozen_output, model_input);
AnomalyDetection_GetMode(&ctx, &mode);
}
Calculate threshold (after training):
AnomalyDetection_GetMode(&ctx, &mode);
while (mode == MODE_THRESHOLD_CALC) {
prepare_frozen_output(normal_data[k++], frozen_output, model_input);
AnomalyDetection_ProcessThresholdFrame(&ctx, frozen_output, model_input);
AnomalyDetection_GetMode(&ctx, &mode);
}
// Now in MODE_INFERENCE with new threshold
Forward pass only (no side effects):
float output[FINAL_OUTPUT_SIZE];
float error;
AnomalyDetection_ForwardPass(&ctx, frozen_output, model_input, output, &error);
The following subsections show these patterns in full context with complete working code.
8.12.3.8.10.2. 9.2 Initialization and Pre-Training Inference
Before training, you can run inference using the initial weights and the PC-exported threshold to establish a baseline accuracy.
#include "anomaly_detection_odt.h"
#include "ondevice_training_data.h"
AnomalyDetectionContext_t ad_ctx;
float frozen_output[FROZEN_OUTPUT_SIZE];
float model_input[FE_STACKING_CHANNELS * FE_STACKING_FRAME_WIDTH];
void run_baseline(void) {
// Step 1: Configure and initialize
AnomalyDetectionConfig_t config = {
.learning_rate = 0.0001f,
.patience = 10,
.min_improvement_delta = 0.5f,
.ema_alpha = 0.5f,
.batches_per_epoch = 10,
.initial_threshold = RECONSTRUCTION_ERROR_THRESHOLD,
.threshold_percentile = 0.99f,
.gaussian_std_multiplier = 4.0f,
.use_percentile_threshold = 1,
.threshold_samples = 500,
.inference_window_size = 1,
.stats_window_size = 100
};
if (AnomalyDetection_Init(&ad_ctx, &config) != 0) {
printf("Init failed!\n");
return;
}
// Step 2: Run inference on test data with initial (PC-trained) weights
uint16_t correct = 0;
for (uint16_t i = 0; i < NUM_TEST_SAMPLES; i++) {
prepare_frozen_output(TEST_INPUTS[i], frozen_output, model_input);
AnomalyDetectionResult_t result;
AnomalyDetection_ProcessInferenceFrame(&ad_ctx, frozen_output, model_input, &result);
// label=0 means anomaly, label=1 means normal
uint16_t expected_anomaly = (TEST_LABELS[i] == 0) ? 1 : 0;
if (result.is_anomaly == expected_anomaly) correct++;
printf("Sample %d: error=%.2f threshold=%.2f -> %s\n", i, result.reconstruction_error, result.threshold, result.is_anomaly ? "ANOMALY" : "NORMAL");
}
printf("Baseline accuracy: %d/%d\n", correct, NUM_TEST_SAMPLES);
}
8.12.3.8.10.3. 9.3 The Complete Training Loop
Training is driven by calling ProcessTrainingFrame in a loop. The library handles epoch management, phase switching, and early stopping internally. You just need to feed the right data based on the current phase.
void run_training(void) {
// Start training (must currently be in MODE_INFERENCE)
AnomalyDetection_StartTraining(&ad_ctx);
// Simple indices to cycle through data
uint16_t train_idx = 0;
uint16_t val_idx = 0;
// Loop until training ends (early stopping or manual stop)
AnomalyDetectionTrainingMode_t mode;
AnomalyDetection_GetMode(&ad_ctx, &mode);
while (mode == MODE_TRAINING) {
// Feed the right data based on current phase
if (ad_ctx.current_phase == PHASE_TRAIN) {
prepare_frozen_output(TRAIN_INPUTS[train_idx], frozen_output, model_input);
train_idx = (train_idx + 1) % NUM_TRAIN_SAMPLES;
} else {
prepare_frozen_output(VALIDATION_INPUTS[val_idx], frozen_output, model_input);
val_idx = (val_idx + 1) % NUM_VALIDATION_SAMPLES;
}
// One call per sample — the library handles: batching, forward/backward, loss tracking, smoothing, checkpointing, early stopping
AnomalyDetection_ProcessTrainingFrame( &ad_ctx, frozen_output, model_input);
// Check if training ended (early stop -> MODE_THRESHOLD_CALC)
AnomalyDetection_GetMode(&ad_ctx, &mode);
}
printf("Training complete!\n");
}
Key points:
Check
ad_ctx.current_phaseto know whether to feed training or validation dataYou don’t manage epochs, batches, or early stopping — the library does all of that
The loop exits when the mode changes from
MODE_TRAINING
8.12.3.8.10.4. 9.4 Threshold Calculation
After training, the model’s reconstruction errors have changed, so the threshold must be recalculated. Feed normal samples until the library has enough and auto-transitions to inference.
void run_threshold_calculation(void) {
uint16_t val_idx = 0;
AnomalyDetectionTrainingMode_t mode;
AnomalyDetection_GetMode(&ad_ctx, &mode);
while (mode == MODE_THRESHOLD_CALC) {
// Feed ONLY normal (non-anomalous) samples
prepare_frozen_output(VALIDATION_INPUTS[val_idx], frozen_output, model_input);
val_idx = (val_idx + 1) % NUM_VALIDATION_SAMPLES;
AnomalyDetection_ProcessThresholdFrame(&ad_ctx, frozen_output, model_input);
AnomalyDetection_GetMode(&ad_ctx, &mode);
}
// Mode is now MODE_INFERENCE with the new threshold
printf("Threshold calculated! Ready for inference.\n");
}
Key points:
Only feed normal data — anomalous samples will corrupt the threshold
The library auto-transitions to
MODE_INFERENCEafter computing the thresholdBoth percentile and Gaussian thresholds are logged for comparison
8.12.3.8.10.5. 9.5 Post-Training Inference
After training and threshold calculation, inference works identically to pre-training — but now with updated weights and a recalculated threshold.
void run_post_training_inference(void) {
uint16_t correct = 0;
for (uint16_t i = 0; i < NUM_TEST_SAMPLES; i++) {
prepare_frozen_output(TEST_INPUTS[i], frozen_output, model_input);
AnomalyDetectionResult_t result;
AnomalyDetection_ProcessInferenceFrame(&ad_ctx, frozen_output, model_input, &result);
uint16_t expected_anomaly = (TEST_LABELS[i] == 0) ? 1 : 0;
if (result.is_anomaly == expected_anomaly) correct++;
printf("Sample %d: error=%.2f threshold=%.2f -> %s\n", i, result.reconstruction_error, result.threshold, result.is_anomaly ? "ANOMALY" : "NORMAL");
}
printf("Post-training accuracy: %d/%d\n", correct, NUM_TEST_SAMPLES);
}
8.12.3.8.10.6. 9.6 Putting It All Together
The complete workflow in main():
int main(void) {
init_board(); // Hardware, peripherals, feature extraction
// 1. Initialize and check baseline accuracy
run_baseline();
// 2. Train the model on-device (runs until early stopping)
run_training();
// 3. Recalculate the anomaly threshold with updated model
run_threshold_calculation();
// 4. Evaluate with the trained model + new threshold
run_post_training_inference();
}
This is the same flow used in the SDK fan blade example — see Application Example for the full implementation with feature extraction, golden vector validation, and debugger-triggered training.
8.12.3.8.11. 10. Further Reading
Document |
Description |
|---|---|
Core library that this task library is built on |
|
Model architecture and weight files used by both libraries |
|
Complete working example showing this library in action |
|
High-level architecture and concepts |