ベースに gcr.io/deeplearning-platform-release/tf-gpu を使って tensorflow-gpu をインストールした Docker イメージを作る

# やりたいこと
ベースイメージに `gcr.io/deeplearning-platform-release/tf-gpu.2-9:m95` などを使用して `tensorflow-gpu` をインストールした Docker イメージを作ります。

:::note warn
イメージ名的に `tensorflow-gpu` をインストールする必要はなさそうな雰囲気を感じますが、インストールしないと GPU を使っての学習はしてくれないようです。
:::

2022年12月時点で GCP から案内されている、利用可能なイメージは以下のドキュメントで案内されています。

https://cloud.google.com/deep-learning-containers/docs/choosing-container#deciding

以下の GCS バケットでは各ベースイメージにインストールされている Python パッケージの内容を確認できます。

https://console.cloud.google.com/storage/browser/deeplearning-platform-release/installed-dependencies/containers


# まずは結論

 - 最新版の tensorflow を使う場合は `gcr.io/deeplearning-platform-release/tf-gpu.2-6:m95` を使い、インストール済みの tensorflow のバージョンを上書きするのが一番楽
 - `gcr.io/deeplearning-platform-release/tf-gpu.2-9:m95` を使うとインストール済みの `numpy` を上書きする必要がある

結論だけ見てもよくわからないと思うので、上記結論に至った経緯をつらつらと書いていきます。

# 最新版のベースイメージを使用して `tesnorflow-gpu` をインストール
2022年12月時点で GCP が案内している最新版のベースイメージは `gcr.io/deeplearning-platform-release/tf-gpu.2-9:m95` です。このベースイメージには元々 `tensorflow == 2.9.2` がインストールされていますが、`tensorflow-gpu` をインストールして最新版の `tensorflow` で上書きします。

以下の Dockerfile でビルドします。

```Dockerfile
FROM gcr.io/deeplearning-platform-release/tf-gpu.2-9:m95

RUN pip install tensorflow-gpu
```

ビルドしてコンテナを起動し、インストールされている tensorflow のバージョンを確認すると現時点での最新版である`tensorflow==2.11.0` がインストールされていることがわかります。

```shell
$ docker build -t tf29 .
# 依存関係がぶっ壊れている、という pip の警告メッセージが大量に出ますがビルド自体は成功しているので一旦無視で OK です
$ docker run -it --rm tf29 /bin/bash
root@380290e871f0:/# pip list | grep tensor
tensorboard                           2.11.0
tensorboard-data-server               0.6.1
tensorboard-plugin-wit                1.8.1
tensorflow                            2.9.1
tensorflow-cloud                      0.1.16
tensorflow-datasets                   4.4.0
tensorflow-estimator                  2.11.0
tensorflow-gpu                        2.11.0
tensorflow-hub                        0.12.0
tensorflow-io                         0.23.1
tensorflow-io-gcs-filesystem          0.26.0
tensorflow-metadata                   1.9.0
tensorflow-probability                0.14.1
tensorflow-serving-api                2.9.1
tensorflow-transform                  1.9.0
```

**しかし ipython で tensorflow のバージョンを確認するために import してみると以下のエラーで落ちます**。numpy のバージョンエラーのようです。

>RuntimeError: module compiled against API version 0xe but this version of numpy is 0xd

