「Pythonではじめる機械学習」 scikit-learn 1.2.0 にともなうmglearnのimportエラーについて

機械学習の勉強のため、良書と評判の「Pythonではじめる機械学習」を写経していた際に直面したエラーと対処方法について説明します。
2022年末にかけて`scikit-learn`や`numpy`のバージョン更新に伴い、本書記載通りに環境を作成しても、本書のpythonコードが実行できなくなっています。この記事では、本書冒頭でハマりやすいエラーに関し、その対応方法についてまとめています。

## 環境
- Python 3.10.7
- Dockerコンテナ(python:3.10-slim-buster)
- Jupyter Lab 3.5.1

## 問題
ライブラリ`mglearn`をimportしたところ`scikit-learn`に関する下記エラーが発生
```
ImportError: 
`load_boston` has been removed from scikit-learn since version 1.2.
```

## 問題が起こった経緯
本書の[サポートサイト](https://github.com/amueller/introduction_to_ml_with_python)にあるように、何気なく環境構築のため  
`$ pip install numpy scipy scikit-learn matplotlib pandas pillow graphviz` 
を実行しました。

その後、本書 p.11の「1.4.6 mglearn」に記載の通り、Pythonライブラリである`mglearn`をpipコマンドで同様にインストールしました。
`$ pip install mglearn`

上記ライブラリをインストールした後、Jypyter notebook上でライブラリをimportするため下記コマンドを実行しました。
```
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import mglearn
from IPython.display import display
```

このタイミングで以下のようなエラーが生じました。
```
File /usr/local/lib/python3.10/site-packages/sklearn/datasets/__init__.py:156, in __getattr__(name)
    105 if name == "load_boston":
    106     msg = textwrap.dedent(
    107         """
    108         `load_boston` has been removed from scikit-learn since version 1.2.
   (...)
    154         """
    155     )
--> 156     raise ImportError(msg)
    157 try:
    158     return globals()[name]

ImportError: 
`load_boston` has been removed from scikit-learn since version 1.2.

The Boston housing prices dataset has an ethical problem: as
investigated in [1], the authors of this dataset engineered a
non-invertible variable "B" assuming that racial self-segregation had a
positive impact on house prices [2]. Furthermore the goal of the
research that led to the creation of this dataset was to study the
impact of air quality but it did not give adequate demonstration of the
validity of this assumption.

The scikit-learn maintainers therefore strongly discourage the use of
...
[2] Harrison Jr, David, and Daniel L. Rubinfeld.
"Hedonic housing prices and the demand for clean air."
Journal of environmental economics and management 5.1 (1978): 81-102.
<https://www.researchgate.net/publication/4974606_Hedonic_housing_prices_and_the_demand_for_clean_air>
```

どうやらエラーのとおり、scikit-learnのload_boston、つまり「ボストンの住宅価格のデータセット」が**scikit-learn 1.2.0で撤廃されてしまったのが原因なようです。**
このエラーに直面(及び本記事執筆)したのが、2022年12月13日だったのですが、scikit-learnがver.1.2.0を公開したのは2022年12月8日であり、どうやらタイムリーな地雷を踏んでしまったようでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676956/876ae7aa-685f-d41e-8d8f-91c2917d6864.png)
参考: [pypi scikit-learn 1.2.0](https://pypi.org/project/scikit-learn/1.2.0/)

## 解決策
とりあえずエラーが出ないようにするだけであれば、scikit-learnを再度インストールし、バージョンを下げるだけで解決します。
今回はscikit-learn ver.1系の最新版の1.1.3を使用しました。
```
$ !pip uninstall -y scikit-learn
$ pip install scikit-learn==1.1.3
```

## しかし、新たに生じる別のエラー...

~~気を取り直して、また再度`mglearn`をimportしようとすると、先ほどとは異なる下記のエラーが生じました。~~
```
File /usr/local/lib/python3.10/site-packages/mglearn/plot_pca.py:7
      3 import numpy as np
      5 from joblib import Memory
----> 7 memory = Memory(cachedir="cache")
     10 def plot_pca_illustration():
     11     rnd = np.random.RandomState(5)

TypeError: Memory.__init__() got an unexpected keyword argument 'cachedir'
```

~~こちらもどうやら`mglearn`の内部で利用しているライブラリのバージョンの問題のようですが、修正がまだのようです。
こちらの解決方法についてはどうやらいくつか参考になる記事があるので、そちらに譲りたいと思います。~~
- ~~[Qiita - import mglearn: Memory.__init__() got an unexpected keyword argument 'cachedir'](https://qiita.com/TLIsOnTL/items/6cd06a1569112568c8d0)~~
- ~~[teratail - Google Colaboratory　memory = Memory(cachedir="cache")　エラー](https://teratail.com/questions/jep3vj7dxob4yy)~~
- ~~[github - koyachi/introduction_to_ml_with_python](https://github.com/koyachi/introduction_to_ml_with_python/commit/12285107ad9b8aee5da46faf619fe03d8571fcbd)~~

~~※ 私は[本書サポート用githubレポジトリ](https://github.com/amueller/introduction_to_ml_with_python)上の`mglearn`ライブラリが含まれるフォルダを自信が作成したJupyter notebookのファイルと同じ場所に配置し、当該箇所を書き換えることで対応しました。~~

~~上記2つの対応を行うことで`mglearn`をimportすることができるようになりました。~~


**以下追記**
2023年2月現在、`mglearn`の上記エラーについては下記のgithubのプルリクエストがマージされたため、解決した模様です。
[Merge pull request #165 from koyachi/fix/joblib-memory-interface](https://github.com/amueller/introduction_to_ml_with_python/commit/842e623dc16d194cb23c19882fc013e6b425d443)

なお、この状態でも`mglearn`に関し以下2つのエラーが発生します。その説明と解決歩法について説明します。
### **`imageio`のimportエラー**

**【エラー内容】**
`mglearn`をimportすると下記エラーが発生します。
```
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[3], line 4
      2 import matplotlib.pyplot as plt
      3 import pandas as pd
----> 4 import mglearn
      5 from IPython.display import display

File /home/src/mglearn/__init__.py:1
----> 1 from . import plots
      2 from . import tools
      3 from .plots import cm3, cm2

File /home/src/mglearn/plots.py:2
      1 from .plot_linear_svc_regularization import plot_linear_svc_regularization
----> 2 from .plot_interactive_tree import plot_tree_progressive, plot_tree_partition
      3 from .plot_animal_tree import plot_animal_tree
      4 from .plot_rbf_svm_parameters import plot_svm

File /home/src/mglearn/plot_interactive_tree.py:8
      6 from io import StringIO
      7 from sklearn.tree import export_graphviz
----> 8 from imageio import imread
      9 from scipy import ndimage
     10 from sklearn.datasets import make_moons

ModuleNotFoundError: No module named 'imageio'
```
**【解決方法】**
これは素直に`imageio`をinstallすれば解決します。  
`$ pip install imageio`


### **`numpy`のバージョン問題**
**【エラー内容】**
第二章のp.32において`mglearn`よりデータを取得するコマンド `X, y = mglearn.datasets.make_forge()` を実行すると下記エラーが発生します。

```
AttributeError                            Traceback (most recent call last)
Cell In[3], line 1
----> 1 X, y = mglearn.datasets.make_forge()

File /home/src/mglearn/datasets.py:16, in make_forge()
     14 X, y = make_blobs(centers=2, random_state=4, n_samples=30)
     15 y[np.array([7, 27])] = 0
---> 16 mask = np.ones(len(X), dtype=np.bool)
     17 mask[np.array([0, 1, 5, 26])] = 0
     18 X, y = X[mask], y[mask]

File /usr/local/lib/python3.10/site-packages/numpy/__init__.py:305, in __getattr__(attr)
    300     warnings.warn(
    301         f"In the future `np.{attr}` will be defined as the "
    302         "corresponding NumPy scalar.", FutureWarning, stacklevel=2)
    304 if attr in __former_attrs__:
--> 305     raise AttributeError(__former_attrs__[attr])
    307 # Importing Tester requires importing all of UnitTest which is not a
    308 # cheap import Since it is mainly used in test suits, we lazy import it
    309 # here to save on the order of 10 ms of import time for most users
    310 #
    311 # The previous way Tester was imported also had a side effect of adding
    312 # the full `numpy.testing` namespace
    313 if attr == 'testing':

AttributeError: module 'numpy' has no attribute 'bool'.
```
これは`numpy`のバージョンが1.24.0以上のときに発生するエラーのようです。
※ 参考までに`numpy`のバージョン1.24.0は2022年12月19日にリリースされたようです
解決方法としては`numpy`を一度アンインストールし、ダウングレードする必要があります。
```
$ !pip uninstall -y numpy
$ pip install numpy==1.23
```
参考: https://github.com/NVIDIA/TensorRT/issues/2567


## まとめ
2022年12月現在、本書記載通りにpipコマンドで`mglearn`, `scikit-learn`をインストールすると、`mglearn`のimportの際にエラーが生じてしまいます。
そのため以下の対応を行う必要があります。
- `scikit-learn`のバージョンを1.2.0未満のものにする
- `numpy`のバージョンを1.24.0未満のものにする
- `imageio` をinstallする
- ~~`mglearn`のファイルを一部書き換える~~ **【解決済み】**


「Pythonではじめる機械学習」を読んで困った方の救いになれば幸いです。