🚘

TensorFlow 3Dを試す

2021/03/20に公開
3

TensorFlow 3D(以下、TF3D)なるものが公開されていたので、動かしてみました。

環境構築はわりと引っかかることが多かったので動かし方を書いていきます。
一度設定したあとに書いているので、抜け・誤りがあるかもしれませんがご了承下さい。あと、TF3Dは変化が激しい予感がするので、しばらくするとこの記事は陳腐化すると思います。

マシンを用意する

GCPのVMを用意しました。というのも公式の手順書がGCPを前提として書かれていたからです。

The following steps are based on a single GPU-enabled machine (node) with an Ubuntu 18.04 image on Google Cloud Platform, please modify it based on your settings.
次の手順は、Google CloudPlatform上のUbuntu18.04イメージを備えた単一のGPU対応マシン(ノード)に基づいています。設定に基づいて変更してください。

私が使用したインスタンスタイプは「TensorFlow Enterprise 2.3 With 1 NVIDIA Tesla T4」です。デフォルトの設定から変更し、OS「Ubuntu18.04」にしました。

GCPのマシンの用意については次の記事に書いてありますのでご覧ください。
https://qiita.com/TokyoYoshida/items/f60b165d0b7e60ecb864

TensorFlow 3D(TF3D)をセットアップする

セットアップの手順書に従ってセットアップしていきます。

CUDAは、今回使用したタイプだと11.0がインストールされているため、10.1をインストールします。11.0でも大丈夫だろうと思っていたら途中で、libcudart.so.10.1が見つからないというエラーが出てしまいました。

CUDAのインストールは、TensorFlow GPU サポートページの手順にそってインストールします。

このページはCUDA11.0のインストール方法が書かれていますが、10.1に読み替えてインストールしていきます。

読み替えたところを抜粋します。

# ・・・
# Install NVIDIA driver
# ・・・
wget https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/libnvinfer6_6.0.1-1+cuda10.1_amd64.deb
sudo apt install ./libnvinfer6_6.0.1-1+cuda10.1_amd64.deb
# ・・・

# Install development and runtime libraries (~4GB)
sudo apt-get install --no-install-recommends \
     cuda-10-1 \
     libcudnn8=8.0.4.30-1+cuda10.1  \
     libcudnn8-dev=8.0.4.30-1+cuda10.1

# Install TensorRT. Requires that libcudnn8 is installed above.
sudo apt-get install -y --no-install-recommends libnvinfer6=6.0.1-1+cuda10.1 \
    libnvinfer-dev=6.0.1-1+cuda10.1 \
    libnvinfer-plugin6=6.0.1-1+cuda10.1

requirements.txtを利用してpipのインストールをする箇所がありますが、その前にrequirements.txtを編集して=>となっているところを==に変更しました。最新版が入ってしまうと思わぬエラーが出てしまうからです。たとえば、tensorflow_probabilityは0.11.1ではなく、最新の0.12.1が入ってしまうとTensorFlow 2.4.0がインストールされてしまい動作しなくなってしまいました。

requirements.txt
# Tensorflow object detection API needs manual installation separately
tensorflow==2.3.0
tensorflow_datasets==4.1.0 # >=を==に変更
numpy==1.18.5 # >=を==に変更
gin-config==0.4.0 # >=を==に変更
tensorflow-probability==0.11.1 # >=を==に変更
shapely==1.7.1 # >=を==に変更

Tensorflow Object Detection APIも忘れずにインストールしておく必要があります。

ただし、Tensorflow Object Detection APIのページに飛んでそのとおりにインストールすると、TensorFlow 2.4.0がインストールされてしまいます。TF3DはTensorFlow 2.3.0を前提としているので、Object Detection APIもv2.3.0のインストールページにある手順でインストールします。

なお、tensorflow/models.gitをcloneしたあとは、v2.3.0のタグをcheckoutします。

$ git checkout refs/tags/v2.3.0

tf3d/opsにある設定をする

TF3Dのセットアップの最後にtf3d/opsの指示に従えとあるので、そのとおりにします。

  1. Follow the instructions in tf3d/ops folder to prepare the sparse conv ops.
    tf3d / opsフォルダーの指示に従って、スパースconvopsを準備します。

手順書に従って設定していきます。

ここはUsing the pre-compiled packageの方式を使えば、わりとすんなりできたと思います。
※(2021/5/2追記)現在はこの方式についての記述は消えているようです。詳しくはコメント欄をご覧ください。

