Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Straggler handling Follow-up #1097

Draft
wants to merge 14 commits into
base: develop
Choose a base branch
from

Conversation

ishant162
Copy link
Collaborator

@ishant162 ishant162 commented Oct 23, 2024

This pull request is a follow-up to the Straggler Handling PR #996 . Based on the feedback from @MasterSkepticista and @teoparvanov , the review comments have been addressed and incorporated accordingly.

Summary of changes:

  • Relocated straggler_handling to the openfl/component/aggregator directory.
  • Consolidated all policies into openfl/component/aggregator/straggler_handling.py
  • Renamed CutoffTimeBasedStragglerHandling to CutoffPolicy and PercentageBasedStragglerHandling to PercentagePolicy
  • Removed the attribute deletion functionality from CutoffPolicy

PR is currently in draft

@teoparvanov
Copy link
Collaborator

teoparvanov commented Oct 23, 2024

@ishant162 , did you test for regression by raising a PR in OpenFL-Security?

@scngupta-dsp
Copy link
Contributor

@ishant162 , did you test for regression by raising a PR in OpenFL-Security?

This needs to be done @teoparvanov . We will feedback once it is done

Copy link
Collaborator

@MasterSkepticista MasterSkepticista left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good improvement over the previous layout.

Most suggestions, though seem like nitpicks, have measurable changes on developers and users in the look-and-feel of the framework. Please take a look.

@@ -69,7 +69,7 @@ def __init__(
best_state_path,
last_state_path,
assigner,
straggler_handling_policy=None,
straggler_handling_policy=CutoffPolicy,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a typehint.

straggler_handling_policy: BasePolicy = CutoffPolicy,

@@ -92,7 +92,7 @@ def __init__(
weight.
assigner: Assigner object.
straggler_handling_policy (optional): Straggler handling policy.
Defaults to CutoffTimeBasedStragglerHandling.
Defaults to CutoffPolicy.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest removing the mention of default. Most IDEs show this when defaults are provided. One less line to maintain if default changes in the future.

Comment on lines +120 to +124
if straggler_handling_policy == CutoffPolicy:
self.straggler_handling_policy = straggler_handling_policy()
else:
self.straggler_handling_policy = straggler_handling_policy

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest creating a DefaultPolicy. This simplifies:

  1. self.straggler_handling_policy = straggler_handling_policy() regardless of what is given, saves 4 lines.
  2. DefaultPolicy would be the equivalent of providing no straggler handling capabilities. Dummy class.

Also the condition check is wrong. What if PercentagePolicy is provided? It will go to else block and fail in code, it seems.

def start_policy(self, **kwargs) -> None:
"""
Start straggler handling policy for collaborator for a particular round.
NOTE: Refer CutoffPolicy for reference.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is CutoffPolicy mentioned here?

Comment on lines +35 to +42
"""
Reset policy variable for the next round.

Args:
None

Returns:
None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the case for subclassed policies, please eliminate empty lines. It suffices to say Resets policy for the next round and not using 6 lines to say it takes or returns nothing. The -> None and no args make it obvious to the user in an IDE.

Also on the info itself: It is better to mention what exactly is being reset without leaking into the detail. Example Resets countdown timer for collaborator reporting until next round, or along those lines.

Comment on lines +201 to +209
"""Initialize a PercentagePolicy object.

Args:
percent_collaborators_needed (float, optional): The percentage of
collaborators needed. Defaults to 1.0.
minimum_reporting (int, optional): The minimum number of
collaborators that should report. Defaults to 1.
**kwargs: Variable length argument list.
"""
Copy link
Collaborator

@MasterSkepticista MasterSkepticista Oct 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class docstring should go near attributes, not in __init__. Example:

class PercentagePolicy(StragglerHandlingPolicy):
    """Percentage based Straggler Handling function.

    Args:
            percent_collaborators_needed (float, optional): The percentage of
                collaborators required before concluding the round. Defaults to 1.0.
            minimum_reporting (int, optional): The minimum number of
                collaborators that should report, regardless of percentage threshold. Defaults to 1.
            **kwargs: Variable length argument list.
    """

    def __init__(self, percent_collaborators_needed=1.0, minimum_reporting=1, **kwargs):
        if minimum_reporting <= 0:
            raise ValueError("minimum_reporting must be >0")

        self.percent_collaborators_needed = percent_collaborators_needed
        self.minimum_reporting = minimum_reporting
        self.logger = getLogger(__name__)

On the **kwargs: Why would a user provide this? Where does it get used, please mention like keyword arguments forwarded to some.module.class). If they are never forwarded or stored (which seems to be the case looking at certain classes, they should not be accepted from the user either.

Comment on lines +210 to +211
if minimum_reporting <= 0:
raise ValueError("minimum_reporting must be >0")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One liner: assert minimum_reporting > 0, "Minimum reporting must be >0"


self.percent_collaborators_needed = percent_collaborators_needed
self.minimum_reporting = minimum_reporting
self.logger = getLogger(__name__)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we start defining logger = getLogger(__name__) below imports and use it globally within this file?
We should slowly move away from these tiny design choices.

Comment on lines +218 to +220
"""
Not required in PercentagePolicy.
"""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Save lines!

def reset_policy_for_round(self) -> None:
  """Ignored in `PercentagePolicy`."""
  pass

@@ -421,7 +421,7 @@ def get_tensor_pipe(self):

def get_straggler_handling_policy(self):
"""Get straggler handling policy."""
template = "openfl.component.straggler_handling_functions.CutoffTimeBasedStragglerHandling"
template = "openfl.component.aggregator.straggler_handling.CutoffPolicy"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this default? What is the default behavior?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants