Cannot save any model

### Describe the bug

Hi,

Hope everything is going well. I have been having issues saving any model either using pickle or joblib getting this error:

`PicklingError: Can't pickle <function <lambda> at 0x28bf58fe0>: it's not found as __main__.<lambda>`

When using Skops, I am able to save the model, but when loading it back I get this error:

`AttributeError: module '__main__' has no attribute '<lambda>'`

I used Skop's module get_untrusted_types to see what is being saved and I see at the beginning of the model a line with the main as the following

`
['__main__.<lambda>', 'sklearn._loss._loss.CyHalfSquaredError', 'sklearn._loss.link.IdentityLink', 'sklearn._loss.link.Interval', 'sklearn._loss.loss.HalfSquaredError', 'sklearn.ensemble._hist_gradient_boosting.binning._BinMapper', 'sklearn.ensemble._hist_gradient_boosting.predictor.TreePredictor', 'sklearn.model_selection._split.KFold', 'sklearn.utils._bunch.Bunch']
`
I wonder if this is a normal behaviour or if this is an issue either on my end or on my environment, because I had been able to save models normally until yesterday when I ran into this issue. I just gave up on trying different ways on fixing it.

Thanks

Best

### Steps/Code to Reproduce

import joblib

joblib.dump(hgbc_model, './models/hgbc_test.joblib')

### Expected Results

No error

### Actual Results

