mlflow: [BUG] Can't pickle : it's not the same object as sklearn.metrics._regression.r2_score

Willingness to contribute

No. I cannot contribute a bug fix at this time.

MLflow version

mlflow, version 1.27.0

System information

  • OS Platform and Distribution (e.g., Linux Ubuntu 16.04): Ubuntu 20.04.3 LTS
  • Python version: Python 3.9.7
  • yarn version, if running the dev UI: N/A

Describe the problem

Root cause

cannot pickle a model on the local side (unable to serialize a model on the local side)


Expected behavior

can pickle a model (can serialize a model) on the local side


Error logs

for classification

got via the python command

_pickle.PicklingError: Can’t pickle <function accuracy_score at 0x7efad52c2280>: it’s not the same object as sklearn.metrics._classification.accuracy_score

got via the ipykernel (Notebook kernel)

PicklingError: Can’t pickle <function accuracy_score at 0x7f17a5ca2a60>: it’s not the same object as sklearn.metrics._classification.accuracy_score

for regression:

got via the python command

_pickle.PicklingError: Can’t pickle <function r2_score at 0x7f162c7fd0d0>: it’s not the same object as sklearn.metrics._regression.r2_score

got via the ipykernel (Notebook kernel)

PicklingError: Can’t pickle <function r2_score at 0x7ff325880f70>: it’s not the same object as sklearn.metrics._regression.r2_score


How to reproduce?

for classification

import mlflow
mlflow.autolog()

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from autosklearn.classification import AutoSklearnClassifier

X, y = load_iris(return_X_y=True)
train_X, test_X, train_y, test_y = train_test_split(X, y)

# training
#model = RandomForestClassifier() # pass
model = AutoSklearnClassifier(    # failed
    memory_limit = 1024 * 12,     # (in MB) (default: 3072 MB)
    time_left_for_this_task = 60, # 60 sec.
    per_run_time_limit = 15)      # 15 sec.
model.fit(train_X, train_y)

# inference
print('score:', model.score(test_X, test_y))

# save the model to the local disk
import pickle
with open('model.pkl', 'wb') as f:
    pickle.dump(model, f)
    print('Model saved successfully')

image

for regression:

import mlflow
mlflow.autolog()

from sklearn.linear_model import LinearRegression
from autosklearn.regression import AutoSklearnRegressor

train_X = [[1], [3], [5], [7], [9]]
train_y = [2, 6, 10, 14, 18]

# training
# model = LinearRegression()      # pass
model = AutoSklearnRegressor(     # failed
    memory_limit = 1024 * 12,     # (in MB) (default: 3072 MB)
    time_left_for_this_task = 60, # 60 sec.
    per_run_time_limit = 15)      # 15 sec.
model.fit(train_X, train_y)

# inference
test_X = [[2], [4], [6], [8], [10]]
test_y = [4, 8, 12, 16, 20]
print('score:', model.score(test_X, test_y))

# save the model to the local disk
import pickle
with open('model.pkl', 'wb') as f:
    pickle.dump(model, f)
    print('Model saved successfully')

image

Tracking information

MLflow version: 1.27.0 Tracking URI: file:///workspace/mlruns Artifact URI: file:///workspace/mlruns/0/37326b5ea62046bcbd9b7eb8ab26cf23/artifacts

snapshot

image

Code to reproduce issue

import mlflow
import sklearn.metrics
import pickle

cannot_pickle = True

if cannot_pickle:
    # failed
    mlflow.sklearn.autolog()
    r2_public = sklearn.metrics.r2_score
    r2_private = sklearn.metrics._regression.r2_score
else:
    # pass
    r2_public = sklearn.metrics.r2_score
    r2_private = sklearn.metrics._regression.r2_score
    mlflow.sklearn.autolog()

print('sklearn.metrics.r2_score:')
print('__hash__:', r2_public.__hash__)
print('__code__:', r2_public.__code__)
print('-' * 60)
print('sklearn.metrics._regression.r2_score:')
print('__hash__:', r2_private.__hash__)
print('__code__:', r2_private.__code__)
print('-' * 60)

with open('r2.pkl', 'wb') as f:
    pickle.dump(r2_public, f)
    print('r2.pkl saved successfully')

