deel-ai/xplique

[Bug]: - Pickling of a KernelShap using dill or cloudpickle in a test doesn't work

thomasdulacatos opened this issue · 0 comments

Module

Attributions Methods

Current Behavior

Pickling of a KernelShap using dill or cloudpickle in a test doesn't work in parralel with a explain running on a second test, using Vscode. But the test alone works fine.

Expected Behavior

No error should be present.

Version

1.1.0

Environment

- OS:Centos 7
- Python version: 3.9.10
- Tensorflow version: 2.12.0
- Packages used version:
numpy                         1.22.0
pandas                        1.2.0
dill                          0.3.7
scikit-learn                  1.2.2

Relevant log output

./incident_prediction/feature_contribution_tests/xplique_example_simpler_tests.py::XpliqueKernelFeatureContributionTests::test_stable_pickle_size Failed with Error: cannot pickle '_thread._local' object
  File "/usr/local/lib/python3.9/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/usr/local/lib/python3.9/unittest/case.py", line 592, in run
    self._callTestMethod(testMethod)
  File "/usr/local/lib/python3.9/unittest/case.py", line 550, in _callTestMethod
    method()
  File "/opt/cloned_file/proma-one-engine/src/unittest/python/incident_prediction/feature_contribution_tests/xplique_example_simpler_tests.py", line 40, in test_stable_pickle_size
    dump1 = dill.dumps(explainer)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 278, in dumps
    dump(obj, file, protocol, byref, fmode, recurse, **kwds)#, strictio)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 250, in dump
    Pickler(file, protocol, **_kwds).dump(obj)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 418, in dump
    StockPickler.dump(self, obj)
  File "/usr/local/lib/python3.9/pickle.py", line 487, in dump
    self.save(obj)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 603, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/local/lib/python3.9/pickle.py", line 717, in save_reduce
    save(state)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 560, in save
    f(self, obj)  # Call unbound method with explicit self
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 1212, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/local/lib/python3.9/pickle.py", line 971, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/local/lib/python3.9/pickle.py", line 997, in _batch_setitems
    save(v)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 603, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/local/lib/python3.9/pickle.py", line 717, in save_reduce
    save(state)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 560, in save
    f(self, obj)  # Call unbound method with explicit self
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 1212, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/local/lib/python3.9/pickle.py", line 971, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/local/lib/python3.9/pickle.py", line 997, in _batch_setitems
    save(v)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 603, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/local/lib/python3.9/pickle.py", line 717, in save_reduce
    save(state)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 560, in save
    f(self, obj)  # Call unbound method with explicit self
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 1212, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/local/lib/python3.9/pickle.py", line 971, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/local/lib/python3.9/pickle.py", line 997, in _batch_setitems
    save(v)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 560, in save
    f(self, obj)  # Call unbound method with explicit self
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 1965, in save_function
    _save_with_postproc(pickler, (_create_function, (
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 1112, in _save_with_postproc
    pickler.save_reduce(*reduction)
  File "/usr/local/lib/python3.9/pickle.py", line 692, in save_reduce
    save(args)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 560, in save
    f(self, obj)  # Call unbound method with explicit self
  File "/usr/local/lib/python3.9/pickle.py", line 886, in save_tuple
    save(element)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 560, in save
    f(self, obj)  # Call unbound method with explicit self
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 1965, in save_function
    _save_with_postproc(pickler, (_create_function, (
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 1112, in _save_with_postproc
    pickler.save_reduce(*reduction)
  File "/usr/local/lib/python3.9/pickle.py", line 692, in save_reduce
    save(args)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 560, in save
    f(self, obj)  # Call unbound method with explicit self
  File "/usr/local/lib/python3.9/pickle.py", line 886, in save_tuple
    save(element)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 603, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/local/lib/python3.9/pickle.py", line 717, in save_reduce
    save(state)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 560, in save
    f(self, obj)  # Call unbound method with explicit self
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 1212, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/local/lib/python3.9/pickle.py", line 971, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/local/lib/python3.9/pickle.py", line 997, in _batch_setitems
    save(v)
  File "/opt/cloned_file/proma-one-engine/target/venv/build/cpython-3.9.18.final.0/lib/python3.9/site-packages/dill/_dill.py", line 412, in save
    StockPickler.save(self, obj, save_persistent_id)
  File "/usr/local/lib/python3.9/pickle.py", line 578, in save
    rv = reduce(self.proto)
TypeError: cannot pickle '_thread._local' object

To Reproduce

from unittest import TestCase

import numpy as np
import pandas as pd
import dill

from sklearn.preprocessing import MinMaxScaler
from xplique.attributions import KernelShap

class SimpleModel:
    
    def fit(self, X):
        self.scaler = MinMaxScaler(feature_range=(0, 1))
        self.scaler.fit(X)
        return self
    def transform(self, X):
        X = self.scaler.transform(X)
        return X
    def predict_proba(self, X):
        return self.transform(X)    

class XpliqueKernelFeatureContributionTests(TestCase):

    def test_explanation_shap(self) -> None:
        model = SimpleModel()
        model.fit(pd.DataFrame(np.random.randint(1,100,size=(100, 100))))

        explainer = KernelShap(model = model)

        explainer.explain(np.random.randint(1,100,size=(100, 100)), 
                          np.random.randint(1,100,size=(100, 100)))

    def test_stable_pickle_size(self) -> None:

        model = SimpleModel()
        model.fit(pd.DataFrame(np.random.randint(1,100,size=(100, 100))))

        explainer = KernelShap(model = model)

        dump1 = dill.dumps(explainer)

And then use Vscode to launch both test. Using pytest produce the same result.