```
{
	"name": "PicklingError",
	"message": "Can't pickle <function <lambda> at 0x28bf58fe0>: it's not found as __main__.<lambda>",
	"stack": "---------------------------------------------------------------------------
PicklingError                             Traceback (most recent call last)
/Users/xxxx/kaggle_2/streamlit/cv/pages/diamonds_st/diamonds.ipynb Cell 110 line 3
      <a href='vscode-notebook-cell:/Users/alejandrodelgado/kaggle_2/streamlit/cv/pages/diamonds_st/diamonds.ipynb#Y235sZmlsZQ%3D%3D?line=0'>1</a> import joblib
----> <a href='vscode-notebook-cell:/Users/alejandrodelgado/kaggle_2/streamlit/cv/pages/diamonds_st/diamonds.ipynb#Y235sZmlsZQ%3D%3D?line=2'>3</a> joblib.dump(hgbc_model, './models/hgbc_test.joblib')

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:553, in dump(value, filename, compress, protocol, cache_size)
    551 elif is_filename:
    552     with open(filename, 'wb') as f:
--> 553         NumpyPickler(f, protocol=protocol).dump(value)
    554 else:
    555     NumpyPickler(filename, protocol=protocol).dump(value)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:487, in _Pickler.dump(self, obj)
    485 if self.proto >= 4:
    486     self.framer.start_framing()
--> 487 self.save(obj)
    488 self.write(STOP)
    489 self.framer.end_framing()

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:603, in _Pickler.save(self, obj, save_persistent_id)
    599     raise PicklingError(\"Tuple returned by %s must have \"
    600                         \"two to six elements\" % reduce)
    602 # Save the reduce() output and finally memoize the object
--> 603 self.save_reduce(obj=obj, *rv)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:717, in _Pickler.save_reduce(self, func, args, state, listitems, dictitems, state_setter, obj)
    715 if state is not None:
    716     if state_setter is None:
--> 717         save(state)
    718         write(BUILD)
    719     else:
    720         # If a state_setter is specified, call it instead of load_build
    721         # to update obj's with its previous state.
    722         # First, push state_setter and its tuple of expected arguments
    723         # (obj, state) onto the stack.

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:972, in _Pickler.save_dict(self, obj)
    969     self.write(MARK + DICT)
    971 self.memoize(obj)
--> 972 self._batch_setitems(obj.items())

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:998, in _Pickler._batch_setitems(self, items)
    996     for k, v in tmp:
    997         save(k)
--> 998         save(v)
    999     write(SETITEMS)
   1000 elif n:

    [... skipping similar frames: NumpyPickler.save at line 355 (1 times)]

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:603, in _Pickler.save(self, obj, save_persistent_id)
    599     raise PicklingError(\"Tuple returned by %s must have \"
    600                         \"two to six elements\" % reduce)
    602 # Save the reduce() output and finally memoize the object
--> 603 self.save_reduce(obj=obj, *rv)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:717, in _Pickler.save_reduce(self, func, args, state, listitems, dictitems, state_setter, obj)
    715 if state is not None:
    716     if state_setter is None:
--> 717         save(state)
    718         write(BUILD)
    719     else:
    720         # If a state_setter is specified, call it instead of load_build
    721         # to update obj's with its previous state.
    722         # First, push state_setter and its tuple of expected arguments
    723         # (obj, state) onto the stack.

    [... skipping similar frames: NumpyPickler.save at line 355 (1 times)]

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:972, in _Pickler.save_dict(self, obj)
    969     self.write(MARK + DICT)
    971 self.memoize(obj)
--> 972 self._batch_setitems(obj.items())

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:998, in _Pickler._batch_setitems(self, items)
    996     for k, v in tmp:
    997         save(k)
--> 998         save(v)
    999     write(SETITEMS)
   1000 elif n:

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:932, in _Pickler.save_list(self, obj)
    929     self.write(MARK + LIST)
    931 self.memoize(obj)
--> 932 self._batch_appends(obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:956, in _Pickler._batch_appends(self, items)
    954     write(MARK)
    955     for x in tmp:
--> 956         save(x)
    957     write(APPENDS)
    958 elif n:

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:887, in _Pickler.save_tuple(self, obj)
    885 if n <= 3 and self.proto >= 2:
    886     for element in obj:
--> 887         save(element)
    888     # Subtle.  Same as in the big comment below.
    889     if id(obj) in memo:

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:603, in _Pickler.save(self, obj, save_persistent_id)
    599     raise PicklingError(\"Tuple returned by %s must have \"
    600                         \"two to six elements\" % reduce)
    602 # Save the reduce() output and finally memoize the object
--> 603 self.save_reduce(obj=obj, *rv)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:717, in _Pickler.save_reduce(self, func, args, state, listitems, dictitems, state_setter, obj)
    715 if state is not None:
    716     if state_setter is None:
--> 717         save(state)
    718         write(BUILD)
    719     else:
    720         # If a state_setter is specified, call it instead of load_build
    721         # to update obj's with its previous state.
    722         # First, push state_setter and its tuple of expected arguments
    723         # (obj, state) onto the stack.

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:972, in _Pickler.save_dict(self, obj)
    969     self.write(MARK + DICT)
    971 self.memoize(obj)
--> 972 self._batch_setitems(obj.items())

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:998, in _Pickler._batch_setitems(self, items)
    996     for k, v in tmp:
    997         save(k)
--> 998         save(v)
    999     write(SETITEMS)
   1000 elif n:

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:932, in _Pickler.save_list(self, obj)
    929     self.write(MARK + LIST)
    931 self.memoize(obj)
--> 932 self._batch_appends(obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:956, in _Pickler._batch_appends(self, items)
    954     write(MARK)
    955     for x in tmp:
--> 956         save(x)
    957     write(APPENDS)
    958 elif n:

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:887, in _Pickler.save_tuple(self, obj)
    885 if n <= 3 and self.proto >= 2:
    886     for element in obj:
--> 887         save(element)
    888     # Subtle.  Same as in the big comment below.
    889     if id(obj) in memo:

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:603, in _Pickler.save(self, obj, save_persistent_id)
    599     raise PicklingError(\"Tuple returned by %s must have \"
    600                         \"two to six elements\" % reduce)
    602 # Save the reduce() output and finally memoize the object
--> 603 self.save_reduce(obj=obj, *rv)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:717, in _Pickler.save_reduce(self, func, args, state, listitems, dictitems, state_setter, obj)
    715 if state is not None:
    716     if state_setter is None:
--> 717         save(state)
    718         write(BUILD)
    719     else:
    720         # If a state_setter is specified, call it instead of load_build
    721         # to update obj's with its previous state.
    722         # First, push state_setter and its tuple of expected arguments
    723         # (obj, state) onto the stack.

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:972, in _Pickler.save_dict(self, obj)
    969     self.write(MARK + DICT)
    971 self.memoize(obj)
--> 972 self._batch_setitems(obj.items())

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:998, in _Pickler._batch_setitems(self, items)
    996     for k, v in tmp:
    997         save(k)
--> 998         save(v)
    999     write(SETITEMS)
   1000 elif n:

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:932, in _Pickler.save_list(self, obj)
    929     self.write(MARK + LIST)
    931 self.memoize(obj)
--> 932 self._batch_appends(obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:956, in _Pickler._batch_appends(self, items)
    954     write(MARK)
    955     for x in tmp:
--> 956         save(x)
    957     write(APPENDS)
    958 elif n:

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:887, in _Pickler.save_tuple(self, obj)
    885 if n <= 3 and self.proto >= 2:
    886     for element in obj:
--> 887         save(element)
    888     # Subtle.  Same as in the big comment below.
    889     if id(obj) in memo:

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:603, in _Pickler.save(self, obj, save_persistent_id)
    599     raise PicklingError(\"Tuple returned by %s must have \"
    600                         \"two to six elements\" % reduce)
    602 # Save the reduce() output and finally memoize the object
--> 603 self.save_reduce(obj=obj, *rv)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:717, in _Pickler.save_reduce(self, func, args, state, listitems, dictitems, state_setter, obj)
    715 if state is not None:
    716     if state_setter is None:
--> 717         save(state)
    718         write(BUILD)
    719     else:
    720         # If a state_setter is specified, call it instead of load_build
    721         # to update obj's with its previous state.
    722         # First, push state_setter and its tuple of expected arguments
    723         # (obj, state) onto the stack.

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:972, in _Pickler.save_dict(self, obj)
    969     self.write(MARK + DICT)
    971 self.memoize(obj)
--> 972 self._batch_setitems(obj.items())

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:998, in _Pickler._batch_setitems(self, items)
    996     for k, v in tmp:
    997         save(k)
--> 998         save(v)
    999     write(SETITEMS)
   1000 elif n:

File ~/kaggle_2/new_env/lib/python3.11/site-packages/joblib/numpy_pickle.py:355, in NumpyPickler.save(self, obj)
    352     wrapper.write_array(obj, self)
    353     return
--> 355 return Pickler.save(self, obj)

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:560, in _Pickler.save(self, obj, save_persistent_id)
    558 f = self.dispatch.get(t)
    559 if f is not None:
--> 560     f(self, obj)  # Call unbound method with explicit self
    561     return
    563 # Check private dispatch table if any, or else
    564 # copyreg.dispatch_table

File ~/kaggle_2/new_env/lib/python3.11/pickle.py:1071, in _Pickler.save_global(self, obj, name)
   1069     obj2, parent = _getattribute(module, name)
   1070 except (ImportError, KeyError, AttributeError):
-> 1071     raise PicklingError(
   1072         \"Can't pickle %r: it's not found as %s.%s\" %
   1073         (obj, module_name, name)) from None
   1074 else:
   1075     if obj2 is not obj:

PicklingError: Can't pickle <function <lambda> at 0x28bf58fe0>: it's not found as __main__.<lambda>"
}
```

