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

fix: Support placeholders for hyperparameters passed to TrainingStep #159

Merged

Conversation

ca-nguyen
Copy link
Contributor

@ca-nguyen ca-nguyen commented Sep 9, 2021

Issue #, if available: #152

Description of changes:
A breaking change was introduced in V2.2.0 where it was no longer possible to use Placeholders as input for hyperparameters in TrainingStep.

With this solution, TrainingStep hyperparameters will be compatible with Placeholders with the same behaviour as in V2.1.0: if a Placeholder is provided as input to hyperparameters in TrainingStep constructor, the estimator hyperparameters are overwritten

A feature request to be able to merge the Placeholder hyperparameters was made in #152, which will be addressed in another PR.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

f"hyperparameter property: <{key}> with value: <{merged_hyperparameters[key]}> provided in the"
f" estimator will be overwritten with value provided in constructor: <{value}>")
merged_hyperparameters[key] = value
merge_dicts(merged_hyperparameters, training_step_hyperparameters)
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't this do the same thing as before? The only difference seems to be merge_dicts does a deep copy, but that shouldn't be an issue. Seems like merging was already supported before. HyperParameters doesn't have nested fields.

I don't think this actually addresses #152, where the requester wanted to pass a Placeholder to hyperparameters arg, not a dict containing Placeholders as values.

Copy link
Contributor Author

@ca-nguyen ca-nguyen Sep 9, 2021

Choose a reason for hiding this comment

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

Merging was only supported for non Placeholder values before. We would only merge if the constructor hyperparameters was a dict as well, otherwise, we would overwrite with the Estimator hyperparameters would through an error (Edited)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had also considered going back to what was done before the merge fix - if the hyperparameter passed in the constructor was a Placeholder, it would overwrite the estimator hyperparameters. But this would not satisfy the need for merging constructor and estimator hyperparameters.

Any suggestions?

Copy link
Contributor

Choose a reason for hiding this comment

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

Merging was only supported for non Placeholder values before.

How so? The previous code doesn't do any type checks.

Copy link
Contributor

Choose a reason for hiding this comment

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

I had also considered going back to what was done before the merge fix - if the hyperparameter passed in the constructor was a Placeholder, it would overwrite the estimator hyperparameters. But this would not satisfy the need for merging constructor and estimator hyperparameters.

I think we need to dive a little deeper here to understand the use case and what works today.

There are 3 scenarios:

  1. hyperparameters is None - we use the hyperparameters from the estimator (already handled in v2.2.0)
  2. hyperparameters is a dict (which may have Placeholder values) - we merge constructor's hyperparameters with the estimator's hyper parameters (already handled in v2.2.0)
  3. hyperparameters is a Placeholder, not a dict with Placeholder values (raises Error)

In the initial comment, the requester is trying to use the following, which is case 3:

hyperparameters=execution_input["TrainingParameters"],

In 2.2.0 and this version, it doesn't work, because ExecutionInput doesn't have an items() method because it isn't a dict. This raises an Error:

AttributeError: 'ExecutionInput' object has no attribute 'items'

They used the following to convert it to a dict, but that doesn't work either.

hyperparameters=execution_input["TrainingParameters"].get_schema_as_dict(),

The result of execution_input["TrainingParameters"].get_schema_as_dict() is the schema, not the placeholder values. e.g.

{
   'C': str,
   'D': str
}

I think @ohbuchim is actually trying to merge $$.Execution.Input.TrainingParameters with the estimator's hyperparameters. There are two things we can do there.

  1. @shivlaks' proposal to just use the JSONPath for HypeterParameters (totally ignoring the estimator)
'HyperParameters.$': "$$.Execution.Input['TrainingParameters']"
  1. Try to merge the two by deriving values from the PlaceHolder schema. This requires all keys to be modelled in the Placeholder schema.

Suppose the estimator has these hyperparameters:

{
   'A': 'a',
   'B': 'b'
}

And the ExecutionInput schema is:

{
  'TrainingInput': {
     'B': str,
     'C': str
   },
}

