sagemaker-python-sdk: Hyperparameter values forcefully converted to strings, thus unable to pass a list
I want ro pass a list as a hyperparameter to an estimator. For example, hyperparameter key = ‘a’ hyperparameter value = [‘def’, ‘xyz’]
This type of value is accepted and get verified:
>>> estimator.set_hyperparameters(a=['def', 'xyz'])
>>> estimator.hyperparameters()['a']
['def', 'xyz']
>>> type(estimator.hyperparameters()['a'])
<class 'list'>
I expect that sagemaker renders hyperparameters to the following json to be found inside /opt/ml/input/config/hyperparameters.json:
>>> print(json.dumps(estimator.hyperparameters()))
{"a": ["def", "xyz"]}
Instead, the contents of /opt/ml/input/config/hyperparameters.json is:
{"a": "['def', 'xyz']"}
The list
type of the value is lost, being forcefully converted to a string by this code:
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 13
- Comments: 22 (3 by maintainers)
Good morning, @yangaws,
Yes, I am using my own container. No, I do not find it difficult to decode a string back to list… if I know that I have to do it.
The problem is that sagemaker changes the type of the passed value when it is not expected to do so. The sagemaker doc reads:
Indeed, it mentions that the return type is dict[str, str]. However, this notice of the return type is present only for
EstimatorBase.hyperparameters()
and is absent forEstimator.hyperparameters()
.Moreover, the
Estimator.hyperparameters()
method returns the dict with value types as they’ve been passed toEstimator
such as lists, numbers, etc.So one is ok to assume that this is a “dictionary to use for training”.
Errors that can be introduced could be quite nasty. For example, the list
['bb', 'cc']
and the string"['bb', 'cc']"
respond to the same calls, such aslen()
or__getitem__()
, but with quite different results.My understanding is that you’re being compliant to the following specification:
(AWS Documentation - Amazon SageMaker - Developer Guide - API Reference - Actions - Amazon SageMaker Service - CreateTrainingJob)
Forcing hyperparameter values to be strings looks rather unwarranted. I am interested what is the reason behind it. To work around, I have to implement additional checks to differentiate between reading “conventional” json and “sagemaker-style” json, doing additional decoding for the latter. I see this as an unnecessary complication.
If this restriction cannot be lifted, I suggest that
Estimator.hyperparameters()
returns the dictionary with keys and values converted to strings (as it’s probably supposed to do).Hey!
This is something that has being a pain for us too.
Yes. The right solution to adapt ourselves to sagemaker’s contract with hyperparameters is: keeping track of all possible key/values and their types in a custom image, just to parse it back to their expected format. And undoing the
str()
applied by sagemaker, casting every hyperparameter that is not a string is quite time consuming.This is not easy to do when we have to send dozens of hyperparameters. Also, it is quite strange to send something like
and getting inside of the container something like:
Is there any chance of changing how sagemaker deals with hyperparameters?
I imagine that the decision of forcing an
str()
conversion was taken to prevent having to deal with json encoders or it’s more related to some concern with safety. Therefore, letting us control the json encoding and decoding and expecting just a json string as an argument ofEstimator
would help both sides?Nothing? I am currently trying to pass a list of str (or int even) to an estimator and getting my list cut at the comma. Any developments on this issue?
Hi team, for those still having trouble here, I’ve been using these two functions to parse out the hyperparameters in my own estimator containers (it also strips leading 0’s, just in case you work with coordinates in your day to day, which can cause trouble when trying to cast to an int or float):
It uses
literal_eval
fromast
, which is not ideal, but still much safer thaneval
, since it raises aValueError
if it can’t cast to a basic Python type.any updates? parsing values can be a pain if it’s all nested and has different data types. This adds a lot of extra workload. If user simply passes in a python dict, they would expect to get the same thru a simple load using the json module.
Hi
Currently I’m doing
https://docs.aws.amazon.com/sagemaker/latest/dg/ex1-train-model.html
I’m in: “Step 4: Train a Model”
And in the “3. Set the hyperparameters for the XGBoost algorithm by calling the set_hyperparameters method of the estimator.”
I’m having a problem after executing:
xgb_model.fit({"train": train_input, "validation": validation_input}, wait=True)
After going through some documentation: https://www.analyticsvidhya.com/blog/2016/03/complete-guide-parameter-tuning-xgboost-with-codes-python/
https://xgboost.readthedocs.io/en/latest/parameter.html
I see that the list of available options in
objective
This is how I have it right now 1:
I’ve tried to use
2:
3:
The error output varies but it’s always like this:
Do you guys now if I’m doing something wrong? I would think the tutorial is a no brainer but maybe I’m missing something
we are facing this issue as well. And in our case it is nested dict (we use our customer docker images). its very hard to reparse this to json because we have different types of values.
you guys also convert double quotes to single quotes, so to effectively get this to work, i have to do this
Glad to create a PR for this immediately. I can’t believe this has not been addressed in nearly a year.
+1 I wasted a whole day of my life trying to debug this…training the docker container worked locally, but on sagemaker it did not! Truly an insidious issue.
What is the status on this issue? I believe the relevant changes were not included on v2.0.0. @laurenyu