最後に、実際にインポートできるかを試します。
こんな表示になったら成功です。

$ cd google-research
$ python
>>> import tf3d.ops.tensorflow_sparse_conv_ops as sparse_conv_ops
2021-02-21 09:02:19.778279: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened 
dynamic library libcudart.so.10.1

Waymoのデータセットをダウンロードする

データセットはいくつかサポートしているみたいですが、Waymoのデータセットを使って試してみます。

手順のページにそってデータセットを用意します。

なお、データセットを全部落とすと2TBのストレージが必要らしいので、私はトレーニングデータをいくつかだけ落とすことにしました。

Waymo Open DatasetのページからGmailアカウントを登録します。この時に登録するアカウントはGCPで使用しているものと同じもの使用します。

gcloud initでGmailのアカウントを設定した後は、GCPのStrage Bucketからgsutil cpでコピーができます。Strage BucketのURLはWaymo Open Datasetのダウンロードページの下の方にリンクがあります。

$ gsutil cp gs://waymo_open_dataset_tf_example_tf3d/original_tfrecords/train-00000-of-01212.tfrecords .

なおgsutil lsを使うとどんなファイルがあるかを確認できます。

$ gsutil ls gs://waymo_open_dataset_tf_example_tf3d/original_tfrecords

ダウンロードしたデータセットを任意のデータセット用のフォルダに入れます。

$ mkdir -p /tmp/dataset/waymo/
$ mv train-00000-of-01212.tfrecords /tmp/dataset/waymo

トレーニングを実行する前に、データセットのディレクトリ名を変更します。

$ vim tf3d/semantic_segmentation/scripts/waymo/run_train_locally.sh

# 次のように編集します
# DATASET_PATH="保存したディレクトリ名" # REPLACE

実行します。

$ bash tf3d/semantic_segmentation/scripts/waymo/r
un_train_locally.sh
Deleting TRAIN_DIR at /tmp/tf3d_experiment/seg_waymo_001...
2021-02-21 08:17:23.979438: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened 
# 〜略〜
Epoch 1/100
num_valid_voxels [52528 60950 60789 60527]
num_valid_voxels [52492 60787 52240 37118]
num_valid_voxels [60512 61168 60995 60839]
num_valid_voxels [52196 36967 61074 52355]
num_valid_voxels [52363 61182 52311 65743]
# 〜略

このように表示が出れば成功です。

私の場合はエラーがでました。このあたりエラーのメモを取っていなかったのですが、PYTHONPATHを設定することで回避できたので、PYTHONPATHの設定内容を記載しておきます。

$ export PYTHONPATH=$PYTHONPATH:/home/ホームディレクトリ/models:/home/ホームディレクトリ/models/research:/home/ホームディレクトリ/models/research/slim

また、nvidia-smi -l 1コマンドを利用して、GPUの使用率を確認したところ、GPUを使用していないことが発覚しました。コンソールの出力を見ると、libcublas.so.10が見つからずGPUの設定をスキップしたと出ていたので、libcublas.so.10を検索して、LD_LIBRARY_PATHに通しました。

$ export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/cuda-10.2/targets/x86_64-linux/lib/"

この状態で実行したら問題なくトレーニングできました。

評価をする

次に評価を実行してみます。

まずデータセットのディレクトリ名を指定しておきます。

$ vim tf3d/semantic_segmentation/scripts/waymo/run_eval_locally.sh

# 次のように編集します
# DATASET_PATH="保存したディレクトリ名" # REPLACE

実行します。

$ bash tf3d/semantic_segmentation/scripts/waymo/run_eval_locally.sh
2021-03-01 05:17:15.775008: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
2021-03-01 05:17:17.393492: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcuda.so.1
2021-03-01 05:17:17.434964: I 
# 〜略

これまでちゃんと設定してきたおかげで、こちらは問題なく動きました。

TensorBoardで結果を確認する

ファイルをダウンロードしてローカルPCに保存して、Jupyter LabからTensorBoardを起動して結果を確認します。

トレーニングはできているようです。

TensorBoardではMeshを指定すると3Dオブジェクトも表示できますが、こちらはデータ量が多いせいか、かろうじて表示できるものの途中でフリーズしてしまいます。

そこで、データを間引いて、特定の3Dオブジェクトだけ表示するようにしてみます。
データは、tfeventsというファイルに保存されているので、まずはサマリーを確認してみます。

