From fc376e9e42d54a1c0cc6d31941bd45f47213a47d Mon Sep 17 00:00:00 2001 From: WEN Hao Date: Wed, 28 Oct 2026 08:17:25 +0800 Subject: [PATCH 1/2] docs: add how-to guides for custom algorithms, datasets and optimizers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - howto-custom-algorithm: walk through the complete custom algorithm in test-files/custom_alg.py — config classes with validators, the server downlink/aggregation hooks, the client typed uplink contract and its parallel-safety rationale, named parameter exchange, registration, and the two layers (golden regression, hook contract) that protect registered algorithms; the legacy hook style is documented as sequential-only - howto-custom-dataset: the full FedVisionDataset-based example plus a minimal NumPy-to-federated template, verified by execution; the template documents three easy-to-miss conventions (FedDataset has no constructor of its own, get_dataloader must handle client_idx=None, candidate models should be fl-sim models) - howto-custom-optimizer: the optimizer registry, ProxSGD subclassing, name-based selection and its caveats - test-files/custom_*.py: add literalinclude fragment markers (comments only; test_custom.py still passes) - index.rst: new How-to guides toctree section; the CLI tutorial's next steps now link the how-tos instead of promising them - zh_CN translations for the new and updated pages --- docs/source/howto-custom-algorithm.rst | 158 ++++++++++ docs/source/howto-custom-dataset.rst | 200 +++++++++++++ docs/source/howto-custom-optimizer.rst | 75 +++++ docs/source/index.rst | 8 + .../LC_MESSAGES/howto-custom-algorithm.po | 274 ++++++++++++++++++ .../zh_CN/LC_MESSAGES/howto-custom-dataset.po | 201 +++++++++++++ .../LC_MESSAGES/howto-custom-optimizer.po | 135 +++++++++ docs/source/locale/zh_CN/LC_MESSAGES/index.po | 18 +- .../zh_CN/LC_MESSAGES/tutorial-cli-matrix.po | 78 ++--- docs/source/tutorial-cli-matrix.rst | 8 +- test-files/custom_alg.py | 12 + test-files/custom_dataset.py | 16 + test-files/custom_optimizer.py | 4 + 13 files changed, 1138 insertions(+), 49 deletions(-) create mode 100644 docs/source/howto-custom-algorithm.rst create mode 100644 docs/source/howto-custom-dataset.rst create mode 100644 docs/source/howto-custom-optimizer.rst create mode 100644 docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-algorithm.po create mode 100644 docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-dataset.po create mode 100644 docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-optimizer.po diff --git a/docs/source/howto-custom-algorithm.rst b/docs/source/howto-custom-algorithm.rst new file mode 100644 index 00000000..abbd2a37 --- /dev/null +++ b/docs/source/howto-custom-algorithm.rst @@ -0,0 +1,158 @@ +.. _howto-custom-algorithm: + +How to: implement a custom algorithm +==================================== + +Built-in algorithms cover the classics, but research lives in the +variations. This guide implements a complete algorithm — a FedProx +variant with named parameter exchange and optional variance reduction — +walking through every file you need and every hook you can override. +The running example is ``test-files/custom_alg.py``; the code shown +below is included from that file, so it is exercised in CI (the CLI runs +it via ``test-files/custom_conf.yml``). + +The one-sentence model: **an algorithm is two config classes plus hook +overrides on the Server and Client classes** — the training loop, the +communication schedule scaffolding, logging, evaluation and the parallel +executors all stay untouched (:doc:`concepts` explains why). + +When do you need this? +---------------------- + +- your algorithm changes **what clients compute** locally (new local + objective, extra local state) → override client hooks; +- it changes **what is exchanged or how replies are aggregated** → + override server hooks; +- it needs **new hyperparameters** → add config fields. + +If you only need different *values* for existing hyperparameters, no +code is needed — use the built-in algorithm's config. + +The config classes +------------------ + +Two pydantic models, one for each side. New fields become CLI/CLI-YAML +options automatically; ``server_config_kw_doc`` / ``client_config_kw_doc`` +append the inherited fields' docs so the API reference stays complete: + +.. literalinclude:: ../../test-files/custom_alg.py + :language: python + :start-after: # --- begin: configs --- + :end-before: # --- end: configs --- + +The ``model_validator`` here fixes the local optimizer from the ``vr`` +flag — config-level invariants belong in validators, so invalid +combinations fail at construction, not mid-run. + +The server hooks +---------------- + +The server customizes the **downlink** (``communicate``) and the +**aggregation** (``update``). Everything else — sampling, the round +loop, metrics — is inherited: + +.. literalinclude:: ../../test-files/custom_alg.py + :language: python + :start-after: # --- begin: server --- + :end-before: # --- end: server --- + +Points worth noting: + +- ``communicate(target)`` builds a typed :class:`~fl_sim.messages.ServerToClient` + message; ``extras`` carries algorithm-specific payloads (here, + server-side gradients for variance reduction) alongside the model. +- ``update()`` runs on the main thread after the sampled clients' + replies arrive in ``self._received_messages`` — in sampled-client + order, which is what keeps aggregation bitwise identical across + :doc:`parallel_execution` backends. +- ``client_cls`` and ``config_cls`` wire the custom classes together; + ``@register_algorithm()`` (shown above each class) makes the algorithm + usable by name. + +The client hooks +---------------- + +The client customizes the **local training** (``update``) and the +**uplink** (``communicate``): + +.. literalinclude:: ../../test-files/custom_alg.py + :language: python + :start-after: # --- begin: client --- + :end-before: # --- end: client --- + +The most important contract on this page is in ``communicate``: it +**builds and returns** a :class:`~fl_sim.messages.ClientToServer` and +touches nothing but the client's own state. The framework sends the +message and maintains the communication counters. That is what makes +the hook safe to run inside a worker thread when +:doc:`parallel execution ` is on — a hook that +reached into server state would only work sequentially. + +Named exchange, by the way +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This example exchanges parameters **by name** (a ``model_state`` +mapping from parameter name to tensor, i.e. :data:`~fl_sim.messages.ModelState`) +instead of the legacy flat positional list. Names survive reordering +and partial modules, and the loading side (``accept_parameters``, +``update``) looks parameters up by name. The flat-list style still +works everywhere; prefer names for new algorithms. + +Register and run +---------------- + +``@register_algorithm()`` (applied to all four classes above) puts the +algorithm into the registry; from then on the CLI and YAML matrices +accept it like any built-in: + +.. code-block:: yaml + + algorithm: + name: Custom + server: + num_iters: 100 + vr: true + client: + num_epochs: 5 + +The same four classes work programmatically, exactly like +:doc:`quickstart`: + +.. code-block:: python + + server = CustomServer( + model, dataset, CustomServerConfig(num_iters=5), CustomClientConfig(num_epochs=1) + ) + server.train() + +How your algorithm is kept honest +--------------------------------- + +Two layers protect a registered algorithm: + +- the **golden regression** suite (``test/test_parallel_equivalence/``) + pins each algorithm's observable trajectory (messages, decisions, + counters, metrics positions) so refactors cannot silently change + behavior; +- the **hook contract test** (``test/test_hook_contract.py``) asserts + that client hooks touch no server state — the parallel-safety + property ``communicate`` above was written for. + +When you add an algorithm to the repository itself, add it to both. + +Legacy hook style +----------------- + +Older code overrides ``communicate(self, target)`` on the *client* and +mutates server state directly. It still works under the sequential +backend (with a ``DeprecationWarning``), but it is incompatible with +parallel backends and will be removed; new algorithms should build and +return the typed message as shown above. + +Next steps +---------- + +- :ref:`howto-custom-dataset` — bring your own data partitioning. +- :ref:`howto-custom-optimizer` — add a local optimizer. +- :doc:`parallel_execution` — the full hook contract and backends. +- :doc:`api/nodes` — the complete Server/Client API. diff --git a/docs/source/howto-custom-dataset.rst b/docs/source/howto-custom-dataset.rst new file mode 100644 index 00000000..bc85bc30 --- /dev/null +++ b/docs/source/howto-custom-dataset.rst @@ -0,0 +1,200 @@ +.. _howto-custom-dataset: + +How to: add a custom dataset +============================ + +A federated dataset in fl-sim is two things bundled together: the **raw +data** and a **partitioning scheme** that spreads it across simulated +clients. This guide walks through the complete example in +``test-files/custom_dataset.py`` (a re-implementation of the FedProx +paper's FEMNIST variant, exercised in CI via +``test-files/custom_conf.yml``) and then distills the minimal template +for "I have NumPy arrays and want them federated". + +Choose the base class +--------------------- + +- :class:`~fl_sim.data_processing.fed_dataset.FedVisionDataset` — image + data with the usual ``TensorDataset`` plumbing; what the example uses; +- :class:`~fl_sim.data_processing.fed_dataset.FedDataset` — the fully + abstract base, for anything non-vision. + +The required interface is small: load data in ``_preload``, hand out +dataloaders per client in ``get_dataloader``, and score predictions in +``evaluate``. + +The full example +---------------- + +.. literalinclude:: ../../test-files/custom_dataset.py + :language: python + :start-after: # --- begin: dataset --- + :end-before: # --- end: preload --- + +``_preload`` runs once at construction: resolve the data directory, +declare the defaults the framework queries (client counts, batch size, +the file layout), set the ``criterion``, download if needed, and read +the raw files into memory. fl-sim datasets are **immutable after +construction** — partitioning happens here, once. + +.. literalinclude:: ../../test-files/custom_dataset.py + :language: python + :start-after: # --- begin: dataloader --- + :end-before: # --- end: dataloader --- + +The contract of ``get_dataloader``: given a client index (or ``None`` +for "everything"), return a **pair** of dataloaders ``(train, test)`` +with shuffling enabled — the framework re-requests them every round, +and under ``rng_mode="per_client"`` (:doc:`tutorial-parallel-reproducible`) +their shuffling is what gets re-seeded per client. + +.. literalinclude:: ../../test-files/custom_dataset.py + :language: python + :start-after: # --- begin: evaluate --- + :end-before: # --- end: evaluate --- + +``evaluate`` turns a batch of predictions and labels into the metric +dictionary that lands in the logs (``acc``, ``loss``, ``num_samples``, +... — the same keys :doc:`quickstart` read back). + +.. literalinclude:: ../../test-files/custom_dataset.py + :language: python + :start-after: # --- begin: mirrors-and-models --- + :end-before: # --- end: mirrors-and-models --- + +The remaining properties are conventions rather than requirements: +``mirrors`` / ``url`` feed the download mechanism (fallback URLs tried +in order — see the :meth:`~fl_sim.data_processing.fed_dataset.FedDataset.mirrors` +API for the protocol), ``candidate_models`` declares the models that +fit this data (the CLI's ``model: name:`` picks from here), +``label_map`` names the classes, ``doi`` carries citations. + +The minimal template +-------------------- + +From NumPy arrays to a usable federated dataset. This template is +executed verbatim in CI-style verification — three conventions it +encodes are easy to miss and expensive to debug: + +- **define your own ``__init__``**: the abstract + :class:`~fl_sim.data_processing.fed_dataset.FedDataset` base has no + constructor of its own (the vision subclass supplies one); accept + ``(datadir, transform, seed, **extra_config)`` and re-seed via + ``set_seed`` so every constructor re-seeds the global RNG exactly + like the built-ins do; +- **the framework requests global loaders too**: ``get_dataloader`` + must handle ``client_idx=None`` ("all clients"), not just per-client + indices; +- **``candidate_models`` should return fl-sim models** (they carry the + ``.device`` attribute the device-allocation step relies on). + +Two ``load_partition_data*`` methods look intimidating but are a legacy +tff-style bulk-loading API the training loop never calls — a stub +satisfies the abstract interface: + +.. code-block:: python + + import numpy as np + import torch + import torch.utils.data as torchdata + + from fl_sim.models import nn as mnn + from fl_sim.utils.misc import set_seed + from fl_sim.data_processing._register import register_fed_dataset + from fl_sim.data_processing.fed_dataset import FedDataset + + + @register_fed_dataset() + class MyArrays(FedDataset): + __name__ = "MyArrays" + + def __init__(self, datadir=None, transform="none", seed=0, **extra_config): + super().__init__() + self.datadir = datadir + self.transform = transform + self.seed = seed + set_seed(seed) # so that every constructor re-seeds, as the built-ins do + for k, v in extra_config.items(): + setattr(self, k, v) + self._preload() + + def _preload(self, datadir=None): + rng = np.random.default_rng(self.seed) + n_clients = 30 + self._parts = [ # one (features, labels) pair per client + ( + rng.normal(size=(100, 20)).astype(np.float32), + rng.integers(0, 2, size=100).astype(np.int64), + ) + for _ in range(n_clients) + ] + self.DEFAULT_TRAIN_CLIENTS_NUM = n_clients + self.DEFAULT_TEST_CLIENTS_NUM = n_clients + self.DEFAULT_BATCH_SIZE = 32 + self.criterion = torch.nn.CrossEntropyLoss() + + def get_dataloader(self, train_bs=None, test_bs=None, client_idx=None): + parts = self._parts if client_idx is None else [self._parts[client_idx]] + x = np.concatenate([p[0] for p in parts]) + y = np.concatenate([p[1] for p in parts]) + ds = torchdata.TensorDataset(torch.from_numpy(x), torch.from_numpy(y)) + dl = torchdata.DataLoader(ds, batch_size=train_bs or self.DEFAULT_BATCH_SIZE, shuffle=True) + return dl, dl # train and test loaders; a real dataset separates them + + def evaluate(self, probs, truths): + return { + "acc": (probs.argmax(1) == truths).float().mean().item(), + "loss": self.criterion(probs, truths).item(), + "num_samples": probs.shape[0], + } + + # legacy tff-style bulk loaders; the training loop never calls them + def load_partition_data(self, batch_size=None): + raise NotImplementedError + + def load_partition_data_distributed(self, process_id, batch_size=None): + raise NotImplementedError + + @property + def url(self): + return "" # nothing to download + + @property + def candidate_models(self): + # fl-sim models carry a .device attribute the framework relies on + return {"mlp": mnn.MLP(dim_in=20, dim_out=2, ndim=0)} + + @property + def doi(self): + return [] + +Use it +------ + +With the ``@register_fed_dataset()`` decorator applied, the dataset is +constructible by name once its module has been imported (or via the +module *path* as the dataset ``name`` — see +:class:`~fl_sim.data_processing.fed_data_args.FedDataArgs`), and every +built-in works on it unchanged: + +.. code-block:: python + + import my_arrays # the module above; registers the dataset + + from fl_sim.data_processing import FedDataArgs + from fl_sim.algorithms.fedopt import FedAvgClientConfig, FedAvgServer, FedAvgServerConfig + + dataset = FedDataArgs(name="MyArrays")._create_fed_dataset() + server = FedAvgServer( + dataset.candidate_models["mlp"], + dataset, + FedAvgServerConfig(num_iters=5, num_clients=10, clients_sample_ratio=1.0), + FedAvgClientConfig(num_epochs=1, batch_size=32), + ) + server.train() # identical to :doc:`quickstart` from here on + +Next steps +---------- + +- :ref:`howto-custom-algorithm` — the algorithm side of the contract. +- :doc:`api/datasets` — the full FedDataset API reference. diff --git a/docs/source/howto-custom-optimizer.rst b/docs/source/howto-custom-optimizer.rst new file mode 100644 index 00000000..843af0af --- /dev/null +++ b/docs/source/howto-custom-optimizer.rst @@ -0,0 +1,75 @@ +.. _howto-custom-optimizer: + +How to: add a custom optimizer +============================== + +The local training step is pluggable: fl-sim keeps its own optimizer +registry, and any optimizer registered there can be selected by name in +the client config. This is the lightest of the three how-tos — the +complete example is 48 lines (``test-files/custom_optimizer.py``, +exercised in CI via ``test-files/custom_conf.yml``). + +Where optimizers plug in +------------------------ + +fl-sim's local optimizers live in :mod:`fl_sim.optimizers` and +subclass :class:`~fl_sim.optimizers.base.ProxSGD` — the proximal SGD +base that supports the extra local-training knobs federated algorithms +need (proximal terms, variance-reduction buffers passed by +``optimizer.step(...)``, per the custom algorithm in +:ref:`howto-custom-algorithm`). + +Registration makes the optimizer discoverable by name — the same +pattern as algorithms and datasets: + +.. literalinclude:: ../../test-files/custom_optimizer.py + :language: python + :start-after: # --- begin: optimizer --- + :end-before: # --- end: optimizer --- + +This example wraps ``FedProxOptimizer``'s behavior under a new name; +a genuinely new optimizer overrides the ``ProxSGD`` step methods +instead of the constructor. + +Use it +------ + +Registered optimizers are referenced by name in the client config — +programmatically: + +.. code-block:: python + + FedAvgClientConfig(optimizer="CustomOptimizer", lr=1e-2) + +or in a YAML experiment matrix: + +.. code-block:: yaml + + algorithm: + client: + optimizer: CustomOptimizer + +The name is resolved through :func:`~fl_sim.optimizers.get_optimizer` +at client setup (a trailing ``Optimizer`` suffix is optional); an +unregistered name fails there with ``Optimizer ... is not registered``. + +Caveats +------- + +- the optimizer is constructed **once per client per run**; anything + stateful it keeps lives for the whole federated run; +- some algorithms fix the client optimizer in their client-config + validators (e.g. the FedProx family selects ``FedProx`` / + ``FedProx_VR`` from its ``vr`` flag) — for those algorithms the + ``optimizer`` config field is ignored by design; +- the server-side optimizers (FedAdam, FedYogi, … in the FedOpt + family) are a different mechanism — those are selected through the + server config's ``optimizer`` field and are not part of this + registry. + +Next steps +---------- + +- :ref:`howto-custom-algorithm` — when the *algorithm*, not just the + optimizer, is what's new. +- :doc:`api/optimizers` — the optimizer API reference. diff --git a/docs/source/index.rst b/docs/source/index.rst index 6fb62e21..5ed85680 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -27,6 +27,14 @@ Project Links: `GitHub `_ | `gitee , 2026. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: fl-sim \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-10-28 08:06+0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: zh_CN\n" +"Language-Team: zh_CN \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/howto-custom-algorithm.rst:4 +msgid "How to: implement a custom algorithm" +msgstr "操作指南:实现一个自定义算法" + +#: ../../source/howto-custom-algorithm.rst:6 +msgid "" +"Built-in algorithms cover the classics, but research lives in the " +"variations. This guide implements a complete algorithm — a FedProx " +"variant with named parameter exchange and optional variance reduction — " +"walking through every file you need and every hook you can override. The " +"running example is ``test-files/custom_alg.py``; the code shown below is " +"included from that file, so it is exercised in CI (the CLI runs it via " +"``test-files/custom_conf.yml``)." +msgstr "" +"内置算法覆盖了经典方法,但研究的天地在变体里。本指南实现一个完整算法——带按名参数" +"交换与可选方差削减的 FedProx 变体——走遍你需要的每个文件与每个可覆写的钩子。贯穿示例是 " +"``test-files/custom_alg.py``;下文展示的代码都从该文件引入,因此在 CI 中真实执行" +"(CLI 经 ``test-files/custom_conf.yml`` 运行它)。" + +#: ../../source/howto-custom-algorithm.rst:14 +msgid "" +"The one-sentence model: **an algorithm is two config classes plus hook " +"overrides on the Server and Client classes** — the training loop, the " +"communication schedule scaffolding, logging, evaluation and the parallel " +"executors all stay untouched (:doc:`concepts` explains why)." +msgstr "" +"一句话模型: **算法 = 一对配置类 + Server/Client 类上的钩子覆写** ——训练循环、" +"通信调度脚手架、日志、评估、并行执行器全部原样( :doc:`concepts` 解释了为什么)。" + +#: ../../source/howto-custom-algorithm.rst:20 +msgid "When do you need this?" +msgstr "什么时候需要它?" + +#: ../../source/howto-custom-algorithm.rst:22 +msgid "" +"your algorithm changes **what clients compute** locally (new local " +"objective, extra local state) → override client hooks;" +msgstr "算法改变了客户端 **本地计算的内容** (新的本地目标、额外的本地状态)→ 覆写客户端钩子;" + +#: ../../source/howto-custom-algorithm.rst:24 +msgid "" +"it changes **what is exchanged or how replies are aggregated** → override" +" server hooks;" +msgstr "算法改变了 **交换的内容或回复的聚合方式** → 覆写服务器钩子;" + +#: ../../source/howto-custom-algorithm.rst:26 +msgid "it needs **new hyperparameters** → add config fields." +msgstr "算法需要 **新的超参数** → 添加配置字段。" + +#: ../../source/howto-custom-algorithm.rst:28 +msgid "" +"If you only need different *values* for existing hyperparameters, no code" +" is needed — use the built-in algorithm's config." +msgstr "如果只是想给既有超参数设不同的 *值* ,不需要写任何代码——直接用内置算法的配置。" + +#: ../../source/howto-custom-algorithm.rst:32 +msgid "The config classes" +msgstr "配置类" + +#: ../../source/howto-custom-algorithm.rst:34 +msgid "" +"Two pydantic models, one for each side. New fields become CLI/CLI-YAML " +"options automatically; ``server_config_kw_doc`` / " +"``client_config_kw_doc`` append the inherited fields' docs so the API " +"reference stays complete:" +msgstr "" +"两个 pydantic 模型,各管一侧。新字段自动成为 CLI/YAML 选项; ``server_config_kw_doc`` / " +"``client_config_kw_doc`` 把继承字段的文档追加进来,API 参考得以保持完整:" + +#: ../../source/howto-custom-algorithm.rst:43 +msgid "" +"The ``model_validator`` here fixes the local optimizer from the ``vr`` " +"flag — config-level invariants belong in validators, so invalid " +"combinations fail at construction, not mid-run." +msgstr "" +"这里的 ``model_validator`` 由 ``vr`` 标志固定本地优化器——配置级不变式就该放在 " +"validator 里,让非法组合在构造时失败,而不是跑到一半。" + +#: ../../source/howto-custom-algorithm.rst:48 +msgid "The server hooks" +msgstr "服务器钩子" + +#: ../../source/howto-custom-algorithm.rst:50 +msgid "" +"The server customizes the **downlink** (``communicate``) and the " +"**aggregation** (``update``). Everything else — sampling, the round loop," +" metrics — is inherited:" +msgstr "" +"服务器定制 **下行** ( ``communicate`` )与 **聚合** ( ``update`` )。其余一切——" +"采样、轮循环、指标——全部继承:" + +#: ../../source/howto-custom-algorithm.rst:59 +msgid "Points worth noting:" +msgstr "值得注意的点:" + +#: ../../source/howto-custom-algorithm.rst:61 +msgid "" +"``communicate(target)`` builds a typed " +":class:`~fl_sim.messages.ServerToClient` message; ``extras`` carries " +"algorithm-specific payloads (here, server-side gradients for variance " +"reduction) alongside the model." +msgstr "" +"``communicate(target)`` 构建带类型的 :class:`~fl_sim.messages.ServerToClient` 消息;" +"``extras`` 携带算法特有载荷(此处是方差削减用的服务器侧梯度),与模型并列。" + +#: ../../source/howto-custom-algorithm.rst:64 +msgid "" +"``update()`` runs on the main thread after the sampled clients' replies " +"arrive in ``self._received_messages`` — in sampled-client order, which is" +" what keeps aggregation bitwise identical across " +":doc:`parallel_execution` backends." +msgstr "" +"``update()`` 在主线程上运行,此时被采样客户端的回复已进入 ``self._received_messages``" +"——按被采样客户端顺序排列,这正是聚合在所有 :doc:`parallel_execution` 后端上逐位一致的" +"原因。" + +#: ../../source/howto-custom-algorithm.rst:68 +msgid "" +"``client_cls`` and ``config_cls`` wire the custom classes together; " +"``@register_algorithm()`` (shown above each class) makes the algorithm " +"usable by name." +msgstr "" +"``client_cls`` 与 ``config_cls`` 把自定义类互相接起来; ``@register_algorithm()`` " +"(见各类上方)让算法可按名字使用。" + +#: ../../source/howto-custom-algorithm.rst:73 +msgid "The client hooks" +msgstr "客户端钩子" + +#: ../../source/howto-custom-algorithm.rst:75 +msgid "" +"The client customizes the **local training** (``update``) and the " +"**uplink** (``communicate``):" +msgstr "客户端定制 **本地训练** ( ``update`` )与 **上行** ( ``communicate`` ):" + +#: ../../source/howto-custom-algorithm.rst:83 +msgid "" +"The most important contract on this page is in ``communicate``: it " +"**builds and returns** a :class:`~fl_sim.messages.ClientToServer` and " +"touches nothing but the client's own state. The framework sends the " +"message and maintains the communication counters. That is what makes the " +"hook safe to run inside a worker thread when :doc:`parallel execution " +"` is on — a hook that reached into server state would" +" only work sequentially." +msgstr "" +"本页最重要的契约在 ``communicate`` 里:它 **构建并返回** 一个 " +":class:`~fl_sim.messages.ClientToServer`,除客户端自身状态外不碰任何东西。消息的发送" +"与通信计数由框架维护。正因如此,开启 :doc:`并行执行 ` 时该钩子才能" +"安全地跑在工作线程里——一个伸手去碰服务器状态的钩子只能在串行模式下工作。" + +#: ../../source/howto-custom-algorithm.rst:92 +msgid "Named exchange, by the way" +msgstr "顺带说一句:按名交换" + +#: ../../source/howto-custom-algorithm.rst:94 +msgid "" +"This example exchanges parameters **by name** (a ``model_state`` mapping " +"from parameter name to tensor, i.e. :data:`~fl_sim.messages.ModelState`) " +"instead of the legacy flat positional list. Names survive reordering and " +"partial modules, and the loading side (``accept_parameters``, ``update``)" +" looks parameters up by name. The flat-list style still works everywhere;" +" prefer names for new algorithms." +msgstr "" +"本示例按 **参数名** 交换参数( ``model_state`` 映射:参数名 → 张量,即 " +":data:`~fl_sim.messages.ModelState` ),而不是遗留的扁平位置列表。名字能扛住重排序与" +"部分模块,加载侧( ``accept_parameters`` 、 ``update`` )按名查找。扁平列表写法依然处处" +"可用;但新算法请优先用名字。" + +#: ../../source/howto-custom-algorithm.rst:102 +msgid "Register and run" +msgstr "注册与运行" + +#: ../../source/howto-custom-algorithm.rst:104 +msgid "" +"``@register_algorithm()`` (applied to all four classes above) puts the " +"algorithm into the registry; from then on the CLI and YAML matrices " +"accept it like any built-in:" +msgstr "" +"``@register_algorithm()`` (上面四个类都应用了)把算法送进注册表;此后 CLI 与 YAML " +"矩阵像对待内置算法一样接受它:" + +#: ../../source/howto-custom-algorithm.rst:118 +msgid "" +"The same four classes work programmatically, exactly like " +":doc:`quickstart`:" +msgstr "同样这四个类也可以编程方式使用,与 :doc:`quickstart` 完全一致:" + +#: ../../source/howto-custom-algorithm.rst:129 +msgid "How your algorithm is kept honest" +msgstr "你的算法如何被守护" + +#: ../../source/howto-custom-algorithm.rst:131 +msgid "Two layers protect a registered algorithm:" +msgstr "两层机制守护一个注册算法:" + +#: ../../source/howto-custom-algorithm.rst:133 +msgid "" +"the **golden regression** suite (``test/test_parallel_equivalence/``) " +"pins each algorithm's observable trajectory (messages, decisions, " +"counters, metrics positions) so refactors cannot silently change " +"behavior;" +msgstr "" +" **golden 回归** 套件( ``test/test_parallel_equivalence/`` )钉住每个算法的可观测轨迹" +"(消息、决策、计数器、指标位置),重构无法悄悄改变行为;" + +#: ../../source/howto-custom-algorithm.rst:137 +msgid "" +"the **hook contract test** (``test/test_hook_contract.py``) asserts that " +"client hooks touch no server state — the parallel-safety property " +"``communicate`` above was written for." +msgstr "" +" **钩子契约测试** ( ``test/test_hook_contract.py`` )断言客户端钩子不触碰服务器状态" +"——上面 ``communicate`` 的并行安全性质正是为此而写。" + +#: ../../source/howto-custom-algorithm.rst:141 +msgid "When you add an algorithm to the repository itself, add it to both." +msgstr "把算法合入本仓库时,两层都要接入。" + +#: ../../source/howto-custom-algorithm.rst:144 +msgid "Legacy hook style" +msgstr "遗留钩子写法" + +#: ../../source/howto-custom-algorithm.rst:146 +msgid "" +"Older code overrides ``communicate(self, target)`` on the *client* and " +"mutates server state directly. It still works under the sequential " +"backend (with a ``DeprecationWarning``), but it is incompatible with " +"parallel backends and will be removed; new algorithms should build and " +"return the typed message as shown above." +msgstr "" +"更老的代码在 *客户端* 上覆写 ``communicate(self, target)`` 并直接改动服务器状态。它在" +"串行后端下仍然可用(会带 ``DeprecationWarning`` ),但与并行后端不兼容,终将移除;" +"新算法请按上文所示构建并返回带类型的消息。" + +#: ../../source/howto-custom-algorithm.rst:153 +msgid "Next steps" +msgstr "下一步" + +#: ../../source/howto-custom-algorithm.rst:155 +msgid ":ref:`howto-custom-dataset` — bring your own data partitioning." +msgstr ":ref:`howto-custom-dataset`——带上你自己的数据划分。" + +#: ../../source/howto-custom-algorithm.rst:156 +msgid ":ref:`howto-custom-optimizer` — add a local optimizer." +msgstr ":ref:`howto-custom-optimizer`——添加一个本地优化器。" + +#: ../../source/howto-custom-algorithm.rst:157 +msgid ":doc:`parallel_execution` — the full hook contract and backends." +msgstr ":doc:`parallel_execution`——完整的钩子契约与各后端。" + +#: ../../source/howto-custom-algorithm.rst:158 +msgid ":doc:`api/nodes` — the complete Server/Client API." +msgstr ":doc:`api/nodes`——完整的 Server/Client API。" diff --git a/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-dataset.po b/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-dataset.po new file mode 100644 index 00000000..37be5a24 --- /dev/null +++ b/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-dataset.po @@ -0,0 +1,201 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2023, WEN Hao +# This file is distributed under the same license as the fl-sim package. +# FIRST AUTHOR , 2026. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: fl-sim \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-10-28 08:06+0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: zh_CN\n" +"Language-Team: zh_CN \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/howto-custom-dataset.rst:4 +msgid "How to: add a custom dataset" +msgstr "操作指南:添加自定义数据集" + +#: ../../source/howto-custom-dataset.rst:6 +msgid "" +"A federated dataset in fl-sim is two things bundled together: the **raw " +"data** and a **partitioning scheme** that spreads it across simulated " +"clients. This guide walks through the complete example in ``test-" +"files/custom_dataset.py`` (a re-implementation of the FedProx paper's " +"FEMNIST variant, exercised in CI via ``test-files/custom_conf.yml``) and " +"then distills the minimal template for \"I have NumPy arrays and want " +"them federated\"." +msgstr "" +"fl-sim 中的联邦数据集是两样东西的打包: **原始数据** 与把它分散到各模拟客户端的" +" **划分方案** 。本指南先走完 ``test-files/custom_dataset.py`` 的完整示例" +"(FedProx 论文所用 FEMNIST 变体的复刻,经 ``test-files/custom_conf.yml`` 在 CI 中" +"执行),再提炼出\"我有 NumPy 数组,想联邦起来\"的最小模板。" + +#: ../../source/howto-custom-dataset.rst:15 +msgid "Choose the base class" +msgstr "选择基类" + +#: ../../source/howto-custom-dataset.rst:17 +msgid "" +":class:`~fl_sim.data_processing.fed_dataset.FedVisionDataset` — image " +"data with the usual ``TensorDataset`` plumbing; what the example uses;" +msgstr "" +":class:`~fl_sim.data_processing.fed_dataset.FedVisionDataset`——图像数据," +"带常规的 ``TensorDataset`` 管线;示例用的就是它;" + +#: ../../source/howto-custom-dataset.rst:19 +msgid "" +":class:`~fl_sim.data_processing.fed_dataset.FedDataset` — the fully " +"abstract base, for anything non-vision." +msgstr "" +":class:`~fl_sim.data_processing.fed_dataset.FedDataset`——完全抽象的基类," +"适合一切非视觉数据。" + +#: ../../source/howto-custom-dataset.rst:22 +msgid "" +"The required interface is small: load data in ``_preload``, hand out " +"dataloaders per client in ``get_dataloader``, and score predictions in " +"``evaluate``." +msgstr "" +"必需接口很小:在 ``_preload`` 里加载数据,在 ``get_dataloader`` 里按客户端分发" +"dataloader,在 ``evaluate`` 里为预测打分。" + +#: ../../source/howto-custom-dataset.rst:27 +msgid "The full example" +msgstr "完整示例" + +#: ../../source/howto-custom-dataset.rst:34 +msgid "" +"``_preload`` runs once at construction: resolve the data directory, " +"declare the defaults the framework queries (client counts, batch size, " +"the file layout), set the ``criterion``, download if needed, and read the" +" raw files into memory. fl-sim datasets are **immutable after " +"construction** — partitioning happens here, once." +msgstr "" +"``_preload`` 在构造时只跑一次:解析数据目录、声明框架会查询的默认值(客户端数、" +"批大小、文件布局)、设置 ``criterion``、按需下载,并把原始文件读入内存。fl-sim " +"数据集 **构造后不可变** ——划分就在这里做,且只做一次。" + +#: ../../source/howto-custom-dataset.rst:45 +msgid "" +"The contract of ``get_dataloader``: given a client index (or ``None`` for" +" \"everything\"), return a **pair** of dataloaders ``(train, test)`` with" +" shuffling enabled — the framework re-requests them every round, and " +"under ``rng_mode=\"per_client\"`` (:doc:`tutorial-parallel-reproducible`)" +" their shuffling is what gets re-seeded per client." +msgstr "" +"``get_dataloader`` 的契约:给定客户端索引(或 ``None`` 表示\"全部\"),返回一对开启" +"洗牌的 dataloader ``(train, test)`` —— 框架每轮都会重新请求它们;在 " +"``rng_mode=\"per_client\"`` ( :doc:`tutorial-parallel-reproducible` )下,被按客户端" +"重新播种的正是这里的洗牌。" + +#: ../../source/howto-custom-dataset.rst:56 +msgid "" +"``evaluate`` turns a batch of predictions and labels into the metric " +"dictionary that lands in the logs (``acc``, ``loss``, ``num_samples``, " +"... — the same keys :doc:`quickstart` read back)." +msgstr "" +"``evaluate`` 把一批预测与标签变成写进日志的指标字典( ``acc`` 、 ``loss`` 、 " +"``num_samples`` 等——正是 :doc:`quickstart` 读回的那些键)。" + +#: ../../source/howto-custom-dataset.rst:65 +msgid "" +"The remaining properties are conventions rather than requirements: " +"``mirrors`` / ``url`` feed the download mechanism (fallback URLs tried in" +" order — see the " +":meth:`~fl_sim.data_processing.fed_dataset.FedDataset.mirrors` API for " +"the protocol), ``candidate_models`` declares the models that fit this " +"data (the CLI's ``model: name:`` picks from here), ``label_map`` names " +"the classes, ``doi`` carries citations." +msgstr "" +"其余 property 是惯例而非硬性要求: ``mirrors`` / ``url`` 供下载机制使用(备选 URL " +"按序尝试——协议见 :meth:`~fl_sim.data_processing.fed_dataset.FedDataset.mirrors` " +"API); ``candidate_models`` 声明适合本数据的模型(CLI 的 ``model: name:`` 从这里" +"选取); ``label_map`` 给类别命名; ``doi`` 携带引用。" + +#: ../../source/howto-custom-dataset.rst:73 +msgid "The minimal template" +msgstr "最小模板" + +#: ../../source/howto-custom-dataset.rst:75 +msgid "" +"From NumPy arrays to a usable federated dataset. This template is " +"executed verbatim in CI-style verification — three conventions it encodes" +" are easy to miss and expensive to debug:" +msgstr "" +"从 NumPy 数组到可用的联邦数据集。这个模板经过 CI 式验证原样执行——它编码的三个约定" +"容易漏掉、调起来又贵:" + +#: ../../source/howto-custom-dataset.rst:79 +msgid "" +"**define your own ``__init__``**: the abstract " +":class:`~fl_sim.data_processing.fed_dataset.FedDataset` base has no " +"constructor of its own (the vision subclass supplies one); accept " +"``(datadir, transform, seed, **extra_config)`` and re-seed via " +"``set_seed`` so every constructor re-seeds the global RNG exactly like " +"the built-ins do;" +msgstr "" +" **自己定义 ``__init__``** :抽象基类 :class:`~fl_sim.data_processing.fed_dataset.FedDataset` " +"本身没有构造器(视觉子类提供了一个);接受 ``(datadir, transform, seed, **extra_config)`` " +"并经 ``set_seed`` 重新播种,让每个构造器都和内置数据集一样重置全局 RNG;" + +#: ../../source/howto-custom-dataset.rst:85 +msgid "" +"**the framework requests global loaders too**: ``get_dataloader`` must " +"handle ``client_idx=None`` (\"all clients\"), not just per-client " +"indices;" +msgstr "" +" **框架也会请求全局 loader** : ``get_dataloader`` 必须处理 ``client_idx=None`` " +"(\"全部客户端\"),不能只处理逐客户端的索引;" + +#: ../../source/howto-custom-dataset.rst:88 +msgid "" +"**``candidate_models`` should return fl-sim models** (they carry the " +"``.device`` attribute the device-allocation step relies on)." +msgstr "" +" **``candidate_models`` 应返回 fl-sim 模型** (它们带有设备分配步骤依赖的 " +"``.device`` 属性)。" + +#: ../../source/howto-custom-dataset.rst:91 +msgid "" +"Two ``load_partition_data*`` methods look intimidating but are a legacy " +"tff-style bulk-loading API the training loop never calls — a stub " +"satisfies the abstract interface:" +msgstr "" +"两个 ``load_partition_data*`` 方法看着吓人,其实是训练循环从不调用的遗留 tff 风格" +"批量加载 API——一个桩就满足抽象接口:" + +#: ../../source/howto-custom-dataset.rst:172 +msgid "Use it" +msgstr "使用" + +#: ../../source/howto-custom-dataset.rst:174 +msgid "" +"With the ``@register_fed_dataset()`` decorator applied, the dataset is " +"constructible by name once its module has been imported (or via the " +"module *path* as the dataset ``name`` — see " +":class:`~fl_sim.data_processing.fed_data_args.FedDataArgs`), and every " +"built-in works on it unchanged:" +msgstr "" +"应用 ``@register_fed_dataset()`` 装饰器后,只要模块被导入过,数据集即可按名字构造" +"(或以模块 *路径* 作为数据集 ``name`` ——见 " +":class:`~fl_sim.data_processing.fed_data_args.FedDataArgs`),且所有内置组件原样可用:" + +#: ../../source/howto-custom-dataset.rst:197 +msgid "Next steps" +msgstr "下一步" + +#: ../../source/howto-custom-dataset.rst:199 +msgid ":ref:`howto-custom-algorithm` — the algorithm side of the contract." +msgstr ":ref:`howto-custom-algorithm`——契约的算法一侧。" + +#: ../../source/howto-custom-dataset.rst:200 +msgid ":doc:`api/datasets` — the full FedDataset API reference." +msgstr ":doc:`api/datasets`——完整的 FedDataset API 参考。" diff --git a/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-optimizer.po b/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-optimizer.po new file mode 100644 index 00000000..11dd62a0 --- /dev/null +++ b/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-optimizer.po @@ -0,0 +1,135 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2023, WEN Hao +# This file is distributed under the same license as the fl-sim package. +# FIRST AUTHOR , 2026. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: fl-sim \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-10-28 08:06+0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: zh_CN\n" +"Language-Team: zh_CN \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.16.0\n" + +#: ../../source/howto-custom-optimizer.rst:4 +msgid "How to: add a custom optimizer" +msgstr "操作指南:添加自定义优化器" + +#: ../../source/howto-custom-optimizer.rst:6 +msgid "" +"The local training step is pluggable: fl-sim keeps its own optimizer " +"registry, and any optimizer registered there can be selected by name in " +"the client config. This is the lightest of the three how-tos — the " +"complete example is 48 lines (``test-files/custom_optimizer.py``, " +"exercised in CI via ``test-files/custom_conf.yml``)." +msgstr "" +"本地训练步是可插拔的:fl-sim 维护自己的优化器注册表,任何注册过的优化器都能在客户端" +"配置里按名字选用。这是三篇操作指南里最轻的一篇——完整示例只有 48 行" +"(``test-files/custom_optimizer.py``,经 ``test-files/custom_conf.yml`` 在 CI 中执行)。" + +#: ../../source/howto-custom-optimizer.rst:13 +msgid "Where optimizers plug in" +msgstr "优化器接在哪里" + +#: ../../source/howto-custom-optimizer.rst:15 +msgid "" +"fl-sim's local optimizers live in :mod:`fl_sim.optimizers` and subclass " +":class:`~fl_sim.optimizers.base.ProxSGD` — the proximal SGD base that " +"supports the extra local-training knobs federated algorithms need " +"(proximal terms, variance-reduction buffers passed by " +"``optimizer.step(...)``, per the custom algorithm in :ref:`howto-custom-" +"algorithm`)." +msgstr "" +"fl-sim 的本地优化器都在 :mod:`fl_sim.optimizers` 里,继承 " +":class:`~fl_sim.optimizers.base.ProxSGD`——这个近端 SGD 基类支持联邦算法需要的额外本地" +"训练旋钮(近端项、由 ``optimizer.step(...)`` 传入的方差削减缓冲区,用法见 " +":ref:`howto-custom-algorithm` 的自定义算法)。" + +#: ../../source/howto-custom-optimizer.rst:22 +msgid "" +"Registration makes the optimizer discoverable by name — the same pattern " +"as algorithms and datasets:" +msgstr "注册让优化器可按名字发现——与算法、数据集是同一模式:" + +#: ../../source/howto-custom-optimizer.rst:30 +msgid "" +"This example wraps ``FedProxOptimizer``'s behavior under a new name; a " +"genuinely new optimizer overrides the ``ProxSGD`` step methods instead of" +" the constructor." +msgstr "" +"这个例子把 ``FedProxOptimizer`` 的行为换了个名字;真正全新的优化器应覆写 ``ProxSGD`` " +"的 step 方法而不是构造器。" + +#: ../../source/howto-custom-optimizer.rst:35 +msgid "Use it" +msgstr "使用" + +#: ../../source/howto-custom-optimizer.rst:37 +msgid "" +"Registered optimizers are referenced by name in the client config — " +"programmatically:" +msgstr "注册过的优化器在客户端配置里按名字引用——编程方式:" + +#: ../../source/howto-custom-optimizer.rst:44 +msgid "or in a YAML experiment matrix:" +msgstr "或在 YAML 实验矩阵里:" + +#: ../../source/howto-custom-optimizer.rst:52 +msgid "" +"The name is resolved through :func:`~fl_sim.optimizers.get_optimizer` at " +"client setup (a trailing ``Optimizer`` suffix is optional); an " +"unregistered name fails there with ``Optimizer ... is not registered``." +msgstr "" +"名字在客户端初始化时经 :func:`~fl_sim.optimizers.get_optimizer` 解析(结尾的 " +"``Optimizer`` 后缀可省略);未注册的名字会在此报错 ``Optimizer ... is not registered`` 。" + +#: ../../source/howto-custom-optimizer.rst:57 +msgid "Caveats" +msgstr "注意事项" + +#: ../../source/howto-custom-optimizer.rst:59 +msgid "" +"the optimizer is constructed **once per client per run**; anything " +"stateful it keeps lives for the whole federated run;" +msgstr "优化器 **每次运行中每个客户端只构造一次** ;它保存的任何状态会贯穿整个联邦运行;" + +#: ../../source/howto-custom-optimizer.rst:61 +msgid "" +"some algorithms fix the client optimizer in their client-config " +"validators (e.g. the FedProx family selects ``FedProx`` / ``FedProx_VR`` " +"from its ``vr`` flag) — for those algorithms the ``optimizer`` config " +"field is ignored by design;" +msgstr "" +"某些算法在客户端配置的 validator 里固定了本地优化器(如 FedProx 家族由 ``vr`` 标志选择 " +"``FedProx`` / ``FedProx_VR`` )——对这些算法, ``optimizer`` 配置字段被设计性地忽略;" + +#: ../../source/howto-custom-optimizer.rst:65 +msgid "" +"the server-side optimizers (FedAdam, FedYogi, … in the FedOpt family) are" +" a different mechanism — those are selected through the server config's " +"``optimizer`` field and are not part of this registry." +msgstr "" +"服务器侧优化器(FedOpt 家族的 FedAdam、FedYogi 等)是另一套机制——经服务器配置的 " +"``optimizer`` 字段选择,不在本注册表内。" + +#: ../../source/howto-custom-optimizer.rst:71 +msgid "Next steps" +msgstr "下一步" + +#: ../../source/howto-custom-optimizer.rst:73 +msgid "" +":ref:`howto-custom-algorithm` — when the *algorithm*, not just the " +"optimizer, is what's new." +msgstr ":ref:`howto-custom-algorithm`——如果新的不只是优化器,而是 *算法本身* 。" + +#: ../../source/howto-custom-optimizer.rst:75 +msgid ":doc:`api/optimizers` — the optimizer API reference." +msgstr ":doc:`api/optimizers`——优化器 API 参考。" diff --git a/docs/source/locale/zh_CN/LC_MESSAGES/index.po b/docs/source/locale/zh_CN/LC_MESSAGES/index.po index b515f516..a199c85d 100644 --- a/docs/source/locale/zh_CN/LC_MESSAGES/index.po +++ b/docs/source/locale/zh_CN/LC_MESSAGES/index.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: fl-sim\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-10-27 08:46+0800\n" +"POT-Creation-Date: 2026-10-28 08:06+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: zh_CN\n" @@ -27,11 +27,15 @@ msgstr "快速开始" msgid "Tutorials" msgstr "教程" -#: ../../source/index.rst:28 +#: ../../source/index.rst:30 +msgid "How-to guides" +msgstr "操作指南" + +#: ../../source/index.rst:38 msgid "API Reference" msgstr "API 参考" -#: ../../source/index.rst:41 +#: ../../source/index.rst:51 msgid "Advanced Topics" msgstr "进阶主题" @@ -51,18 +55,18 @@ msgstr "" "项目链接: `GitHub `_ | `gitee " "`_" -#: ../../source/index.rst:51 +#: ../../source/index.rst:61 msgid "Indices and tables" msgstr "索引和表格" -#: ../../source/index.rst:53 +#: ../../source/index.rst:63 msgid ":ref:`genindex`" msgstr "" -#: ../../source/index.rst:54 +#: ../../source/index.rst:64 msgid ":ref:`modindex`" msgstr "" -#: ../../source/index.rst:55 +#: ../../source/index.rst:65 msgid ":ref:`search`" msgstr "" diff --git a/docs/source/locale/zh_CN/LC_MESSAGES/tutorial-cli-matrix.po b/docs/source/locale/zh_CN/LC_MESSAGES/tutorial-cli-matrix.po index 1573bbaf..ba28f468 100644 --- a/docs/source/locale/zh_CN/LC_MESSAGES/tutorial-cli-matrix.po +++ b/docs/source/locale/zh_CN/LC_MESSAGES/tutorial-cli-matrix.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: fl-sim \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-10-27 08:46+0800\n" +"POT-Creation-Date: 2026-10-28 08:06+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: zh_CN\n" @@ -31,17 +31,14 @@ msgid "" "and executes every combination. This tutorial builds a four-run matrix on" " synthetic data and shows where the results land." msgstr "" -"每个实验写一个 Python 脚本是无法扩展的。fl-sim 的命令行接口能用单个 YAML 文件跑起" -"一整个 **实验矩阵** :你把配置网格描述一次,CLI 负责展开并执行每个组合。本教程在" -"合成数据上构建一个四次运行的矩阵,并展示结果落在哪里。" +"每个实验写一个 Python 脚本是无法扩展的。fl-sim 的命令行接口能用单个 YAML 文件跑起一整个 **实验矩阵** " +":你把配置网格描述一次,CLI 负责展开并执行每个组合。本教程在合成数据上构建一个四次运行的矩阵,并展示结果落在哪里。" #: ../../source/tutorial-cli-matrix.rst:12 msgid "" "The YAML syntax reference lives in :doc:`cli`; this page is the workflow " "narrative. Prerequisites: :doc:`tutorial-first-experiment`." -msgstr "" -"YAML 语法参考见 :doc:`cli` ;本页讲的是工作流程。前置阅读:" -":doc:`tutorial-first-experiment` 。" +msgstr "YAML 语法参考见 :doc:`cli` ;本页讲的是工作流程。前置阅读::doc:`tutorial-first-experiment` 。" #: ../../source/tutorial-cli-matrix.rst:16 msgid "The matrix file" @@ -61,9 +58,7 @@ msgstr "三个关键部分:" msgid "" "``strategy.matrix`` — the grid. Every combination of the listed values is" " run once: 2 sampling ratios × 2 seeds = 4 experiments." -msgstr "" -"``strategy.matrix``——网格。列出的值每个组合各跑一次:2 个采样比例 × 2 个种子" -"= 4 个实验。" +msgstr "``strategy.matrix``——网格。列出的值每个组合各跑一次:2 个采样比例 × 2 个种子= 4 个实验。" #: ../../source/tutorial-cli-matrix.rst:28 msgid "" @@ -71,8 +66,8 @@ msgid "" "the CLI substitutes the current combination's value. ``seed`` sits at the" " top level, so it seeds the dataset *and* the server config of that run." msgstr "" -"``${{ matrix. }}``——插值。该模式出现之处,CLI 都会代入当前组合的值。" -"``seed`` 位于顶层,因此它同时为该次运行的数据集 *和* 服务器配置设定种子。" +"``${{ matrix. }}``——插值。该模式出现之处,CLI 都会代入当前组合的值。``seed`` " +"位于顶层,因此它同时为该次运行的数据集 *和* 服务器配置设定种子。" #: ../../source/tutorial-cli-matrix.rst:32 msgid "" @@ -83,10 +78,10 @@ msgid "" ":class:`~fl_sim.data_processing.fed_synthetic.FedSynthetic`) are passed " "straight through." msgstr "" -"其余部分—— ``algorithm`` (名字加服务器/客户端配置)、 ``dataset`` 、 ``model`` ——" -"与你在 Python 里传入的配置(见 :doc:`quickstart` )完全一致,只是改写成 YAML。" -"数据集各自的构造参数( :class:`~fl_sim.data_processing.fed_synthetic.FedSynthetic` " -"的 ``alpha`` 、 ``beta`` 、 ``iid`` 、 ``num_clients`` )原样透传。" +"其余部分—— ``algorithm`` (名字加服务器/客户端配置)、 ``dataset`` 、 ``model`` ——与你在 Python" +" 里传入的配置(见 :doc:`quickstart` )完全一致,只是改写成 YAML。数据集各自的构造参数( " +":class:`~fl_sim.data_processing.fed_synthetic.FedSynthetic` 的 ``alpha`` 、" +" ``beta`` 、 ``iid`` 、 ``num_clients`` )原样透传。" #: ../../source/tutorial-cli-matrix.rst:40 msgid "Run it" @@ -97,9 +92,7 @@ msgid "" "You will see the four runs execute in sequence, each logging its " "combination (``ratio=0.5 seed=0``, ...). Total time: seconds — no " "downloads." -msgstr "" -"你会看到四次运行依次执行,每次都会记录自己的组合(``ratio=0.5 seed=0`` 等)。" -"总耗时:几秒——无下载。" +msgstr "你会看到四次运行依次执行,每次都会记录自己的组合(``ratio=0.5 seed=0`` 等)。总耗时:几秒——无下载。" #: ../../source/tutorial-cli-matrix.rst:51 msgid "Where the results land" @@ -114,10 +107,10 @@ msgid "" "recent run (a previous ``config.json`` is rotated to " "``config.json.bak``)." msgstr "" -"所有运行都写入 ``~/.cache/fl-sim/.logs/fedsynthetic-demo/``(本配置的 ``log_dir`` )。" -"每次运行各有一份 ``.txt`` 与一份 ``.json`` 日志——即 :doc:`quickstart` 描述过的" -"那些文件——另有一份 ``config.json`` 保存最近一次运行的确切服务器/客户端配置" -"(已有的 ``config.json`` 会被轮转为 ``config.json.bak`` )。" +"所有运行都写入 ``~/.cache/fl-sim/.logs/fedsynthetic-demo/``(本配置的 ``log_dir`` " +")。每次运行各有一份 ``.txt`` 与一份 ``.json`` 日志——即 :doc:`quickstart` 描述过的那些文件——另有一份 " +"``config.json`` 保存最近一次运行的确切服务器/客户端配置(已有的 ``config.json`` 会被轮转为 " +"``config.json.bak`` )。" #: ../../source/tutorial-cli-matrix.rst:60 msgid "" @@ -127,9 +120,9 @@ msgid "" "other. That is what the ``tag: ratio_${{ ... }}_seed_${{ ... }}`` line in" " the config above is for — give every matrix arm its own tag." msgstr "" -"一个在多次运行同一分钟内结束时就会生效的细节:日志文件名带有时间戳 **加上运行的 " -"``tag``** 。若每个矩阵组合没有独特的 tag,同名的日志会互相覆盖。上面配置里的 " -"``tag: ratio_${{ ... }}_seed_${{ ... }}`` 正是为此——给矩阵的每个分支一个专属 tag。" +"一个在多次运行同一分钟内结束时就会生效的细节:日志文件名带有时间戳 **加上运行的 ``tag``** 。若每个矩阵组合没有独特的 " +"tag,同名的日志会互相覆盖。上面配置里的 ``tag: ratio_${{ ... }}_seed_${{ ... }}`` " +"正是为此——给矩阵的每个分支一个专属 tag。" #: ../../source/tutorial-cli-matrix.rst:67 msgid "Beyond serial execution" @@ -146,11 +139,10 @@ msgid "" "two are orthogonal: process-level parallelism across matrix arms, " "executor-level parallelism across clients." msgstr "" -"长实验组成的矩阵需要不止一个进程。 ``strategy`` 节还接受并行选项(``n_parallel``," -"或 ``parallel: {mode: parallel_task, num_workers: N}`` 块),把矩阵分支派发给工作" -"进程——完整示例见 ``example-configs/multiprocessing-demo.yml``;进程内把 *单次运行* " -"的客户端并行化的执行器后端见 :doc:`parallel_execution` 。两者正交:进程级并行作用于" -"矩阵分支之间,执行器级并行作用于客户端之间。" +"长实验组成的矩阵需要不止一个进程。 ``strategy`` 节还接受并行选项(``n_parallel``,或 ``parallel: " +"{mode: parallel_task, num_workers: N}`` 块),把矩阵分支派发给工作进程——完整示例见 ``example-" +"configs/multiprocessing-demo.yml``;进程内把 *单次运行* 的客户端并行化的执行器后端见 " +":doc:`parallel_execution` 。两者正交:进程级并行作用于矩阵分支之间,执行器级并行作用于客户端之间。" #: ../../source/tutorial-cli-matrix.rst:79 msgid "Next steps" @@ -166,11 +158,21 @@ msgstr ":doc:`parallel_execution`——加速每一次单独的运行。" #: ../../source/tutorial-cli-matrix.rst:83 msgid "" -"How-to guides on custom algorithms, datasets and optimizers are on the " -"way; until then, ``test-files/custom_alg.py``, ``test-" -"files/custom_dataset.py`` and ``test-files/custom_optimizer.py`` are " -"complete, runnable examples." +":ref:`howto-custom-algorithm`, :ref:`howto-custom-dataset` and :ref" +":`howto-custom-optimizer` — extend the framework itself; the ``test-" +"files/custom_*.py`` files they walk through are complete, runnable " +"examples that the CLI accepts as algorithm/dataset paths." msgstr "" -"自定义算法、数据集与优化器的 how-to 指南正在准备中;在那之前," -"``test-files/custom_alg.py``、``test-files/custom_dataset.py`` 与 " -"``test-files/custom_optimizer.py`` 就是完整可运行的例子。" +":ref:`howto-custom-algorithm` 、 :ref:`howto-custom-dataset` 与 :ref:`howto-custom-optimizer` ——扩展框架本身;它们讲解的 ``test-files/custom_*.py`` 就是完整可运行的例子,CLI 可直接接受为算法/数据集路径。" + +#~ msgid "" +#~ "How-to guides on custom algorithms, " +#~ "datasets and optimizers are on the " +#~ "way; until then, ``test-files/custom_alg.py``," +#~ " ``test-files/custom_dataset.py`` and ``test-" +#~ "files/custom_optimizer.py`` are complete, runnable" +#~ " examples." +#~ msgstr "" +#~ "自定义算法、数据集与优化器的 how-to 指南正在准备中;在那之前,``test-" +#~ "files/custom_alg.py``、``test-files/custom_dataset.py`` 与" +#~ " ``test-files/custom_optimizer.py`` 就是完整可运行的例子。" diff --git a/docs/source/tutorial-cli-matrix.rst b/docs/source/tutorial-cli-matrix.rst index 316748a2..6a24a07d 100644 --- a/docs/source/tutorial-cli-matrix.rst +++ b/docs/source/tutorial-cli-matrix.rst @@ -80,7 +80,7 @@ Next steps - :doc:`cli` — the full YAML syntax reference and CLI options. - :doc:`parallel_execution` — speed up each individual run. -- How-to guides on custom algorithms, datasets and optimizers are on - the way; until then, ``test-files/custom_alg.py``, - ``test-files/custom_dataset.py`` and ``test-files/custom_optimizer.py`` - are complete, runnable examples. +- :ref:`howto-custom-algorithm`, :ref:`howto-custom-dataset` and + :ref:`howto-custom-optimizer` — extend the framework itself; the + ``test-files/custom_*.py`` files they walk through are complete, + runnable examples that the CLI accepts as algorithm/dataset paths. diff --git a/test-files/custom_alg.py b/test-files/custom_alg.py index 5de03e56..2e195bcd 100644 --- a/test-files/custom_alg.py +++ b/test-files/custom_alg.py @@ -28,6 +28,7 @@ ] +# --- begin: configs --- @register_algorithm() @add_docstring(server_config_kw_doc, "append") class CustomServerConfig(ServerConfig): @@ -93,6 +94,10 @@ def _fix_optimizer(self) -> "CustomClientConfig": return self +# --- end: configs --- + + +# --- begin: server --- @register_algorithm() @add_docstring( Server.__doc__.replace( @@ -156,6 +161,10 @@ def doi(self) -> List[str]: return ["10.48550/ARXIV.1812.06127"] +# --- end: server --- + + +# --- begin: client --- @register_algorithm() @add_docstring( Client.__doc__.replace( @@ -251,3 +260,6 @@ def train(self) -> None: # free memory del X, y, output, loss self.lr_scheduler.step() + + +# --- end: client --- diff --git a/test-files/custom_dataset.py b/test-files/custom_dataset.py index 3dac0c27..62340f27 100644 --- a/test-files/custom_dataset.py +++ b/test-files/custom_dataset.py @@ -27,6 +27,7 @@ _label_mapping = {i: c for i, c in enumerate("abcdefghijklmnopqrstuvwxyz"[:10])} +# --- begin: dataset --- @register_fed_dataset() class CustomFEMNIST(FedVisionDataset): """ @@ -62,6 +63,7 @@ class CustomFEMNIST(FedVisionDataset): __name__ = "CustomFEMNIST" + # --- begin: preload --- def _preload(self, datadir: Optional[Union[str, Path]] = None) -> None: self.datadir = Path(datadir or CUSTOM_FEMNIST_DATA_DIR).expanduser().resolve() @@ -105,6 +107,9 @@ def _preload(self, datadir: Optional[Union[str, Path]] = None) -> None: ) ) + # --- end: preload --- + + # --- begin: dataloader --- def get_dataloader( self, train_bs: Optional[int] = None, @@ -152,11 +157,14 @@ def get_dataloader( return train_dl, test_dl + # --- end: dataloader --- + def extra_repr_keys(self) -> List[str]: return [ "n_class", ] + super().extra_repr_keys() + # --- begin: evaluate --- def evaluate(self, probs: torch.Tensor, truths: torch.Tensor) -> Dict[str, float]: return { "acc": top_n_accuracy(probs, truths, 1), @@ -166,6 +174,9 @@ def evaluate(self, probs: torch.Tensor, truths: torch.Tensor) -> Dict[str, float "num_samples": probs.shape[0], } + # --- end: evaluate --- + + # --- begin: mirrors-and-models --- @property def url(self) -> str: # https://drive.google.com/file/d/1tCEcJgRJ8NdRo11UJZR6WSKMNdmox4GC/view?usp=sharing @@ -205,3 +216,8 @@ def doi(self) -> List[str]: @property def label_map(self) -> dict: return _label_mapping + + # --- end: mirrors-and-models --- + + +# --- end: dataset --- diff --git a/test-files/custom_optimizer.py b/test-files/custom_optimizer.py index 5b5a91fc..34352c2b 100644 --- a/test-files/custom_optimizer.py +++ b/test-files/custom_optimizer.py @@ -10,6 +10,7 @@ ] +# --- begin: optimizer --- @register_optimizer() class CustomOptimizer(ProxSGD): """Local optimizer for ``CustomOptimizer`` using ``ProxSGD``. @@ -46,3 +47,6 @@ def __init__( ) -> None: self.mu = mu super().__init__(params, lr=lr, prox=mu, momentum=0) + + +# --- end: optimizer --- From fe01269873f1df84a80a86811b5e366b74ca7c4f Mon Sep 17 00:00:00 2001 From: WEN Hao Date: Thu, 29 Oct 2026 00:15:16 +0800 Subject: [PATCH 2/2] docs: tighten the tone of the three how-to guides Align the phrasing with the rest of the documentation: "research lives in the variations" becomes a plain declarative opening, question headings become noun phrases ("When do you need this?" -> "When is a custom algorithm needed?"), and metaphorical wording is replaced ("How your algorithm is kept honest" -> "Safeguards for registered algorithms", "two things bundled together" -> "combines two components", "look intimidating" -> "appear required", "the lightest of the three how-tos" -> "the shortest of the three guides"). The zh_CN translations are reworded to match. No technical content changes. --- docs/source/howto-custom-algorithm.rst | 14 +- docs/source/howto-custom-dataset.rst | 26 +-- docs/source/howto-custom-optimizer.rst | 6 +- .../LC_MESSAGES/howto-custom-algorithm.po | 195 ++++++------------ .../zh_CN/LC_MESSAGES/howto-custom-dataset.po | 164 +++++---------- .../LC_MESSAGES/howto-custom-optimizer.po | 92 +++------ 6 files changed, 158 insertions(+), 339 deletions(-) diff --git a/docs/source/howto-custom-algorithm.rst b/docs/source/howto-custom-algorithm.rst index abbd2a37..559f97b7 100644 --- a/docs/source/howto-custom-algorithm.rst +++ b/docs/source/howto-custom-algorithm.rst @@ -3,8 +3,8 @@ How to: implement a custom algorithm ==================================== -Built-in algorithms cover the classics, but research lives in the -variations. This guide implements a complete algorithm — a FedProx +Built-in algorithms cover the classical methods; research often requires +variants. This guide implements a complete algorithm — a FedProx variant with named parameter exchange and optional variance reduction — walking through every file you need and every hook you can override. The running example is ``test-files/custom_alg.py``; the code shown @@ -16,8 +16,8 @@ overrides on the Server and Client classes** — the training loop, the communication schedule scaffolding, logging, evaluation and the parallel executors all stay untouched (:doc:`concepts` explains why). -When do you need this? ----------------------- +When is a custom algorithm needed? +---------------------------------- - your algorithm changes **what clients compute** locally (new local objective, extra local state) → override client hooks; @@ -125,10 +125,10 @@ The same four classes work programmatically, exactly like ) server.train() -How your algorithm is kept honest ---------------------------------- +Safeguards for registered algorithms +------------------------------------ -Two layers protect a registered algorithm: +Two layers guard a registered algorithm: - the **golden regression** suite (``test/test_parallel_equivalence/``) pins each algorithm's observable trajectory (messages, decisions, diff --git a/docs/source/howto-custom-dataset.rst b/docs/source/howto-custom-dataset.rst index bc85bc30..5fc6732e 100644 --- a/docs/source/howto-custom-dataset.rst +++ b/docs/source/howto-custom-dataset.rst @@ -3,13 +3,13 @@ How to: add a custom dataset ============================ -A federated dataset in fl-sim is two things bundled together: the **raw -data** and a **partitioning scheme** that spreads it across simulated -clients. This guide walks through the complete example in +A federated dataset in fl-sim combines two components: the **raw data** +and a **partitioning scheme** that spreads it across the simulated +clients. This guide covers the complete example in ``test-files/custom_dataset.py`` (a re-implementation of the FedProx paper's FEMNIST variant, exercised in CI via -``test-files/custom_conf.yml``) and then distills the minimal template -for "I have NumPy arrays and want them federated". +``test-files/custom_conf.yml``) and then derives a minimal template +starting from NumPy arrays. Choose the base class --------------------- @@ -72,9 +72,9 @@ fit this data (the CLI's ``model: name:`` picks from here), The minimal template -------------------- -From NumPy arrays to a usable federated dataset. This template is -executed verbatim in CI-style verification — three conventions it -encodes are easy to miss and expensive to debug: +From NumPy arrays to a usable federated dataset. The template below has +been verified by end-to-end execution; three conventions it encodes are +easy to miss and costly to debug: - **define your own ``__init__``**: the abstract :class:`~fl_sim.data_processing.fed_dataset.FedDataset` base has no @@ -88,9 +88,9 @@ encodes are easy to miss and expensive to debug: - **``candidate_models`` should return fl-sim models** (they carry the ``.device`` attribute the device-allocation step relies on). -Two ``load_partition_data*`` methods look intimidating but are a legacy -tff-style bulk-loading API the training loop never calls — a stub -satisfies the abstract interface: +Two ``load_partition_data*`` methods appear required but are a legacy +tff-style bulk-loading API that the training loop never calls — stub +implementations satisfy the abstract interface: .. code-block:: python @@ -168,8 +168,8 @@ satisfies the abstract interface: def doi(self): return [] -Use it ------- +Usage +----- With the ``@register_fed_dataset()`` decorator applied, the dataset is constructible by name once its module has been imported (or via the diff --git a/docs/source/howto-custom-optimizer.rst b/docs/source/howto-custom-optimizer.rst index 843af0af..b8e386ec 100644 --- a/docs/source/howto-custom-optimizer.rst +++ b/docs/source/howto-custom-optimizer.rst @@ -5,7 +5,7 @@ How to: add a custom optimizer The local training step is pluggable: fl-sim keeps its own optimizer registry, and any optimizer registered there can be selected by name in -the client config. This is the lightest of the three how-tos — the +the client config. This is the shortest of the three guides; the complete example is 48 lines (``test-files/custom_optimizer.py``, exercised in CI via ``test-files/custom_conf.yml``). @@ -31,8 +31,8 @@ This example wraps ``FedProxOptimizer``'s behavior under a new name; a genuinely new optimizer overrides the ``ProxSGD`` step methods instead of the constructor. -Use it ------- +Usage +----- Registered optimizers are referenced by name in the client config — programmatically: diff --git a/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-algorithm.po b/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-algorithm.po index 76ab1e0d..722bbc63 100644 --- a/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-algorithm.po +++ b/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-algorithm.po @@ -6,17 +6,17 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: fl-sim \n" +"Project-Id-Version: fl-sim\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-10-28 08:06+0800\n" +"POT-Creation-Date: 2026-10-29 00:10+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" -"Language: zh_CN\n" "Language-Team: zh_CN \n" -"Plural-Forms: nplurals=1; plural=0;\n" +"Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" "Generated-By: Babel 2.16.0\n" #: ../../source/howto-custom-algorithm.rst:4 @@ -25,43 +25,27 @@ msgstr "操作指南:实现一个自定义算法" #: ../../source/howto-custom-algorithm.rst:6 msgid "" -"Built-in algorithms cover the classics, but research lives in the " -"variations. This guide implements a complete algorithm — a FedProx " -"variant with named parameter exchange and optional variance reduction — " -"walking through every file you need and every hook you can override. The " -"running example is ``test-files/custom_alg.py``; the code shown below is " -"included from that file, so it is exercised in CI (the CLI runs it via " -"``test-files/custom_conf.yml``)." -msgstr "" -"内置算法覆盖了经典方法,但研究的天地在变体里。本指南实现一个完整算法——带按名参数" -"交换与可选方差削减的 FedProx 变体——走遍你需要的每个文件与每个可覆写的钩子。贯穿示例是 " -"``test-files/custom_alg.py``;下文展示的代码都从该文件引入,因此在 CI 中真实执行" -"(CLI 经 ``test-files/custom_conf.yml`` 运行它)。" +"Built-in algorithms cover the classical methods; research often requires variants. This guide implements a complete algorithm — a FedProx variant with named parameter exchange and optional variance " +"reduction — walking through every file you need and every hook you can override. The running example is ``test-files/custom_alg.py``; the code shown below is included from that file, so it is " +"exercised in CI (the CLI runs it via ``test-files/custom_conf.yml``)." +msgstr "内置算法覆盖了经典方法,而研究往往需要变体。本指南实现一个完整算法——带按名参数交换与可选方差削减的 FedProx 变体——走遍你需要的每个文件与每个可覆写的钩子。贯穿示例是 ``test-files/custom_alg.py``;下文展示的代码都从该文件引入,因此在 CI 中真实执行(CLI 经 ``test-files/custom_conf.yml`` 运行它)。" #: ../../source/howto-custom-algorithm.rst:14 msgid "" -"The one-sentence model: **an algorithm is two config classes plus hook " -"overrides on the Server and Client classes** — the training loop, the " -"communication schedule scaffolding, logging, evaluation and the parallel " -"executors all stay untouched (:doc:`concepts` explains why)." -msgstr "" -"一句话模型: **算法 = 一对配置类 + Server/Client 类上的钩子覆写** ——训练循环、" -"通信调度脚手架、日志、评估、并行执行器全部原样( :doc:`concepts` 解释了为什么)。" +"The one-sentence model: **an algorithm is two config classes plus hook overrides on the Server and Client classes** — the training loop, the communication schedule scaffolding, logging, evaluation " +"and the parallel executors all stay untouched (:doc:`concepts` explains why)." +msgstr "一句话模型: **算法 = 一对配置类 + Server/Client 类上的钩子覆写** ——训练循环、通信调度脚手架、日志、评估、并行执行器全部原样( :doc:`concepts` 解释了为什么)。" #: ../../source/howto-custom-algorithm.rst:20 -msgid "When do you need this?" -msgstr "什么时候需要它?" +msgid "When is a custom algorithm needed?" +msgstr "何时需要自定义算法" #: ../../source/howto-custom-algorithm.rst:22 -msgid "" -"your algorithm changes **what clients compute** locally (new local " -"objective, extra local state) → override client hooks;" +msgid "your algorithm changes **what clients compute** locally (new local objective, extra local state) → override client hooks;" msgstr "算法改变了客户端 **本地计算的内容** (新的本地目标、额外的本地状态)→ 覆写客户端钩子;" #: ../../source/howto-custom-algorithm.rst:24 -msgid "" -"it changes **what is exchanged or how replies are aggregated** → override" -" server hooks;" +msgid "it changes **what is exchanged or how replies are aggregated** → override server hooks;" msgstr "算法改变了 **交换的内容或回复的聚合方式** → 覆写服务器钩子;" #: ../../source/howto-custom-algorithm.rst:26 @@ -69,9 +53,7 @@ msgid "it needs **new hyperparameters** → add config fields." msgstr "算法需要 **新的超参数** → 添加配置字段。" #: ../../source/howto-custom-algorithm.rst:28 -msgid "" -"If you only need different *values* for existing hyperparameters, no code" -" is needed — use the built-in algorithm's config." +msgid "If you only need different *values* for existing hyperparameters, no code is needed — use the built-in algorithm's config." msgstr "如果只是想给既有超参数设不同的 *值* ,不需要写任何代码——直接用内置算法的配置。" #: ../../source/howto-custom-algorithm.rst:32 @@ -80,35 +62,21 @@ msgstr "配置类" #: ../../source/howto-custom-algorithm.rst:34 msgid "" -"Two pydantic models, one for each side. New fields become CLI/CLI-YAML " -"options automatically; ``server_config_kw_doc`` / " -"``client_config_kw_doc`` append the inherited fields' docs so the API " +"Two pydantic models, one for each side. New fields become CLI/CLI-YAML options automatically; ``server_config_kw_doc`` / ``client_config_kw_doc`` append the inherited fields' docs so the API " "reference stays complete:" -msgstr "" -"两个 pydantic 模型,各管一侧。新字段自动成为 CLI/YAML 选项; ``server_config_kw_doc`` / " -"``client_config_kw_doc`` 把继承字段的文档追加进来,API 参考得以保持完整:" +msgstr "两个 pydantic 模型,各管一侧。新字段自动成为 CLI/YAML 选项; ``server_config_kw_doc`` / ``client_config_kw_doc`` 把继承字段的文档追加进来,API 参考得以保持完整:" #: ../../source/howto-custom-algorithm.rst:43 -msgid "" -"The ``model_validator`` here fixes the local optimizer from the ``vr`` " -"flag — config-level invariants belong in validators, so invalid " -"combinations fail at construction, not mid-run." -msgstr "" -"这里的 ``model_validator`` 由 ``vr`` 标志固定本地优化器——配置级不变式就该放在 " -"validator 里,让非法组合在构造时失败,而不是跑到一半。" +msgid "The ``model_validator`` here fixes the local optimizer from the ``vr`` flag — config-level invariants belong in validators, so invalid combinations fail at construction, not mid-run." +msgstr "这里的 ``model_validator`` 由 ``vr`` 标志固定本地优化器——配置级不变式就该放在 validator 里,让非法组合在构造时失败,而不是跑到一半。" #: ../../source/howto-custom-algorithm.rst:48 msgid "The server hooks" msgstr "服务器钩子" #: ../../source/howto-custom-algorithm.rst:50 -msgid "" -"The server customizes the **downlink** (``communicate``) and the " -"**aggregation** (``update``). Everything else — sampling, the round loop," -" metrics — is inherited:" -msgstr "" -"服务器定制 **下行** ( ``communicate`` )与 **聚合** ( ``update`` )。其余一切——" -"采样、轮循环、指标——全部继承:" +msgid "The server customizes the **downlink** (``communicate``) and the **aggregation** (``update``). Everything else — sampling, the round loop, metrics — is inherited:" +msgstr "服务器定制 **下行** ( ``communicate`` )与 **聚合** ( ``update`` )。其余一切——采样、轮循环、指标——全部继承:" #: ../../source/howto-custom-algorithm.rst:59 msgid "Points worth noting:" @@ -116,58 +84,36 @@ msgstr "值得注意的点:" #: ../../source/howto-custom-algorithm.rst:61 msgid "" -"``communicate(target)`` builds a typed " -":class:`~fl_sim.messages.ServerToClient` message; ``extras`` carries " -"algorithm-specific payloads (here, server-side gradients for variance " -"reduction) alongside the model." -msgstr "" -"``communicate(target)`` 构建带类型的 :class:`~fl_sim.messages.ServerToClient` 消息;" -"``extras`` 携带算法特有载荷(此处是方差削减用的服务器侧梯度),与模型并列。" +"``communicate(target)`` builds a typed :class:`~fl_sim.messages.ServerToClient` message; ``extras`` carries algorithm-specific payloads (here, server-side gradients for variance reduction) alongside" +" the model." +msgstr "``communicate(target)`` 构建带类型的 :class:`~fl_sim.messages.ServerToClient` 消息;``extras`` 携带算法特有载荷(此处是方差削减用的服务器侧梯度),与模型并列。" #: ../../source/howto-custom-algorithm.rst:64 msgid "" -"``update()`` runs on the main thread after the sampled clients' replies " -"arrive in ``self._received_messages`` — in sampled-client order, which is" -" what keeps aggregation bitwise identical across " +"``update()`` runs on the main thread after the sampled clients' replies arrive in ``self._received_messages`` — in sampled-client order, which is what keeps aggregation bitwise identical across " ":doc:`parallel_execution` backends." -msgstr "" -"``update()`` 在主线程上运行,此时被采样客户端的回复已进入 ``self._received_messages``" -"——按被采样客户端顺序排列,这正是聚合在所有 :doc:`parallel_execution` 后端上逐位一致的" -"原因。" +msgstr "``update()`` 在主线程上运行,此时被采样客户端的回复已进入 ``self._received_messages``——按被采样客户端顺序排列,这正是聚合在所有 :doc:`parallel_execution` 后端上逐位一致的原因。" #: ../../source/howto-custom-algorithm.rst:68 -msgid "" -"``client_cls`` and ``config_cls`` wire the custom classes together; " -"``@register_algorithm()`` (shown above each class) makes the algorithm " -"usable by name." -msgstr "" -"``client_cls`` 与 ``config_cls`` 把自定义类互相接起来; ``@register_algorithm()`` " -"(见各类上方)让算法可按名字使用。" +msgid "``client_cls`` and ``config_cls`` wire the custom classes together; ``@register_algorithm()`` (shown above each class) makes the algorithm usable by name." +msgstr "``client_cls`` 与 ``config_cls`` 把自定义类互相接起来; ``@register_algorithm()`` (见各类上方)让算法可按名字使用。" #: ../../source/howto-custom-algorithm.rst:73 msgid "The client hooks" msgstr "客户端钩子" #: ../../source/howto-custom-algorithm.rst:75 -msgid "" -"The client customizes the **local training** (``update``) and the " -"**uplink** (``communicate``):" +msgid "The client customizes the **local training** (``update``) and the **uplink** (``communicate``):" msgstr "客户端定制 **本地训练** ( ``update`` )与 **上行** ( ``communicate`` ):" #: ../../source/howto-custom-algorithm.rst:83 msgid "" -"The most important contract on this page is in ``communicate``: it " -"**builds and returns** a :class:`~fl_sim.messages.ClientToServer` and " -"touches nothing but the client's own state. The framework sends the " -"message and maintains the communication counters. That is what makes the " -"hook safe to run inside a worker thread when :doc:`parallel execution " -"` is on — a hook that reached into server state would" -" only work sequentially." +"The most important contract on this page is in ``communicate``: it **builds and returns** a :class:`~fl_sim.messages.ClientToServer` and touches nothing but the client's own state. The framework " +"sends the message and maintains the communication counters. That is what makes the hook safe to run inside a worker thread when :doc:`parallel execution ` is on — a hook that " +"reached into server state would only work sequentially." msgstr "" -"本页最重要的契约在 ``communicate`` 里:它 **构建并返回** 一个 " -":class:`~fl_sim.messages.ClientToServer`,除客户端自身状态外不碰任何东西。消息的发送" -"与通信计数由框架维护。正因如此,开启 :doc:`并行执行 ` 时该钩子才能" -"安全地跑在工作线程里——一个伸手去碰服务器状态的钩子只能在串行模式下工作。" +"本页最重要的契约在 ``communicate`` 里:它 **构建并返回** 一个 :class:`~fl_sim.messages.ClientToServer`,除客户端自身状态外不碰任何东西。消息的发送与通信计数由框架维护。正因如此,开启 :doc:`并行执行 ` " +"时该钩子才能安全地跑在工作线程里——一个伸手去碰服务器状态的钩子只能在串行模式下工作。" #: ../../source/howto-custom-algorithm.rst:92 msgid "Named exchange, by the way" @@ -175,63 +121,40 @@ msgstr "顺带说一句:按名交换" #: ../../source/howto-custom-algorithm.rst:94 msgid "" -"This example exchanges parameters **by name** (a ``model_state`` mapping " -"from parameter name to tensor, i.e. :data:`~fl_sim.messages.ModelState`) " -"instead of the legacy flat positional list. Names survive reordering and " -"partial modules, and the loading side (``accept_parameters``, ``update``)" -" looks parameters up by name. The flat-list style still works everywhere;" -" prefer names for new algorithms." -msgstr "" -"本示例按 **参数名** 交换参数( ``model_state`` 映射:参数名 → 张量,即 " -":data:`~fl_sim.messages.ModelState` ),而不是遗留的扁平位置列表。名字能扛住重排序与" -"部分模块,加载侧( ``accept_parameters`` 、 ``update`` )按名查找。扁平列表写法依然处处" -"可用;但新算法请优先用名字。" +"This example exchanges parameters **by name** (a ``model_state`` mapping from parameter name to tensor, i.e. :data:`~fl_sim.messages.ModelState`) instead of the legacy flat positional list. Names " +"survive reordering and partial modules, and the loading side (``accept_parameters``, ``update``) looks parameters up by name. The flat-list style still works everywhere; prefer names for new " +"algorithms." +msgstr "本示例按 **参数名** 交换参数( ``model_state`` 映射:参数名 → 张量,即 :data:`~fl_sim.messages.ModelState` ),而不是遗留的扁平位置列表。名字能扛住重排序与部分模块,加载侧( ``accept_parameters`` 、 ``update`` )按名查找。扁平列表写法依然处处可用;但新算法请优先用名字。" #: ../../source/howto-custom-algorithm.rst:102 msgid "Register and run" msgstr "注册与运行" #: ../../source/howto-custom-algorithm.rst:104 -msgid "" -"``@register_algorithm()`` (applied to all four classes above) puts the " -"algorithm into the registry; from then on the CLI and YAML matrices " -"accept it like any built-in:" -msgstr "" -"``@register_algorithm()`` (上面四个类都应用了)把算法送进注册表;此后 CLI 与 YAML " -"矩阵像对待内置算法一样接受它:" +msgid "``@register_algorithm()`` (applied to all four classes above) puts the algorithm into the registry; from then on the CLI and YAML matrices accept it like any built-in:" +msgstr "``@register_algorithm()`` (上面四个类都应用了)把算法送进注册表;此后 CLI 与 YAML 矩阵像对待内置算法一样接受它:" #: ../../source/howto-custom-algorithm.rst:118 -msgid "" -"The same four classes work programmatically, exactly like " -":doc:`quickstart`:" +msgid "The same four classes work programmatically, exactly like :doc:`quickstart`:" msgstr "同样这四个类也可以编程方式使用,与 :doc:`quickstart` 完全一致:" #: ../../source/howto-custom-algorithm.rst:129 -msgid "How your algorithm is kept honest" -msgstr "你的算法如何被守护" +msgid "Safeguards for registered algorithms" +msgstr "注册算法的行为保障" #: ../../source/howto-custom-algorithm.rst:131 -msgid "Two layers protect a registered algorithm:" +msgid "Two layers guard a registered algorithm:" msgstr "两层机制守护一个注册算法:" #: ../../source/howto-custom-algorithm.rst:133 msgid "" -"the **golden regression** suite (``test/test_parallel_equivalence/``) " -"pins each algorithm's observable trajectory (messages, decisions, " -"counters, metrics positions) so refactors cannot silently change " -"behavior;" -msgstr "" -" **golden 回归** 套件( ``test/test_parallel_equivalence/`` )钉住每个算法的可观测轨迹" -"(消息、决策、计数器、指标位置),重构无法悄悄改变行为;" +"the **golden regression** suite (``test/test_parallel_equivalence/``) pins each algorithm's observable trajectory (messages, decisions, counters, metrics positions) so refactors cannot silently " +"change behavior;" +msgstr " **golden 回归** 套件( ``test/test_parallel_equivalence/`` )钉住每个算法的可观测轨迹(消息、决策、计数器、指标位置),重构无法悄悄改变行为;" #: ../../source/howto-custom-algorithm.rst:137 -msgid "" -"the **hook contract test** (``test/test_hook_contract.py``) asserts that " -"client hooks touch no server state — the parallel-safety property " -"``communicate`` above was written for." -msgstr "" -" **钩子契约测试** ( ``test/test_hook_contract.py`` )断言客户端钩子不触碰服务器状态" -"——上面 ``communicate`` 的并行安全性质正是为此而写。" +msgid "the **hook contract test** (``test/test_hook_contract.py``) asserts that client hooks touch no server state — the parallel-safety property ``communicate`` above was written for." +msgstr " **钩子契约测试** ( ``test/test_hook_contract.py`` )断言客户端钩子不触碰服务器状态——上面 ``communicate`` 的并行安全性质正是为此而写。" #: ../../source/howto-custom-algorithm.rst:141 msgid "When you add an algorithm to the repository itself, add it to both." @@ -243,15 +166,9 @@ msgstr "遗留钩子写法" #: ../../source/howto-custom-algorithm.rst:146 msgid "" -"Older code overrides ``communicate(self, target)`` on the *client* and " -"mutates server state directly. It still works under the sequential " -"backend (with a ``DeprecationWarning``), but it is incompatible with " -"parallel backends and will be removed; new algorithms should build and " -"return the typed message as shown above." -msgstr "" -"更老的代码在 *客户端* 上覆写 ``communicate(self, target)`` 并直接改动服务器状态。它在" -"串行后端下仍然可用(会带 ``DeprecationWarning`` ),但与并行后端不兼容,终将移除;" -"新算法请按上文所示构建并返回带类型的消息。" +"Older code overrides ``communicate(self, target)`` on the *client* and mutates server state directly. It still works under the sequential backend (with a ``DeprecationWarning``), but it is " +"incompatible with parallel backends and will be removed; new algorithms should build and return the typed message as shown above." +msgstr "更老的代码在 *客户端* 上覆写 ``communicate(self, target)`` 并直接改动服务器状态。它在串行后端下仍然可用(会带 ``DeprecationWarning`` ),但与并行后端不兼容,终将移除;新算法请按上文所示构建并返回带类型的消息。" #: ../../source/howto-custom-algorithm.rst:153 msgid "Next steps" @@ -272,3 +189,9 @@ msgstr ":doc:`parallel_execution`——完整的钩子契约与各后端。" #: ../../source/howto-custom-algorithm.rst:158 msgid ":doc:`api/nodes` — the complete Server/Client API." msgstr ":doc:`api/nodes`——完整的 Server/Client API。" + +#~ msgid "When do you need this?" +#~ msgstr "什么时候需要它?" + +#~ msgid "How your algorithm is kept honest" +#~ msgstr "你的算法如何被守护" diff --git a/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-dataset.po b/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-dataset.po index 37be5a24..5bd71aeb 100644 --- a/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-dataset.po +++ b/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-dataset.po @@ -6,17 +6,17 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: fl-sim \n" +"Project-Id-Version: fl-sim\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-10-28 08:06+0800\n" +"POT-Creation-Date: 2026-10-29 00:10+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" -"Language: zh_CN\n" "Language-Team: zh_CN \n" -"Plural-Forms: nplurals=1; plural=0;\n" +"Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" "Generated-By: Babel 2.16.0\n" #: ../../source/howto-custom-dataset.rst:4 @@ -25,47 +25,26 @@ msgstr "操作指南:添加自定义数据集" #: ../../source/howto-custom-dataset.rst:6 msgid "" -"A federated dataset in fl-sim is two things bundled together: the **raw " -"data** and a **partitioning scheme** that spreads it across simulated " -"clients. This guide walks through the complete example in ``test-" -"files/custom_dataset.py`` (a re-implementation of the FedProx paper's " -"FEMNIST variant, exercised in CI via ``test-files/custom_conf.yml``) and " -"then distills the minimal template for \"I have NumPy arrays and want " -"them federated\"." -msgstr "" -"fl-sim 中的联邦数据集是两样东西的打包: **原始数据** 与把它分散到各模拟客户端的" -" **划分方案** 。本指南先走完 ``test-files/custom_dataset.py`` 的完整示例" -"(FedProx 论文所用 FEMNIST 变体的复刻,经 ``test-files/custom_conf.yml`` 在 CI 中" -"执行),再提炼出\"我有 NumPy 数组,想联邦起来\"的最小模板。" +"A federated dataset in fl-sim combines two components: the **raw data** and a **partitioning scheme** that spreads it across the simulated clients. This guide covers the complete example in ``test-" +"files/custom_dataset.py`` (a re-implementation of the FedProx paper's FEMNIST variant, exercised in CI via ``test-files/custom_conf.yml``) and then derives a minimal template starting from NumPy " +"arrays." +msgstr "fl-sim 中的联邦数据集由两部分组成: **原始数据** 与把它分散到各模拟客户端的 **划分方案** 。本指南先走完 ``test-files/custom_dataset.py`` 的完整示例(FedProx 论文所用 FEMNIST 变体的复刻,经 ``test-files/custom_conf.yml`` 在 CI 中执行),再从 NumPy 数组出发给出最小模板。" #: ../../source/howto-custom-dataset.rst:15 msgid "Choose the base class" msgstr "选择基类" #: ../../source/howto-custom-dataset.rst:17 -msgid "" -":class:`~fl_sim.data_processing.fed_dataset.FedVisionDataset` — image " -"data with the usual ``TensorDataset`` plumbing; what the example uses;" -msgstr "" -":class:`~fl_sim.data_processing.fed_dataset.FedVisionDataset`——图像数据," -"带常规的 ``TensorDataset`` 管线;示例用的就是它;" +msgid ":class:`~fl_sim.data_processing.fed_dataset.FedVisionDataset` — image data with the usual ``TensorDataset`` plumbing; what the example uses;" +msgstr ":class:`~fl_sim.data_processing.fed_dataset.FedVisionDataset`——图像数据,带常规的 ``TensorDataset`` 管线;示例用的就是它;" #: ../../source/howto-custom-dataset.rst:19 -msgid "" -":class:`~fl_sim.data_processing.fed_dataset.FedDataset` — the fully " -"abstract base, for anything non-vision." -msgstr "" -":class:`~fl_sim.data_processing.fed_dataset.FedDataset`——完全抽象的基类," -"适合一切非视觉数据。" +msgid ":class:`~fl_sim.data_processing.fed_dataset.FedDataset` — the fully abstract base, for anything non-vision." +msgstr ":class:`~fl_sim.data_processing.fed_dataset.FedDataset`——完全抽象的基类,适合一切非视觉数据。" #: ../../source/howto-custom-dataset.rst:22 -msgid "" -"The required interface is small: load data in ``_preload``, hand out " -"dataloaders per client in ``get_dataloader``, and score predictions in " -"``evaluate``." -msgstr "" -"必需接口很小:在 ``_preload`` 里加载数据,在 ``get_dataloader`` 里按客户端分发" -"dataloader,在 ``evaluate`` 里为预测打分。" +msgid "The required interface is small: load data in ``_preload``, hand out dataloaders per client in ``get_dataloader``, and score predictions in ``evaluate``." +msgstr "必需接口很小:在 ``_preload`` 里加载数据,在 ``get_dataloader`` 里按客户端分发dataloader,在 ``evaluate`` 里为预测打分。" #: ../../source/howto-custom-dataset.rst:27 msgid "The full example" @@ -73,120 +52,66 @@ msgstr "完整示例" #: ../../source/howto-custom-dataset.rst:34 msgid "" -"``_preload`` runs once at construction: resolve the data directory, " -"declare the defaults the framework queries (client counts, batch size, " -"the file layout), set the ``criterion``, download if needed, and read the" -" raw files into memory. fl-sim datasets are **immutable after " -"construction** — partitioning happens here, once." -msgstr "" -"``_preload`` 在构造时只跑一次:解析数据目录、声明框架会查询的默认值(客户端数、" -"批大小、文件布局)、设置 ``criterion``、按需下载,并把原始文件读入内存。fl-sim " -"数据集 **构造后不可变** ——划分就在这里做,且只做一次。" +"``_preload`` runs once at construction: resolve the data directory, declare the defaults the framework queries (client counts, batch size, the file layout), set the ``criterion``, download if " +"needed, and read the raw files into memory. fl-sim datasets are **immutable after construction** — partitioning happens here, once." +msgstr "``_preload`` 在构造时只跑一次:解析数据目录、声明框架会查询的默认值(客户端数、批大小、文件布局)、设置 ``criterion``、按需下载,并把原始文件读入内存。fl-sim 数据集 **构造后不可变** ——划分就在这里做,且只做一次。" #: ../../source/howto-custom-dataset.rst:45 msgid "" -"The contract of ``get_dataloader``: given a client index (or ``None`` for" -" \"everything\"), return a **pair** of dataloaders ``(train, test)`` with" -" shuffling enabled — the framework re-requests them every round, and " -"under ``rng_mode=\"per_client\"`` (:doc:`tutorial-parallel-reproducible`)" -" their shuffling is what gets re-seeded per client." -msgstr "" -"``get_dataloader`` 的契约:给定客户端索引(或 ``None`` 表示\"全部\"),返回一对开启" -"洗牌的 dataloader ``(train, test)`` —— 框架每轮都会重新请求它们;在 " -"``rng_mode=\"per_client\"`` ( :doc:`tutorial-parallel-reproducible` )下,被按客户端" -"重新播种的正是这里的洗牌。" +"The contract of ``get_dataloader``: given a client index (or ``None`` for \"everything\"), return a **pair** of dataloaders ``(train, test)`` with shuffling enabled — the framework re-requests them " +"every round, and under ``rng_mode=\"per_client\"`` (:doc:`tutorial-parallel-reproducible`) their shuffling is what gets re-seeded per client." +msgstr "``get_dataloader`` 的契约:给定客户端索引(或 ``None`` 表示\"全部\"),返回一对开启洗牌的 dataloader ``(train, test)`` —— 框架每轮都会重新请求它们;在 ``rng_mode=\"per_client\"`` ( :doc:`tutorial-parallel-reproducible` )下,被按客户端重新播种的正是这里的洗牌。" #: ../../source/howto-custom-dataset.rst:56 -msgid "" -"``evaluate`` turns a batch of predictions and labels into the metric " -"dictionary that lands in the logs (``acc``, ``loss``, ``num_samples``, " -"... — the same keys :doc:`quickstart` read back)." -msgstr "" -"``evaluate`` 把一批预测与标签变成写进日志的指标字典( ``acc`` 、 ``loss`` 、 " -"``num_samples`` 等——正是 :doc:`quickstart` 读回的那些键)。" +msgid "``evaluate`` turns a batch of predictions and labels into the metric dictionary that lands in the logs (``acc``, ``loss``, ``num_samples``, ... — the same keys :doc:`quickstart` read back)." +msgstr "``evaluate`` 把一批预测与标签变成写进日志的指标字典( ``acc`` 、 ``loss`` 、 ``num_samples`` 等——正是 :doc:`quickstart` 读回的那些键)。" #: ../../source/howto-custom-dataset.rst:65 msgid "" -"The remaining properties are conventions rather than requirements: " -"``mirrors`` / ``url`` feed the download mechanism (fallback URLs tried in" -" order — see the " -":meth:`~fl_sim.data_processing.fed_dataset.FedDataset.mirrors` API for " -"the protocol), ``candidate_models`` declares the models that fit this " -"data (the CLI's ``model: name:`` picks from here), ``label_map`` names " -"the classes, ``doi`` carries citations." +"The remaining properties are conventions rather than requirements: ``mirrors`` / ``url`` feed the download mechanism (fallback URLs tried in order — see the " +":meth:`~fl_sim.data_processing.fed_dataset.FedDataset.mirrors` API for the protocol), ``candidate_models`` declares the models that fit this data (the CLI's ``model: name:`` picks from here), " +"``label_map`` names the classes, ``doi`` carries citations." msgstr "" -"其余 property 是惯例而非硬性要求: ``mirrors`` / ``url`` 供下载机制使用(备选 URL " -"按序尝试——协议见 :meth:`~fl_sim.data_processing.fed_dataset.FedDataset.mirrors` " -"API); ``candidate_models`` 声明适合本数据的模型(CLI 的 ``model: name:`` 从这里" -"选取); ``label_map`` 给类别命名; ``doi`` 携带引用。" +"其余 property 是惯例而非硬性要求: ``mirrors`` / ``url`` 供下载机制使用(备选 URL 按序尝试——协议见 :meth:`~fl_sim.data_processing.fed_dataset.FedDataset.mirrors` API); ``candidate_models`` 声明适合本数据的模型(CLI 的 ``model: name:`` " +"从这里选取); ``label_map`` 给类别命名; ``doi`` 携带引用。" #: ../../source/howto-custom-dataset.rst:73 msgid "The minimal template" msgstr "最小模板" #: ../../source/howto-custom-dataset.rst:75 -msgid "" -"From NumPy arrays to a usable federated dataset. This template is " -"executed verbatim in CI-style verification — three conventions it encodes" -" are easy to miss and expensive to debug:" -msgstr "" -"从 NumPy 数组到可用的联邦数据集。这个模板经过 CI 式验证原样执行——它编码的三个约定" -"容易漏掉、调起来又贵:" +msgid "From NumPy arrays to a usable federated dataset. The template below has been verified by end-to-end execution; three conventions it encodes are easy to miss and costly to debug:" +msgstr "从 NumPy 数组到可用的联邦数据集。下述模板已通过端到端执行验证;它编码的三个约定容易漏掉、调起来又贵:" #: ../../source/howto-custom-dataset.rst:79 msgid "" -"**define your own ``__init__``**: the abstract " -":class:`~fl_sim.data_processing.fed_dataset.FedDataset` base has no " -"constructor of its own (the vision subclass supplies one); accept " -"``(datadir, transform, seed, **extra_config)`` and re-seed via " -"``set_seed`` so every constructor re-seeds the global RNG exactly like " -"the built-ins do;" +"**define your own ``__init__``**: the abstract :class:`~fl_sim.data_processing.fed_dataset.FedDataset` base has no constructor of its own (the vision subclass supplies one); accept ``(datadir, " +"transform, seed, **extra_config)`` and re-seed via ``set_seed`` so every constructor re-seeds the global RNG exactly like the built-ins do;" msgstr "" -" **自己定义 ``__init__``** :抽象基类 :class:`~fl_sim.data_processing.fed_dataset.FedDataset` " -"本身没有构造器(视觉子类提供了一个);接受 ``(datadir, transform, seed, **extra_config)`` " -"并经 ``set_seed`` 重新播种,让每个构造器都和内置数据集一样重置全局 RNG;" +" **自己定义 ``__init__``** :抽象基类 :class:`~fl_sim.data_processing.fed_dataset.FedDataset` 本身没有构造器(视觉子类提供了一个);接受 ``(datadir, transform, seed, **extra_config)`` 并经 ``set_seed`` 重新播种,让每个构造器都和内置数据集一样重置全局 " +"RNG;" #: ../../source/howto-custom-dataset.rst:85 -msgid "" -"**the framework requests global loaders too**: ``get_dataloader`` must " -"handle ``client_idx=None`` (\"all clients\"), not just per-client " -"indices;" -msgstr "" -" **框架也会请求全局 loader** : ``get_dataloader`` 必须处理 ``client_idx=None`` " -"(\"全部客户端\"),不能只处理逐客户端的索引;" +msgid "**the framework requests global loaders too**: ``get_dataloader`` must handle ``client_idx=None`` (\"all clients\"), not just per-client indices;" +msgstr " **框架也会请求全局 loader** : ``get_dataloader`` 必须处理 ``client_idx=None`` (\"全部客户端\"),不能只处理逐客户端的索引;" #: ../../source/howto-custom-dataset.rst:88 -msgid "" -"**``candidate_models`` should return fl-sim models** (they carry the " -"``.device`` attribute the device-allocation step relies on)." -msgstr "" -" **``candidate_models`` 应返回 fl-sim 模型** (它们带有设备分配步骤依赖的 " -"``.device`` 属性)。" +msgid "**``candidate_models`` should return fl-sim models** (they carry the ``.device`` attribute the device-allocation step relies on)." +msgstr " **``candidate_models`` 应返回 fl-sim 模型** (它们带有设备分配步骤依赖的 ``.device`` 属性)。" #: ../../source/howto-custom-dataset.rst:91 -msgid "" -"Two ``load_partition_data*`` methods look intimidating but are a legacy " -"tff-style bulk-loading API the training loop never calls — a stub " -"satisfies the abstract interface:" -msgstr "" -"两个 ``load_partition_data*`` 方法看着吓人,其实是训练循环从不调用的遗留 tff 风格" -"批量加载 API——一个桩就满足抽象接口:" +msgid "Two ``load_partition_data*`` methods appear required but are a legacy tff-style bulk-loading API that the training loop never calls — stub implementations satisfy the abstract interface:" +msgstr "两个 ``load_partition_data*`` 方法看似必需,实为训练循环从不调用的遗留 tff 风格批量加载 API——桩实现即可满足抽象接口:" #: ../../source/howto-custom-dataset.rst:172 -msgid "Use it" +msgid "Usage" msgstr "使用" #: ../../source/howto-custom-dataset.rst:174 msgid "" -"With the ``@register_fed_dataset()`` decorator applied, the dataset is " -"constructible by name once its module has been imported (or via the " -"module *path* as the dataset ``name`` — see " -":class:`~fl_sim.data_processing.fed_data_args.FedDataArgs`), and every " -"built-in works on it unchanged:" -msgstr "" -"应用 ``@register_fed_dataset()`` 装饰器后,只要模块被导入过,数据集即可按名字构造" -"(或以模块 *路径* 作为数据集 ``name`` ——见 " -":class:`~fl_sim.data_processing.fed_data_args.FedDataArgs`),且所有内置组件原样可用:" +"With the ``@register_fed_dataset()`` decorator applied, the dataset is constructible by name once its module has been imported (or via the module *path* as the dataset ``name`` — see " +":class:`~fl_sim.data_processing.fed_data_args.FedDataArgs`), and every built-in works on it unchanged:" +msgstr "应用 ``@register_fed_dataset()`` 装饰器后,只要模块被导入过,数据集即可按名字构造(或以模块 *路径* 作为数据集 ``name`` ——见 :class:`~fl_sim.data_processing.fed_data_args.FedDataArgs`),且所有内置组件原样可用:" #: ../../source/howto-custom-dataset.rst:197 msgid "Next steps" @@ -199,3 +124,6 @@ msgstr ":ref:`howto-custom-algorithm`——契约的算法一侧。" #: ../../source/howto-custom-dataset.rst:200 msgid ":doc:`api/datasets` — the full FedDataset API reference." msgstr ":doc:`api/datasets`——完整的 FedDataset API 参考。" + +#~ msgid "Use it" +#~ msgstr "使用" diff --git a/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-optimizer.po b/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-optimizer.po index 11dd62a0..8b92802c 100644 --- a/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-optimizer.po +++ b/docs/source/locale/zh_CN/LC_MESSAGES/howto-custom-optimizer.po @@ -6,17 +6,17 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: fl-sim \n" +"Project-Id-Version: fl-sim\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-10-28 08:06+0800\n" +"POT-Creation-Date: 2026-10-29 00:10+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" -"Language: zh_CN\n" "Language-Team: zh_CN \n" -"Plural-Forms: nplurals=1; plural=0;\n" +"Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" "Generated-By: Babel 2.16.0\n" #: ../../source/howto-custom-optimizer.rst:4 @@ -25,15 +25,9 @@ msgstr "操作指南:添加自定义优化器" #: ../../source/howto-custom-optimizer.rst:6 msgid "" -"The local training step is pluggable: fl-sim keeps its own optimizer " -"registry, and any optimizer registered there can be selected by name in " -"the client config. This is the lightest of the three how-tos — the " -"complete example is 48 lines (``test-files/custom_optimizer.py``, " -"exercised in CI via ``test-files/custom_conf.yml``)." -msgstr "" -"本地训练步是可插拔的:fl-sim 维护自己的优化器注册表,任何注册过的优化器都能在客户端" -"配置里按名字选用。这是三篇操作指南里最轻的一篇——完整示例只有 48 行" -"(``test-files/custom_optimizer.py``,经 ``test-files/custom_conf.yml`` 在 CI 中执行)。" +"The local training step is pluggable: fl-sim keeps its own optimizer registry, and any optimizer registered there can be selected by name in the client config. This is the shortest of the three " +"guides; the complete example is 48 lines (``test-files/custom_optimizer.py``, exercised in CI via ``test-files/custom_conf.yml``)." +msgstr "本地训练步是可插拔的:fl-sim 维护自己的优化器注册表,任何注册过的优化器都能在客户端配置里按名字选用。这是三篇指南里最短的一篇——完整示例只有 48 行( ``test-files/custom_optimizer.py`` ,经 ``test-files/custom_conf.yml`` 在 CI 中执行)。" #: ../../source/howto-custom-optimizer.rst:13 msgid "Where optimizers plug in" @@ -41,41 +35,26 @@ msgstr "优化器接在哪里" #: ../../source/howto-custom-optimizer.rst:15 msgid "" -"fl-sim's local optimizers live in :mod:`fl_sim.optimizers` and subclass " -":class:`~fl_sim.optimizers.base.ProxSGD` — the proximal SGD base that " -"supports the extra local-training knobs federated algorithms need " -"(proximal terms, variance-reduction buffers passed by " -"``optimizer.step(...)``, per the custom algorithm in :ref:`howto-custom-" -"algorithm`)." +"fl-sim's local optimizers live in :mod:`fl_sim.optimizers` and subclass :class:`~fl_sim.optimizers.base.ProxSGD` — the proximal SGD base that supports the extra local-training knobs federated " +"algorithms need (proximal terms, variance-reduction buffers passed by ``optimizer.step(...)``, per the custom algorithm in :ref:`howto-custom-algorithm`)." msgstr "" -"fl-sim 的本地优化器都在 :mod:`fl_sim.optimizers` 里,继承 " -":class:`~fl_sim.optimizers.base.ProxSGD`——这个近端 SGD 基类支持联邦算法需要的额外本地" -"训练旋钮(近端项、由 ``optimizer.step(...)`` 传入的方差削减缓冲区,用法见 " -":ref:`howto-custom-algorithm` 的自定义算法)。" +"fl-sim 的本地优化器都在 :mod:`fl_sim.optimizers` 里,继承 :class:`~fl_sim.optimizers.base.ProxSGD`——这个近端 SGD 基类支持联邦算法需要的额外本地训练旋钮(近端项、由 ``optimizer.step(...)`` 传入的方差削减缓冲区,用法见 :ref:`howto-custom-algorithm` " +"的自定义算法)。" #: ../../source/howto-custom-optimizer.rst:22 -msgid "" -"Registration makes the optimizer discoverable by name — the same pattern " -"as algorithms and datasets:" +msgid "Registration makes the optimizer discoverable by name — the same pattern as algorithms and datasets:" msgstr "注册让优化器可按名字发现——与算法、数据集是同一模式:" #: ../../source/howto-custom-optimizer.rst:30 -msgid "" -"This example wraps ``FedProxOptimizer``'s behavior under a new name; a " -"genuinely new optimizer overrides the ``ProxSGD`` step methods instead of" -" the constructor." -msgstr "" -"这个例子把 ``FedProxOptimizer`` 的行为换了个名字;真正全新的优化器应覆写 ``ProxSGD`` " -"的 step 方法而不是构造器。" +msgid "This example wraps ``FedProxOptimizer``'s behavior under a new name; a genuinely new optimizer overrides the ``ProxSGD`` step methods instead of the constructor." +msgstr "这个例子把 ``FedProxOptimizer`` 的行为换了个名字;真正全新的优化器应覆写 ``ProxSGD`` 的 step 方法而不是构造器。" #: ../../source/howto-custom-optimizer.rst:35 -msgid "Use it" +msgid "Usage" msgstr "使用" #: ../../source/howto-custom-optimizer.rst:37 -msgid "" -"Registered optimizers are referenced by name in the client config — " -"programmatically:" +msgid "Registered optimizers are referenced by name in the client config — programmatically:" msgstr "注册过的优化器在客户端配置里按名字引用——编程方式:" #: ../../source/howto-custom-optimizer.rst:44 @@ -84,52 +63,41 @@ msgstr "或在 YAML 实验矩阵里:" #: ../../source/howto-custom-optimizer.rst:52 msgid "" -"The name is resolved through :func:`~fl_sim.optimizers.get_optimizer` at " -"client setup (a trailing ``Optimizer`` suffix is optional); an " -"unregistered name fails there with ``Optimizer ... is not registered``." -msgstr "" -"名字在客户端初始化时经 :func:`~fl_sim.optimizers.get_optimizer` 解析(结尾的 " -"``Optimizer`` 后缀可省略);未注册的名字会在此报错 ``Optimizer ... is not registered`` 。" +"The name is resolved through :func:`~fl_sim.optimizers.get_optimizer` at client setup (a trailing ``Optimizer`` suffix is optional); an unregistered name fails there with ``Optimizer ... is not " +"registered``." +msgstr "名字在客户端初始化时经 :func:`~fl_sim.optimizers.get_optimizer` 解析(结尾的 ``Optimizer`` 后缀可省略);未注册的名字会在此报错 ``Optimizer ... is not registered`` 。" #: ../../source/howto-custom-optimizer.rst:57 msgid "Caveats" msgstr "注意事项" #: ../../source/howto-custom-optimizer.rst:59 -msgid "" -"the optimizer is constructed **once per client per run**; anything " -"stateful it keeps lives for the whole federated run;" +msgid "the optimizer is constructed **once per client per run**; anything stateful it keeps lives for the whole federated run;" msgstr "优化器 **每次运行中每个客户端只构造一次** ;它保存的任何状态会贯穿整个联邦运行;" #: ../../source/howto-custom-optimizer.rst:61 msgid "" -"some algorithms fix the client optimizer in their client-config " -"validators (e.g. the FedProx family selects ``FedProx`` / ``FedProx_VR`` " -"from its ``vr`` flag) — for those algorithms the ``optimizer`` config " -"field is ignored by design;" -msgstr "" -"某些算法在客户端配置的 validator 里固定了本地优化器(如 FedProx 家族由 ``vr`` 标志选择 " -"``FedProx`` / ``FedProx_VR`` )——对这些算法, ``optimizer`` 配置字段被设计性地忽略;" +"some algorithms fix the client optimizer in their client-config validators (e.g. the FedProx family selects ``FedProx`` / ``FedProx_VR`` from its ``vr`` flag) — for those algorithms the " +"``optimizer`` config field is ignored by design;" +msgstr "某些算法在客户端配置的 validator 里固定了本地优化器(如 FedProx 家族由 ``vr`` 标志选择 ``FedProx`` / ``FedProx_VR`` )——对这些算法, ``optimizer`` 配置字段被设计性地忽略;" #: ../../source/howto-custom-optimizer.rst:65 msgid "" -"the server-side optimizers (FedAdam, FedYogi, … in the FedOpt family) are" -" a different mechanism — those are selected through the server config's " -"``optimizer`` field and are not part of this registry." -msgstr "" -"服务器侧优化器(FedOpt 家族的 FedAdam、FedYogi 等)是另一套机制——经服务器配置的 " -"``optimizer`` 字段选择,不在本注册表内。" +"the server-side optimizers (FedAdam, FedYogi, … in the FedOpt family) are a different mechanism — those are selected through the server config's ``optimizer`` field and are not part of this " +"registry." +msgstr "服务器侧优化器(FedOpt 家族的 FedAdam、FedYogi 等)是另一套机制——经服务器配置的 ``optimizer`` 字段选择,不在本注册表内。" #: ../../source/howto-custom-optimizer.rst:71 msgid "Next steps" msgstr "下一步" #: ../../source/howto-custom-optimizer.rst:73 -msgid "" -":ref:`howto-custom-algorithm` — when the *algorithm*, not just the " -"optimizer, is what's new." +msgid ":ref:`howto-custom-algorithm` — when the *algorithm*, not just the optimizer, is what's new." msgstr ":ref:`howto-custom-algorithm`——如果新的不只是优化器,而是 *算法本身* 。" #: ../../source/howto-custom-optimizer.rst:75 msgid ":doc:`api/optimizers` — the optimizer API reference." msgstr ":doc:`api/optimizers`——优化器 API 参考。" + +#~ msgid "Use it" +#~ msgstr "使用"