TensorFlow 2.xでObject Detection APIを使うにはTensorFlowバージョンを揃える必要がある話


機械学習周りで物体検出したいとき、TensorFlowとともに[Object Detection API](https://github.com/tensorflow/models/tree/master/research/object_detection)を利用することがあります。

Object Detection API側のアップデートで新規Docker環境が動かなくなったことがありました。

<!-- more -->

## TL; DR
tensorflowおよびtensorflow関連ライブラリでバージョンを揃えればOKです。以下どちらかを行います。

1. ベースとなるimageで最新のTensorFlow (GPU)バージョンのimageを使用する
1. (workaround) Dockerfile内で同一バージョンのtensorflow, tensorflow-text, tf-models-officialをインストールする
    - TensorFlow 2.4.0未満の場合、Object Detection APIの特定コミットをチェックアウトする

## 検証環境
- Ubuntu 20.04 LTS(カーネル: 5.4.0-81-generic)
- Docker version 19.03.13
- Dockerfile: Object Detection API公式で提供されている[Dockerfile](https://github.com/tensorflow/models/blob/4b617781f28e6cccd5b081856e7797d4c5b9acd4/research/object_detection/dockerfiles/tf2/Dockerfile)から、ベースとなるimageを`tensorflow/tensorflow:2.4.0-gpu`に変更したもの

## 発生した問題
Object Detection API学習用のDockerコンテナを作って、model_main_tf2.pyで学習をかけました。すると、tensorflow-textのライブラリ部分で参照エラーが発生しクラッシュしました。

```bash
tensorflow.python.framework.errors_impl.NotFoundError: /usr/local/lib/python3.6/dist-packages/tensorflow_text/python/metrics/_text_similarity_metric_ops.so: undefined symbol: _ZN10tensorflow8OpKernel11TraceStringEPNS_15OpKernelContextEb
```

このエラーが出る理由は、tensorflow関連ライブラリ間でバージョンが一致していないためです。バージョンが異なると、tensorflow関連ライブラリのバイナリ間で差異が出ます。そのため、一部シンボルを読み出せなくなる可能性があります。

なぜバージョンが一致しなくなるかですが、Object Detection API(object_detectionライブラリ)のインストール時、常に最新のtensorflowライブラリをインストールしてしまうためです。これにより、2つのバージョンのtensorflowライブラリがインストールされてしまい、整合性が取れなくなります(詳しい理由については[付記](#付記: なぜObject Detection APIで最新のTensorFlowライブラリがインストールされてしまうのか？)を参照ください)。

## 対策
tensorflowライブラリとその関連ライブラリのバージョンを合わせればよいので、以下のどちらかで解消されます。

1. ベースとなるimageで最新のTensorFlow (GPU)バージョンのimageを使用する
1. Dockerfile内で同一バージョンのtensorflow, tensorflow-text, tf-models-officialをインストールする
    - TensorFlow 2.4.0未満の場合、Object Detection APIの特定コミットをチェックアウトする

### 1.ベースとなるimageで最新のTensorFlow (GPU)バージョンのimageを使用する
DockerfileでベースにしているTensorFlow (GPU)を最新版に変更します。

2021.09.23現在、2.6.0が最新です。そこで、Dockerfileを以下の通り書き換えます(適宜、最新のバージョンに読み替えてください)。

```diff
- FROM tensorflow/tensorflow:2.2.0-gpu
+ FROM tensorflow/tensorflow:2.6.0-gpu
```

### 2. (workaround) Dockerfile内で同一バージョンのtensorflow, tensorflow-text, tf-models-officialをインストールする
学習環境側で制約があり、どうしてもTensorFlowのバージョンを特定のバージョンに揃えたい場合があります。

その場合、Dockerfileを以下のように書き換えます。

※ TensorFlow 2.5.0をNVIDIA GPUと共に使いたい場合

```Dockerfile
FROM tensorflow/tensorflow:2.5.0-gpu
ARG DEBIAN_FRONTEND=noninteractive

# Install apt dependencies
RUN apt-get update && apt-get install -y \
    git \
    gpg-agent \
    python3-cairocffi \
    protobuf-compiler \
    python3-pil \
    python3-lxml \
    python3-tk \
    wget

# Add new user to avoid running as root
RUN useradd -ms /bin/bash tensorflow
USER tensorflow
WORKDIR /home/tensorflow

# Clone Object Detection API
RUN git clone https://github.com/tensorflow/models/ /home/tensorflow/models/

# Workaround: If you use TF 2.2.x, uncomment the line below.
# WORKDIR /home/tensorflow/models/
# RUN git checkout 03a6d6c8e79b426231a4d5ba0cf45be9afc8bad5

# Workaround: If you use TF 2.3.x, uncomment the line below.
# WORKDIR /home/tensorflow/models/
# RUN git checkout cf82a72480a41a62b4bbe0f1378d319f0d6f5d5c

# Compile protobuf configs
RUN (cd /home/tensorflow/models/research/ && protoc object_detection/protos/*.proto --python_out=.)
WORKDIR /home/tensorflow/models/research/

RUN cp object_detection/packages/tf2/setup.py ./
ENV PATH="/home/tensorflow/.local/bin:${PATH}"

# Workaround (For Tensorflow < 2.5.1): Remove tf-models-official dependency from object_detection, will install it manually.
RUN sed -i -e 's/^.*tf-models-official.*$//g' ./setup.py

RUN python -m pip install -U pip

# Workaround: Lock tensorflow and corresponding tf-models-official versions.
RUN python -m pip install tensorflow==2.5.0 tensorflow-text==2.5.0 tf-models-official==2.5.0
RUN python -m pip install .

ENV TF_CPP_MIN_LOG_LEVEL 3
```

変更点1. 使いたいバージョンのtensorflow、および対応するtensorflow-textとtf-models-official(学習済みモデル利用のためのライブラリ)を手動でインストールします。なお、tensorflow/tensorflow:x.x.x-gpuをベースimageとして使う場合、初期状態でtensorflowライブラリは入っていないため、特定バージョンのTensorFlowを指定する必要があります。


```diff
RUN python -m pip install -U pip
+ 
+ # Workaround: install tensorflow and corresponding tf-models-official versions.
+ RUN python -m pip install tensorflow==2.5.0 tensorflow-text==2.5.0 tf-models-official==2.5.0
RUN python -m pip install .

ENV TF_CPP_MIN_LOG_LEVEL 3
```

変更点2. (使いたいTensorFlowのバージョンが2.5.1未満の場合) object_detectionライブラリの依存関係から、tf-models-officialを削除してください(その代わりに、変更点1でtensorflowに対応したバージョンのtf-models-officialをインストールしています)。

```diff
ENV PATH="/home/tensorflow/.local/bin:${PATH}"

+ # Workaround (For tensorflow < 2.5.1): Remove tf-models-official dependency from object_detection, will install it manually.
+ RUN sed -i -e 's/^.*tf-models-official.*$//g' ./setup.py

RUN python -m pip install -U pip
```

最新のobject_detectionライブラリの[setup.py](https://github.com/tensorflow/models/blob/08b68031c56863a7439030f41537cfd0cc979966/research/object_detection/packages/tf2/setup.py)では、tf-models-official>=2.5.1に依存関係に指定されています。そのため、それ以前のtf-models-officialを依存関係を含めるために上記変更が必要です(object_detectionライブラリはバージョニング未対応で、過去のリリースバージョンを特定することが難しいことが理由です)。

変更点3. (使いたいTensorFlowのバージョンが2.4.0未満の場合) インストールに使うobject_detectionのソースコードについて、古いコミットを指定します。最新のobject_detectionライブラリでは、tf-models-officialの2.4.0未満に対応していないため実行時エラーになります[^1]。

以下の部分について、TensorFlow 2.2.xを使う場合は上の2行、2.3.xを使う場合は下の2行をコメントアウトしてください。

TensorFlow 2.2.xの場合

```diff
+ 
+ # Workaround: If you use TF 2.2.x, uncomment the line below.
+ WORKDIR /home/tensorflow/models/
+ RUN git checkout 03a6d6c8e79b426231a4d5ba0cf45be9afc8bad5
```

TensorFlow 2.3.xの場合

```diff
+ 
+ # Workaround: If you use TF 2.3.x, uncomment the line below.
+ WORKDIR /home/tensorflow/models/
+ RUN git checkout cf82a72480a41a62b4bbe0f1378d319f0d6f5d5c
```
以上の変更により、使いたいバージョンのTensorFlowのみがインストールされ、学習が可能になります。

## 付記: なぜObject Detection APIで最新のTensorFlowライブラリがインストールされてしまうのか？

object_detectionライブラリは、[setup.py上](https://github.com/tensorflow/models/blob/08b68031c56863a7439030f41537cfd0cc979966/research/object_detection/packages/tf2/setup.py#L9-L25)でtf-models-officialライブラリ(学習済みモデルを使うためのライブラリ)を依存関係に持ちます。

```python
REQUIRED_PACKAGES = [
    ...,
    'tf-models-official>=2.5.1',
]
```

tf-models-officialライブラリの[setup.py](https://github.com/tensorflow/models/blob/4b617781f28e6cccd5b081856e7797d4c5b9acd4/official/pip_package/setup.py#L59-L65)を見てみると、

```python
...
if project_name == 'tf-models-nightly':
  version += '.dev' + datetime.datetime.now().strftime('%Y%m%d')
  install_requires.append('tf-nightly')
  install_requires.append('tensorflow-text-nightly')
else:
  install_requires.append('tensorflow>=2.4.0')
  install_requires.append('tensorflow-text>=2.4.0')
...
```

となっていて、特定バージョン以上のtensorflow, tensorflow-textライブラリを依存関係を持ちます。そのため、すでにtensorflowライブラリをインストールしていても最新のバージョンがインストールされます[^2]。

一方で、tensorflow-gpuは自動的にアップデートされません。そのため、前述の通りtensorflowとバージョンが不整合になり得ます。

### 参考: どうすればこの問題が起こらないか？
ライブラリ側で以下の2点が対応されれば、本問題は解消されます。また、TensorFlow 2.4.0未満でObject Detection APIが実行時エラー問題も解消されます。

- object_detectionライブラリがtf-models-officialライブラリ同様にTensorFlowのバージョンに揃えてバージョニングされている
  - 例えば、TensorFlow 2.4.0に対し、tf-models-officialとobject_detectionのバージョンがともに2.4.0になる
- tf-models-officialが特定のTensorFlowのバージョンに依存関係が固定されている
  - 例えば、TensorFlow 2.4.0に対し、tensorflow==2.4.0, tensorflow-text==2.4.0が依存関係で固定されている

## 参考ページ
[公式GitHub issue #9911: Dockerfile installs two tensorflow versions when building](https://github.com/tensorflow/models/issues/9911)

- - - 

以上、ご覧いただきありがとうございました！

[^1]: object_detectionライブラリがtf-models-officialライブラリと対応してバージョニングされていないため、特定のコミットを指定する必要があります。
[^2]: tf-models-officialのバージョンは対応するtensorflowライブラリのバージョンと同じになっており、そのバージョンが依存関係の下限になります。