adap / flower

Flower: A Friendly Federated AI Framework
https://flower.ai
Apache License 2.0
5.08k stars 875 forks source link

Can the implementation of FedXgbNnAvg only be used in Flower VCE? #2532

Open JackFroster opened 1 year ago

JackFroster commented 1 year ago

What is your question?

Version:flower 1.5

I noticed that parameters_aggregated and trees_aggregated are not serialized and returned in FedXgbNnAvg.aggregate_fit(). Does this mean that the implementation of FedXgbNnAvg can only be used in Flower VCE? If FedXgbNgAvg is to be used in Flower ECE, should parameters_aggregated and trees_aggregated be serialized and returned together?

class FedXgbNnAvg(FedAvg):
    """Configurable FedXgbNnAvg strategy implementation."""

    def __repr__(self) -> str:
        """Compute a string representation of the strategy."""
        rep = f"FedXgbNnAvg(accept_failures={self.accept_failures})"
        return rep

    def evaluate(
        self, server_round: int, parameters: Any
    ) -> Optional[Tuple[float, Dict[str, Scalar]]]:
        """Evaluate model parameters using an evaluation function."""
        if self.evaluate_fn is None:
            # No evaluation function provided
            return None
        eval_res = self.evaluate_fn(server_round, parameters, {})
        if eval_res is None:
            return None
        loss, metrics = eval_res
        return loss, metrics

    def aggregate_fit(
        self,
        server_round: int,
        results: List[Tuple[ClientProxy, FitRes]],
        failures: List[Union[Tuple[ClientProxy, FitRes], BaseException]],
    ) -> Tuple[Optional[Any], Dict[str, Scalar]]:
        """Aggregate fit results using weighted average."""
        if not results:
            return None, {}
        # Do not aggregate if there are failures and failures are not accepted
        if not self.accept_failures and failures:
            return None, {}

        # Convert results
        weights_results = [
            (
                parameters_to_ndarrays(fit_res.parameters[0].parameters),  # type: ignore # noqa: E501 # pylint: disable=line-too-long
                fit_res.num_examples,
            )
            for _, fit_res in results
        ]
        parameters_aggregated = ndarrays_to_parameters(aggregate(weights_results))

        # Aggregate XGBoost trees from all clients
        trees_aggregated = [fit_res.parameters[1] for _, fit_res in results]  # type: ignore # noqa: E501 # pylint: disable=line-too-long

        # Aggregate custom metrics if aggregation fn was provided
        metrics_aggregated = {}
        if self.fit_metrics_aggregation_fn:
            fit_metrics = [(res.num_examples, res.metrics) for _, res in results]
            metrics_aggregated = self.fit_metrics_aggregation_fn(fit_metrics)
        elif server_round == 1:  # Only log this warning once
            log(WARNING, "No fit_metrics_aggregation_fn provided")

        return [parameters_aggregated, trees_aggregated], metrics_aggregated
jafermarq commented 11 months ago

Hi @JackFroster, FedXgbNgAvg is a bit of an exception compared to all other strategies. Precisely because of what you point out: clients don't sent a standard Parameters object. This means a custom server object is also needed so the communication between clients and strategies can work fine. Did you see this xgboost example using FedXgbNgAvg?

Please note that you can run other forms of boosting using a different strategy. Check a very recent example from @yan-gao-GY : https://github.com/adap/flower/tree/main/examples/xgboost-quickstart