FLAML 2.4.0__tar.gz → 2.4.1__tar.gz
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.
- {flaml-2.4.0 → flaml-2.4.1}/FLAML.egg-info/PKG-INFO +4 -4
- {flaml-2.4.0 → flaml-2.4.1}/FLAML.egg-info/requires.txt +2 -2
- {flaml-2.4.0 → flaml-2.4.1}/PKG-INFO +4 -4
- {flaml-2.4.0 → flaml-2.4.1}/README.md +1 -1
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/automl.py +371 -10
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/model.py +3 -1
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/nlp/huggingface/training_args.py +1 -1
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/nlp/huggingface/utils.py +1 -1
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/task/time_series_task.py +1 -1
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/time_series/sklearn.py +10 -1
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/time_series/ts_data.py +7 -2
- flaml-2.4.1/flaml/version.py +1 -0
- {flaml-2.4.0 → flaml-2.4.1}/setup.py +2 -2
- flaml-2.4.0/flaml/version.py +0 -1
- {flaml-2.4.0 → flaml-2.4.1}/FLAML.egg-info/SOURCES.txt +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/FLAML.egg-info/dependency_links.txt +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/FLAML.egg-info/top_level.txt +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/LICENSE +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/NOTICE.md +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/agentchat/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/agentchat/agent.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/agentchat/assistant_agent.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/agentchat/conversable_agent.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/agentchat/groupchat.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/agentchat/user_proxy_agent.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/code_utils.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/extensions/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/math_utils.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/oai/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/oai/completion.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/oai/openai_utils.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/autogen/retrieve_utils.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/contrib/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/contrib/histgb.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/data.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/logger.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/ml.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/nlp/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/nlp/huggingface/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/nlp/huggingface/data_collator.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/nlp/huggingface/trainer.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/nlp/utils.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/spark/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/spark/metrics.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/spark/utils.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/state.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/task/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/task/factory.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/task/generic_task.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/task/task.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/time_series/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/time_series/feature.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/time_series/tcn.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/time_series/tft.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/time_series/ts_model.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/automl/training_log.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/config.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/all/binary.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/all/multiclass.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/all/regression.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/estimator.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/extra_tree/binary.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/extra_tree/multiclass.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/extra_tree/regression.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/greedy.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/lgbm/binary.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/lgbm/multiclass.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/lgbm/regression.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/portfolio.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/regret.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/rf/binary.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/rf/multiclass.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/rf/regression.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/suggest.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/xgb_limitdepth/binary.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/xgb_limitdepth/multiclass.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/xgb_limitdepth/regression.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/xgboost/binary.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/xgboost/multiclass.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/default/xgboost/regression.json +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/fabric/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/fabric/mlflow.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/ml.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/onlineml/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/onlineml/autovw.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/onlineml/trial.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/onlineml/trial_runner.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/analysis.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/logger.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/result.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/sample.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/scheduler/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/scheduler/online_scheduler.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/scheduler/trial_scheduler.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/searcher/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/searcher/blendsearch.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/searcher/cfo_cat.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/searcher/flow2.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/searcher/online_searcher.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/searcher/search_thread.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/searcher/suggestion.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/searcher/variant_generator.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/space.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/spark/__init__.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/spark/utils.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/trial.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/trial_runner.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/tune.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/flaml/tune/utils.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/pyproject.toml +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/setup.cfg +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/test/test_autovw.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/test/test_conda_distribution.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/test/test_gpu.py +0 -0
- {flaml-2.4.0 → flaml-2.4.1}/test/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: FLAML
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Summary: A fast library for automated machine learning and tuning
|
|
5
5
|
Home-page: https://github.com/microsoft/FLAML
|
|
6
6
|
Author: Microsoft Corporation
|
|
@@ -87,13 +87,13 @@ Provides-Extra: vw
|
|
|
87
87
|
Requires-Dist: vowpalwabbit<9.0.0,>=8.10.0; extra == "vw"
|
|
88
88
|
Requires-Dist: scikit-learn; extra == "vw"
|
|
89
89
|
Provides-Extra: hf
|
|
90
|
-
Requires-Dist: transformers[torch]
|
|
90
|
+
Requires-Dist: transformers[torch]>=4.26; extra == "hf"
|
|
91
91
|
Requires-Dist: datasets; extra == "hf"
|
|
92
92
|
Requires-Dist: nltk<=3.8.1; extra == "hf"
|
|
93
93
|
Requires-Dist: rouge_score; extra == "hf"
|
|
94
94
|
Requires-Dist: seqeval; extra == "hf"
|
|
95
95
|
Provides-Extra: nlp
|
|
96
|
-
Requires-Dist: transformers[torch]
|
|
96
|
+
Requires-Dist: transformers[torch]>=4.26; extra == "nlp"
|
|
97
97
|
Requires-Dist: datasets; extra == "nlp"
|
|
98
98
|
Requires-Dist: nltk<=3.8.1; extra == "nlp"
|
|
99
99
|
Requires-Dist: rouge_score; extra == "nlp"
|
|
@@ -172,7 +172,7 @@ FLAML has a .NET implementation in [ML.NET](http://dot.net/ml), an open-source,
|
|
|
172
172
|
|
|
173
173
|
## Installation
|
|
174
174
|
|
|
175
|
-
FLAML requires **Python
|
|
175
|
+
The latest version of FLAML requires **Python >= 3.10 and \< 3.13**. While other Python versions may work for core components, full model support is not guaranteed. FLAML can be installed via `pip`:
|
|
176
176
|
|
|
177
177
|
```bash
|
|
178
178
|
pip install flaml
|
|
@@ -40,14 +40,14 @@ tensorboardX>=2.6
|
|
|
40
40
|
pytorch-forecasting>=0.10.4
|
|
41
41
|
|
|
42
42
|
[hf]
|
|
43
|
-
transformers[torch]
|
|
43
|
+
transformers[torch]>=4.26
|
|
44
44
|
datasets
|
|
45
45
|
nltk<=3.8.1
|
|
46
46
|
rouge_score
|
|
47
47
|
seqeval
|
|
48
48
|
|
|
49
49
|
[nlp]
|
|
50
|
-
transformers[torch]
|
|
50
|
+
transformers[torch]>=4.26
|
|
51
51
|
datasets
|
|
52
52
|
nltk<=3.8.1
|
|
53
53
|
rouge_score
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: FLAML
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Summary: A fast library for automated machine learning and tuning
|
|
5
5
|
Home-page: https://github.com/microsoft/FLAML
|
|
6
6
|
Author: Microsoft Corporation
|
|
@@ -87,13 +87,13 @@ Provides-Extra: vw
|
|
|
87
87
|
Requires-Dist: vowpalwabbit<9.0.0,>=8.10.0; extra == "vw"
|
|
88
88
|
Requires-Dist: scikit-learn; extra == "vw"
|
|
89
89
|
Provides-Extra: hf
|
|
90
|
-
Requires-Dist: transformers[torch]
|
|
90
|
+
Requires-Dist: transformers[torch]>=4.26; extra == "hf"
|
|
91
91
|
Requires-Dist: datasets; extra == "hf"
|
|
92
92
|
Requires-Dist: nltk<=3.8.1; extra == "hf"
|
|
93
93
|
Requires-Dist: rouge_score; extra == "hf"
|
|
94
94
|
Requires-Dist: seqeval; extra == "hf"
|
|
95
95
|
Provides-Extra: nlp
|
|
96
|
-
Requires-Dist: transformers[torch]
|
|
96
|
+
Requires-Dist: transformers[torch]>=4.26; extra == "nlp"
|
|
97
97
|
Requires-Dist: datasets; extra == "nlp"
|
|
98
98
|
Requires-Dist: nltk<=3.8.1; extra == "nlp"
|
|
99
99
|
Requires-Dist: rouge_score; extra == "nlp"
|
|
@@ -172,7 +172,7 @@ FLAML has a .NET implementation in [ML.NET](http://dot.net/ml), an open-source,
|
|
|
172
172
|
|
|
173
173
|
## Installation
|
|
174
174
|
|
|
175
|
-
FLAML requires **Python
|
|
175
|
+
The latest version of FLAML requires **Python >= 3.10 and \< 3.13**. While other Python versions may work for core components, full model support is not guaranteed. FLAML can be installed via `pip`:
|
|
176
176
|
|
|
177
177
|
```bash
|
|
178
178
|
pip install flaml
|
|
@@ -34,7 +34,7 @@ FLAML has a .NET implementation in [ML.NET](http://dot.net/ml), an open-source,
|
|
|
34
34
|
|
|
35
35
|
## Installation
|
|
36
36
|
|
|
37
|
-
FLAML requires **Python
|
|
37
|
+
The latest version of FLAML requires **Python >= 3.10 and \< 3.13**. While other Python versions may work for core components, full model support is not guaranteed. FLAML can be installed via `pip`:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
pip install flaml
|
|
@@ -413,13 +413,47 @@ class AutoML(BaseEstimator):
|
|
|
413
413
|
"""
|
|
414
414
|
|
|
415
415
|
state = self.__dict__.copy()
|
|
416
|
-
|
|
416
|
+
# Keep mlflow_integration for post-load visualization (e.g., infos), but
|
|
417
|
+
# strip non-picklable runtime-only members (thread futures, clients).
|
|
418
|
+
mlflow_integration = state.get("mlflow_integration", None)
|
|
419
|
+
if mlflow_integration is not None:
|
|
420
|
+
import copy
|
|
421
|
+
|
|
422
|
+
mi = copy.copy(mlflow_integration)
|
|
423
|
+
# These are runtime-only and often contain locks / threads.
|
|
424
|
+
if hasattr(mi, "futures"):
|
|
425
|
+
mi.futures = {}
|
|
426
|
+
if hasattr(mi, "futures_log_model"):
|
|
427
|
+
mi.futures_log_model = {}
|
|
428
|
+
if hasattr(mi, "train_func"):
|
|
429
|
+
mi.train_func = None
|
|
430
|
+
if hasattr(mi, "mlflow_client"):
|
|
431
|
+
mi.mlflow_client = None
|
|
432
|
+
state["mlflow_integration"] = mi
|
|
433
|
+
# MLflow signature objects may hold references to Spark/pandas-on-Spark
|
|
434
|
+
# inputs and can indirectly capture SparkContext, which is not picklable.
|
|
435
|
+
state.pop("estimator_signature", None)
|
|
436
|
+
state.pop("pipeline_signature", None)
|
|
417
437
|
return state
|
|
418
438
|
|
|
419
439
|
def __setstate__(self, state):
|
|
420
440
|
self.__dict__.update(state)
|
|
421
|
-
# Ensure
|
|
422
|
-
self
|
|
441
|
+
# Ensure mlflow_integration runtime members exist post-unpickle.
|
|
442
|
+
mi = getattr(self, "mlflow_integration", None)
|
|
443
|
+
if mi is not None:
|
|
444
|
+
if not hasattr(mi, "futures") or mi.futures is None:
|
|
445
|
+
mi.futures = {}
|
|
446
|
+
if not hasattr(mi, "futures_log_model") or mi.futures_log_model is None:
|
|
447
|
+
mi.futures_log_model = {}
|
|
448
|
+
if not hasattr(mi, "train_func"):
|
|
449
|
+
mi.train_func = None
|
|
450
|
+
if not hasattr(mi, "mlflow_client") or mi.mlflow_client is None:
|
|
451
|
+
try:
|
|
452
|
+
import mlflow as _mlflow
|
|
453
|
+
|
|
454
|
+
mi.mlflow_client = _mlflow.tracking.MlflowClient()
|
|
455
|
+
except Exception:
|
|
456
|
+
mi.mlflow_client = None
|
|
423
457
|
|
|
424
458
|
def get_params(self, deep: bool = False) -> dict:
|
|
425
459
|
return self._settings.copy()
|
|
@@ -1114,17 +1148,344 @@ class AutoML(BaseEstimator):
|
|
|
1114
1148
|
return self._state.data_size[0] if self._sample else None
|
|
1115
1149
|
|
|
1116
1150
|
def pickle(self, output_file_name):
|
|
1151
|
+
"""Serialize the AutoML instance to a pickle file.
|
|
1152
|
+
|
|
1153
|
+
Notes:
|
|
1154
|
+
When the trained estimator(s) are Spark-based, they may hold references
|
|
1155
|
+
to SparkContext/SparkSession via Spark ML objects. Such objects are not
|
|
1156
|
+
safely picklable and can cause pickling/broadcast errors.
|
|
1157
|
+
|
|
1158
|
+
This method externalizes Spark ML models into an adjacent artifact
|
|
1159
|
+
directory and stores only lightweight metadata in the pickle.
|
|
1160
|
+
"""
|
|
1161
|
+
|
|
1162
|
+
import os
|
|
1117
1163
|
import pickle
|
|
1164
|
+
import re
|
|
1165
|
+
|
|
1166
|
+
def _safe_name(name: str) -> str:
|
|
1167
|
+
return re.sub(r"[^A-Za-z0-9_.-]+", "_", name)
|
|
1168
|
+
|
|
1169
|
+
def _iter_trained_estimators():
|
|
1170
|
+
trained = getattr(self, "_trained_estimator", None)
|
|
1171
|
+
if trained is not None:
|
|
1172
|
+
yield "_trained_estimator", trained
|
|
1173
|
+
for est_name in getattr(self, "estimator_list", []) or []:
|
|
1174
|
+
ss = getattr(self, "_search_states", {}).get(est_name)
|
|
1175
|
+
te = ss and getattr(ss, "trained_estimator", None)
|
|
1176
|
+
if te is not None:
|
|
1177
|
+
yield f"_search_states.{est_name}.trained_estimator", te
|
|
1178
|
+
|
|
1179
|
+
def _scrub_pyspark_refs(root_obj):
|
|
1180
|
+
"""Best-effort removal of pyspark objects prior to pickling.
|
|
1181
|
+
|
|
1182
|
+
SparkContext/SparkSession and Spark DataFrame objects are not picklable.
|
|
1183
|
+
This function finds such objects within common containers and instance
|
|
1184
|
+
attributes and replaces them with None, returning a restore mapping.
|
|
1185
|
+
"""
|
|
1186
|
+
|
|
1187
|
+
try:
|
|
1188
|
+
import pyspark
|
|
1189
|
+
from pyspark.broadcast import Broadcast
|
|
1190
|
+
from pyspark.sql import DataFrame as SparkDataFrame
|
|
1191
|
+
from pyspark.sql import SparkSession
|
|
1118
1192
|
|
|
1193
|
+
try:
|
|
1194
|
+
import pyspark.pandas as ps
|
|
1195
|
+
|
|
1196
|
+
psDataFrameType = getattr(ps, "DataFrame", None)
|
|
1197
|
+
psSeriesType = getattr(ps, "Series", None)
|
|
1198
|
+
except Exception:
|
|
1199
|
+
psDataFrameType = None
|
|
1200
|
+
psSeriesType = None
|
|
1201
|
+
|
|
1202
|
+
bad_types = [
|
|
1203
|
+
pyspark.SparkContext,
|
|
1204
|
+
SparkSession,
|
|
1205
|
+
SparkDataFrame,
|
|
1206
|
+
Broadcast,
|
|
1207
|
+
]
|
|
1208
|
+
if psDataFrameType is not None:
|
|
1209
|
+
bad_types.append(psDataFrameType)
|
|
1210
|
+
if psSeriesType is not None:
|
|
1211
|
+
bad_types.append(psSeriesType)
|
|
1212
|
+
bad_types = tuple(t for t in bad_types if t is not None)
|
|
1213
|
+
except Exception:
|
|
1214
|
+
return {}
|
|
1215
|
+
|
|
1216
|
+
restore = {}
|
|
1217
|
+
visited = set()
|
|
1218
|
+
|
|
1219
|
+
def _mark(parent, key, value, path):
|
|
1220
|
+
restore[(id(parent), key)] = (parent, key, value)
|
|
1221
|
+
try:
|
|
1222
|
+
if isinstance(parent, dict):
|
|
1223
|
+
parent[key] = None
|
|
1224
|
+
elif isinstance(parent, list):
|
|
1225
|
+
parent[key] = None
|
|
1226
|
+
elif isinstance(parent, tuple):
|
|
1227
|
+
# tuples are immutable; we can't modify in-place
|
|
1228
|
+
pass
|
|
1229
|
+
else:
|
|
1230
|
+
setattr(parent, key, None)
|
|
1231
|
+
except Exception:
|
|
1232
|
+
# Best-effort.
|
|
1233
|
+
pass
|
|
1234
|
+
|
|
1235
|
+
def _walk(obj, depth, parent=None, key=None, path="self"):
|
|
1236
|
+
if obj is None:
|
|
1237
|
+
return
|
|
1238
|
+
oid = id(obj)
|
|
1239
|
+
if oid in visited:
|
|
1240
|
+
return
|
|
1241
|
+
visited.add(oid)
|
|
1242
|
+
|
|
1243
|
+
if isinstance(obj, bad_types):
|
|
1244
|
+
if parent is not None:
|
|
1245
|
+
_mark(parent, key, obj, path)
|
|
1246
|
+
return
|
|
1247
|
+
if depth <= 0:
|
|
1248
|
+
return
|
|
1249
|
+
|
|
1250
|
+
if isinstance(obj, dict):
|
|
1251
|
+
for k, v in list(obj.items()):
|
|
1252
|
+
_walk(v, depth - 1, parent=obj, key=k, path=f"{path}[{k!r}]")
|
|
1253
|
+
return
|
|
1254
|
+
if isinstance(obj, list):
|
|
1255
|
+
for i, v in enumerate(list(obj)):
|
|
1256
|
+
_walk(v, depth - 1, parent=obj, key=i, path=f"{path}[{i}]")
|
|
1257
|
+
return
|
|
1258
|
+
if isinstance(obj, tuple):
|
|
1259
|
+
# Can't scrub inside tuples safely; but still inspect for diagnostics.
|
|
1260
|
+
for i, v in enumerate(obj):
|
|
1261
|
+
_walk(v, depth - 1, parent=None, key=None, path=f"{path}[{i}]")
|
|
1262
|
+
return
|
|
1263
|
+
if isinstance(obj, set):
|
|
1264
|
+
for v in list(obj):
|
|
1265
|
+
_walk(v, depth - 1, parent=None, key=None, path=f"{path}{{...}}")
|
|
1266
|
+
return
|
|
1267
|
+
|
|
1268
|
+
d = getattr(obj, "__dict__", None)
|
|
1269
|
+
if isinstance(d, dict):
|
|
1270
|
+
for attr, v in list(d.items()):
|
|
1271
|
+
_walk(v, depth - 1, parent=obj, key=attr, path=f"{path}.{attr}")
|
|
1272
|
+
|
|
1273
|
+
_walk(root_obj, depth=6)
|
|
1274
|
+
return restore
|
|
1275
|
+
|
|
1276
|
+
# Temporarily remove non-picklable pieces (e.g., SparkContext-backed objects)
|
|
1277
|
+
# and externalize spark models.
|
|
1119
1278
|
estimator_to_training_function = {}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1279
|
+
spark_restore = []
|
|
1280
|
+
artifact_dir = None
|
|
1281
|
+
state_restore = {}
|
|
1282
|
+
automl_restore = {}
|
|
1283
|
+
scrub_restore = {}
|
|
1284
|
+
|
|
1285
|
+
try:
|
|
1286
|
+
# Signatures are only used for MLflow logging; they are not required
|
|
1287
|
+
# for inference and can capture SparkContext via pyspark objects.
|
|
1288
|
+
for attr in ("estimator_signature", "pipeline_signature"):
|
|
1289
|
+
if hasattr(self, attr):
|
|
1290
|
+
automl_restore[attr] = getattr(self, attr)
|
|
1291
|
+
setattr(self, attr, None)
|
|
1292
|
+
|
|
1293
|
+
for estimator in self.estimator_list:
|
|
1294
|
+
search_state = self._search_states[estimator]
|
|
1295
|
+
if hasattr(search_state, "training_function"):
|
|
1296
|
+
estimator_to_training_function[estimator] = search_state.training_function
|
|
1297
|
+
del search_state.training_function
|
|
1298
|
+
|
|
1299
|
+
# AutoMLState may keep Spark / pandas-on-Spark dataframes which are not picklable.
|
|
1300
|
+
# They are not required for inference, so strip them for serialization.
|
|
1301
|
+
state = getattr(self, "_state", None)
|
|
1302
|
+
if state is not None:
|
|
1303
|
+
for attr in (
|
|
1304
|
+
"X_train",
|
|
1305
|
+
"y_train",
|
|
1306
|
+
"X_train_all",
|
|
1307
|
+
"y_train_all",
|
|
1308
|
+
"X_val",
|
|
1309
|
+
"y_val",
|
|
1310
|
+
"weight_val",
|
|
1311
|
+
"groups_val",
|
|
1312
|
+
"sample_weight_all",
|
|
1313
|
+
"groups",
|
|
1314
|
+
"groups_all",
|
|
1315
|
+
"kf",
|
|
1316
|
+
):
|
|
1317
|
+
if hasattr(state, attr):
|
|
1318
|
+
state_restore[attr] = getattr(state, attr)
|
|
1319
|
+
setattr(state, attr, None)
|
|
1320
|
+
|
|
1321
|
+
for key, est in _iter_trained_estimators():
|
|
1322
|
+
if getattr(est, "estimator_baseclass", None) != "spark":
|
|
1323
|
+
continue
|
|
1324
|
+
|
|
1325
|
+
# Drop training data reference (Spark DataFrame / pandas-on-Spark).
|
|
1326
|
+
old_df_train = getattr(est, "df_train", None)
|
|
1327
|
+
old_model = getattr(est, "_model", None)
|
|
1328
|
+
|
|
1329
|
+
model_meta = None
|
|
1330
|
+
if old_model is not None:
|
|
1331
|
+
if artifact_dir is None:
|
|
1332
|
+
artifact_dir = output_file_name + ".flaml_artifacts"
|
|
1333
|
+
os.makedirs(artifact_dir, exist_ok=True)
|
|
1334
|
+
# store relative dirname so the pickle+folder can be moved together
|
|
1335
|
+
self._flaml_pickle_artifacts_dirname = os.path.basename(artifact_dir)
|
|
1336
|
+
|
|
1337
|
+
model_dir = os.path.join(artifact_dir, _safe_name(key))
|
|
1338
|
+
# Spark ML models are saved as directories.
|
|
1339
|
+
try:
|
|
1340
|
+
writer = old_model.write()
|
|
1341
|
+
writer.overwrite().save(model_dir)
|
|
1342
|
+
except Exception as e:
|
|
1343
|
+
raise RuntimeError(
|
|
1344
|
+
"Failed to externalize Spark model for pickling. "
|
|
1345
|
+
"Please ensure the Spark ML model supports write().overwrite().save(path)."
|
|
1346
|
+
) from e
|
|
1347
|
+
|
|
1348
|
+
model_meta = {
|
|
1349
|
+
"path": os.path.relpath(model_dir, os.path.dirname(output_file_name) or "."),
|
|
1350
|
+
"class": old_model.__class__.__module__ + "." + old_model.__class__.__name__,
|
|
1351
|
+
}
|
|
1352
|
+
# Replace in-memory Spark model with metadata only.
|
|
1353
|
+
est._model = None
|
|
1354
|
+
est._flaml_spark_model_meta = model_meta
|
|
1355
|
+
|
|
1356
|
+
est.df_train = None
|
|
1357
|
+
spark_restore.append((est, old_model, old_df_train, model_meta))
|
|
1358
|
+
|
|
1359
|
+
with open(output_file_name, "wb") as f:
|
|
1360
|
+
try:
|
|
1361
|
+
pickle.dump(self, f, pickle.HIGHEST_PROTOCOL)
|
|
1362
|
+
except Exception:
|
|
1363
|
+
# Some pyspark objects can still be captured indirectly.
|
|
1364
|
+
scrub_restore = _scrub_pyspark_refs(self)
|
|
1365
|
+
if scrub_restore:
|
|
1366
|
+
f.seek(0)
|
|
1367
|
+
f.truncate()
|
|
1368
|
+
pickle.dump(self, f, pickle.HIGHEST_PROTOCOL)
|
|
1369
|
+
else:
|
|
1370
|
+
raise
|
|
1371
|
+
finally:
|
|
1372
|
+
# Restore training_function and Spark models so current object remains usable.
|
|
1373
|
+
for estimator, tf in estimator_to_training_function.items():
|
|
1374
|
+
self._search_states[estimator].training_function = tf
|
|
1375
|
+
|
|
1376
|
+
for attr, val in automl_restore.items():
|
|
1377
|
+
setattr(self, attr, val)
|
|
1378
|
+
|
|
1379
|
+
state = getattr(self, "_state", None)
|
|
1380
|
+
if state is not None and state_restore:
|
|
1381
|
+
for attr, val in state_restore.items():
|
|
1382
|
+
setattr(state, attr, val)
|
|
1383
|
+
|
|
1384
|
+
for est, old_model, old_df_train, model_meta in spark_restore:
|
|
1385
|
+
est._model = old_model
|
|
1386
|
+
est.df_train = old_df_train
|
|
1387
|
+
if model_meta is not None and hasattr(est, "_flaml_spark_model_meta"):
|
|
1388
|
+
delattr(est, "_flaml_spark_model_meta")
|
|
1389
|
+
|
|
1390
|
+
if scrub_restore:
|
|
1391
|
+
for _, (parent, key, value) in scrub_restore.items():
|
|
1392
|
+
try:
|
|
1393
|
+
if isinstance(parent, dict):
|
|
1394
|
+
parent[key] = value
|
|
1395
|
+
elif isinstance(parent, list):
|
|
1396
|
+
parent[key] = value
|
|
1397
|
+
else:
|
|
1398
|
+
setattr(parent, key, value)
|
|
1399
|
+
except Exception:
|
|
1400
|
+
pass
|
|
1401
|
+
|
|
1402
|
+
@classmethod
|
|
1403
|
+
def load_pickle(cls, input_file_name: str, load_spark_models: bool = True):
|
|
1404
|
+
"""Load an AutoML instance saved by :meth:`pickle`.
|
|
1405
|
+
|
|
1406
|
+
Args:
|
|
1407
|
+
input_file_name: Path to the pickle file created by :meth:`pickle`.
|
|
1408
|
+
load_spark_models: Whether to load externalized Spark ML models back
|
|
1409
|
+
into the estimator objects. If False, Spark estimators will remain
|
|
1410
|
+
without their underlying Spark model and cannot be used for predict.
|
|
1411
|
+
|
|
1412
|
+
Returns:
|
|
1413
|
+
The deserialized AutoML instance.
|
|
1414
|
+
"""
|
|
1415
|
+
import importlib
|
|
1416
|
+
import os
|
|
1417
|
+
import pickle
|
|
1418
|
+
|
|
1419
|
+
with open(input_file_name, "rb") as f:
|
|
1420
|
+
automl = pickle.load(f)
|
|
1421
|
+
|
|
1422
|
+
# Recreate per-estimator training_function if it was removed for pickling.
|
|
1423
|
+
try:
|
|
1424
|
+
for est_name, ss in getattr(automl, "_search_states", {}).items():
|
|
1425
|
+
if not hasattr(ss, "training_function"):
|
|
1426
|
+
ss.training_function = partial(
|
|
1427
|
+
AutoMLState._compute_with_config_base,
|
|
1428
|
+
state=automl._state,
|
|
1429
|
+
estimator=est_name,
|
|
1430
|
+
)
|
|
1431
|
+
except Exception:
|
|
1432
|
+
# Best-effort; training_function is only needed for re-searching.
|
|
1433
|
+
pass
|
|
1434
|
+
|
|
1435
|
+
if not load_spark_models:
|
|
1436
|
+
return automl
|
|
1437
|
+
|
|
1438
|
+
base_dir = os.path.dirname(input_file_name) or "."
|
|
1439
|
+
|
|
1440
|
+
def _iter_trained_estimators_loaded():
|
|
1441
|
+
trained = getattr(automl, "_trained_estimator", None)
|
|
1442
|
+
if trained is not None:
|
|
1443
|
+
yield trained
|
|
1444
|
+
for ss in getattr(automl, "_search_states", {}).values():
|
|
1445
|
+
te = ss and getattr(ss, "trained_estimator", None)
|
|
1446
|
+
if te is not None:
|
|
1447
|
+
yield te
|
|
1448
|
+
|
|
1449
|
+
for est in _iter_trained_estimators_loaded():
|
|
1450
|
+
meta = getattr(est, "_flaml_spark_model_meta", None)
|
|
1451
|
+
if not meta:
|
|
1452
|
+
continue
|
|
1453
|
+
model_path = meta.get("path")
|
|
1454
|
+
model_class = meta.get("class")
|
|
1455
|
+
if not model_path or not model_class:
|
|
1456
|
+
continue
|
|
1457
|
+
|
|
1458
|
+
abs_model_path = os.path.join(base_dir, model_path)
|
|
1459
|
+
|
|
1460
|
+
module_name, _, class_name = model_class.rpartition(".")
|
|
1461
|
+
try:
|
|
1462
|
+
module = importlib.import_module(module_name)
|
|
1463
|
+
model_cls = getattr(module, class_name)
|
|
1464
|
+
except Exception as e:
|
|
1465
|
+
raise RuntimeError(f"Failed to import Spark model class '{model_class}'") from e
|
|
1466
|
+
|
|
1467
|
+
# Most Spark ML models support either Class.load(path) or Class.read().load(path).
|
|
1468
|
+
if hasattr(model_cls, "load"):
|
|
1469
|
+
est._model = model_cls.load(abs_model_path)
|
|
1470
|
+
elif hasattr(model_cls, "read"):
|
|
1471
|
+
est._model = model_cls.read().load(abs_model_path)
|
|
1472
|
+
else:
|
|
1473
|
+
try:
|
|
1474
|
+
from pyspark.ml.pipeline import PipelineModel
|
|
1475
|
+
|
|
1476
|
+
loaded_model = PipelineModel.load(abs_model_path)
|
|
1477
|
+
if not isinstance(loaded_model, model_cls):
|
|
1478
|
+
raise RuntimeError(
|
|
1479
|
+
f"Loaded model type '{type(loaded_model).__name__}' does not match expected type '{model_class}'."
|
|
1480
|
+
)
|
|
1481
|
+
est._model = loaded_model
|
|
1482
|
+
except Exception as e:
|
|
1483
|
+
raise RuntimeError(
|
|
1484
|
+
f"Spark model class '{model_class}' does not support load/read(). "
|
|
1485
|
+
"Unable to restore Spark model from artifacts."
|
|
1486
|
+
) from e
|
|
1125
1487
|
|
|
1126
|
-
|
|
1127
|
-
pickle.dump(self, f, pickle.HIGHEST_PROTOCOL)
|
|
1488
|
+
return automl
|
|
1128
1489
|
|
|
1129
1490
|
@property
|
|
1130
1491
|
def trainable(self) -> Callable[[dict], float | None]:
|
|
@@ -135,6 +135,7 @@ class BaseEstimator(sklearn.base.ClassifierMixin, sklearn.base.BaseEstimator):
|
|
|
135
135
|
self._task = task if isinstance(task, Task) else task_factory(task, None, None)
|
|
136
136
|
self.params = self.config2params(config)
|
|
137
137
|
self.estimator_class = self._model = None
|
|
138
|
+
self.estimator_baseclass = "sklearn"
|
|
138
139
|
if "_estimator_type" in self.params:
|
|
139
140
|
self._estimator_type = self.params.pop("_estimator_type")
|
|
140
141
|
else:
|
|
@@ -439,6 +440,7 @@ class SparkEstimator(BaseEstimator):
|
|
|
439
440
|
raise SPARK_ERROR
|
|
440
441
|
super().__init__(task, **config)
|
|
441
442
|
self.df_train = None
|
|
443
|
+
self.estimator_baseclass = "spark"
|
|
442
444
|
|
|
443
445
|
def _preprocess(
|
|
444
446
|
self,
|
|
@@ -974,7 +976,7 @@ class TransformersEstimator(BaseEstimator):
|
|
|
974
976
|
from .nlp.huggingface.utils import tokenize_text
|
|
975
977
|
from .nlp.utils import is_a_list_of_str
|
|
976
978
|
|
|
977
|
-
is_str = str(X.dtypes[0]) in ("string", "str")
|
|
979
|
+
is_str = str(X.dtypes.iloc[0]) in ("string", "str")
|
|
978
980
|
is_list_of_str = is_a_list_of_str(X[list(X.keys())[0]].to_list()[0])
|
|
979
981
|
|
|
980
982
|
if is_str or is_list_of_str:
|
|
@@ -396,7 +396,7 @@ def load_model(checkpoint_path, task, num_labels=None):
|
|
|
396
396
|
|
|
397
397
|
if task in (SEQCLASSIFICATION, SEQREGRESSION):
|
|
398
398
|
return AutoModelForSequenceClassification.from_pretrained(
|
|
399
|
-
checkpoint_path, config=model_config, ignore_mismatched_sizes=True
|
|
399
|
+
checkpoint_path, config=model_config, ignore_mismatched_sizes=True, trust_remote_code=True
|
|
400
400
|
)
|
|
401
401
|
elif task == TOKENCLASSIFICATION:
|
|
402
402
|
return AutoModelForTokenClassification.from_pretrained(checkpoint_path, config=model_config)
|
|
@@ -151,7 +151,7 @@ class TimeSeriesTask(Task):
|
|
|
151
151
|
raise ValueError("Must supply either X_train_all and y_train_all, or dataframe and label")
|
|
152
152
|
|
|
153
153
|
try:
|
|
154
|
-
dataframe[self.time_col] = pd.to_datetime(dataframe[self.time_col])
|
|
154
|
+
dataframe.loc[:, self.time_col] = pd.to_datetime(dataframe[self.time_col])
|
|
155
155
|
except Exception:
|
|
156
156
|
raise ValueError(
|
|
157
157
|
f"For '{TS_FORECAST}' task, time column {self.time_col} must contain timestamp values."
|
|
@@ -76,6 +76,8 @@ class SklearnWrapper:
|
|
|
76
76
|
self.pca = None
|
|
77
77
|
|
|
78
78
|
def fit(self, X: pd.DataFrame, y: pd.Series, **kwargs):
|
|
79
|
+
if "is_retrain" in kwargs:
|
|
80
|
+
kwargs.pop("is_retrain")
|
|
79
81
|
self._X = X
|
|
80
82
|
self._y = y
|
|
81
83
|
|
|
@@ -92,7 +94,14 @@ class SklearnWrapper:
|
|
|
92
94
|
|
|
93
95
|
for i, model in enumerate(self.models):
|
|
94
96
|
offset = i + self.lags
|
|
95
|
-
|
|
97
|
+
if len(X) - offset > 2:
|
|
98
|
+
# series with length 2 will meet All features are either constant or ignored.
|
|
99
|
+
# TODO: see why the non-constant features are ignored. Selector?
|
|
100
|
+
model.fit(X_trans[: len(X) - offset], y[offset:], **fit_params)
|
|
101
|
+
elif len(X) > offset and "catboost" not in str(model).lower():
|
|
102
|
+
model.fit(X_trans[: len(X) - offset], y[offset:], **fit_params)
|
|
103
|
+
else:
|
|
104
|
+
print("[INFO]: Length of data should longer than period + lags.")
|
|
96
105
|
return self
|
|
97
106
|
|
|
98
107
|
def predict(self, X, X_train=None, y_train=None):
|
|
@@ -121,7 +121,12 @@ class TimeSeriesDataset:
|
|
|
121
121
|
|
|
122
122
|
@property
|
|
123
123
|
def X_all(self) -> pd.DataFrame:
|
|
124
|
-
|
|
124
|
+
# Remove empty or all-NA columns before concatenation
|
|
125
|
+
X_train_filtered = self.X_train.dropna(axis=1, how="all")
|
|
126
|
+
X_val_filtered = self.X_val.dropna(axis=1, how="all")
|
|
127
|
+
|
|
128
|
+
# Concatenate the filtered DataFrames
|
|
129
|
+
return pd.concat([X_train_filtered, X_val_filtered], axis=0)
|
|
125
130
|
|
|
126
131
|
@property
|
|
127
132
|
def y_train(self) -> pd.DataFrame:
|
|
@@ -472,7 +477,7 @@ class DataTransformerTS:
|
|
|
472
477
|
if "__NAN__" not in X[col].cat.categories:
|
|
473
478
|
X[col] = X[col].cat.add_categories("__NAN__").fillna("__NAN__")
|
|
474
479
|
else:
|
|
475
|
-
X[col] = X[col].fillna("__NAN__")
|
|
480
|
+
X[col] = X[col].fillna("__NAN__").infer_objects(copy=False)
|
|
476
481
|
X[col] = X[col].astype("category")
|
|
477
482
|
|
|
478
483
|
for column in self.num_columns:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.4.1"
|
|
@@ -116,14 +116,14 @@ setuptools.setup(
|
|
|
116
116
|
"scikit-learn",
|
|
117
117
|
],
|
|
118
118
|
"hf": [
|
|
119
|
-
"transformers[torch]
|
|
119
|
+
"transformers[torch]>=4.26",
|
|
120
120
|
"datasets",
|
|
121
121
|
"nltk<=3.8.1",
|
|
122
122
|
"rouge_score",
|
|
123
123
|
"seqeval",
|
|
124
124
|
],
|
|
125
125
|
"nlp": [ # for backward compatibility; hf is the new option name
|
|
126
|
-
"transformers[torch]
|
|
126
|
+
"transformers[torch]>=4.26",
|
|
127
127
|
"datasets",
|
|
128
128
|
"nltk<=3.8.1",
|
|
129
129
|
"rouge_score",
|
flaml-2.4.0/flaml/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.4.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|