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

feat: Bandits part 4: Evaluation #27

Merged
merged 78 commits into from
Jul 24, 2024
Merged

feat: Bandits part 4: Evaluation #27

merged 78 commits into from
Jul 24, 2024

Conversation

typotter
Copy link
Collaborator

@typotter typotter commented Jul 5, 2024

Motivation and Context

Here is where the magic happens; where user data is matrix multiplied against bandit coefficient models to determine the best action for the user.

Description

  • Bandit Evaluator, a class to compute scores, weigh actions, and bucket the user to ultimately select an action

How has this been tested

  • Unit Tests

@typotter typotter marked this pull request as ready for review July 5, 2024 21:40
@typotter typotter changed the base branch from main to tp/config/opt July 15, 2024 18:04
@typotter typotter requested a review from aarsilv July 15, 2024 20:10
Comment on lines 38 to 40
if (empty($actionsWithContexts)) {
throw new InvalidArgumentException("No actions provided for bandit evaluation");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We only want to do this if the bandit is still at play (e.g., this flag has a bandit).

This is to support the case of users removing bandits from the flag more in a friendly way, such as rolling out a winning variation. That way, if they just stop supplying actions but leave this in, it will still work as they expect.

You can see the node example here: https://github.com/Eppo-exp/js-client-sdk-common/blob/main/src/client/eppo-client.ts#L471

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is actually handled up in the EppoClient. My reasoning for throwing an exception here is that if we're invoking the BanditEvaluator, we've already matched an assignment to a bandit, pulled the Bandit model and should not have done all that work if the action list is empty.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Following the parallel discussion in Slack and the PR for the JS commons SDK, does this path eventually return the default variation? Or is the InvalidArgumentException handled differently? (Maybe it never even reaches this point if the action list is empty?)

Copy link
Contributor

Choose a reason for hiding this comment

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

This will return but not log the default variation. It sounds like our latest plan is for us to wait to check for zero actions until a variation has been assigned?

src/Bandits/BanditEvaluator.php Show resolved Hide resolved
Base automatically changed from tp/config/opt to main July 16, 2024 04:11
@typotter typotter requested a review from aarsilv July 16, 2024 04:18
Copy link
Collaborator

@giorgiomartini0 giorgiomartini0 left a comment

Choose a reason for hiding this comment

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

👏 The math and overall logic look good to me (I don't know PHP). Left one really minor naming suggestion.

Comment on lines 38 to 40
if (empty($actionsWithContexts)) {
throw new InvalidArgumentException("No actions provided for bandit evaluation");
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Following the parallel discussion in Slack and the PR for the JS commons SDK, does this path eventually return the default variation? Or is the InvalidArgumentException handled differently? (Maybe it never even reaches this point if the action list is empty?)

Comment on lines 55 to 57
$selectedActionContext = $actionsWithContexts[$selectedAction];
$actionScore = $actionScores[$selectedAction];
$actionWeight = $actionWeights[$selectedAction];
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: for consistency, should these be selectedActionScore and selectedActionWeight?

Comment on lines +165 to +166
$remainingWeight = max(0.0, 1.0 - array_sum($weights));
$weights[$bestActionKey] = $remainingWeight;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Very clever! Took me a moment to grok; this is adding a new key to weights (bestActionKey was filtered out above), not overwriting it.

Copy link
Contributor

@aarsilv aarsilv left a comment

Choose a reason for hiding this comment

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

Thanks for iterating! Approving for now, although we may need to revisit this SDK (and all the others) once we align on our updated method for handling empty actions

Comment on lines 38 to 40
if (empty($actionsWithContexts)) {
throw new InvalidArgumentException("No actions provided for bandit evaluation");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This will return but not log the default variation. It sounds like our latest plan is for us to wait to check for zero actions until a variation has been assigned?

Comment on lines 260 to 263
if ($aValue == $bValue) {
return $a->action <=> $b->action;
}
return $aValue <=> $bValue;
Copy link
Contributor

Choose a reason for hiding this comment

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

🙌

Could this be simplified to:

return ($aValue <=> $bValue) ?: ($a->action <=> $b->action);

$this->assertEquals($expectedScore, $actualScore);
}

public function testScoreNumericIgnoringNonNumericAttributes(): void
Copy link
Contributor

Choose a reason for hiding this comment

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

🙌


public function testScoreNumericIgnoringNonNumericAttributes(): void
{
$numericAttributes = ['age' => 30, 'height' => 170, 'shouldBeANumber' => 'but_it_is_not'];
Copy link
Contributor

Choose a reason for hiding this comment

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

excellent naming

@typotter
Copy link
Collaborator Author

Thanks for the review, folks. Integrating this and applying the recent no-actions action in the Client PR.

@typotter typotter merged commit 3bc88a3 into main Jul 24, 2024
1 check passed
@typotter typotter deleted the tp/bandits/eval branch July 24, 2024 16:20
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.

3 participants