はじめに(なにか始まったぞ...)
(この物語は、Python 3.11.8で作った仮想環境でお届けします)
あるところにtransformersをインストールして開発を進めるPythonプロジェクトがありました。
% pip install transformers
そのプロジェクトではrouge-scoreも追加でインストールしました1。
% pip install rouge-score
プロジェクトが進む中でrouge-scoreだけをアンインストールすることになりました。
さあどうしますか?
目次
単にpip uninstallしては?
Pythonチュートリアルではpip uninstall rouge-score
が案内されます。
https://docs.python.org/ja/3/tutorial/venv.html#managing-packages-with-pip
pip install
の逆操作ですね。
しかしこれだと不都合があります。
% pip uninstall rouge-score Found existing installation: rouge_score 0.1.2 Uninstalling rouge_score-0.1.2: Would remove: /.../.venv/lib/python3.11/site-packages/rouge_score-0.1.2.dist-info/* /.../.venv/lib/python3.11/site-packages/rouge_score/* Proceed (Y/n)? Y Successfully uninstalled rouge_score-0.1.2
pip uninstall rouge-score
ではrouge-scoreしかアンインストールされません。
rouge-scoreが依存するライブラリも不要になっていますが、残っているのです。
transitiveな依存
以下の記事の中でtransitiveという語を使いました。
ルーツはこちらです。
今回のPythonプロジェクトの場合、直接(direct)の依存がtransformersとrouge-scoreです。
transformersとrouge-scoreが依存する数々のライブラリが(プロジェクトから見て)transitiveな依存となります。
rouge-scoreをアンインストールする場合、rouge-scoreが依存するライブラリ(プロジェクトから見てtransitiveな依存)もアンインストールしたいです。
しかしながら、rouge-scoreとtransformersとで共通の依存ライブラリは(transformersで使うので)アンインストールしたくはありません。
箇条書きでまとめてみます
- プロジェクトの直接の依存
- transformers
- rouge-score
- rouge-scoreをアンインストールする
- rouge-scoreの依存(プロジェクトから見てtransitiveな依存)のうち
- rouge-scoreのみが依存するライブラリはアンインストール
- transformersも依存ライブラリは残す(アンインストールしたら環境が壊れてしまうため)
transitiveな依存の一部を残すのがツラい
私の知っている方法を記録のために書きます(アップデートしたい!)
transitiveな依存を把握するためにツールを使います。
CLIから使うPythonライブラリなのでpipx2で入れました(pipx install
)。
依存を木構造で書き出します。
% pipdeptree --python .venv/bin/python pip==24.0 rouge_score==0.1.2 ├── absl-py [required: Any, installed: 2.1.0] ├── nltk [required: Any, installed: 3.8.1] │ ├── click [required: Any, installed: 8.1.7] │ ├── joblib [required: Any, installed: 1.4.0] │ ├── regex [required: >=2021.8.3, installed: 2024.4.16] │ └── tqdm [required: Any, installed: 4.66.2] ├── numpy [required: Any, installed: 1.26.4] └── six [required: >=1.14.0, installed: 1.16.0] setuptools==69.5.1 transformers==4.40.1 ├── filelock [required: Any, installed: 3.13.4] ├── huggingface-hub [required: >=0.19.3,<1.0, installed: 0.22.2] │ ├── filelock [required: Any, installed: 3.13.4] │ ├── fsspec [required: >=2023.5.0, installed: 2024.3.1] │ ├── packaging [required: >=20.9, installed: 24.0] │ ├── PyYAML [required: >=5.1, installed: 6.0.1] │ ├── requests [required: Any, installed: 2.31.0] │ │ ├── certifi [required: >=2017.4.17, installed: 2024.2.2] │ │ ├── charset-normalizer [required: >=2,<4, installed: 3.3.2] │ │ ├── idna [required: >=2.5,<4, installed: 3.7] │ │ └── urllib3 [required: >=1.21.1,<3, installed: 2.2.1] │ ├── tqdm [required: >=4.42.1, installed: 4.66.2] │ └── typing_extensions [required: >=3.7.4.3, installed: 4.11.0] ├── numpy [required: >=1.17, installed: 1.26.4] ├── packaging [required: >=20.0, installed: 24.0] ├── PyYAML [required: >=5.1, installed: 6.0.1] ├── regex [required: !=2019.12.17, installed: 2024.4.16] ├── requests [required: Any, installed: 2.31.0] │ ├── certifi [required: >=2017.4.17, installed: 2024.2.2] │ ├── charset-normalizer [required: >=2,<4, installed: 3.3.2] │ ├── idna [required: >=2.5,<4, installed: 3.7] │ └── urllib3 [required: >=1.21.1,<3, installed: 2.2.1] ├── safetensors [required: >=0.4.1, installed: 0.4.3] ├── tokenizers [required: >=0.19,<0.20, installed: 0.19.1] │ └── huggingface-hub [required: >=0.16.4,<1.0, installed: 0.22.2] │ ├── filelock [required: Any, installed: 3.13.4] │ ├── fsspec [required: >=2023.5.0, installed: 2024.3.1] │ ├── packaging [required: >=20.9, installed: 24.0] │ ├── PyYAML [required: >=5.1, installed: 6.0.1] │ ├── requests [required: Any, installed: 2.31.0] │ │ ├── certifi [required: >=2017.4.17, installed: 2024.2.2] │ │ ├── charset-normalizer [required: >=2,<4, installed: 3.3.2] │ │ ├── idna [required: >=2.5,<4, installed: 3.7] │ │ └── urllib3 [required: >=1.21.1,<3, installed: 2.2.1] │ ├── tqdm [required: >=4.42.1, installed: 4.66.2] │ └── typing_extensions [required: >=3.7.4.3, installed: 4.11.0] └── tqdm [required: >=4.27, installed: 4.66.2]
出力はファイルにリダイレクトして参照しました。
アンインストールしたいrouge-scoreについて、木構造の葉にあたるライブラリを確認します。
- rouge-scoreのみに依存するtransitiveな依存(=一緒にアンインストールできる)
- absl-py
- click
- joblib
- (葉ではないが nltk)
- six
- transformersにも依存するtransitiveな依存(=アンインストールしてはならない)
- regex
- tqdm
- numpy
というわけで、rouge-scoreと一緒にアンインストールできるライブラリも消すコマンドは
% pip uninstall -y rouge-score absl-py click joblib nltk six
となります
終わりに
依存ライブラリをpipで管理すると、直接の依存をアンインストールするときに、一緒にアンインストールできるtransitiveな依存を把握するのが大変なことを書きました。
思うに、pip以外のツールが必要なタイミングなのだと思います。
Poetry、Pipenvなどなど色々なツールがありますよね。
直接の依存をaddしたらremoveはツールに任せられる、という理解です。
単一のスクリプトを開発する場合なら直接の依存の増減はあまりないのでpipで事足りてきたのではないかと思うのですが、より大きなアプリケーションの開発では直接の依存を変更しやすくするためにツールの使用を検討したいですね