autogluon.tabular 1.4.1b20251214__py3-none-any.whl → 1.5.0b20251222__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autogluon/tabular/configs/hyperparameter_configs.py +4 -0
- autogluon/tabular/configs/presets_configs.py +39 -2
- autogluon/tabular/configs/zeroshot/zeroshot_portfolio_2025.py +2 -44
- autogluon/tabular/configs/zeroshot/zeroshot_portfolio_cpu_2025_12_18.py +2 -0
- autogluon/tabular/configs/zeroshot/zeroshot_portfolio_gpu_2025_12_18.py +2 -0
- autogluon/tabular/learner/default_learner.py +1 -0
- autogluon/tabular/models/__init__.py +3 -1
- autogluon/tabular/models/abstract/__init__.py +0 -0
- autogluon/tabular/models/abstract/abstract_torch_model.py +148 -0
- autogluon/tabular/models/catboost/catboost_model.py +1 -1
- autogluon/tabular/models/fastainn/tabular_nn_fastai.py +5 -1
- autogluon/tabular/models/lgb/lgb_model.py +58 -8
- autogluon/tabular/models/lgb/lgb_utils.py +2 -2
- autogluon/tabular/models/mitra/_internal/core/trainer_finetune.py +14 -1
- autogluon/tabular/models/mitra/mitra_model.py +53 -22
- autogluon/tabular/models/realmlp/realmlp_model.py +8 -2
- autogluon/tabular/models/tabdpt/__init__.py +0 -0
- autogluon/tabular/models/tabdpt/tabdpt_model.py +253 -0
- autogluon/tabular/models/tabicl/tabicl_model.py +15 -2
- autogluon/tabular/models/tabm/tabm_model.py +23 -79
- autogluon/tabular/models/tabpfnv2/tabpfnv2_5_model.py +451 -0
- autogluon/tabular/models/tabpfnv2/tabpfnv2_model.py +86 -8
- autogluon/tabular/models/tabprep/__init__.py +0 -0
- autogluon/tabular/models/tabprep/prep_lgb_model.py +21 -0
- autogluon/tabular/models/tabprep/prep_mixin.py +220 -0
- autogluon/tabular/models/tabular_nn/torch/tabular_nn_torch.py +1 -1
- autogluon/tabular/models/tabular_nn/utils/data_preprocessor.py +12 -4
- autogluon/tabular/models/xgboost/xgboost_model.py +2 -0
- autogluon/tabular/predictor/predictor.py +47 -18
- autogluon/tabular/registry/_ag_model_registry.py +8 -2
- autogluon/tabular/testing/fit_helper.py +33 -0
- autogluon/tabular/trainer/abstract_trainer.py +45 -9
- autogluon/tabular/trainer/auto_trainer.py +5 -0
- autogluon/tabular/version.py +1 -1
- {autogluon_tabular-1.4.1b20251214.dist-info → autogluon_tabular-1.5.0b20251222.dist-info}/METADATA +36 -35
- {autogluon_tabular-1.4.1b20251214.dist-info → autogluon_tabular-1.5.0b20251222.dist-info}/RECORD +43 -33
- /autogluon.tabular-1.4.1b20251214-py3.11-nspkg.pth → /autogluon.tabular-1.5.0b20251222-py3.11-nspkg.pth +0 -0
- {autogluon_tabular-1.4.1b20251214.dist-info → autogluon_tabular-1.5.0b20251222.dist-info}/WHEEL +0 -0
- {autogluon_tabular-1.4.1b20251214.dist-info → autogluon_tabular-1.5.0b20251222.dist-info}/licenses/LICENSE +0 -0
- {autogluon_tabular-1.4.1b20251214.dist-info → autogluon_tabular-1.5.0b20251222.dist-info}/licenses/NOTICE +0 -0
- {autogluon_tabular-1.4.1b20251214.dist-info → autogluon_tabular-1.5.0b20251222.dist-info}/namespace_packages.txt +0 -0
- {autogluon_tabular-1.4.1b20251214.dist-info → autogluon_tabular-1.5.0b20251222.dist-info}/top_level.txt +0 -0
- {autogluon_tabular-1.4.1b20251214.dist-info → autogluon_tabular-1.5.0b20251222.dist-info}/zip-safe +0 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Type
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
from autogluon.features import ArithmeticFeatureGenerator
|
|
10
|
+
from autogluon.features import CategoricalInteractionFeatureGenerator
|
|
11
|
+
from autogluon.features import OOFTargetEncodingFeatureGenerator
|
|
12
|
+
from autogluon.features import BulkFeatureGenerator
|
|
13
|
+
from autogluon.features.generators.abstract import AbstractFeatureGenerator
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
# TODO: In future we can have a feature generator registry like what is done for models
|
|
18
|
+
_feature_generator_class_lst = [
|
|
19
|
+
ArithmeticFeatureGenerator,
|
|
20
|
+
CategoricalInteractionFeatureGenerator,
|
|
21
|
+
OOFTargetEncodingFeatureGenerator,
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
_feature_generator_class_map = {
|
|
25
|
+
feature_generator_cls.__name__: feature_generator_cls for feature_generator_cls in _feature_generator_class_lst
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _recursive_expand_prep_param(prep_param: tuple | list[list | tuple]) -> list[tuple]:
|
|
30
|
+
if isinstance(prep_param, list):
|
|
31
|
+
if len(prep_param) == 0:
|
|
32
|
+
param_type = "list"
|
|
33
|
+
elif len(prep_param) == 2:
|
|
34
|
+
if isinstance(prep_param[0], (str, AbstractFeatureGenerator)):
|
|
35
|
+
param_type = "generator"
|
|
36
|
+
else:
|
|
37
|
+
param_type = "list"
|
|
38
|
+
else:
|
|
39
|
+
param_type = "list"
|
|
40
|
+
elif isinstance(prep_param, tuple):
|
|
41
|
+
param_type = "generator"
|
|
42
|
+
else:
|
|
43
|
+
raise ValueError(f"Invalid value for prep_param: {prep_param}")
|
|
44
|
+
if param_type == "list":
|
|
45
|
+
out = []
|
|
46
|
+
for p in prep_param:
|
|
47
|
+
out += _recursive_expand_prep_param(p)
|
|
48
|
+
return out
|
|
49
|
+
elif param_type == "generator":
|
|
50
|
+
return [prep_param]
|
|
51
|
+
else:
|
|
52
|
+
raise ValueError(f"Invalid value for prep_param: {prep_param}")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# FIXME: Why is preprocessing twice as slow per fold when bagging LightGBM??? Need to investigate. Try sequential fold fit
|
|
56
|
+
# TODO: Why is `prep_params` a dict instead of a list?
|
|
57
|
+
class ModelAgnosticPrepMixin:
|
|
58
|
+
def _estimate_dtypes_after_preprocessing(self, X: pd.DataFrame, **kwargs) -> int:
|
|
59
|
+
prep_params = self._get_ag_params().get("prep_params", None)
|
|
60
|
+
if prep_params is None:
|
|
61
|
+
prep_params = []
|
|
62
|
+
|
|
63
|
+
# FIXME: Temporarily simplify for memory calculation
|
|
64
|
+
prep_params = _recursive_expand_prep_param(prep_params)
|
|
65
|
+
|
|
66
|
+
X_nunique = X.nunique().values
|
|
67
|
+
n_categorical = X.select_dtypes(exclude=[np.number]).shape[1]
|
|
68
|
+
n_numeric = X.loc[:, X_nunique > 2].select_dtypes(include=[np.number]).shape[1]
|
|
69
|
+
n_binary = X.loc[:, X_nunique <= 2].select_dtypes(include=[np.number]).shape[
|
|
70
|
+
1] # NOTE: It can happen that features have less than two unique values if cleaning is applied before the bagging, i.e. Bioresponse
|
|
71
|
+
|
|
72
|
+
assert n_numeric + n_categorical + n_binary == X.shape[1] # NOTE: FOr debugging, to be removed later
|
|
73
|
+
for preprocessor_cls_name, init_params in prep_params:
|
|
74
|
+
if preprocessor_cls_name == 'ArithmeticFeatureGenerator':
|
|
75
|
+
prep_cls = ArithmeticFeatureGenerator(target_type=self.problem_type, **init_params)
|
|
76
|
+
elif preprocessor_cls_name == 'CategoricalInteractionFeatureGenerator':
|
|
77
|
+
prep_cls = CategoricalInteractionFeatureGenerator(target_type=self.problem_type, **init_params)
|
|
78
|
+
elif preprocessor_cls_name == 'OOFTargetEncodingFeatureGenerator':
|
|
79
|
+
prep_cls = OOFTargetEncodingFeatureGenerator(target_type=self.problem_type, **init_params)
|
|
80
|
+
else:
|
|
81
|
+
raise ValueError(f"Unknown preprocessor class name: {preprocessor_cls_name}")
|
|
82
|
+
n_numeric, n_categorical, n_binary = prep_cls.estimate_new_dtypes(n_numeric, n_categorical, n_binary,
|
|
83
|
+
num_classes=self.num_classes)
|
|
84
|
+
|
|
85
|
+
return n_numeric, n_categorical, n_binary
|
|
86
|
+
|
|
87
|
+
def _estimate_memory_usage(self, X: pd.DataFrame, **kwargs) -> int:
|
|
88
|
+
hyperparameters = self._get_model_params()
|
|
89
|
+
n_numeric, n_categorical, n_binary = self._estimate_dtypes_after_preprocessing(X=X, **kwargs)
|
|
90
|
+
|
|
91
|
+
if hasattr(self, "_estimate_memory_usage_static_lite"):
|
|
92
|
+
return self._estimate_memory_usage_static_lite(
|
|
93
|
+
num_samples=X.shape[0],
|
|
94
|
+
num_features=n_numeric + n_categorical + n_binary,
|
|
95
|
+
num_bytes_per_cell=4,
|
|
96
|
+
hyperparameters=hyperparameters,
|
|
97
|
+
problem_type=self.problem_type,
|
|
98
|
+
num_classes=self.num_classes,
|
|
99
|
+
**kwargs,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# TODO: Replace with memory estimation logic based on no. of features instead of dataframe generation
|
|
103
|
+
shape = X.shape[0]
|
|
104
|
+
df_lst = []
|
|
105
|
+
if n_numeric > 0:
|
|
106
|
+
X_estimate = np.random.random(size=[shape, n_numeric]).astype(np.float32)
|
|
107
|
+
X_estimate_numeric = pd.DataFrame(X_estimate)
|
|
108
|
+
df_lst.append(X_estimate_numeric)
|
|
109
|
+
if n_categorical > 0:
|
|
110
|
+
cardinality = int(X.select_dtypes(exclude=[np.number]).nunique().mean())
|
|
111
|
+
X_estimate = np.random.randint(0, cardinality, [shape, n_categorical]).astype('str')
|
|
112
|
+
X_estimate_cat = pd.DataFrame(X_estimate)
|
|
113
|
+
df_lst.append(X_estimate_cat)
|
|
114
|
+
if n_binary > 0:
|
|
115
|
+
X_estimate = np.random.randint(0, 2, [shape, n_binary]).astype(np.int8)
|
|
116
|
+
X_estimate_binary = pd.DataFrame(X_estimate)
|
|
117
|
+
df_lst.append(X_estimate_binary)
|
|
118
|
+
X = pd.concat(df_lst, ignore_index=True, axis=1)
|
|
119
|
+
|
|
120
|
+
return self.estimate_memory_usage_static(
|
|
121
|
+
X=X,
|
|
122
|
+
problem_type=self.problem_type,
|
|
123
|
+
num_classes=self.num_classes,
|
|
124
|
+
hyperparameters=hyperparameters,
|
|
125
|
+
**kwargs,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def _init_preprocessor(
|
|
129
|
+
self,
|
|
130
|
+
preprocessor_cls: Type[AbstractFeatureGenerator] | str,
|
|
131
|
+
init_params: dict | None,
|
|
132
|
+
) -> AbstractFeatureGenerator:
|
|
133
|
+
if isinstance(preprocessor_cls, str):
|
|
134
|
+
preprocessor_cls = _feature_generator_class_map[preprocessor_cls]
|
|
135
|
+
if init_params is None:
|
|
136
|
+
init_params = {}
|
|
137
|
+
_init_params = dict(
|
|
138
|
+
verbosity=0,
|
|
139
|
+
random_state=self.random_seed, # FIXME: Not a generic param
|
|
140
|
+
target_type=self.problem_type, # FIXME: Not a generic param
|
|
141
|
+
)
|
|
142
|
+
_init_params.update(**init_params)
|
|
143
|
+
return preprocessor_cls(
|
|
144
|
+
**_init_params,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
def _recursive_init_preprocessors(self, prep_param: tuple | list[list | tuple]):
|
|
148
|
+
if isinstance(prep_param, list):
|
|
149
|
+
if len(prep_param) == 0:
|
|
150
|
+
param_type = "list"
|
|
151
|
+
elif len(prep_param) == 2:
|
|
152
|
+
if isinstance(prep_param[0], (str, AbstractFeatureGenerator)):
|
|
153
|
+
param_type = "generator"
|
|
154
|
+
else:
|
|
155
|
+
param_type = "list"
|
|
156
|
+
else:
|
|
157
|
+
param_type = "list"
|
|
158
|
+
elif isinstance(prep_param, tuple):
|
|
159
|
+
param_type = "generator"
|
|
160
|
+
else:
|
|
161
|
+
raise ValueError(f"Invalid value for prep_param: {prep_param}")
|
|
162
|
+
|
|
163
|
+
if param_type == "list":
|
|
164
|
+
out = []
|
|
165
|
+
for i, p in enumerate(prep_param):
|
|
166
|
+
out.append(self._recursive_init_preprocessors(p))
|
|
167
|
+
return out
|
|
168
|
+
elif param_type == "generator":
|
|
169
|
+
assert len(prep_param) == 2
|
|
170
|
+
preprocessor_cls = prep_param[0]
|
|
171
|
+
init_params = prep_param[1]
|
|
172
|
+
return self._init_preprocessor(preprocessor_cls=preprocessor_cls, init_params=init_params)
|
|
173
|
+
else:
|
|
174
|
+
raise ValueError(f"Invalid value for prep_param: {prep_param}")
|
|
175
|
+
|
|
176
|
+
def get_preprocessors(self) -> list[AbstractFeatureGenerator]:
|
|
177
|
+
ag_params = self._get_ag_params()
|
|
178
|
+
prep_params = ag_params.get("prep_params", None)
|
|
179
|
+
passthrough_types = ag_params.get("prep_params.passthrough_types", None)
|
|
180
|
+
if prep_params is None:
|
|
181
|
+
return []
|
|
182
|
+
if not prep_params:
|
|
183
|
+
return []
|
|
184
|
+
|
|
185
|
+
preprocessors = self._recursive_init_preprocessors(prep_param=prep_params)
|
|
186
|
+
if len(preprocessors) == 0:
|
|
187
|
+
return []
|
|
188
|
+
if len(preprocessors) == 1 and isinstance(preprocessors[0], AbstractFeatureGenerator):
|
|
189
|
+
return preprocessors
|
|
190
|
+
else:
|
|
191
|
+
preprocessors = [BulkFeatureGenerator(
|
|
192
|
+
generators=preprocessors,
|
|
193
|
+
# TODO: "false_recursive" technically can slow down inference, but need to optimize `True` first
|
|
194
|
+
# Refer to `Bioresponse` dataset where setting to `True` -> 200s fit time vs `false_recursive` -> 1s fit time
|
|
195
|
+
remove_unused_features="false_recursive",
|
|
196
|
+
post_drop_duplicates=True,
|
|
197
|
+
passthrough=True,
|
|
198
|
+
passthrough_types=passthrough_types,
|
|
199
|
+
verbosity=0,
|
|
200
|
+
)]
|
|
201
|
+
return preprocessors
|
|
202
|
+
|
|
203
|
+
def _preprocess(self, X: pd.DataFrame, y=None, is_train: bool = False, **kwargs):
|
|
204
|
+
if is_train:
|
|
205
|
+
self.preprocessors = self.get_preprocessors()
|
|
206
|
+
if self.preprocessors:
|
|
207
|
+
assert y is not None, f"y must be specified to fit preprocessors... Likely the inheriting class isn't passing `y` in its `preprocess` call."
|
|
208
|
+
# FIXME: add `post_drop_useless`, example: anneal has many useless features
|
|
209
|
+
feature_metadata_in = self._feature_metadata
|
|
210
|
+
for prep in self.preprocessors:
|
|
211
|
+
X = prep.fit_transform(X, y, feature_metadata_in=feature_metadata_in)
|
|
212
|
+
# FIXME: Nick: This is incorrect because it strips away special dtypes. Need to do this properly by fixing in the preprocessors
|
|
213
|
+
feature_metadata_in = prep.feature_metadata
|
|
214
|
+
self._feature_metadata = feature_metadata_in
|
|
215
|
+
self._features_internal = self._feature_metadata.get_features()
|
|
216
|
+
else:
|
|
217
|
+
for prep in self.preprocessors:
|
|
218
|
+
X = prep.transform(X)
|
|
219
|
+
|
|
220
|
+
return super()._preprocess(X, y=y, is_train=is_train, **kwargs)
|
|
@@ -495,7 +495,7 @@ class TabularNeuralNetTorchModel(AbstractNeuralNetworkModel):
|
|
|
495
495
|
|
|
496
496
|
if time_limit is not None:
|
|
497
497
|
time_elapsed = time.time() - start_fit_time
|
|
498
|
-
time_epoch_average = time_elapsed / (epoch
|
|
498
|
+
time_epoch_average = time_elapsed / max(epoch, 1) # avoid divide by 0
|
|
499
499
|
time_left = time_limit - time_elapsed
|
|
500
500
|
if time_left < time_epoch_average:
|
|
501
501
|
logger.log(20, f"\tRan out of time, stopping training early. (Stopping on epoch {epoch})")
|
|
@@ -37,10 +37,18 @@ def create_preprocessor(
|
|
|
37
37
|
steps=[("ordinal", OrdinalMergeRaresHandleUnknownEncoder(max_levels=max_category_levels))]
|
|
38
38
|
) # returns 0-n when max_category_levels = n-1. category n is reserved for unknown test-time categories.
|
|
39
39
|
transformers.append(("ordinal", ordinal_transformer, embed_features))
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
try:
|
|
41
|
+
out = ColumnTransformer(
|
|
42
|
+
transformers=transformers, remainder="passthrough", force_int_remainder_cols=False,
|
|
43
|
+
) # numeric features are processed in the same order as in numeric_features vector, so feature-names remain the same.
|
|
44
|
+
except:
|
|
45
|
+
# TODO: Avoid try/except once scikit-learn 1.5 is minimum
|
|
46
|
+
# Needed for scikit-learn 1.4 and 1.9+, force_int_remainder_cols is deprecated in 1.7 and introduced in 1.5
|
|
47
|
+
# ref: https://github.com/autogluon/autogluon/issues/5289
|
|
48
|
+
out = ColumnTransformer(
|
|
49
|
+
transformers=transformers, remainder="passthrough",
|
|
50
|
+
) # numeric features are processed in the same order as in numeric_features vector, so feature-names remain the same.
|
|
51
|
+
return out
|
|
44
52
|
|
|
45
53
|
def convert_df_dtype_to_str(df):
|
|
46
54
|
return df.astype(str)
|
|
@@ -122,6 +122,8 @@ class XGBoostModel(AbstractModel):
|
|
|
122
122
|
if eval_metric is not None:
|
|
123
123
|
params["eval_metric"] = eval_metric
|
|
124
124
|
eval_metric_name = eval_metric.__name__ if not isinstance(eval_metric, str) else eval_metric
|
|
125
|
+
else:
|
|
126
|
+
eval_metric_name = params["eval_metric"].__name__ if not isinstance(params["eval_metric"], str) else params["eval_metric"]
|
|
125
127
|
|
|
126
128
|
if X_val is None:
|
|
127
129
|
early_stopping_rounds = None
|
|
@@ -19,6 +19,8 @@ from packaging import version
|
|
|
19
19
|
from autogluon.common import FeatureMetadata, TabularDataset
|
|
20
20
|
from autogluon.common.loaders import load_json
|
|
21
21
|
from autogluon.common.savers import save_json
|
|
22
|
+
from autogluon.common.utils.cv_splitter import CVSplitter
|
|
23
|
+
from autogluon.common.utils.decorators import apply_presets
|
|
22
24
|
from autogluon.common.utils.file_utils import get_directory_size, get_directory_size_per_file
|
|
23
25
|
from autogluon.common.utils.resource_utils import ResourceManager, get_resource_manager
|
|
24
26
|
from autogluon.common.utils.hyperparameter_utils import get_hyperparameter_str_deprecation_msg, is_advanced_hyperparameter_format
|
|
@@ -46,10 +48,9 @@ from autogluon.core.pseudolabeling.pseudolabeling import filter_ensemble_pseudo,
|
|
|
46
48
|
from autogluon.core.scheduler.scheduler_factory import scheduler_factory
|
|
47
49
|
from autogluon.core.stacked_overfitting.utils import check_stacked_overfitting_from_leaderboard
|
|
48
50
|
from autogluon.core.utils import get_pred_from_proba_df, plot_performance_vs_trials, plot_summary_of_models, plot_tabular_models
|
|
49
|
-
from autogluon.core.utils.decorators import apply_presets
|
|
50
51
|
from autogluon.core.utils.loaders import load_pkl, load_str
|
|
51
52
|
from autogluon.core.utils.savers import save_pkl, save_str
|
|
52
|
-
from autogluon.core.utils.utils import
|
|
53
|
+
from autogluon.core.utils.utils import generate_train_test_split_combined
|
|
53
54
|
|
|
54
55
|
from ..configs.feature_generator_presets import get_default_feature_generator
|
|
55
56
|
from ..configs.hyperparameter_configs import get_hyperparameter_config
|
|
@@ -422,7 +423,7 @@ class TabularPredictor:
|
|
|
422
423
|
num_gpus: int | str = "auto",
|
|
423
424
|
fit_strategy: Literal["sequential", "parallel"] = "sequential",
|
|
424
425
|
memory_limit: float | str = "auto",
|
|
425
|
-
callbacks: list[AbstractCallback] = None,
|
|
426
|
+
callbacks: list[AbstractCallback | list | tuple] = None,
|
|
426
427
|
**kwargs,
|
|
427
428
|
) -> "TabularPredictor":
|
|
428
429
|
"""
|
|
@@ -463,16 +464,23 @@ class TabularPredictor:
|
|
|
463
464
|
It is recommended to only use one `quality` based preset in a given call to `fit()` as they alter many of the same arguments and are not compatible with each-other.
|
|
464
465
|
|
|
465
466
|
In-depth Preset Info:
|
|
466
|
-
extreme_quality={
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
467
|
+
extreme_quality={...}
|
|
468
|
+
New in v1.5: The state-of-the-art for tabular machine learning.
|
|
469
|
+
Requires `pip install autogluon.tabular[tabarena]` to install TabPFN, TabICL, and TabDPT.
|
|
470
|
+
Significantly more accurate than `best_quality` on datasets <= 100000 samples. Requires a GPU.
|
|
471
|
+
Will use recent tabular foundation models TabPFNv2, TabICL, TabDPT, and Mitra to maximize performance.
|
|
470
472
|
Recommended for applications that benefit from the best possible model accuracy.
|
|
471
473
|
|
|
474
|
+
best_quality_v150={...}
|
|
475
|
+
New in v1.5: Better quality than 'best_quality' and 5x+ faster to train. Give it a try!
|
|
476
|
+
|
|
472
477
|
best_quality={'auto_stack': True, 'dynamic_stacking': 'auto', 'hyperparameters': 'zeroshot'}
|
|
473
478
|
Best predictive accuracy with little consideration to inference time or disk usage. Achieve even better results by specifying a large time_limit value.
|
|
474
479
|
Recommended for applications that benefit from the best possible model accuracy.
|
|
475
480
|
|
|
481
|
+
high_quality_v150={...}
|
|
482
|
+
New in v1.5: Better quality than 'high_quality' and 5x+ faster to train. Give it a try!
|
|
483
|
+
|
|
476
484
|
high_quality={'auto_stack': True, 'dynamic_stacking': 'auto', 'hyperparameters': 'zeroshot', 'refit_full': True, 'set_best_to_refit_full': True, 'save_bag_folds': False}
|
|
477
485
|
High predictive accuracy with fast inference. ~8x faster inference and ~8x lower disk usage than `best_quality`.
|
|
478
486
|
Recommended for applications that require reasonable inference speed and/or model size.
|
|
@@ -1106,11 +1114,13 @@ class TabularPredictor:
|
|
|
1106
1114
|
20,
|
|
1107
1115
|
"No presets specified! To achieve strong results with AutoGluon, it is recommended to use the available presets. Defaulting to `'medium'`...\n"
|
|
1108
1116
|
"\tRecommended Presets (For more details refer to https://auto.gluon.ai/stable/tutorials/tabular/tabular-essentials.html#presets):\n"
|
|
1109
|
-
"\tpresets='extreme'
|
|
1110
|
-
"\tpresets='best'
|
|
1111
|
-
"\tpresets='
|
|
1112
|
-
"\tpresets='
|
|
1113
|
-
"\tpresets='
|
|
1117
|
+
"\tpresets='extreme' : New in v1.5: The state-of-the-art for tabular data. Massively better than 'best' on datasets <100000 samples by using new Tabular Foundation Models (TFMs) meta-learned on https://tabarena.ai: TabPFNv2, TabICL, Mitra, TabDPT, and TabM. Requires a GPU and `pip install autogluon.tabular[tabarena]` to install TabPFN, TabICL, and TabDPT.\n"
|
|
1118
|
+
"\tpresets='best' : Maximize accuracy. Recommended for most users. Use in competitions and benchmarks.\n"
|
|
1119
|
+
"\tpresets='best_v150': New in v1.5: Better quality than 'best' and 5x+ faster to train. Give it a try!\n"
|
|
1120
|
+
"\tpresets='high' : Strong accuracy with fast inference speed.\n"
|
|
1121
|
+
"\tpresets='high_v150': New in v1.5: Better quality than 'high' and 5x+ faster to train. Give it a try!\n"
|
|
1122
|
+
"\tpresets='good' : Good accuracy with very fast inference speed.\n"
|
|
1123
|
+
"\tpresets='medium' : Fast training time, ideal for initial prototyping.",
|
|
1114
1124
|
)
|
|
1115
1125
|
|
|
1116
1126
|
kwargs_orig = kwargs.copy()
|
|
@@ -1164,7 +1174,7 @@ class TabularPredictor:
|
|
|
1164
1174
|
# TODO: Temporary for v1.4. Make this more extensible for v1.5 by letting users make their own dynamic hyperparameters.
|
|
1165
1175
|
dynamic_hyperparameters = kwargs["_experimental_dynamic_hyperparameters"]
|
|
1166
1176
|
if dynamic_hyperparameters:
|
|
1167
|
-
logger.log(20, f"`
|
|
1177
|
+
logger.log(20, f"`extreme_v140` preset uses a dynamic portfolio based on dataset size...")
|
|
1168
1178
|
assert hyperparameters is None, f"hyperparameters must be unspecified when `_experimental_dynamic_hyperparameters=True`."
|
|
1169
1179
|
n_samples = len(train_data)
|
|
1170
1180
|
if n_samples > 30000:
|
|
@@ -1593,6 +1603,25 @@ class TabularPredictor:
|
|
|
1593
1603
|
memory_safe_fits = ds_fit_kwargs.get("memory_safe_fits", True)
|
|
1594
1604
|
enable_ray_logging = ds_fit_kwargs.get("enable_ray_logging", True)
|
|
1595
1605
|
normal_fit = False
|
|
1606
|
+
total_resources = ag_fit_kwargs["core_kwargs"]["total_resources"]
|
|
1607
|
+
|
|
1608
|
+
if memory_safe_fits == "auto":
|
|
1609
|
+
num_gpus = total_resources.get("num_gpus", "auto")
|
|
1610
|
+
if num_gpus == "auto":
|
|
1611
|
+
num_gpus = ResourceManager.get_gpu_count_torch()
|
|
1612
|
+
if num_gpus > 0:
|
|
1613
|
+
logger.log(
|
|
1614
|
+
30,
|
|
1615
|
+
f"DyStack: Disabling memory safe fit mode in DyStack "
|
|
1616
|
+
f"because GPUs were detected and num_gpus='auto' (GPUs cannot be used in memory safe fit mode). "
|
|
1617
|
+
f"If you want to use memory safe fit mode, manually set `num_gpus=0`."
|
|
1618
|
+
)
|
|
1619
|
+
if num_gpus > 0:
|
|
1620
|
+
memory_safe_fits = False
|
|
1621
|
+
else:
|
|
1622
|
+
memory_safe_fits = True
|
|
1623
|
+
|
|
1624
|
+
|
|
1596
1625
|
if memory_safe_fits:
|
|
1597
1626
|
try:
|
|
1598
1627
|
_ds_ray = try_import_ray()
|
|
@@ -1633,8 +1662,6 @@ class TabularPredictor:
|
|
|
1633
1662
|
# Handle resources
|
|
1634
1663
|
# FIXME: what about distributed?
|
|
1635
1664
|
|
|
1636
|
-
total_resources = ag_fit_kwargs["core_kwargs"]["total_resources"]
|
|
1637
|
-
|
|
1638
1665
|
num_cpus = total_resources.get("num_cpus", "auto")
|
|
1639
1666
|
|
|
1640
1667
|
if num_cpus == "auto":
|
|
@@ -5244,11 +5271,11 @@ class TabularPredictor:
|
|
|
5244
5271
|
holdout_frac=1 / 9,
|
|
5245
5272
|
n_folds=2,
|
|
5246
5273
|
n_repeats=1,
|
|
5247
|
-
memory_safe_fits=
|
|
5274
|
+
memory_safe_fits="auto",
|
|
5248
5275
|
clean_up_fits=True,
|
|
5249
5276
|
holdout_data=None,
|
|
5250
5277
|
enable_ray_logging=True,
|
|
5251
|
-
enable_callbacks=
|
|
5278
|
+
enable_callbacks=True,
|
|
5252
5279
|
)
|
|
5253
5280
|
allowed_kes = set(ds_args.keys())
|
|
5254
5281
|
|
|
@@ -5263,9 +5290,11 @@ class TabularPredictor:
|
|
|
5263
5290
|
(not isinstance(ds_args["validation_procedure"], str)) or (ds_args["validation_procedure"] not in ["holdout", "cv"])
|
|
5264
5291
|
):
|
|
5265
5292
|
raise ValueError("`validation_procedure` in `ds_args` must be str in {'holdout','cv'}. " + f"Got: {ds_args['validation_procedure']}")
|
|
5266
|
-
for arg_name in ["
|
|
5293
|
+
for arg_name in ["clean_up_fits", "enable_ray_logging"]:
|
|
5267
5294
|
if (arg_name in ds_args) and (not isinstance(ds_args[arg_name], bool)):
|
|
5268
5295
|
raise ValueError(f"`{arg_name}` in `ds_args` must be bool. Got: {type(ds_args[arg_name])}")
|
|
5296
|
+
if "memory_safe_fits" in ds_args and not isinstance(ds_args["memory_safe_fits"], (bool, str)):
|
|
5297
|
+
raise ValueError(f"`memory_safe_fits` in `ds_args` must be bool or 'auto'. Got: {type(ds_args['memory_safe_fits'])}")
|
|
5269
5298
|
for arg_name in ["detection_time_frac", "holdout_frac"]:
|
|
5270
5299
|
if (arg_name in ds_args) and ((not isinstance(ds_args[arg_name], float)) or (ds_args[arg_name] >= 1) or (ds_args[arg_name] <= 0)):
|
|
5271
5300
|
raise ValueError(f"`{arg_name}` in `ds_args` must be float in (0,1). Got: {type(ds_args[arg_name])}, {ds_args[arg_name]}")
|
|
@@ -20,14 +20,17 @@ from ..models import (
|
|
|
20
20
|
LinearModel,
|
|
21
21
|
MultiModalPredictorModel,
|
|
22
22
|
NNFastAiTabularModel,
|
|
23
|
+
PrepLGBModel,
|
|
23
24
|
RealMLPModel,
|
|
24
25
|
RFModel,
|
|
25
26
|
RuleFitModel,
|
|
27
|
+
TabDPTModel,
|
|
26
28
|
TabICLModel,
|
|
27
29
|
TabMModel,
|
|
28
30
|
TabPFNMixModel,
|
|
29
31
|
MitraModel,
|
|
30
|
-
|
|
32
|
+
RealTabPFNv2Model,
|
|
33
|
+
RealTabPFNv25Model,
|
|
31
34
|
TabularNeuralNetTorchModel,
|
|
32
35
|
TextPredictorModel,
|
|
33
36
|
XGBoostModel,
|
|
@@ -47,14 +50,17 @@ REGISTERED_MODEL_CLS_LST = [
|
|
|
47
50
|
TabularNeuralNetTorchModel,
|
|
48
51
|
LinearModel,
|
|
49
52
|
NNFastAiTabularModel,
|
|
53
|
+
PrepLGBModel,
|
|
50
54
|
TextPredictorModel,
|
|
51
55
|
ImagePredictorModel,
|
|
52
56
|
MultiModalPredictorModel,
|
|
53
57
|
FTTransformerModel,
|
|
58
|
+
TabDPTModel,
|
|
54
59
|
TabICLModel,
|
|
55
60
|
TabMModel,
|
|
56
61
|
TabPFNMixModel,
|
|
57
|
-
|
|
62
|
+
RealTabPFNv2Model,
|
|
63
|
+
RealTabPFNv25Model,
|
|
58
64
|
MitraModel,
|
|
59
65
|
FastTextModel,
|
|
60
66
|
GreedyWeightedEnsembleModel,
|
|
@@ -4,6 +4,9 @@ import copy
|
|
|
4
4
|
import os
|
|
5
5
|
import pandas as pd
|
|
6
6
|
import shutil
|
|
7
|
+
import sys
|
|
8
|
+
import subprocess
|
|
9
|
+
import textwrap
|
|
7
10
|
import uuid
|
|
8
11
|
from typing import Any, Type
|
|
9
12
|
|
|
@@ -12,6 +15,7 @@ from autogluon.core.constants import BINARY, MULTICLASS, REGRESSION
|
|
|
12
15
|
from autogluon.core.metrics import METRICS
|
|
13
16
|
from autogluon.core.models import AbstractModel, BaggedEnsembleModel
|
|
14
17
|
from autogluon.core.stacked_overfitting.utils import check_stacked_overfitting_from_leaderboard
|
|
18
|
+
from autogluon.core.testing.global_context_snapshot import GlobalContextSnapshot
|
|
15
19
|
from autogluon.core.utils import download, generate_train_test_split_combined, infer_problem_type, unzip
|
|
16
20
|
|
|
17
21
|
from autogluon.tabular import TabularDataset, TabularPredictor
|
|
@@ -176,6 +180,7 @@ class FitHelper:
|
|
|
176
180
|
raise_on_model_failure: bool | None = None,
|
|
177
181
|
deepcopy_fit_args: bool = True,
|
|
178
182
|
verify_model_seed: bool = False,
|
|
183
|
+
verify_load_wo_cuda: bool = False,
|
|
179
184
|
) -> TabularPredictor:
|
|
180
185
|
if compiler_configs is None:
|
|
181
186
|
compiler_configs = {}
|
|
@@ -219,6 +224,8 @@ class FitHelper:
|
|
|
219
224
|
expected_model_count -= 1
|
|
220
225
|
fit_args["fit_weighted_ensemble"] = fit_weighted_ensemble
|
|
221
226
|
|
|
227
|
+
ctx_before = GlobalContextSnapshot.capture()
|
|
228
|
+
|
|
222
229
|
predictor: TabularPredictor = FitHelper.fit_dataset(
|
|
223
230
|
train_data=train_data,
|
|
224
231
|
init_args=init_args,
|
|
@@ -227,6 +234,10 @@ class FitHelper:
|
|
|
227
234
|
scikit_api=scikit_api,
|
|
228
235
|
min_cls_count_train=min_cls_count_train,
|
|
229
236
|
)
|
|
237
|
+
|
|
238
|
+
ctx_after = GlobalContextSnapshot.capture()
|
|
239
|
+
ctx_before.assert_unchanged(ctx_after)
|
|
240
|
+
|
|
230
241
|
if compile:
|
|
231
242
|
predictor.compile(models="all", compiler_configs=compiler_configs)
|
|
232
243
|
predictor.persist(models="all")
|
|
@@ -287,6 +298,28 @@ class FitHelper:
|
|
|
287
298
|
predictor_load = predictor.load(path=predictor.path)
|
|
288
299
|
predictor_load.predict(test_data)
|
|
289
300
|
|
|
301
|
+
# TODO: This is expensive, only do this sparingly.
|
|
302
|
+
if verify_load_wo_cuda:
|
|
303
|
+
import torch
|
|
304
|
+
if torch.cuda.is_available():
|
|
305
|
+
# Checks if the model is able to predict w/o CUDA.
|
|
306
|
+
# This verifies that a model artifact works on a CPU machine.
|
|
307
|
+
predictor_path = predictor.path
|
|
308
|
+
|
|
309
|
+
code = textwrap.dedent(f"""
|
|
310
|
+
import os
|
|
311
|
+
os.environ["CUDA_VISIBLE_DEVICES"] = ""
|
|
312
|
+
from autogluon.tabular import TabularPredictor
|
|
313
|
+
|
|
314
|
+
import torch
|
|
315
|
+
assert torch.cuda.is_available() is False
|
|
316
|
+
predictor = TabularPredictor.load(r"{predictor_path}")
|
|
317
|
+
X, y = predictor.load_data_internal()
|
|
318
|
+
predictor.persist("all")
|
|
319
|
+
predictor.predict_multi(X, transform_features=False)
|
|
320
|
+
""")
|
|
321
|
+
subprocess.run([sys.executable, "-c", code], check=True)
|
|
322
|
+
|
|
290
323
|
assert os.path.realpath(save_path) == os.path.realpath(predictor.path)
|
|
291
324
|
if delete_directory:
|
|
292
325
|
shutil.rmtree(save_path, ignore_errors=True) # Delete AutoGluon output directory to ensure runs' information has been removed.
|
|
@@ -27,7 +27,7 @@ from autogluon.core.calibrate.conformity_score import compute_conformity_score
|
|
|
27
27
|
from autogluon.core.calibrate.temperature_scaling import apply_temperature_scaling, tune_temperature_scaling
|
|
28
28
|
from autogluon.core.callbacks import AbstractCallback
|
|
29
29
|
from autogluon.core.constants import BINARY, MULTICLASS, QUANTILE, REFIT_FULL_NAME, REGRESSION, SOFTCLASS
|
|
30
|
-
from autogluon.core.data.label_cleaner import LabelCleanerMulticlassToBinary
|
|
30
|
+
from autogluon.core.data.label_cleaner import LabelCleanerMulticlassToBinary, LabelCleaner
|
|
31
31
|
from autogluon.core.metrics import Scorer, compute_metric, get_metric
|
|
32
32
|
from autogluon.core.models import (
|
|
33
33
|
AbstractModel,
|
|
@@ -530,7 +530,7 @@ class AbstractTabularTrainer(AbstractTrainer[AbstractModel]):
|
|
|
530
530
|
self.save()
|
|
531
531
|
return model_names_fit
|
|
532
532
|
|
|
533
|
-
def _fit_setup(self, time_limit: float | None = None, callbacks: list[AbstractCallback] | None = None):
|
|
533
|
+
def _fit_setup(self, time_limit: float | None = None, callbacks: list[AbstractCallback | list | tuple] | None = None):
|
|
534
534
|
"""
|
|
535
535
|
Prepare the trainer state at the start of / prior to a fit call.
|
|
536
536
|
Should be paired with a `self._fit_cleanup()` at the conclusion of the fit call.
|
|
@@ -539,15 +539,45 @@ class AbstractTabularTrainer(AbstractTrainer[AbstractModel]):
|
|
|
539
539
|
self._time_train_start_last = self._time_train_start
|
|
540
540
|
self._time_limit = time_limit
|
|
541
541
|
self.reset_callbacks()
|
|
542
|
+
callbacks_new = []
|
|
542
543
|
if callbacks is not None:
|
|
543
544
|
assert isinstance(callbacks, list), f"`callbacks` must be a list. Found invalid type: `{type(callbacks)}`."
|
|
544
545
|
for callback in callbacks:
|
|
545
|
-
|
|
546
|
-
callback,
|
|
547
|
-
|
|
546
|
+
if isinstance(callback, (list, tuple)):
|
|
547
|
+
assert len(callback) == 2, f"Callback must either be an initialized object or a tuple/list of length 2, found: {callback}"
|
|
548
|
+
callback_cls = callback[0]
|
|
549
|
+
if isinstance(callback_cls, str):
|
|
550
|
+
from autogluon.core.callbacks._early_stopping_count_callback import EarlyStoppingCountCallback
|
|
551
|
+
from autogluon.core.callbacks._early_stopping_callback import EarlyStoppingCallback
|
|
552
|
+
from autogluon.core.callbacks._early_stopping_ensemble_callback import EarlyStoppingEnsembleCallback
|
|
553
|
+
|
|
554
|
+
_callback_cls_lst = [
|
|
555
|
+
EarlyStoppingCallback,
|
|
556
|
+
EarlyStoppingCountCallback,
|
|
557
|
+
EarlyStoppingEnsembleCallback,
|
|
558
|
+
]
|
|
559
|
+
|
|
560
|
+
_callback_cls_name_map = {
|
|
561
|
+
c.__name__: c for c in _callback_cls_lst
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
assert callback_cls in _callback_cls_name_map.keys(), (
|
|
565
|
+
f"Unknown callback class: {callback_cls}. "
|
|
566
|
+
f"Valid classes: {list(_callback_cls_name_map.keys())}"
|
|
567
|
+
)
|
|
568
|
+
callback_cls = _callback_cls_name_map[callback_cls]
|
|
569
|
+
|
|
570
|
+
callback_kwargs = callback[1]
|
|
571
|
+
assert isinstance(callback_kwargs, dict), f"Callback kwargs must be a dictionary, found: {callback_kwargs}"
|
|
572
|
+
callback = callback_cls(**callback_kwargs)
|
|
573
|
+
else:
|
|
574
|
+
assert isinstance(
|
|
575
|
+
callback, AbstractCallback
|
|
576
|
+
), f"Elements in `callbacks` must be of type AbstractCallback. Found invalid type: `{type(callback)}`."
|
|
577
|
+
callbacks_new.append(callback)
|
|
548
578
|
else:
|
|
549
|
-
|
|
550
|
-
self.callbacks =
|
|
579
|
+
callbacks_new = []
|
|
580
|
+
self.callbacks = callbacks_new
|
|
551
581
|
|
|
552
582
|
def _fit_cleanup(self):
|
|
553
583
|
"""
|
|
@@ -2493,6 +2523,7 @@ class AbstractTabularTrainer(AbstractTrainer[AbstractModel]):
|
|
|
2493
2523
|
errors_ignore: list | None = None,
|
|
2494
2524
|
errors_raise: list | None = None,
|
|
2495
2525
|
is_ray_worker: bool = False,
|
|
2526
|
+
label_cleaner: None | LabelCleaner = None,
|
|
2496
2527
|
**kwargs,
|
|
2497
2528
|
) -> list[str]:
|
|
2498
2529
|
"""
|
|
@@ -2527,7 +2558,8 @@ class AbstractTabularTrainer(AbstractTrainer[AbstractModel]):
|
|
|
2527
2558
|
return []
|
|
2528
2559
|
|
|
2529
2560
|
model_fit_kwargs = self._get_model_fit_kwargs(
|
|
2530
|
-
X=X, X_val=X_val, time_limit=time_limit, k_fold=k_fold, fit_kwargs=fit_kwargs,
|
|
2561
|
+
X=X, X_val=X_val, time_limit=time_limit, k_fold=k_fold, fit_kwargs=fit_kwargs,
|
|
2562
|
+
ens_sample_weight=kwargs.get("ens_sample_weight", None), label_cleaner=label_cleaner,
|
|
2531
2563
|
)
|
|
2532
2564
|
exception = None
|
|
2533
2565
|
if hyperparameter_tune_kwargs:
|
|
@@ -4294,7 +4326,8 @@ class AbstractTabularTrainer(AbstractTrainer[AbstractModel]):
|
|
|
4294
4326
|
return distilled_model_names
|
|
4295
4327
|
|
|
4296
4328
|
def _get_model_fit_kwargs(
|
|
4297
|
-
self, X: pd.DataFrame, X_val: pd.DataFrame, time_limit: float, k_fold: int,
|
|
4329
|
+
self, X: pd.DataFrame, X_val: pd.DataFrame, time_limit: float, k_fold: int,
|
|
4330
|
+
fit_kwargs: dict, ens_sample_weight: list | None = None, label_cleaner: None | LabelCleaner = None
|
|
4298
4331
|
) -> dict:
|
|
4299
4332
|
# Returns kwargs to be passed to AbstractModel's fit function
|
|
4300
4333
|
if fit_kwargs is None:
|
|
@@ -4316,6 +4349,9 @@ class AbstractTabularTrainer(AbstractTrainer[AbstractModel]):
|
|
|
4316
4349
|
if k_fold == self.k_fold: # don't do this on refit full
|
|
4317
4350
|
model_fit_kwargs["groups"] = self._groups
|
|
4318
4351
|
|
|
4352
|
+
if label_cleaner is not None:
|
|
4353
|
+
model_fit_kwargs["label_cleaner"] = label_cleaner
|
|
4354
|
+
|
|
4319
4355
|
# FIXME: Sample weight `extract_column` is a hack, have to compute feature_metadata here because sample weight column could be in X upstream, extract sample weight column upstream instead.
|
|
4320
4356
|
if "feature_metadata" not in model_fit_kwargs:
|
|
4321
4357
|
raise AssertionError(f"Missing expected parameter 'feature_metadata'.")
|
|
@@ -59,6 +59,7 @@ class AutoTrainer(AbstractTabularTrainer):
|
|
|
59
59
|
use_bag_holdout=False,
|
|
60
60
|
groups=None,
|
|
61
61
|
callbacks: list[callable] = None,
|
|
62
|
+
label_cleaner=None,
|
|
62
63
|
**kwargs,
|
|
63
64
|
):
|
|
64
65
|
for key in kwargs:
|
|
@@ -112,6 +113,7 @@ class AutoTrainer(AbstractTabularTrainer):
|
|
|
112
113
|
extra_log_str = ""
|
|
113
114
|
display_all = (n_configs < 20) or (self.verbosity >= 3)
|
|
114
115
|
if not display_all:
|
|
116
|
+
# FIXME: This isn't correct
|
|
115
117
|
extra_log_str = (
|
|
116
118
|
f"Large model count detected ({n_configs} configs) ... " f"Only displaying the first 3 models of each family. To see all, set `verbosity=3`.\n"
|
|
117
119
|
)
|
|
@@ -132,6 +134,9 @@ class AutoTrainer(AbstractTabularTrainer):
|
|
|
132
134
|
log_str += "}"
|
|
133
135
|
logger.log(20, log_str)
|
|
134
136
|
|
|
137
|
+
if label_cleaner is not None:
|
|
138
|
+
core_kwargs["label_cleaner"] = label_cleaner
|
|
139
|
+
|
|
135
140
|
self._train_multi_and_ensemble(
|
|
136
141
|
X=X,
|
|
137
142
|
y=y,
|