Metrics — what FLTest measures¶
Every run produces a RunResult with per-round history and a final dict (last round).
These metrics are what differential and metamorphic tests read.
Always measured (every backend, every round)¶
Computed by evaluating the global model on the central test set after each round:
| Metric | Meaning |
|---|---|
accuracy |
top-1 accuracy of the global model on the test subset |
loss |
mean cross-entropy loss on the test subset |
gm_weight_sum |
sum of all global-model parameters — a cheap fingerprint to spot divergence/NaNs |
The test subset size is max_test_data_size. These three appear in final for every run.
Produced by plugins (when configured)¶
| Metric | Produced by | Meaning |
|---|---|---|
attack_success_rate |
backdoor attack |
fraction of a triggered test set predicted as the target label (excludes samples already of the target) |
reconstruction_mse |
dlg attack |
pixel MSE between the reconstructed and true victim image (lower = better reconstruction = worse privacy) |
reconstruction_psnr |
dlg attack |
peak signal-to-noise ratio of the reconstruction (higher = better reconstruction) |
label_recovery |
dlg attack |
fraction of victim labels correctly recovered |
membership_inference_auc |
membership_inference attack |
probability that a random training sample looks more member-like than a random held-out one; 0.5 is no leakage, 1.0 is perfect separation |
membership_loss_gap |
membership_inference attack |
mean held-out loss minus mean training loss, which is the overfitting gap the attack exploits |
model_replacement_scale |
model_replacement attack |
factor applied to the malicious client's delta from the current global model |
per_client_acc_mean / per_client_acc_min |
per_client listener |
personalized accuracy of the final global model on each client's own data — min exposes representation disparity (project Pitfall-3) |
Add per_client to metrics: to enable personalized evaluation. Attack metrics appear
automatically when the relevant attack is configured.
Secure aggregation¶
Masking either cancels exactly or it does not, and these metrics are how you tell which, since a residue moves the model without necessarily moving accuracy.
| Metric | Produced by | Meaning |
|---|---|---|
secagg_mask_residual |
secure_aggregation |
what the masks left behind after aggregation; 0 means they cancelled |
secagg_mask_to_update_ratio |
secure_aggregation |
size of the mask relative to the update it hides; near 0 means the masking is cosmetic |
secagg_participants |
secure_aggregation |
clients whose masked updates reached aggregation |
secagg_participant_mismatch |
secure_aggregation |
clients that masked but never reached aggregation, so their masks never cancelled |
secagg_client_outside_mask_set |
secure_aggregation |
a client masked against a peer set that did not include it |
mpc_agg_max_abs_error |
mpc_aggregation |
largest absolute gap between the fixed-point aggregate and plain FedAvg |
mpc_agg_rel_error |
mpc_aggregation |
that gap relative to the size of the aggregate |
mpc_overflow_rate |
mpc_aggregation |
share of values that wrapped around the ring; above 0 means a silently wrong aggregate |
mpc_dropouts |
mpc_aggregation |
clients removed after masking, whose pairwise masks stay in the sum |
mpc_max_encoded_bits |
mpc_aggregation |
log2 of the largest encoded magnitude; above 53 the float64 encode drops low-order bits |
Detection¶
| Metric | Produced by | Meaning |
|---|---|---|
fldetector_detected_count |
fldetector |
how many clients were flagged and excluded |
fldetector_detected_clients |
fldetector |
the client ids it flagged |
fldetector_scores |
fldetector |
each client's suspicion score for that round |
fldetector records these in the detection round, not every round, so they appear in
history at that round rather than in final. The run matrix shows
fldetector_detected_count as a column when it is present. The other two are a list and a
mapping, so they stay in the JSON report rather than becoming fixed-width cells.
Was the detection correct?¶
The metrics above say what a detector flagged, not whether it was right. Accuracy does not
settle it either, because a detector that flagged one honest client alongside the attackers
would produce nearly the same recovery curve. Add the detection listener to metrics: and
the comparison is measured.
| Metric | Meaning |
|---|---|
detection_precision |
share of flagged clients that the config actually made malicious |
detection_recall |
share of the config's malicious clients that the defense flagged |
detection_f1 |
harmonic mean of the two |
detection_false_positives |
honest clients the defense wrongly flagged |
detection_missed |
malicious clients the defense never flagged |
Ground truth is the union of target_clients over the attacks that make a client
malicious, which are backdoor, label_flip, sign_flip, gaussian, and
model_replacement. dlg and membership_inference are deliberately excluded, because an
honest-but-curious server names a victim rather than an adversary, and counting one would
mark an honest client as a detection the defense owed you.
The listener records nothing when a run has no detector, or when an attack leaves
target_clients unset and every client is therefore malicious, so adding it to any config
is safe. examples/configs/fldetector.yaml enables it, and the fldetector arm reports
precision 1.0000 and recall 1.0000 against zero false positives, while the undefended and
median arms show a dash.
A zero-division follows scikit-learn's zero_division=0: a detector that flagged nobody
scores 0 rather than a vacuous 1.
Run parameters recorded beside the metrics¶
Each run's params in the JSON report carries its fully resolved settings, which includes
aggregation. That names fedavg or the robust rule that replaced it, since a
robust-aggregation defense substitutes the rule rather than perturbing an update.
Where metrics live¶
result.history[round]— dict of metrics for that round.result.final— metrics from the last round (what tests assert on).result.extras— non-scalar detail (e.g. the DLG reconstruction summary).- JSON report under
reports/contains all of the above.
Which metric do the tests use?¶
Both testers operate on a single scalar metric from final, chosen by the config:
- Differential (
testing.differential.metric, defaultaccuracy): compares that metric across frameworks. See Differential testing. - Metamorphic (per-relation
metric, defaultaccuracy): tracks that metric as one input parameter is swept. See Metamorphic testing.
You can point either at any metric in final — e.g. set a metamorphic relation's
metric: attack_success_rate to assert that ASR is non-decreasing as attack strength rises,
or metric: reconstruction_mse to assert it rises as DP noise increases.