privateでないAPIがscikit-learn v0.24で削除されたために、unpickleできない経験をしました

scikit-learn v0.22 より前に作った`MultiLabelBinarizer`が、scikit-learn v0.24以降の入った環境でunpickleできないことを示します。
ちょっとハマった身から言えること：**pickleする環境とunpickleする環境のバージョン違いにはご注意ください**

## 環境

macOS
Python 3.7.3

## 手順

### `scikit-learn` 0.22より前

scikit-learn-0.21.3

ドキュメントのExamplesを用います
https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MultiLabelBinarizer.html#sklearn.preprocessing.MultiLabelBinarizer

experiment.py

```python
import pickle

from sklearn.preprocessing import MultiLabelBinarizer

mlb = MultiLabelBinarizer()
mlb.fit_transform([{'sci-fi', 'thriller'}, {'comedy'}])
print(list(mlb.classes_))

with open("mlb_021.pkl", "wb") as fout:
    pickle.dump(mlb, fout)
```

```console
$ python experiment.py
['comedy', 'sci-fi', 'thriller']
$ python -q
```

対話モードでunpickleします。

```python
>>> import pickle
>>> with open("mlb_021.pkl", "rb") as fin:
...   mlb = pickle.load(fin)
...
>>> list(mlb.classes_)
['comedy', 'sci-fi', 'thriller']
>>> mlb.transform([['sci-fi', 'comedy']])
array([[1, 1, 0]])
>>> type(mlb)
<class 'sklearn.preprocessing.label.MultiLabelBinarizer'>
```

### `scikit-learn` 0.24以降

`pip install -U scikit-learn`で0.24.0が入りました。

対話モードでunpickleを試みます（エラーになります）。

```python
>>> import pickle
>>> with open("mlb_021.pkl", "rb") as fin:
...   mlb = pickle.load(fin)
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ModuleNotFoundError: No module named 'sklearn.preprocessing.label'
```

## 原因

以下のIssueへの対応で**モジュールをprivateに**しています。
https://github.com/scikit-learn/scikit-learn/issues/9250
>rename file.py into _file.py

それにより、sklearn/preprocessing/label.py（`sklearn.preprocessing.label`）はsklearn/preprocessing/_label.py（`sklearn.preprocessing._label`）とrenameされました。
https://github.com/scikit-learn/scikit-learn/commit/15d5ef04df21f1d8db6583c74d1869c3fad36970#diff-e4f17cabd1bed4babf2fbfb7a31a3d955563e0267e8c310fa61629e722a64488

## 対処法

0.24まで上げなければunpickleはできます（後述するように自己責任でお願いします）。
`pip install -U 'scikit-learn<0.24'`で0.23.2をインストール。

```python
>>> import pickle
>>> with open("mlb_021.pkl", "rb") as fin:
...   mlb = pickle.load(fin)
...
```

このとき、バージョン0.24でremoveされるとwarningも出ています。

>/.../env/lib/python3.7/site-packages/sklearn/utils/deprecation.py:143: FutureWarning: The sklearn.preprocessing.label module is  deprecated in version 0.22 and will be removed in version 0.24. The corresponding classes / functions should instead be imported from sklearn.preprocessing. Anything that cannot be imported from sklearn.preprocessing is now part of the private API.
>  warnings.warn(message, FutureWarning)

ドキュメントでも通知されていました！
https://scikit-learn.org/dev/whats_new/v0.22.html#clear-definition-of-the-public-api

実際、`MultiLabelBinarizer`の`type`を確認すると、renameされた_label.pyを使っています（0.23.2で確認）。

```python
>>> from sklearn.preprocessing import MultiLabelBinarizer
>>> mlb = MultiLabelBinarizer()
>>> type(mlb)
<class 'sklearn.preprocessing._label.MultiLabelBinarizer'>
```

なお、scikit-learnのバージョンが違うことのWarningも出ますので、文面にあるように自己責任でお願いします。

>/.../env/lib/python3.7/site-packages/sklearn/base.py:334: UserWarning: Trying to unpickle estimator MultiLabelBinarizer from version 0.21.3 when using version 0.23.2. This might lead to breaking code or invalid results. Use at your own risk.
>  UserWarning)

## メモ

`pip install -U`（[docs](https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-U)）にバージョン込みで指定すると、パッケージのバージョンを自由に変えられます（--upgradeオプションですが、**ダウングレードもできます**）。
👉バージョン指定の仕方について
https://pip.pypa.io/en/stable/user_guide/#understanding-your-error-message

TODO：pickleの仕組みを今よりももう少し理解したいです