Skip to content
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
0a0a4c6
Change readme to trigger test
Mar 15, 2022
002683f
add dependencies for AG
Mar 15, 2022
60a847c
add user permission to test_notebook_example L81
Mar 15, 2022
60a9e27
add mlflow dependency to setup
Mar 16, 2022
bc7f38d
add textpredictor estimator and test
Mar 16, 2022
f9ca56b
new estimator, no test file
Mar 16, 2022
fe0ecbb
Update automl.py
Qiaochu-Song Mar 16, 2022
4a52ac7
Update automl.py
Qiaochu-Song Mar 16, 2022
30cc834
add test with gc, narrow down mxnet version
Mar 16, 2022
14e6720
Merge branch 'test_main' of github.com:Qiaochu-Song/FLAML into test_main
Mar 16, 2022
6b75a73
skip test for py3.6 and win+py3.8, loose mxnet ver
Mar 16, 2022
d10945e
no ag on windows, remove mlflow dependency
Mar 16, 2022
06f64b2
no ag on windows, remove mlflow dependency
Mar 16, 2022
c9ff3d4
test with direct return
Mar 17, 2022
e7b6f6d
debug without new test
Mar 17, 2022
2307b37
w/o os.environ setting in new test, direct return
Mar 17, 2022
bf3203b
debug, import only in new test
Mar 17, 2022
10c93b2
move new test to automl
Mar 17, 2022
53b5f09
move new test to test/nlp/
Mar 17, 2022
ee3cacb
pass data with X_train
Mar 21, 2022
8096a89
pr fixes, debugging
Mar 24, 2022
fed989b
update with upstream
Mar 24, 2022
c40af7d
Rename to MultimodalEstimator, pr fix
Mar 24, 2022
d0b3b11
remove comment
Mar 24, 2022
30e9f60
Update data.py
Qiaochu-Song Mar 25, 2022
d15dd60
fix bug
Mar 25, 2022
6c42839
Merge branch 'new-test2' of github.com:Qiaochu-Song/FLAML into new-test2
Mar 25, 2022
301eb16
remove useless import
Mar 25, 2022
c59a3b2
remove useless import
Mar 25, 2022
f04b69e
Merge branch 'new-test2' of github.com:Qiaochu-Song/FLAML into new-test2
Mar 25, 2022
2f07223
resolve conflict
Mar 28, 2022
ea515d2
remove task mapping for AG
Mar 28, 2022
6cc2f9e
use 0.5 threshold for text/cat inference
Apr 13, 2022
4cc2b4e
add MM_TASKS; no preprocess on X; pass val_data for early stopping
Apr 14, 2022
4fa136d
adjust testing data and raise budget
Apr 14, 2022
c5d9914
Merge remote-tracking branch 'upstream/main' into new-test2
Apr 14, 2022
25c1baf
shrink test toy data and budget
Apr 14, 2022
f9d3b22
change to regression test
Apr 14, 2022
c1568b4
add metric to kwargs for mm in train_estimator, raise test budget
Apr 14, 2022
1e4201d
use valid data if any for early stopping, raise test budget
Apr 15, 2022
9692d4e
return to the original budget
Apr 15, 2022
1b2cb28
fix valid DF checking
Apr 16, 2022
05941bc
simplify isinstance in ml.py
Apr 18, 2022
984d000
Merge remote-tracking branch 'upstream/main' into new-test2
Apr 18, 2022
74f27b5
reduce text column and budget
Apr 19, 2022
c8848c7
use only 4-row toy test data
Apr 19, 2022
7be2c5c
test 10s budget
Apr 19, 2022
1c7f7ad
minimize test toy dataset
Apr 19, 2022
be60fa6
shorter test sentence
Apr 19, 2022
3a29c5b
give enough test budget
Apr 20, 2022
543b660
give enough test budget
Apr 20, 2022
4296129
solve conflict
May 4, 2022
ca30eab
Merge branch 'mxtextpredictor' of github.com:Qiaochu-Song/FLAML into …
May 6, 2022
5bd061f
add pytorch backend support
May 12, 2022
2b150e7
set pytorch backend to default
May 19, 2022
505c894
pytorch backend support only
May 19, 2022
cd98daf
solve merge conflict
May 19, 2022
98ee138
test remove os and python ver constraints
May 19, 2022
ff8c078
no support for python 3.6
May 19, 2022
24a5333
no support for python 3.6 or windows
May 19, 2022
2aeb563
Merge branch 'main' into mxtextpredictor
Qiaochu-Song May 20, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ jobs:
run: |
pip install -e .[ray,forecast]
pip install 'tensorboardX<=2.2'
- name: If python version > 3.6 and not on windows, install autogluon
if: matrix.python-version >= '3.7' && (matrix.os == 'macOS-latest' || matrix.os == 'ubuntu-latest')
run: |
pip install -e .[autogluon]
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
Expand Down
4 changes: 3 additions & 1 deletion flaml/automl.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
REGRESSION,
_is_nlp_task,
NLG_TASKS,
_is_mm_task,
)
from . import tune
from .training_log import training_log_reader, training_log_writer
Expand Down Expand Up @@ -974,7 +975,8 @@ def _validate_data(
"or all columns of X are integer ids (tokenized)"
)