Other info / logs

---------------------------------------------------------------------------
PicklingError                             Traceback (most recent call last)
/tmp/ipykernel_836/453514921.py in <module>
     26 
     27 with open('r2.pkl', 'wb') as f:
---> 28     pickle.dump(r2_public, f)
     29     print('r2.pkl saved successfully')

PicklingError: Can't pickle <function r2_score at 0x7f1b594cf310>: it's not the same object as sklearn.metrics._regression.r2_score

What component(s) does this bug affect?

  • area/artifacts: Artifact stores and artifact logging
  • area/build: Build and test infrastructure for MLflow
  • area/docs: MLflow documentation pages
  • area/examples: Example code
  • area/model-registry: Model Registry service, APIs, and the fluent client calls for Model Registry
  • area/models: MLmodel format, model serialization/deserialization, flavors
  • area/pipelines: Pipelines, Pipeline APIs, Pipeline configs, Pipeline Templates
  • area/projects: MLproject format, project running backends
  • area/scoring: MLflow Model server, model deployment tools, Spark UDFs
  • area/server-infra: MLflow Tracking server backend
  • area/tracking: Tracking Service, tracking client APIs, autologging

What interface(s) does this bug affect?

  • area/uiux: Front-end, user experience, plotting, JavaScript, JavaScript dev server
  • area/docker: Docker use across MLflow’s components, such as MLflow Projects and MLflow Models
  • area/sqlalchemy: Use of SQLAlchemy in the Tracking Service or Model Registry
  • area/windows: Windows support

What language(s) does this bug affect?

  • language/r: R APIs and clients
  • language/java: Java APIs and clients
  • language/new: Proposals for new client languages

What integration(s) does this bug affect?

  • integrations/azure: Azure and Azure ML integrations
  • integrations/sagemaker: SageMaker integrations
  • integrations/databricks: Databricks integrations

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 25 (13 by maintainers)

Most upvoted comments

same issue also happened in AutoGluon

