Jetson TX2でもPipenvを使いたい

# Intro

データ解析業務なんかでPythonに色々なライブラリを入れて使うとき、環境分離ができるとなにかと助かる。
[pipenv](https://pipenv-ja.readthedocs.io/ja/translate-ja/)はPython界隈で(おそらく)一番使われている構成管理・環境分離のためのツール。私もLinuxPC上での開発や、実環境へのデプロイで大変お世話になっている。

ところで、先日、NVIDIAの出している組み込み向けAIコンピューティングデバイス [Jetson TX2](https://www.nvidia.com/ja-jp/autonomous-machines/embedded-systems/jetson-tx2/)で色々開発する機会があった。
GPUを搭載し深層学習に最適化されたパワフルなマシンらしいのだが…GPUはTegra。そしてCPUはARM製。`nvidia-smi`すら叩けない、ちょっと特殊なハードウェア。(代わりに`tegrastats`というコマンドが用意されている)

このマシンにpipenvを入れたところ、インストールは成功するも開発時に何かとハマったので、解決方法を書いてみた。

(Jetpack 4.2で作業。執筆時点での最新版が4.2.2なので、細かな差異があるかもしれません)

# pipenvを入れるまで
pipenvのインストール自体は、そこまで難しくない。

まずは`pip(pip3)`が入っていなかったのでインストールする。

```sh

# aptでpython3-pipは入れない
# 依存関係が壊れ、筆者環境ではpipコマンドがエラーを吐くようになってしまった(その場合Pythonを再インストール)
sudo apt install curl
curl -kL https://bootstrap.pypa.io/get-pip.py | sudo python3 
pip3 -V
```

pipenvと一緒に使うpyenvをインストールする。

```sh
git clone https://github.com/pyenv/pyenv.git ~/.pyenv
```

環境変数設定とpyenvのinitializeが、シェル起動時に行われるようにする。
筆者の場合zshを使っているので、`~/.zshrc`に以下を追記する。

```vim
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$HOME/.local/bin:$PATH"
eval "$(pyenv init -)"
```

続いてpipでpipenvをインストールする

```sh
sudo pip3 install pipenv 
```

`pipenv`コマンドを叩いて、以下のUsageが表示されたらインストール完了。

```sh
Usage: pipenv [OPTIONS] COMMAND [ARGS]...
(省略)
```
インストール作業自体は普通のUbuntu等とかわらない。

# 普通のpipenvの使い方

pipenvの使い方は [なりとさんのブログ](https://narito.ninja/blog/detail/58/)で非常にわかりやすく説明されています。
また[公式ドキュメント](https://pipenv-ja.readthedocs.io/ja/translate-ja/basics.html#example-pipfile-pipfile-lock)により高度な使い方が説明されています。

多くの場合、下のような`Pipfile`を作成した後、`pipenv install`で仮想環境を作成。
`pipenv shell`や`pipenv run`で仮想環境でコマンドを実行…というのがよくある使い方ではないでしょうか。

```toml
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true

[dev-packages]

[packages]
requests = "*"

[requires]
python_version = "3.6"
```

# OpenCVを使いたい(システムのパッケージを使いたい)

ここからがJetson TX2らしい(?)使い方。
Jetpack(JetsonのOSとソフトをまとめたソフトウェア群)にははじめから、OpenCVがインストールされている。
手動でOpenCVを入れなくとも、`python3`でデフォルトのPythonを起動し、

```python
import cv2
```
でimportできる。

未確認だが、最初から入っているOpenCVならgstreamerやTegraへの対応がされていて、Jetsonのハード性能を引き出せそう(そうであってほしい)。
つまり極力デフォルトのOpenCVを使いたいのだが、仮想環境を構築してしまうとそのOpenCVは参照できなくなる。

そこで環境の分離度は下がるものの、システムのpythonパッケージを継承しその上に仮想環境を構築してみる。

試しに`opencv`(デフォルト)と`requests`(仮想環境が提供)が入った環境を用意してみる。

```bash
# 適当なデモ用ディレクトリを掘る
mkdir opencv_install
cd opencv_install/
```

```bash
# OpenCVがインストールされたPython実行ファイルの場所を調べる
which python3 # -> /usr/bin/python3
```

```bash
# バージョンを調べる。
/usr/bin/python3 -V # -> 3.6.7
```

次のPipfileを用意。`requests`パッケージだけで、`openCV`はどこにも書かれていないことに留意。
仮想環境の`python_version`は念の為、上記で調べたpythonのバージョンに合わせておく。

```toml
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
requests = "*"

[requires]
python_version = "3.6.7"

```

`Pipfile`を用意したら仮想環境を作成するのだが、ここでちょっと工夫をする。

```python
# --python <path> でpythonの実行ファイルを指定
# --site-packages でそのpythonのsite-packagesを継承する
# なお install を省略すると、空の仮想環境を作ることもできる
pipenv --python /usr/bin/python3 --site-packages install
```

`--site-packages`がここのポイント。システムのPythonのライブラリが仮想環境内で参照できるようになる。`--python`で、どのPythonのライブラリを参照するか明示的に指定する。

仮想環境が作成されたら、 `OpenCV`と`requests`が使えるか実際にみてみる。

```sh
pipenv shell
python #インタプリタ起動
```
```python

import cv2 # 仮想環境の外から。読み込めてる
import requests # 仮想環境から読み込めてる
```

# Tensorflowを使いたい(NVIDIAのリポからインストール)
## そもそも、JetsonにTensorflowを入れるには?
(Jetpack4.2.1以降?では、最初からTensorflowがインストール済みであるよう。)

せっかくGPUを搭載したマシンなので、`tensorflow-gpu`を入れたいところだが、`pip install tensorflow-gpu`では入らない。(こんな環境のパッケージなんぞ配布してない、と怒られる。)

NVIDIAがJetson向けにビルド済みのTensorflowを配布しているので、ここからインストールする。[公式ドキュメント](https://docs.nvidia.com/deeplearning/frameworks/install-tf-jetson-platform/index.html)に方法が記載されているのでこれに倣ってすすめる。

```bash
# 必要なパッケージのインストール
sudo apt-get install libhdf5-serial-dev hdf5-tools libhdf5-dev zlib1g-dev zip libjpeg8-dev

# tensorflowをdeveloper.download.nvidia.comからインストールする
sudo pip3 install --pre --extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v42 tensorflow-gpu
```

なおバージョンを指定したインストールもできる。配布されているバージョンは[このサイト](https://docs.nvidia.com/deeplearning/frameworks/install-tf-jetson-platform-release-notes/tf-jetson-rel.html#tf-jetson-rel)で確認できる。

## pipenvでインストールするには

上記のように、デフォルトのpypiリポジトリ外からのインストールも`pipenv`は対応できる。
次のように`Pipfile`を書いておけばよい。

```toml

[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[[source]]
name = "nvidia_repo"
url = "https://developer.download.nvidia.com/compute/redist/jp/v42"
verify_ssl = true


[packages]
tensorflow-gpu = {version="*", index='nvidia_repo'}

[requires]
python_version = "3.6"

```

`[[source]]`でリポジトリを宣言後、`[packages]`の項でどのリポジトリからインストールするか指定する。(未指定の場合はpypiが自動的に使われる?)

# Jetsonでpipenv使ってみた感想

基本的には**Jetsonでもpipenvつかえるのすごい便利！**
なのだが、いくつか難点が。

- scipy, tensorflow-gpu等のインストールにかかる時間がいちいち長い(例えば上記tensorfowの場合15分ほど。CPUがやや弱い?)
- Jetsonの内蔵ストレージは小さい(32GB)…大きなパッケージをあちこちの環境でインストールするとストレージがパンパンに。(`pipenv --rm`ですぐ消せるけれど。)
- 結局OpenCVみたいにシステムに入っているパッケージを参照したい場合が多い

とはいえ`pipenv`にはバージョンを固定できたり、環境構築作業を手順化できたりと他のメリットも多い。
用法用量を守って使っていきます。