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

[Tune] Fix and optimize sample search algorithm quantization logic #28187

Merged
merged 8 commits into from
Sep 16, 2022

Conversation

ilee300a
Copy link
Contributor

Why are these changes needed?

Current quantization logic incorrectly rounds sampled values. In this PR, this error is fixed and quantization for q = 1 is optimized by omitting quantization.

Related issue number

Closes #28045

Checks

  • I've signed off every commit(by using the -s flag, i.e., git commit -s) in this PR.
  • I've run scripts/format.sh to lint the changes in this PR.
  • I've included any doc changes needed for https://docs.ray.io/en/master/.
  • I've made sure the tests are passing. Note that there might be a few flaky tests, see the recent failures at https://flakey-tests.ray.io/
  • Testing Strategy
    • Unit tests
    • Release tests
    • This PR is not tested :(

@xwjiang2010 xwjiang2010 self-assigned this Aug 31, 2022
@Yard1 Yard1 changed the title Bug fix [Tune] Fix and optimize sample search algorithm quantization logic Aug 31, 2022
"lograndint": tune.lograndint(1, 10), # Random integer in log space
"qlograndint": tune.qlograndint(1, 10, 2), # Round to increments of 2
"qlograndint": tune.qlograndint(1, 10, 2), # Round to multiples of 2
Copy link
Contributor

@xwjiang2010 xwjiang2010 Aug 31, 2022

Choose a reason for hiding this comment

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

I was wondering maybe an example is easier to understand than wording?
e.g. the qrandint(1, 10, 3) translates to choosing randomly among [3, 6, 9] as we were discussing yesterday?

Copy link
Contributor

Choose a reason for hiding this comment

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

It should choose between [1, 4, 7, 10] though, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The quantized sampling rounds the sampled values to multiples of q rather than integer increment of q from the lower bound. So qrandint(1,10,3) will randomly sample from [3,6,9] rather than [1,4,7,10]

Copy link
Contributor

Choose a reason for hiding this comment

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

@ilee300a do you want to provide more details as to why the decision was made so? My understanding is there is some impl that is shared with other sampling methods, which may require [3, 6, 9] instead of [1, 4, 7, 10].

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When using other libraries, the expected behavior of quantization is to select those that are divisible by the given q within the bounds. For example, when using BOHB, it expects the provided lower and upper bound to be a multiple of q and provides multiples of q within those bounds as the samples. So if we run qrandint(1,10,3), it actually samples multiples of 3 between 3 and 9(inclusive).

Copy link
Member

@Yard1 Yard1 Sep 1, 2022

Choose a reason for hiding this comment

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

Makes me wonder if we should raise an exception during domain initialisation if the bounds are not divisible instead. Or at least a warning - if we silently change the bounds it may be surprising to some users

@xwjiang2010
Copy link
Contributor

Thanks! Left some comment..

@xwjiang2010
Copy link
Contributor

xwjiang2010 commented Aug 31, 2022

Could you also rebase/merge on the current upstream/master? That should hopefully fix the tests :)

@@ -532,8 +532,15 @@ def sample(
):
if not isinstance(random_state, _BackwardsCompatibleNumpyRng):
random_state = _BackwardsCompatibleNumpyRng(random_state)
values = self.sampler.sample(domain, spec, size, random_state=random_state)

quantized_domain = Domain()
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't we deepcopy the existing domain instead of creating a new one? seems like we are losing subclass type here.

python/ray/tune/search/sample.py Outdated Show resolved Hide resolved
Copy link
Contributor

@krfricke krfricke left a comment

Choose a reason for hiding this comment

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

Thanks!

Can we add some test cases here (in test_sample.py):

self.config = {
        # ...
            "qrandint": tune.qrandint(-21, 12, 3),
            "qrandint_q3": tune.qrandint(1, 10, 3),
            "qrandint_q1": tune.qrandint(1, 5, 1),
            if "qrandint_q3" not in ignore:
                self.assertGreaterEqual(out["qrandint_q3"], 1)
                self.assertLessEqual(out["qrandint_q3"], 10)
                self.assertEqual(out["qrandint_q3"] % 3, 0)
                self.assertTrue(isinstance(out["qrandint_q3"], int))

            if "qrandint_q1" not in ignore:
                self.assertGreaterEqual(out["qrandint_q1"], 1)
                self.assertLessEqual(out["qrandint_q1"], 5)
                self.assertEqual(out["qrandint_q1"] % 1, 0)
                self.assertTrue(isinstance(out["qrandint_q1"], int))
                elif k == "qrandint_q3":
                    for i in range(1, 10, 3):
                        self.assertIn(i, v, msg=f"qrandint_q3 failed for i={i}")

                elif k == "qrandint_q1":
                    for i in range(1, 5, 1):
                        self.assertIn(i, v, msg=f"qrandint_q1 failed for i={i}")

Thanks!

@xwjiang2010 xwjiang2010 added the @author-action-required The PR author is responsible for the next step. Remove tag to send back to the reviewer. label Sep 1, 2022
@Yard1 Yard1 self-assigned this Sep 1, 2022
@krfricke krfricke added @external-author-action-required Alternate tag for PRs where the author doesn't have labeling permission. and removed @author-action-required The PR author is responsible for the next step. Remove tag to send back to the reviewer. labels Sep 7, 2022
Copy link
Contributor

@krfricke krfricke left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks!

Signed-off-by: Kai Fricke <[email protected]>
@richardliaw richardliaw merged commit d59d8c6 into ray-project:master Sep 16, 2022
PaulFenton pushed a commit to PaulFenton/ray that referenced this pull request Sep 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@external-author-action-required Alternate tag for PRs where the author doesn't have labeling permission.
Projects
None yet
5 participants