This repo hosts a real-system research prototype for L-shaped split federated LLM fine-tuning.
- Real split chain already runs with
2Jetson Nano CPU clients and3Huawei nova Android clients against one shared server-side model on the A800 (CUDA_VISIBLE_DEVICES=4) - Both
fullandloraserver-side training modes work with the realbackend=mftclient prefix - Metrics are logged into CSV/JSONL tables on the server
third_party/mobilefinetuner/is the only MobileFineTuner tree modified in this repo- The stable orchestration path is:
- remote server + remote Nano clients via
scripts/run_multi_nano_experiment.py - local Android launch via
scripts/run_android_clients_only.py - one-shot wrapper orchestration via
scripts/run_parallel_hybrid_experiment.py
- remote server + remote Nano clients via
src/lshaped/: Python server, protocol, trainer, loopback clientclients/cpp/: Flower-compatible C++ client for Nano/Android-side prefix uploadconfigs/: runnable YAML configsscripts/: sync/bootstrap/run helpersdocs/: stage plan and troubleshooting notesthird_party/mobilefinetuner/: copied MobileFineTuner source tree used for incremental integration only
On <SERVER_ALIAS>:
cd <SERVER_REPO_ROOT>
<SERVER_PYTHON> -m pip install -U pip
<SERVER_PYTHON> -m pip install -r requirements-server.txt
<SERVER_PYTHON> -m pip install -e .Local sync from this Windows workspace to the server:
powershell -ExecutionPolicy Bypass -File scripts\sync_to_server.ps1Loopback smoke test on the server:
cd <SERVER_REPO_ROOT>
bash scripts/run_loopback_smoke_remote.shStable 5-client hybrid run, one shared server-side model, 2 Nano + 3 nova:
python scripts/run_parallel_hybrid_experiment.py `
--base-config configs/a800_gemma_real_full_five_client_long.yaml `
--nano-client-specs-json configs/dual_nano_clients.json `
--android-client-specs-json configs/five_clients_android_only_mft.json `
--run-label full20_wrapper `
--shared-client-dataset-local-csv data/mmlu/official_mmlu_test_100.csvLoRA variant:
python scripts/run_parallel_hybrid_experiment.py `
--base-config configs/a800_gemma_real_lora_five_client_tuned.yaml `
--nano-client-specs-json configs/dual_nano_clients.json `
--android-client-specs-json configs/five_clients_android_only_mft.json `
--run-label lora20_tuned_wrapper `
--shared-client-dataset-local-csv data/mmlu/official_mmlu_test_100.csvThe helper below installs Nano-side gRPC/protobuf packages, syncs clients/cpp, and builds the client natively on the Nano.
Example from the server or any machine with Python + paramiko:
cd <SERVER_REPO_ROOT>
export NANO_PASSWORD=jetson
<SERVER_PYTHON> scripts/prepare_nano_cpp_client.py \
--host <NANO_CLIENT_1_HOST> \
--username jetson \
--backend mockSecond Nano:
cd <SERVER_REPO_ROOT>
export NANO_PASSWORD=jetson
<SERVER_PYTHON> scripts/prepare_nano_cpp_client.py \
--host <NANO_CLIENT_2_HOST> \
--username jetson \
--backend mockRun the mock client on a Nano after build:
cd <CLIENT_REPO_ROOT>
./build/cpp_client_mock/lshaped_flower_client \
--server_address <SERVER_HOST>:19080 \
--client_id nano64_mock_cpu \
--backend mock \
--batch_size 2 \
--max_seq_len 128 \
--synthetic_samples 32 \
--mock_hidden_size 128 \
--metrics_path <CLIENT_REPO_ROOT>/outputs/client_metrics.csvThe same launch can be triggered from the server with:
cd <SERVER_REPO_ROOT>
export NANO_PASSWORD=jetson
<SERVER_PYTHON> scripts/run_nano_client.py \
--host <NANO_CLIENT_1_HOST> \
--username jetson \
--client-id nano64_mock_cpu \
--backend mockThe MFT-backed binary can be built with:
cd <SERVER_REPO_ROOT>
export NANO_PASSWORD=jetson
<SERVER_PYTHON> scripts/prepare_nano_cpp_client.py \
--host <NANO_CLIENT_1_HOST> \
--username jetson \
--backend mftServer-side metrics table:
outputs/<run_name>/metrics.csvoutputs/<run_name>/metrics.jsonl
Logged fields include:
losscontrastive_ppl_proxyaccuracytransmitted_bytesclient_encode_time_secclient_serialize_time_secclient_round_time_secclient_rss_mbgpu_mem_mbgpu_power_w
Client-side CSV on Nano includes per-batch upload bytes and local timing. client_power_w is currently -1.0 because Jetson power sampling is not wired yet.
Every wrapper run also writes:
outputs/runs/<run_id>/summary_rounds.csvoutputs/runs/<run_id>/summary_clients.csvoutputs/runs/<run_id>/summary.json
contrastive_ppl_proxy is exp(loss) for the activation contrastive objective. It is not LM token perplexity.
- This repo does not modify the original
<MOBILEFINETUNER_ROOT>tree. All MobileFineTuner changes are applied only to the copied tree underthird_party/mobilefinetuner/. - The current split point is
split_layer=0, which means the client uploads scaled input embeddings as the cut-layer activation. - Server responses do not contain gradients, cut activations, or parameter deltas. This keeps Phase 1 strictly L-shaped and prevents accidental U-shaped behavior.
- In the real-device runs, the actual client samples come from the exported flat MMLU CSV passed through
--shared-client-dataset-local-csv. Thedataset:block in the server YAML currently provides control-plane settings such as client ids, batch size, and max sequence length.