Overview
TrainingConfig feeds the client executor on every round. It has a typed surface for the common knobs plus an extra bag for anything unusual.
Fixed Fields
Every fixed field lands in executors[0].executor.args inside the generated config_fed_client.json, exactly as the web wizard produces them.
The extra Bag
Anything not covered by the fixed fields goes into extra. It's a plain dict[str, Any] and is merged into the executor args verbatim. Keys in extra win over fixed fields if they collide — useful for one-off overrides.
In the generated config this becomes:
How Your Code Reads Hyperparameters
From model_def.py (the default path)
The platform adapter (the bit the SDK stitches onto your model class) reads the executor args via the injected _RT_MODEL_ARGS dict plus the standard executor plumbing. You don't usually have to do anything — changing TrainingConfig.learning_rate immediately shows up in the generated JSON.
From a custom executor
If you pass executor=MyExecutor to rt_submit, your class __init__ receives every executor arg as a kwarg. Subclass the FL runtime's Executor base class and declare what you need:
See Custom classes for the full workflow.
Federation Knobs
FederationConfig drives the server-side orchestration. Fields map 1:1 to the default Scatter-and-Gather workflow + persistor setup.
| Field | Meaning |
|---|---|
| num_rounds | Total FL aggregation rounds. |
| min_clients | Minimum workers that must participate per round before aggregation. |
| wait_time_after_min_received | Seconds the server waits after min_clients respond before aggregating (lets stragglers catch up). |
| heart_beat_timeout | Seconds before a silent client is considered dead. |
| job_name | Display name stored in meta.json. |
Federation knobs go into config_fed_server.json and are not customisable at runtime by workers — set them correctly at submit time.
Message quantization
Add quantization="<value>" to FederationConfig to compress per-round model traffic (server → client global weights and client → server trained weights). The SDK installs symmetric ModelQuantizer / ModelDequantizer filters on the train task — other tasks pass through unfiltered.
| Value | Wire size vs fp32 | Needs bitsandbytes | Needs CUDA | What it is |
|---|---|---|---|---|
| None | 100% | no | no | fp32 raw (default). Baseline. |
| "float16" | ~50% | no | no | IEEE half precision via torch cast. Pure-torch, runs on CPU. |
| "blockwise8" | ~25% | yes | yes | int8 with per-block scale/zero-point (bitsandbytes). |
| "float4" | ~14% | yes | yes | 4-bit mini-float (bitsandbytes FP4). |
| "normfloat4" | ~14% | yes | yes | NF4 — value bins by Gaussian (QLoRA paper). |
| "adaquant" | varies | no | no | Learned adaptive per-tensor quantization. CPU-friendly. |
Quick pick for your setup:
- Safe default, no extra deps:
"float16"— ~50% bandwidth cut, works on CPU workers. - Bandwidth-constrained, GPU workers:
"blockwise8"for ~75% bandwidth reduction with minimal convergence impact. - Multi-billion-param / LLM federated runs (GPU):
"normfloat4". - CPU-only worker pool, want smarter compression than fp16:
"adaquant". - Debugging convergence:
Nonefirst — quantization is opt-in for a reason.
The blockwise8, float4, and normfloat4 modes require bitsandbytes in your requirements.txt and a CUDA GPU on every worker (the NVFlare filter forcibly .cuda()s tensors). The server runs the filter symmetrically, so it needs CUDA too — confirm with the ResonTech team that your pool fits. bf16 is not a valid value here despite showing up in NVIDIA marketing material.
Inspecting the Rendered Configs Before Submit
Always a good idea when tweaking hyperparameters:
Nothing touches disk — these are plain strings.