[2022-10-03 14:56:25][Level 25][abstract_trainer.py#60] AutoGluon will gauge predictive performance using evaluation metric: 'accuracy'
[2022-10-03 14:56:25][INFO][abstract_trainer.py#66] 	To change this, specify the eval_metric parameter of Predictor()
Traceback (most recent call last):
  File "/workspace/main.py", line 136, in <module>
    main()
  File "/workspace/main.py", line 36, in main
    train(context)
  File "/workspace/trainer.py", line 38, in train
    model_instance.fit()
  File "/workspace/model/abstract_model.py", line 156, in fit
    self._model.fit(self._x, self._y)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/mlflow/utils/autologging_utils/safety.py", line 548, in safe_patch_function
    patch_function(call_original, *args, **kwargs)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/mlflow/utils/autologging_utils/safety.py", line 254, in patch_with_managed_run
    result = patch_function(original, *args, **kwargs)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/mlflow/sklearn/__init__.py", line 1481, in patched_fit
    result = fit_impl(original, self, *args, **kwargs)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/mlflow/sklearn/__init__.py", line 1285, in fit_mlflow
    fit_output = original(self, *args, **kwargs)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/mlflow/utils/autologging_utils/safety.py", line 529, in call_original
    return call_original_fn_with_event_logging(_original_fn, og_args, og_kwargs)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/mlflow/utils/autologging_utils/safety.py", line 471, in call_original_fn_with_event_logging
    original_fn_result = original_fn(*og_args, **og_kwargs)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/mlflow/utils/autologging_utils/safety.py", line 526, in _original_fn
    original_result = original(*_og_args, **_og_kwargs)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/sklearn/pipeline.py", line 394, in fit
    self._final_estimator.fit(Xt, y, **fit_params_last_step)
  File "/workspace/model/local_ag_tabular_predictor.py", line 123, in fit
    self.__predctor.fit(train_data, **self.__fit_params)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/autogluon/core/utils/decorators.py", line 30, in _call
    return f(*gargs, **gkwargs)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/autogluon/tabular/predictor/predictor.py", line 831, in fit
    self._learner.fit(X=train_data, X_val=tuning_data, X_unlabeled=unlabeled_data,
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/autogluon/tabular/learner/abstract_learner.py", line 118, in fit
    return self._fit(X=X, X_val=X_val, **kwargs)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/autogluon/tabular/learner/default_learner.py", line 125, in _fit
    self.save()
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/autogluon/core/learner/abstract_learner.py", line 88, in save
    save_pkl.save(path=self.save_path, object=self)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/autogluon/common/savers/save_pkl.py", line 21, in save
    save_with_fn(validated_path, object, pickle_fn, format=format, verbose=verbose, compression_fn=compression_fn,
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/autogluon/common/savers/save_pkl.py", line 38, in save_with_fn
    pickle_fn(object, fout)
  File "/usr/local/miniconda3/envs/autogluon/lib/python3.9/site-packages/autogluon/common/savers/save_pkl.py", line 20, in <lambda>
    pickle_fn = lambda o, buffer: pickle.dump(o, buffer, protocol=4)
_pickle.PicklingError: Can't pickle <function accuracy_score at 0x7fe245c7d430>: it's not the same object as sklearn.metrics._classification.accuracy_score

But we can’t modify the code in autogluon

@tsungjung411 Feel free to reopen the issue if you need further help.

cloudpickle worked:

import mlflow

mlflow.autolog()  # failed

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from autosklearn.classification import AutoSklearnClassifier

# mlflow.autolog()  # pass

X, y = load_iris(return_X_y=True)
train_X, test_X, train_y, test_y = train_test_split(X, y)

# training
# model = RandomForestClassifier() # pass
model = AutoSklearnClassifier(  # failed
    memory_limit=1024 * 12,  # (in MB) (default: 3072 MB)
    time_left_for_this_task=60,  # 60 sec.
    per_run_time_limit=15,  # 15 sec.
)  # 15 sec.
model.fit(train_X, train_y)

# inference
print("score:", model.score(test_X, test_y))

# save the model to the local disk
import cloudpickle

with open("model.pkl", "wb") as f:
    cloudpickle.dump(model, f)
print("Model saved successfully")

Output:

2022/07/26 14:23:20 INFO mlflow.tracking.fluent: Autologging successfully enabled for pyspark.
2022/07/26 14:23:20 INFO mlflow.pyspark.ml: No SparkSession detected. Autologging will log pyspark.ml models contained in the default allowlist. To specify a custom allowlist, initialize a SparkSession prior to calling mlflow.pyspark.ml.autolog() and specify the path to your allowlist file via the spark.mlflow.pysparkml.autolog.logModelAllowlistFile conf.
2022/07/26 14:23:20 INFO mlflow.tracking.fluent: Autologging successfully enabled for pyspark.ml.
2022/07/26 14:23:20 INFO mlflow.tracking.fluent: Autologging successfully enabled for sklearn.
2022/07/26 14:23:24 INFO mlflow.utils.autologging_utils: Created MLflow autologging run with ID 'ad52984e82214f91a7fd1847c7592311', which will track hyperparameters, performance metrics, model artifacts, and lineage information for the current sklearn workflow
2022/07/26 14:23:24 WARNING mlflow.sklearn: Training metrics will not be recorded because training labels were not specified. To automatically record training metrics, provide training labels as inputs to the model training function.
2022/07/26 14:23:24 WARNING mlflow.sklearn: Failed to infer model signature: 'StandardScalerComponent' object has no attribute 'predict'
2022/07/26 14:23:25 INFO mlflow.utils.autologging_utils: Created MLflow autologging run with ID '24d241776eb144e5b043eaaf532201b1', which will track hyperparameters, performance metrics, model artifacts, and lineage information for the current sklearn workflow
2022/07/26 14:23:25 WARNING mlflow.sklearn: Training metrics will not be recorded because training labels were not specified. To automatically record training metrics, provide training labels as inputs to the model training function.
2022/07/26 14:23:25 WARNING mlflow.sklearn: Failed to infer model signature: the trained model does not specify a `predict` function, which is required in order to infer the signature
score: 0.9736842105263158
Model saved successfully

fit is an instance method, but accuracy_score is a function. I’m not sure why pickle complains.