-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[Tabular] Introduce compile_models function to tabular predictor #2260
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
Conversation
Job PR-2260-eeb875b is done. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added initial review
core/src/autogluon/core/models/ensemble/weighted_ensemble_model.py
Outdated
Show resolved
Hide resolved
ee8b789
to
5f86f88
Compare
Job PR-2260-57ae20f is done. |
Job PR-2260-710e6ba is done. |
if type(self) in compiler_configs: | ||
configs = compiler_configs[type(self)] | ||
elif self.name in compiler_configs: | ||
configs = compiler_configs[self.name] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we allowing passing of other model compiler configs? This should be handled upstream, and compiler_configs
should simply be {"compiler": "onnx"}
in this context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kind of a model-specific compiler configration. For ensemble models, we could use a specific backend for a specific model. For instance, an ensemable model with RF and TORCH_NN, I think we could configure the compiler to optimize RF with onnx, while optimize TORCH_NN model with tvm, in order to maximize potential performance benefits. Of course, we can still use the top-level compiler config if model-specific compiler is not specified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a model-specific compiler configration
This shouldn't be available at the AbstractModel level, we only need the configuration for the model itself in this context, not others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Model-specific config logic has been moved outside of abstract_model.
if type(self) in compiler_configs: | ||
configs = compiler_configs[type(self)] | ||
elif self.name in compiler_configs: | ||
configs = compiler_configs[self.name] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
core/src/autogluon/core/models/ensemble/stacker_ensemble_model.py
Outdated
Show resolved
Hide resolved
if path is None: | ||
path = self.path | ||
file_path = path + self.model_file_name | ||
def can_compile(self, compiler_configs=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need compiler_config
to be passed by the caller each time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, I think the answer is yes. It makes the interface a little bit difficult to configure, since users may not aware the underlying compilers that are supported.
In the long term, I think we may introduce the presets
argument to the compile_models
method.
To support edge devices, we could support presets like
- rpi
- jetson-tx1
For cloud deployment, presets could be something like
- aws-lambda # for online prediction, batch_size=1
- sagemaker-endpoint # for batch prediction, batch_size=1024
Job PR-2260-c454f37 is done. |
(cherry picked from commit eeb875b)
Job PR-2260-42781d4 is done. |
Job PR-2260-9c7e698 is done. |
Job PR-2260-75b68b7 is done. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Thanks for the contribution!
|
||
Parameters | ||
---------- | ||
compiler_configs : dict, default = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing description of what the key is
Compile models for accelerated prediction. | ||
This can be helpful to reduce prediction latency and improve throughput. | ||
|
||
Note that this is currently an experimental feature, the supported alternative compiler can be ['native', 'onnx']. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explain that this can take significant time to compile.
Parameters | ||
---------- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing models parameter
Parameters | ||
---------- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing with_ancestors parameter
Compile models for accelerated prediction. | ||
This can be helpful to reduce prediction latency and improve throughput. | ||
|
||
Note that this is currently an experimental feature, the supported alternative compiler can be ['native', 'onnx']. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
first line of docstring should be [Experimental]
@@ -1447,6 +1497,7 @@ def _add_model(self, model: AbstractModel, stack_name: str = 'core', level: int | |||
self.model_graph.add_node( | |||
model.name, | |||
fit_time=model.fit_time, | |||
compile_time=model.compile_time, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add # FIXME: This won't accurately reflect compile_time if added prior to compiling occurring. Need to update this value after the model is compiled.
@@ -1950,6 +1950,29 @@ def feature_importance(self, data=None, model=None, features=None, feature_stage | |||
fi_df[low_str] = pd.Series(ci_low_dict) | |||
return fi_df | |||
|
|||
def compile_models(self, models='best', with_ancestors=True, compiler_configs=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is compiler_configs default None if it crashes?
predictor.compile_models()
Traceback (most recent call last):
File "/Users/neerick/workspace/virtual/autogluon38/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3433, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-2-05108f5d756e>", line 1, in <module>
runfile('/Users/neerick/workspace/code/autogluon-scratch/scripts/run_adult_compile.py', wdir='/Users/neerick/workspace/code/autogluon-scratch/scripts')
File "/Users/neerick/Library/Application Support/JetBrains/Toolbox/apps/PyCharm-P/ch-1/223.7255.83/PyCharm 2022.3 EAP.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_umd.py", line 198, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "/Users/neerick/Library/Application Support/JetBrains/Toolbox/apps/PyCharm-P/ch-1/223.7255.83/PyCharm 2022.3 EAP.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/Users/neerick/workspace/code/autogluon-scratch/scripts/run_adult_compile.py", line 37, in <module>
predictor.compile_models()
File "/Users/neerick/workspace/code/autogluon/tabular/src/autogluon/tabular/predictor/predictor.py", line 1974, in compile_models
self._trainer.compile_models(model_names=models, with_ancestors=with_ancestors, compiler_configs=compiler_configs)
File "/Users/neerick/workspace/code/autogluon/core/src/autogluon/core/trainer/abstract_trainer.py", line 1166, in compile_models
if type(model) in compiler_configs:
TypeError: argument of type 'NoneType' is not iterable
@@ -1950,6 +1950,29 @@ def feature_importance(self, data=None, model=None, features=None, feature_stage | |||
fi_df[low_str] = pd.Series(ci_low_dict) | |||
return fi_df | |||
|
|||
def compile_models(self, models='best', with_ancestors=True, compiler_configs=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following code fails. Need to re-load the model after compiling if it was already persisted.
if __name__ == '__main__':
from autogluon.tabular import TabularPredictor, TabularDataset
path_prefix = 'https://autogluon.s3.amazonaws.com/datasets/AdultIncomeBinaryClassification/'
path_train = path_prefix + 'train_data.csv'
path_test = path_prefix + 'test_data.csv'
label = 'class'
sample = 1000 # Number of rows to use to train / infer
train_data = TabularDataset(path_train)
if sample is not None and (sample < len(train_data)):
train_data = train_data.sample(n=sample, random_state=0).reset_index(drop=True)
test_data = TabularDataset(path_test)
fit_kwargs = dict(
train_data=train_data,
hyperparameters={
'RF': {},
},
)
predictor = TabularPredictor(
label=label,
eval_metric='roc_auc',
verbosity=2,
)
predictor.fit(**fit_kwargs)
predictor.persist_models()
leaderboard = predictor.leaderboard(test_data)
compiler_configs = {'RandomForest': {'compiler': 'onnx'}}
predictor.compile_models(compiler_configs=compiler_configs)
predictor.persist_models()
leaderboard2 = predictor.leaderboard(test_data)
Because the old model is still persisted, it tries to use the old model but self.model is None so it crashes with:
TabularPredictor saved. To load, use: predictor = TabularPredictor.load("AutogluonModels/ag-20221104_213857/")
Persisting 2 models in memory. Models will require 0.09% of memory.
model score_test score_val pred_time_test pred_time_val fit_time pred_time_test_marginal pred_time_val_marginal fit_time_marginal stack_level can_infer fit_order
0 RandomForest 0.889409 0.871562 0.128715 0.088706 0.858871 0.128715 0.088706 0.858871 1 True 1
1 WeightedEnsemble_L2 0.889409 0.871562 0.130412 0.091054 0.866322 0.001697 0.002348 0.007451 2 True 2
/Users/neerick/workspace/virtual/autogluon38/lib/python3.8/site-packages/sklearn/utils/deprecation.py:103: FutureWarning: The attribute `n_features_` is deprecated in 1.0 and will be removed in 1.2. Use `n_features_in_` instead.
warnings.warn(msg, category=FutureWarning)
/Users/neerick/workspace/virtual/autogluon38/lib/python3.8/site-packages/sklearn/utils/deprecation.py:103: FutureWarning: Attribute `n_features_` was deprecated in version 1.0 and will be removed in 1.2. Use `n_features_in_` instead.
warnings.warn(msg, category=FutureWarning)
The following 2 models were already persisted and will be ignored in the model loading process: ['WeightedEnsemble_L2', 'RandomForest']
No valid unpersisted models were specified to be persisted, so no change in model persistence was performed.
Traceback (most recent call last):
File "/Users/neerick/workspace/virtual/autogluon38/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3433, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-2-05108f5d756e>", line 1, in <module>
runfile('/Users/neerick/workspace/code/autogluon-scratch/scripts/run_adult_compile.py', wdir='/Users/neerick/workspace/code/autogluon-scratch/scripts')
File "/Users/neerick/Library/Application Support/JetBrains/Toolbox/apps/PyCharm-P/ch-1/223.7255.83/PyCharm 2022.3 EAP.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_umd.py", line 198, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "/Users/neerick/Library/Application Support/JetBrains/Toolbox/apps/PyCharm-P/ch-1/223.7255.83/PyCharm 2022.3 EAP.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/Users/neerick/workspace/code/autogluon-scratch/scripts/run_adult_compile.py", line 41, in <module>
leaderboard2 = predictor.leaderboard(test_data)
File "/Users/neerick/workspace/code/autogluon/tabular/src/autogluon/tabular/predictor/predictor.py", line 1570, in leaderboard
return self._learner.leaderboard(X=data, extra_info=extra_info, extra_metrics=extra_metrics,
File "/Users/neerick/workspace/code/autogluon/tabular/src/autogluon/tabular/learner/abstract_learner.py", line 624, in leaderboard
leaderboard = self.score_debug(X=X, y=y, extra_info=extra_info, extra_metrics=extra_metrics, silent=True)
File "/Users/neerick/workspace/code/autogluon/tabular/src/autogluon/tabular/learner/abstract_learner.py", line 306, in score_debug
model_pred_proba_dict, pred_time_test_marginal = trainer.get_model_pred_proba_dict(X=X, models=all_trained_models_can_infer, record_pred_time=True)
File "/Users/neerick/workspace/code/autogluon/core/src/autogluon/core/trainer/abstract_trainer.py", line 811, in get_model_pred_proba_dict
model_pred_proba_dict[model_name] = model.predict_proba(X)
File "/Users/neerick/workspace/code/autogluon/core/src/autogluon/core/models/abstract/abstract_model.py", line 687, in predict_proba
y_pred_proba = self._predict_proba(X=X, **kwargs)
File "/Users/neerick/workspace/code/autogluon/tabular/src/autogluon/tabular/models/rf/rf_model.py", line 244, in _predict_proba
y_pred_proba = self.model.predict_proba(X)
AttributeError: 'NoneType' object has no attribute 'predict_proba'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A workaround (shouldn't be used directly) is to unpersist and persist after the compile_models call:
predictor.compile_models(compiler_configs=compiler_configs)
predictor.unpersist_models()
predictor.persist_models()
Should instead be done at the trainer level and on a per-model basis.
Note ignore my latest review comments, they are from a draft review prior to me refactoring the code. |
Description of changes:
Following #2225, this PR takes a further step to introduce
compile_models
function to TabularPredictor. This helpscompile_models
fromfit
andsave
function from tabular predictor.Also, it is required to persist model before model compilation. Therefore, the convention for model compilation would be
To compare performance with/without zipmap

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