If the caller passes hyperparameters={ 'B': execution_input['TrainingInput']['B'], 'C': execution_input['TrainingInput']['C'] to TrainingStep, then we can already merge the two to become:

{
   'A': 'a',
   'B.$': '$$.Execution.Input.B',
   'C.$': '$$.Execution.Input.C' 
}

If the caller passes hyperparameters=execution_input['TrainingInput'], then we need to handle this differently. B and C are not specified, so we would have to get all properties of the Placeholder schema at execution_input['TrainingInput'] and insert those keys into Parameters.

This requires the user to map all the properties they want to use in the schema. In ASL it's impossible to merge objects in cases where TrainingInput might have some arbitrary properties at runtime (e.g. D and E) not modelled in the schema.

Copy link
Contributor Author

@ca-nguyen ca-nguyen Sep 9, 2021

Choose a reason for hiding this comment

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

How so? The previous code doesn't do any type checks.

When a Placeholder was passed, an exception would be thrown due to this line since Placeholder does not have items() attribute (rectifying my comment: it would not replace it with the estimator hyperparameters, but would through an error)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(I just read the following comment - please ignore previous one)^

Copy link
Contributor

Choose a reason for hiding this comment

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

With this change, scenario 3 will not throw an error, because of the type check in merge_dicts:

if isinstance(target, dict) and isinstance(source, dict):

But that means we will ignore the hyperparameters arg entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like the solution you are proposing - this will provide the expected behaviour described in #152
Will make the changes in the next commit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will be resolved in 2 steps:

  1. Fix the breaking change:

    • We need to support the same behaviour as in V2.1.0 -> if a Placeholder is provided as input to hyperparameters in TrainingStep constructor, the estimator hyperparameters are overwritten
  2. _Feature request (described in TrainingStep's hyperparameters does not work on v2.2.0 #152 comments): To be able to merge a Placeholder hyperparameters passed to TrainingStep with the estimator hyperparameters.
    This will introduce a breaking change since we need to support the behaviour described in V2.1.0. Proposing to add a utility that translates a Placeholder to a usable jsonpath dict (with $). That translated placeholder_dict can then be used as hyperparameters input to TrainingStep. With that, merging the constructor and estimator hyperparameters will be possible.

    • ex: A Placeholder with schema
{
  'TrainingInput': {
     'B': str,
     'C': str
   }
}

will output the following:

{
   'B.$': '$$.Execution.Input.B',
   'C.$': '$$.Execution.Input.C' 
}

Copy link
Contributor

@shivlaks shivlaks left a comment

Choose a reason for hiding this comment

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

🎉 I think this issue is a fix and not a feat. had a couple other small comments.
please also make sure that @wong-a has a look before merging.

tests/unit/test_sagemaker_steps.py Show resolved Hide resolved
tests/unit/test_sagemaker_steps.py Outdated Show resolved Hide resolved
shivlaks
shivlaks previously approved these changes Sep 10, 2021
@ca-nguyen ca-nguyen changed the title feat: Support placeholders for hyperparameters passed to TrainingStep fix: Support placeholders for hyperparameters passed to TrainingStep Sep 10, 2021
Copy link
Contributor

@wong-a wong-a left a comment

Choose a reason for hiding this comment

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

Looks good, just some minor comments for clarity

tests/unit/test_sagemaker_steps.py Show resolved Hide resolved
tests/unit/test_sagemaker_steps.py Outdated Show resolved Hide resolved
tests/unit/test_sagemaker_steps.py Outdated Show resolved Hide resolved
src/stepfunctions/steps/sagemaker.py Outdated Show resolved Hide resolved
src/stepfunctions/steps/sagemaker.py Outdated Show resolved Hide resolved
Co-authored-by: Adam Wong <55506708+wong-a@users.noreply.github.com>
@ca-nguyen ca-nguyen dismissed stale reviews from shivlaks via effe984 September 10, 2021 05:18
@ca-nguyen
Copy link
Contributor Author

ca-nguyen commented Sep 10, 2021

🎉 I think this issue is a fix and not a feat. had a couple other small comments.

Done!

Looks good, just some minor comments for clarity

Comments addressed :)

@ca-nguyen ca-nguyen merged commit 7501319 into aws:main Sep 10, 2021
@StepFunctions-Bot
Copy link
Contributor

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildProject6AEA49D1-sEHrOdk7acJc
  • Commit ID: 12b019a
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@ca-nguyen ca-nguyen deleted the support-placeholders-for-hyperparameters branch October 27, 2021 01:12
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