```shell
import tensorflow as tf
2022-12-13 01:51:53.047766: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
RuntimeError: module compiled against API version 0xe but this version of numpy is 0xd
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
ImportError: numpy.core.multiarray failed to import

The above exception was the direct cause of the following exception:

SystemError                               Traceback (most recent call last)
SystemError: <built-in method __contains__ of dict object at 0x7f2d8c095690> returned a result with an error set

The above exception was the direct cause of the following exception:

ImportError                               Traceback (most recent call last)
<ipython-input-1-64156d691fe5> in <module>
----> 1 import tensorflow as tf

/opt/conda/lib/python3.7/site-packages/tensorflow/__init__.py in <module>
     35 import typing as _typing
     36 
---> 37 from tensorflow.python.tools import module_util as _module_util
     38 from tensorflow.python.util.lazy_loader import LazyLoader as _LazyLoader
     39 

/opt/conda/lib/python3.7/site-packages/tensorflow/python/__init__.py in <module>
     35 
     36 from tensorflow.python import pywrap_tensorflow as _pywrap_tensorflow
---> 37 from tensorflow.python.eager import context
     38 
     39 # pylint: enable=wildcard-import

/opt/conda/lib/python3.7/site-packages/tensorflow/python/eager/context.py in <module>
     32 from tensorflow.python import pywrap_tfe
     33 from tensorflow.python import tf2
---> 34 from tensorflow.python.client import pywrap_tf_session
     35 from tensorflow.python.eager import executor
     36 from tensorflow.python.eager import monitoring

/opt/conda/lib/python3.7/site-packages/tensorflow/python/client/pywrap_tf_session.py in <module>
     17 # pylint: disable=invalid-import-order,g-bad-import-order, wildcard-import, unused-import
     18 from tensorflow.python import pywrap_tensorflow
---> 19 from tensorflow.python.client._pywrap_tf_session import *
     20 from tensorflow.python.client._pywrap_tf_session import _TF_SetTarget
     21 from tensorflow.python.client._pywrap_tf_session import _TF_SetConfig

ImportError: initialization failed
```

## numpy のバージョンを確認してみる
numpy のバージョンエラーが出たため、使用されている numpy のバージョンを確認します。まずはインストールされている numpy を pip で確認します。

```shell
root@380290e871f0:/# pip list | grep numpy
numpy                                 1.21.6
```
`1.21.6` がインストールされているみたいです。続いて ipython で使用している numpy のバージョンを確認してみます。

```shell
root@380290e871f0:/# ipython
Python 3.7.12 | packaged by conda-forge | (default, Oct 26 2021, 06:08:53) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.33.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import numpy as np
In [2]: np.__version__
Out[2]: '1.19.5'
```
`1.19.5` …？こいつがどこから来ているのかを調べてみます。

```shell
In [3]: np.__file__
Out[3]: '/opt/conda/lib/python3.7/site-packages/numpy/__init__.py'
```

numpy はここの site-packages を参照していることがわかります。続いて参照している numpy のバージョンを確認してみます。

```shell
root@380290e871f0:/# ls -la /opt/conda/lib/python3.7/site-packages | grep numpy
drwxr-xr-x 18 root root    4096 Aug  6 08:08 numpy
drwxr-xr-x  2 root root    4096 Aug  6 08:08 numpy-1.19.5.dist-info
drwxr-xr-x  2 root root    4096 Aug  6 08:08 numpy-1.21.6.dist-info
drwxr-xr-x  2 root root    4096 Aug  6 08:08 numpy.libs

root@380290e871f0:/# cat /opt/conda/lib/python3.7/site-packages/numpy/version.py 
# THIS FILE IS GENERATED FROM NUMPY SETUP.PY
#
# To compare versions robustly, use `numpy.lib.NumpyVersion`
short_version = '1.19.5'
version = '1.19.5'
full_version = '1.19.5'
git_revision = '8f4b73a0d04f7bebb06a154b43e5ef5b5980052f'
release = True

if not release:
    version = full_version
```

numpy は `1.19.5` と `1.21.6` の2つのバージョンがインストールされていますが、参照しているのは `1.19.5` になっていることが確認できました。

:::note warn
なぜ `pip list` で参照される numpy のバージョンが `1.21.6` になっているのかは正直ちゃんとわかっていないです。 pip がどのようにパッケージのバージョンを参照しているのかという仕組みから調査する必要があります。
:::

ちなみにこの `1.21.6` というバージョンは、**ベースイメージに元からインストールされているものであるため、「2つのバージョンがインストールされている」という問題を根本解決することはできません。**

## numpy が複数バージョンインストールされている状況への対処
改めて numpy をインストールするほかありません。普通に `pip install` すると既に最新版である旨のメッセージが出てしまうため、インストール済みの如何に関わらず強制的に再インストールします。