notebook
!tensorboard --inspect --event_file=保存したディレクトリ/seg_waymo_002/eval_val_mesh/events.out.tfevents.1614575857.tensorflow-2-3-20210228-072737.2152.8891.v2
# ======================================================================
# Processing event files... (this can take a few minutes)
# ======================================================================
# 
# These tags are in seg_waymo_002/eval_val_mesh/events.out.tfevents.1614575857.tensorflow-2-3-20210228-072737.2152.8891.v2:
# audio -
# histograms -
# images -
# scalars -
# tensor
#    val_ground_truth/10_COLOR
#    val_ground_truth/10_VERTEX
# 〜略〜
#    val_points/10_COLOR
#    val_points/10_VERTEX
# 〜略〜
#    val_predictions/10_COLOR
#    val_predictions/10_VERTEX
# 〜略〜
# ======================================================================
# 
# Event statistics for seg_waymo_002/eval_val_mesh/events.out.tfevents.1614575857.tensorflow-2-3-20210228-072737.2152.8891.v2:
# audio -
# graph -
# histograms -
# images -
# scalars -
# sessionlog:checkpoint -
# sessionlog:start -
# sessionlog:stop -
# tensor
#    first_step           1
#    last_step            10
#    max_step             10
#    min_step             1
#    num_steps            10
#    outoforder_steps     []
# ======================================================================

val_ground_truthはセグメンテーションの教師データ、
val_pointsはLiDARスキャンデータ、
val_predictionsはセグメンテーションの推論結果
となっています。

次に、tfeventsのデータを間引いて、val_predictionsのデータ1つだけにします。

notebook
import tensorflow.compat.v1 as tf1
from tensorflow.core.util import event_pb2
from tensorflow.python.lib.io import tf_record

tf1.compat.v1.disable_eager_execution()
tf1.compat.v1.disable_v2_behavior()

event_file_path = "保存したディレクトリ/seg_waymo_002/eval_val_mesh/events.out.tfevents.1614575857.tensorflow-2-3-20210228-072737.2152.8891.v2"

def my_summary_iterator(path):
    for r in tf_record.tf_record_iterator(path):
        yield event_pb2.Event.FromString(r)

writer = tf1.summary.FileWriter('tmp/waymo_filters')

flg = 0

for event in my_summary_iterator(event_file_path):
    event_type = event.WhichOneof('what')
    if event_type != 'summary':
        writer.add_event(event)
    else:
        wall_time = event.wall_time
        step = event.step

filtered_values = [value for value in event.summary.value if 'val_predictions/1_' in value.tag]

        summary = tf1.Summary(value=filtered_values)

        filtered_event = tf1.Event(summary=summary,
                                          wall_time=wall_time,
                                          step=step)
        writer.add_event(filtered_event)

writer.close()

TensoorBoardで見てみましょう。

notebook
%load_ext tensorboard
%tensorboard --logdir tmp/waymo_filters

分かりづらいですが、物体の種類ごとに異なる色で描画されていることが確認できました。

最後に

Qiita、Noteで、iOS開発、AR、機械学習などについて定期的に発信しています。
https://qiita.com/TokyoYoshida
https://note.com/tokyoyoshida

Twitterでも発信しています。
https://twitter.com/jugemjugemjugem

Discussion

Kazuya HirumaKazuya Hiruma

貴重な記事ありがとうございます! 今この記事を参考にさせていただいてセットアップを行っているのですが、記事内の以下の部分だけ解決できずに困っています。もしよろしかったら詳細を教えていただけますでしょうか・・。

ここはUsing the pre-compiled packageの方式を使えば、わりとすんなりできたと思います。

ここで言及しているのは手順ページに書かれていることと捉えたのですが、そういう記述が見当たりませんでした。

※ しかたなく、手順に従ってcustom opをビルドしてみたのですがうまく動作せず・・。具体的には、(ファイルはあるものの)import( import tf3d.ops.tensorflow_sparse_conv_ops as sparse_conv_ops )時にno moduleになってしまいました。

Kazuya HirumaKazuya Hiruma

返信ありがとうございます! なるほど、消されてしまっていたのですね。対象コミットを教えていただきありがとうございます!(そして色々苦労して、自分でビルドして動くところまで実現できました)この記事のおかげでモデル作成まで進めたので本当に感謝です!