if issparse(X_train_all):
if issparse(X_train_all) or _is_mm_task(self._state.task):
# leave the preprocessing to the mm_estimator
self._transformer = self._label_transformer = False
self._X_train_all, self._y_train_all = X, y
else:
Expand Down
12 changes: 11 additions & 1 deletion flaml/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
SEQCLASSIFICATION,
MULTICHOICECLASSIFICATION,
TOKENCLASSIFICATION,
"mm_multi",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can you automatically detect "mm_multi" and "mm_binary" so we don't need these two values anymore?

"mm_binary",
)
SEQREGRESSION = "seq-regression"
REGRESSION = ("regression", SEQREGRESSION)
REGRESSION = ("regression", "mm_regression", SEQREGRESSION)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rename "mm_regression" -> "multimodal-regression", define a static variable for it

TS_FORECASTREGRESSION = (
"forecast",
"ts_forecast",
Expand All @@ -47,6 +49,14 @@
TOKENCLASSIFICATION,
)

MM_TASKS = ("mm_binary", "mm_multi", "mm_regression")


## ***** ADDED FOR MULTIMODAL *****
def _is_mm_task(task):
return True if task in MM_TASKS else False
## ***** END ADDED FOR MULTIMODAL *****


def _is_nlp_task(task):
if task in NLU_TASKS or task in NLG_TASKS:
Expand Down
3 changes: 3 additions & 0 deletions flaml/ml.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
ARIMA,
SARIMAX,
TransformersEstimator,
AGTextPredictorEstimator,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
AGTextPredictorEstimator,
MultiModalEstimator,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please update all occurrences

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please update the commit.

)
from .data import CLASSIFICATION, group_counts, TS_FORECAST, TS_VALUE_COL
import logging
Expand Down Expand Up @@ -121,6 +122,8 @@ def get_estimator_class(task, estimator_name):
estimator_class = SARIMAX
elif estimator_name == "transformer":
estimator_class = TransformersEstimator
elif estimator_name == "agtextpredictor":
estimator_class = AGTextPredictorEstimator
else:
raise ValueError(
estimator_name + " is not a built-in learner. "
Expand Down
183 changes: 183 additions & 0 deletions flaml/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,189 @@ class XGBoostLimitDepth_TS(TS_SKLearn):
base_class = XGBoostLimitDepthEstimator


class AGTextPredictorEstimator(BaseEstimator):
"""
The class for tuning AutoGluon TextPredictor
"""
def __init__(self, task="binary", **params,):
from autogluon.text import TextPredictor

super().__init__(task, **params)
self.estimator_class = TextPredictor

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I can remove this and initialize the model with TextPredictor instead. Is that better?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

yes


@classmethod
def search_space(cls, **params):
"""
Add the possible search space configs here, e.g. 'optimization.lr'
reference:
https://auto.gluon.ai/stable/tutorials/text_prediction/customization.html#custom-hyperparameter-values
"""
search_space_dict = {
"model.network.agg_net.mid_units": {
"domain": tune.choice(list(range(32, 129))),
"init_value": 128,
},
"optimization.lr": {
"domain": tune.loguniform(lower=1E-5, upper=1E-4),
"init_value": 1E-4,
},
"optimization.wd": {
"domain": tune.choice([1E-4, 1E-3, 1E-2]),
"init_value":1E-4,
},
"optimization.warmup_portion": {
"domain": tune.choice([0.1, 0.2]),
"init_value":0.1,
},
}
return search_space_dict

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There were only 4 hyperparameters and now there are 9. Which one was the search space used in your original experiment for autogluon?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The original four are "model.network.agg_net.mid_units", "optimization.warmup_portion", "optimization.lr", "optimization.wd".


def _init_fix_args(self, automl_fit_kwargs: dict=None):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need this function? Can we simply remove it?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If we have AGArgs dataclass in utils, and just use the default settings, we can remove this function, and just have self.ag_args=AGArgs() in MultimodalEstimator.fit(). Does it make sense?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, you can implement this, and define a similar init_hf_args if you need to check user input validity.

"""
Save the customed fix args here
this includes:
"output_dir",
"text_backbone": "electra_base"
"multimodal_fusion_strategy":"fuse_late",
"""
fix_args = {}
FIX_ARGS_LIST = ["output_dir", "dataset_name", "label_column", "per_device_batch_size", "backend",
"text_backbone", "multimodal_fusion_strategy", "num_train_epochs", "batch_size",]
for key, value in automl_fit_kwargs["custom_fix_args"].items():
assert (
key in FIX_ARGS_LIST
), "The specified key {} is not in the argument list: output_dir, backend, label_column, dataset_name, text_backbone,\
multimodal_fusion_strategy, num_train_epochs, batch_size, per_device_batch_size".format(key)

fix_args[key] = value

self.fix_args = fix_args

def _init_hp_config(self, text_backbone: str, multimodal_fusion_strategy: str):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please define cfg by defining a function inside of flaml/nlp/utils.py:class AGArgs, the remove this function.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This _init_hp_config is to use the AGArgs and the self.params to get the hyperparametersdiction for the TextPredictor. If removed, still need to assemble this diction inside the MultimodalEstimator.fit(). Do you think it is better without this function and have this part inside the .fit()?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Move this function to a function inside of AGArgs because AGArgs is for managing the config for AG.

""""
Ref:
https://auto.gluon.ai/stable/tutorials/text_prediction/customization.html#custom-hyperparameter-values
"""
if self.fix_args.get("backend", "pytorch") == "mxnet":
from autogluon.text.text_prediction.legacy_presets import ag_text_presets

base_key = f'{text_backbone}_{multimodal_fusion_strategy}'
cfg = ag_text_presets.create(base_key)
# NOTE: if the search_space() is modified, add new items or delete here too.
TUNABLE_HP = set(["model.network.agg_net.mid_units",
"optimization.batch_size",
"optimization.layerwise_lr_decay",
"optimization.lr",
"optimization.nbest",
"optimization.num_train_epochs",
"optimization.per_device_batch_size",
"optimization.wd",
"optimization.warmup_portion",
])
search_space = cfg["models"]["MultimodalTextModel"]["search_space"]
search_space["optimization.per_device_batch_size"] = self.fix_args.get("per_device_batch_size", 4)
search_space["optimization.num_train_epochs"] = self.fix_args.get("num_train_epochs", 10)
search_space["optimization.batch_size"] = self.fix_args.get("batch_size", 128)
for key, value in self.params.items():
if key in TUNABLE_HP:
# NOTE: FLAML uses np.float64 but AG uses float, need to transform
if isinstance(value, np.float64):
search_space[key] = value.item()
else:
search_space[key] = value
return cfg

else:
raise ValueError("the pytorch automm model is not supported. ")
# from autogluon.text.text_prediction.presets import get_text_preset

# cfg, overrides = get_text_preset("default") # get preset for text+num+cat+fusion
# # TODO: set the search space for the auto_mm in AG 0.4.0
# cfg.hf_text.checkpoint_name = self.fix_args["hf_text.checkpoint_name"]
# # get search configs from self.params and set here
# TUNABLE_HP = []
# for key, value in self.params.items():
# if key in TUNABLE_HP:
# # NOTE: FLAML uses np.float64 but AG uses float, might need to transform
# if isinstance(value, np.float64):
# search_space[key] = value.item()
# else:
# search_space[key] = value
return cfg


def _set_seed(self, seed):
import random
import mxnet as mx
import torch as th
th.manual_seed(seed)
mx.random.seed(seed)
np.random.seed(seed)
random.seed(seed)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

please try if you can reproduce the result


def fit(self, X_train=None, y_train=None, budget=None, **kwargs):
self._kwargs = kwargs
self._init_fix_args(kwargs)
# the seed set in the bash script for ag experiment is 123
seed = self.params.get("seed", 123)
self._set_seed(seed)

# get backbone and fusion strategy
text_backbone = self.fix_args["text_backbone"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please remove these local variables by, e.g., simply use self.ag_args.text_backbone

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

will fix and move to AGArgs

multimodal_fusion_strategy = self.fix_args["multimodal_fusion_strategy"]

# get & set the save dir, get the dataset info
save_dir = self.fix_args["output_dir"]
label_column = self.fix_args["label_column"]
dataset_name = self.fix_args["dataset_name"]
ag_model_save_dir = os.path.join(save_dir, f"{dataset_name}_ag_text_multimodal_{text_backbone}\

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we save the model after the HPO and after automl.fit in the test file instead of in here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm afraid we cannot. When initializing the predictor, this path is either defined by a user or created by ag, to save the model. AG will automatically save the model to this directory.
Ref: https://github.com/awslabs/autogluon/blob/0.4.0/text/src/autogluon/text/text_prediction/predictor.py#L56

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ok. Can you use the original directory save_dir instead of the modified directory ag_model_save_dir so users know where to find the saved model?

_{multimodal_fusion_strategy}_no_ensemble")

# set the hyperparameters
self.hyperparameters = self._init_hp_config(text_backbone, multimodal_fusion_strategy)
PROBLEM_TYPE_MAPPING = {"mm_binary": "binary", "mm_multi": "multiclass", "mm_regression": "regression"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please implement the problem type mapping in flaml/nlp/utils.py:load_default_huggingface_metric_for_task

TASK_METRIC_MAPPING = {"mm_multi": "acc", "mm_binary": "roc_auc", "mm_regression": "r2"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please implement the metric mapping in flaml/nlp/utils.py:load_default_huggingface_metric_for_task


# train the model
start_time = time.time()

self._model = self.estimator_class(path=ag_model_save_dir,
Comment thread
Qiaochu-Song marked this conversation as resolved.
Outdated
label=label_column,
problem_type=PROBLEM_TYPE_MAPPING[self._task],
eval_metric=TASK_METRIC_MAPPING[self._task],
backend=self.fix_args.get("backend", "pytorch"))

# train_data = self._kwargs["train_data"]
import pandas as pd
train_data = pd.concat([X_train, y_train], axis=1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

please use estimator._join method. See TransformersEstimator._join

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

tuning_data = pd.concat([X_train, y_train], axis=1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You mean X_val, y_val?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I will remove this line since the tuning data is not necessary anymore.


self._model.fit(train_data=train_data,
tuning_data=kwargs.get("tuning_data", None),
hyperparameters=self.hyperparameters,
time_limit=budget,
seed=seed)

training_time = time.time() - start_time
return training_time

def predict(self, X):
output = self._model.predict(X, as_pandas=False)
return output

def predict_proba(self, X, as_multiclass=True):
# only works for classification tasks
assert (
self._task in CLASSIFICATION
), "predict_proba() only for classification tasks."

output = self._model.predict_proba(X, as_pandas=False)
if not as_multiclass:
if self._task == "mm_binary":
output = output[:, 1]
return output


class suppress_stdout_stderr(object):
def __init__(self):
# Open a pair of null files
Expand Down
5 changes: 5 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@
"hcrystalball==0.1.10",
"seqeval",
],
"autogluon": [
"mxnet<2.0.0",
"autogluon.text==0.4.0",
"autogluon.features==0.4.0",
],
"catboost": ["catboost>=0.26"],
"blendsearch": ["optuna==2.8.0"],
"ray": [
Expand Down
Loading