```shell
root@380290e871f0:/# pip install -U numpy --ignore-installed
root@380290e871f0:/# cat /opt/conda/lib/python3.7/site-packages/numpy/version.py 
from ._version import get_versions
__ALL__ = ['version', 'full_version', 'git_revision', 'release']
vinfo = get_versions()
version: str = vinfo["version"]
full_version: str = vinfo['version']
git_revision: str = vinfo['full-revisionid']
release = 'dev0' not in version and '+' not in version
short_version: str = vinfo['version'].split("+")[0]
del get_versions, vinfo

root@380290e871f0:/# cat /opt/conda/lib/python3.7/site-packages/numpy/_version.py 
# This file was generated by 'versioneer.py' (0.19) from
# revision-control system data, or from the parent directory name of an
# unpacked source archive. Distribution tarballs contain a pre-generated copy
# of this file.
import json
version_json = '''
{
 "date": "2022-04-11T17:43:10-0600",
 "dirty": false,
 "error": null,
 "full-revisionid": "ef0ec786fd4c7622ad2fa0e54d3881f3b9bbd792",
 "version": "1.21.6"
}
'''  # END VERSION_JSON
```

無事 `1.21.6` を参照するようになっていそうです。

## 改めて動作確認

```python
import tensorflow as tf
2022-12-19 15:36:15.199638: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2022-12-19 15:36:16.507049: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/cuda/lib64:/usr/local/cuda/lib:/usr/local/lib/x86_64-linux-gnu:/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/usr/local/nvidia/lib:/usr/local/nvidia/lib64
2022-12-19 15:36:16.507233: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/cuda/lib64:/usr/local/cuda/lib:/usr/local/lib/x86_64-linux-gnu:/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/usr/local/nvidia/lib:/usr/local/nvidia/lib64
2022-12-19 15:36:16.507262: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.
```
ちゃんと tensorflow が import できました。

:::note hint
#### ここまでにわかったこと
- `import tensorflow as tf` で落ちるのは意図した numpy のバージョンを使えていないことが原因
- `gcr.io/deeplearning-platform-release/tf-gpu.2-9:m95` には複数の numpy バージョンがインストールされている
  - 本来 `1.21.6` を参照してほしいのに `1.19.5` を参照している
  - `tensorflow-gpu` を後から追加することで numpy のバージョンエラーが生じるようになった
:::


# ちょっと古いベースイメージを使用して `tesnorflow-gpu` をインストール
ここまでに、複数の numpy がベースイメージに混在していることでその対処が必要になっていたため、逆に最初から単一の numpy がインストールされているベースイメージを探しました。

選ばれたのは（単一バージョンの numpy がインストールされていたのは） `gcr.io/deeplearning-platform-release/tf-gpu.2-6:m95` でした。

前段で実施した手順と同じ手順でイメージをビルドし、動作確認します。

```Dockerfile
FROM gcr.io/deeplearning-platform-release/tf-gpu.2-6:m95

RUN pip install tensorflow-gpu
```

```shell
$ docker build -t tf26 .
$ docker run -it --rm tf26 /bin/bash

root@5e274c7ddda1:/# pip list | grep numpy
numpy                                 1.21.6

root@5e274c7ddda1:/# ipython
Python 3.7.12 | packaged by conda-forge | (default, Oct 26 2021, 06:08:53) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.33.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import tensorflow as tf
2022-12-19 15:51:46.765250: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2022-12-19 15:51:48.094737: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/cuda/lib64:/usr/local/cuda/lib:/usr/local/lib/x86_64-linux-gnu:/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/usr/local/cuda/lib64:/usr/local/cuda/lib:/usr/local/lib/x86_64-linux-gnu:/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/usr/local/nvidia/lib:/usr/local/nvidia/lib64
2022-12-19 15:51:48.094938: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/cuda/lib64:/usr/local/cuda/lib:/usr/local/lib/x86_64-linux-gnu:/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/usr/local/cuda/lib64:/usr/local/cuda/lib:/usr/local/lib/x86_64-linux-gnu:/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/usr/local/nvidia/lib:/usr/local/nvidia/lib64
2022-12-19 15:51:48.094969: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.
```

特に問題なく import できました。

# 改めて結論
 - 最新版である `gcr.io/deeplearning-platform-release/tf-gpu.2-9:m95` を使う場合は numpy のバージョンをいじる必要がある
 - `gcr.io/deeplearning-platform-release/tf-gpu.2-6:m95` では不要
 - つまりベースイメージは `gcr.io/deeplearning-platform-release/tf-gpu.2-6:m95` を使って新しい tensorflow をインストールする、という運用が一番楽

いつかどこかの誰かの役に立つことを祈っています。