DocsAdvanced & theoryPort a centralized recipe to FL
Advanced & theory

Port a centralized recipe to FL

You already have a centralized train.py. This page walks the changes that turn it into a ResonTech FL job — what you wrap, what stays the same, what the platform supplies.

What this page is

If you already have a centralized PyTorch training script (loss + optimizer + DataLoader + a per-epoch loop), this page is the port guide. Most of your code doesn't change. What does:

  • Your model class gets a thin FLModelWrapper around it (adds get_weights / set_weights for the round-trip).
  • Your per-epoch loop moves inside a fl_train_model(payload, ...) function — called once per FL round, receives global weights, returns updated weights.
  • Your dataset stays exactly where it is. Just zip it into shards before upload.

What you don't write at all: the FedAvg aggregator, the per-worker executor, the persistor that saves the global model — the platform supplies those, and they're identical across every FL job in our catalog.

i
Want to read about the FL plumbing instead of porting? Skip to How FL works on ResonTech for the theory deep-dive (FedAvg, per-round loop, persistor lifecycle, tuning lessons).

Required versions

How It Works (Theory, if you need)

The platform calls your training function once per FL round. Your existing training logic runs inside fl_train_model() - or whatever name you have - you receive global weights at the start and return updated weights at the end.

Round 0 - Initialization

Rounds 1..N - Training Loop

Your existing training loop (epochs, early stopping, scheduler, checkpointing) all runs inside fl_train_model(). The only difference: receive weights from the server at the start and return updated weights at the end.

How params are passed

1. Required File Structure in your Workspace (Practice)

!
The folder names scripts, configs, requirements, model, shards, files_out and model_out are required exact names — the platform resolves paths by these names. Your job folder must live under jobs/ at the root of your bucket.
i
Auto-generated by the Submit Job wizard when you click Build workspace: custom_client_executor.py, custom_persistor.py, model_def_wrapper_template.py, config_fed_client.json, config_fed_server.json, meta.json and requirements.txt. If you upload directly to the S3 bucket (e.g. via the Python SDK, the Files page upload button, or boto3), create the folders with the exact names above and place your files inside them.

2. What You Need to Write

For ready sample see: git hub repo

2.1 config_fed_client.json - Hyperparams

These args are the params you pass to your fl_train_model(). forwarded as payload["env"] from custom_client_executor.py to your train function:

2.2 custom_client_executor.py - worker guard to your model

Whatever you passed to "args" in config_fed_client.json you, should copy to client custom_client_executor.py in `__init__` :

and to the `payload["env"]` dict in `_run_adapter()` :

That's the only changes to `custom_client_executor.py`.

2.3 model_def.py - Your Entry Point (watch generated model_def_wrapper_template.py for reference)

The client executor dynamically imports your module. You need two things:

A) A model wrapper class - used by the server persistor to initialize and manage weights:

i
Always include **kwargs in your wrapper's __init__ signature — e.g. def __init__(self, classes, epochs=1, **kwargs). The persistor instantiates your class by unpacking the full args block from config_fed_server.json, which may contain extra keys your constructor doesn't declare. Without **kwargs the job will crash at round 0 with an unexpected keyword argument error.

B) An fl_train_model() function - wraps your existing training logic:

i
Key point: the executor doesn't care what happens inside `fl_train_model()`. Use your `DeepLabLoss`, `GradualUnfreezeScheduler`, `WeightedRandomSampler` — whatever you want. Just load the incoming weights at the start and return updated weights at the end.

2.4 config_fed_server.json - Server Model Init

The server persistor controls initial weights via config_fed_server.json:

  • Custom checkpoint, source_ckpt_file_full_name - load a specific .pt .

    In that case you need to upload it to your S3 bucket and point to its folder in the submission UI, for example 'job_for_test/model'

    If you don't need to pass checkpoint, remove "source_ckpt_file_full_name": "/app/checkpoints/GLOBAL_MODEL.pt" row at all

3. What You Don't Change

FileWhy
custom_client_executor.pyAdd your custom params to __init__ and payload["env"] if needed. The rest is generic plumbing.
custom_persistor.pyExtends PTFileModelPersistor - handles save/load of global model. Model-agnostic.
data_root pathPlatform pulls shard data from your S3 bucket into this path inside the Docker container.
Aggregator configInTimeAccumulateWeightedAggregator (FedAvg) uses your "samples" count for weighting.

4. Dataset Format

The platform shards your dataset across GPU providers. Each provider gets a subset of your dataset, your training code will behave the same, since shards are same dataset but small.

i
Key point: don't create unnecessary folders, if your shard originally consist of 2 folders : images and labels and your code works with those 2 folders, then DON'T do this :
You should keep your data paths same as you have locally, to ensure that you don't need to refactor the code.