### Versions

```shell
System:
    python: 3.11.2 | packaged by conda-forge | (main, Mar 31 2023, 17:57:55) [Clang 14.0.6 ]
executable: /Users/xxx/kaggle_2/new_env/bin/python
   machine: macOS-14.0-arm64-arm-64bit

Python dependencies:
      sklearn: 1.3.0
          pip: 23.2.1
   setuptools: 68.2.2
        numpy: 1.23.5
        scipy: 1.11.2
       Cython: 3.0.2
       pandas: 2.1.0
   matplotlib: 3.7.2
       joblib: 1.3.2
threadpoolctl: 3.2.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 10
         prefix: libopenblas
       filepath: /Users/xxx/kaggle_2/new_env/lib/libopenblas.0.dylib
        version: 0.3.24
threading_layer: openmp
   architecture: VORTEX

       user_api: openmp
   internal_api: openmp
    num_threads: 10
         prefix: libomp
       filepath: /Users/xxx/kaggle_2/new_env/lib/libomp.dylib
        version: None
```

COMMENT:
Sure thing:


```
from sklearn.compose import ColumnTransformer, TransformedTargetRegressor
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import RobustScaler, FunctionTransformer, MinMaxScaler
from sklearn.ensemble import HistGradientBoostingRegressor


ohe = OneHotEncoder(sparse_output=False, handle_unknown="ignore")
rob_scaler = RobustScaler()
minmax_scaler = MinMaxScaler()
inv_cube_scaler = FunctionTransformer(
    func=np.cbrt,
    inverse_func=lambda x: np.power(x, 3),
    check_inverse=True,
    feature_names_out="one-to-one",
)

pip_rob_max = Pipeline(steps=[("rob", rob_scaler), ("max", minmax_scaler)])
pip_cbrt_max = Pipeline(steps=[("cbrt", inv_cube_scaler), ("max", minmax_scaler)])


transformer = ColumnTransformer(
    transformers=[
        ("pip_rob", pip_rob_max, ["carat"]),
        ("pip_cbrt", pip_cbrt_max, ["x", "table_width", "y", "z", "z_depth"]),
        ("ohe", ohe, ["cut", "color", "clarity"]),
    ],
    verbose_feature_names_out=False,
    n_jobs=-1,
    remainder="passthrough",
)

y_transformer = Pipeline(steps=[("pow", pow_scaler), ("max", minmax_scaler)])

hgbc_model = TransformedTargetRegressor(
    regressor=Pipeline(
        steps=[
            ("preprocessor", transformer),
            (
                "hgbc",
                HistGradientBoostingRegressor(
                    learning_rate=0.01,
                    max_iter=30_000,
                    max_depth=12,
                    l2_regularization=0.01,
                ),
            ),
        ]
    ),
    transformer=y_transformer,
)

hgbc_model.fit(X_train, y_train)
```


Would the problem be with the cbrt transformer where I am using the lambda function to transform to a cube transformer?

Thanks

Best

Labels: Bug, Needs Triage