arutema47's blog

書いたり書かなかったり。

点群DNN、3D DNN入門 -3DYOLO, VoxelNet, PointNet, FrustrumPointNet, Pointpillars

またまたQiitaからのお引越し記事です。

センサについてはこちらをどうぞ。

aru47.hatenablog.com

目的

image.png

点群データに対してディープニューラルネットワーク(DNN)を適応する研究は活発で、いくつものアプローチがあります。 大人気のPointNetだけではなく、他の手法も照らし合わせて各手法のメリット・デメリットを理解する自助をすることが記事の目的です。

浅く広く手法について学び、ああこういうアプローチがあるのかと気づくことができると幸いです。 各技術の詳細を知りたい場合は元論文や解説サイトを読むといいと思います。

またPointCloud DNNの論文についてはここにわかりやすくまとまっています。是非一読を!

点群DNNでできること

  • 点群認識

image.png

与えられた点群が何の物体か認識するタスクです。 例えばShapenetにおいては点群がどの家具か当てるタスクがあります。 画像認識と同様のタスクです。

  • 点群セグメンテーション
  • image.png

SUN-RGBDにあるタスクです。 点群がどの物体に属しているかセグメンテーションするタスクです。 画像セグメンテーションと同様のタスクです。

  • 点群物体検出

image.png 自動運転などに重要な点群中に存在する物体の座標を認識するタスクです。 アプリ的に需要が高く、一番研究としては人気のあるタスクな印象です。

3Dセンサ

RealsenseD435はアマゾンで24000円と手頃でAPIも使いやすいです。

点群を取得してみたいのならば試しに買ってみてはいかがでしょうか。

amzn.to

3D DNNの家計図

image.png

独断と偏見で3D DNN系の家系図を作ってみました。

大きくは * 2Dベースのアプローチを結集する2D based * あくまで点群にニューラルネットワークを適応するPoint cloud based approach * 画像から得た結果と点群ニューラルネットワークをフュージョンするアプローチ * また点群ニューラルネットワークで点群を前処理(エンコード)した後に2Dベースの物体検出を適応するPointCloud+2D アプローチ の四種類があると考えています。

それぞれを詳しく見てみましょう。

変更履歴

使うデータドメイン毎にカテゴライズを変更(2019/10/2) データセットなど加筆(2019/10/2) Pointpillars追加(2020/03)

2Dベースアプローチ

Complex YOLO (ECCV workshop 2018), YOLO 3D (ECCV workshop 2018)

Complex-YOLO: An Euler-Region-Proposal for Real-Time 3D Object Detection on Point Clouds

https://link.springer.com/chapter/10.1007/978-3-030-11009-3_11

YOLO 3D: End-to-end real-time 3D Oriented Object Bounding Box Detection from LiDAR Point Cloud

http://openaccess.thecvf.com/content_eccv_2018_workshops/w18/html/Ali_YOLO3D_End-to-end_real-time_3D_Oriented_Object_Bounding_Box_Detection_from_ECCVW_2018_paper.html

手法について

タスク:自動運転用の三次元物体検出 LiDARの点群はデータ量が少なく(疎)であるため、点群から物体の情報を直接抽出するのは難しい。

そこで点群を俯瞰的視点(上からみた)の画像を生成し、それに対し物体検出の手法(YOLOやFaster-RCNN)を適応するという手法です。 点群データも俯瞰視点に変換してしまえば車や人なども画像のように扱うことができます。 そのため成功をすでに収め、人間レベルの検出が可能なCNNを積極的に3Dの分野でも適応しようというのがベースの考え方です。

俯瞰(bird's eye viewまたはBEV)とは?

bird 上の図が正面からみたカメラで下の図が俯瞰視点でみた点群です。 これを画像として扱い、物体検出DNNを適応します。

1564476894677.png

メリット

2D画像で成功したディープなR-CNNベースの手法をそのまま点群データに対し適応することができる。

R-CNNはImageNetやMS-COCOなどから転移学習できるので、必要な学習データ量が少なく済む可能性がある。

YOLOベースのため高速。リアルタイム動作も可能。

デメリット

点群を二次元化してしまうので物体の三次元的特徴(凸凹など)は読み取れない。

多角的に検出を行うことは可能だが、角度間(例えば俯瞰と正面)の検出結果の整合性を取るのが難しく検出を間違えたりしてしまう。

(あと3D研究者からすると手法がストレート過ぎておもしろくない笑)

点群ベースアプローチ (PointNet系)

PointNet(NIPS 2016)

PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation

https://arxiv.org/abs/1612.00593

みんな大好きPointNet。

https://www.slideshare.net/FujimotoKeisuke/point-net

https://qiita.com/KYoshiyama/items/802506ec397559725a1c

に詳しい解説があるのでそちらも読むことをおすすめします。

PointNetの手法について

1564478255143.png

点に対し全結合ネットワークを複数回かけ合わせ、特徴量を抽出していく(Classification Network)。

そして最終的に得られた点に対する特徴量(n*1024部)をmaxpoolingすることで点群全体の特徴量(global feature)を得る。

点に対する特徴量抽出後、maxpoolingで全体特徴量を得ることが出来ることを示したのがPointNet最大の功績じゃないかと考えている。これでいいんだ!!という感じ。。笑

1564478526102.png

点群に対するクラス分類、セグメンテーション両方に対して(ModelNet,SUNRGB-D)非常に高いスコアを叩き出した。

PointNetのタスク

PointNetが効果を発揮するのはデータセットは主に室内で取られた点群データに対してです。 image.png

報告されているのは室内のセグメンテーションを行うSUNRGB-Dデータセット(上図)

image.png

そして家具単体のクラスを当てるModelNet40です。

自動運転のようなKittiデータセットに対しては点群が疎すぎるため、適応できないとのこと。 これは後に2Dと3DをフュージョンするFrustrum PointNetで解決されました。

PointNetの実装

PointNet-Tensorflow(本家) https://github.com/charlesq34/pointnet

PointNet.Pytorch(自分もこれを使っており、本家同等の精度が出るのを確認してます) https://github.com/fxia22/pointnet.pytorch

PointNet-Keras https://github.com/garyli1019/pointnet-keras

PointNet-Chainer https://github.com/corochann/chainer-pointnet

PointNet++(NIPS 2017)

PointNet++: Deep Hierarchical Feature Learning on Point Sets in a Metric Space https://arxiv.org/abs/1706.02413

PointNetの局所的特徴を見ていない(近傍点群の情報を取り込めない)、という課題を改良したPointNet++も人気と実力が高いです。

PointNetに入力する点群を事前にクラスタリングした近傍点を入力することで、擬似的に局所的特徴量も抽出できるようになっています(後日加筆します。。)

まずはとっつき始めるならばPointNet,研究を行うならばPointNet++を元に実装を行うべきと思います。

実装

PointNet++.pytorch https://github.com/erikwijmans/Pointnet2_PyTorch

VoteNet(ICCV 2019)

Deep Hough Voting for 3D Object Detection in Point Clouds http://arxiv.org/abs/1904.09664

PointNet生みの親からの室内のオブジェクトの3D物体検出に向けた最新論文。 現在ScanNetv2ベンチマークにおいてSoTA。

点群の物体検出タスクは複雑な実装が多く、精度もあまり出ていなかった。 一方VoteNetはシンプルなアイデアながら、高い精度を実現。 基本的なアイデアは物体の中央をhough-votingで予測し、予測した中心点を元に物体検出するというもの(後日加筆します)。

今後人気が出てくるようと思うので要ウォッチ。

センサフュージョンベースアプローチ

Frustrum PointNets

Frustum PointNets for 3D Object Detection from RGB-D Data

https://arxiv.org/abs/1711.08488

手法について

PointNetでは物体検出のタスクにはあまり向かない。

そこでカメラとLiDARデータのセンサフュージョンを行い、精度向上を達成。

そこで成功を収めている2DベースR-CNNで物体のある方向を円錐で絞り込んでから、その部分の点群のみPointNetにかけることで高精度な3D物体検出を実現。

これもアイデアは非常に単純だが、効果絶大なアプローチである。自動運転用など高い精度が求められる三次元物体検出を行うならばこのアプローチが一番スタンダードなのではないだろうか。

1564478892398.png

3D bindingbox predictionを行うPointNetも新規に提案している。

1564478950881.png

メリット

カメラ+LiDARを用いた3次元物体検出のタスクにおいてstate-of-the-art。(Kittiデータセット)

有力な2つの手法を組み合わせているので実装がしやすい。

デメリット

モデルを縦続的に使うので演算量は多い。

そもそもカメラで取り逃した物体は検出できない。

実装

https://github.com/charlesq34/frustum-pointnets

点群+2D CNNのアプローチ

VoxelNet(CVPR 2018)

VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection

http://openaccess.thecvf.com/content_cvpr_2018/html/Zhou_VoxelNet_End-to-End_Learning_CVPR_2018_paper.html

手法について

1564477068068.png この論文のポイントは点群にPointNet-likeなネットワークで特徴抽出を行った後に、2Dベースのアプローチ(Region CNN)を適応することで物体検出を行います。2D CNNに直接点群を入力すると点群の精微な情報を読み取れないという弱点を最初にPointNetで特徴抽出を行うことで解決したのが進歩性です。

また物体検出自体はCNN系手法の方が精度が高いため、PointNetとR-CNN両方の長所を使っているという意味で興味深い手法です。

Voxelとは?

voxel 左の絵が生の点群データです。 点群データはポイント数が多く扱いにくいので、複数を一纏まりにしたVoxelに変換します。Voxelは粒度によっては点群のように振る舞いますが、荒くすると昔のゲームのポリゴンくらいになってしまいます。 ざっくばらんに言うとはダウンサンプルした点群と捉えることができます。

空間的情報を保ったままデータ量、演算量を削減できるため、3D DNNが流行る前から点群の位置合わせ(レジストレーション)を行う際にはボクセル化は定番のアプローチです。

メリット

Voxel間で畳み込みを行うため、後述するPointNetではできない近隣点群間の特徴量も抽出することが可能です。

Voxelの特徴量の他にPoint自体の特徴量抽出も行っているため(直感的には)情報のロスはなく、高精度な認識が期待できます。

PointNetとCNNの良いところどりをしたようなアーキテクチャ。

デメリット

メモリ使用量が多い。

ランダムサンプルやVoxel間隔をチューニングすれば軽くすることは可能だが、チューニングが大変。

公式実装がリリースされていない。 ここに公式では有りませんが実装がありました。

PointPillars: Fast Encoders for Object Detection from Point Clouds

CVPR 2019. https://arxiv.org/abs/1812.05784

  • 課題 image.png

従来点群のみ使用したネットワークは精度は高いが低速で、点群を画像に投影するネットワークは高速だが 精度が低いという欠点があった。

  • 提案 image.png 狙いとしては点群の細かい情報量を失わないように情報量をエンコードし、疑似画像に変換する。その疑似画像を2D CNNで使用するような物体検出ネットワークに入力し、物体検出を行う。 この手法の進歩性は従来単純に点群を俯瞰画像といった疑似画像に投影し物体検出CNNに入力するだけでは点群の細かい情報量が失われてしまっていた。そこで点群を画像に投影するためのエンコードネットワークを用いることで点群情報を失わずに物体検出CNN(SSD)に入力データを与え高精度化を達成した。

image.png 具体的に点群を画像にエンコードするために、Pillar(柱)と呼ぶ点群を細かく格子状に分割しPointNetのような点群DNNを使いPillar内の特徴量を抽出。そして得た2Dの特徴量マップをSSDに与えることで物体検出を行う。 アイデア自体は非常にシンプルながら、PointNetと物体検出CNNを結合する手法を初めて提案し点群物体検出の精度でブレイクスルーを果たした。

  • 実験 image.png 発表当時、KITTIベンチマークでstate of the artを達成。

ネットワーク自身も改造しやすく、Kaggleなどのコンペでも頻繁に使われている。

https://www.kaggle.com/c/3d-object-detection-for-autonomous-vehicles

Class-balanced Grouping and Sampling for Point Cloud 3D Object Detection

https://arxiv.org/pdf/1908.09492.pdf 現在のNuscenes一位のソリューション。 image.png

  • 課題 LiDARデータのaugumentationの提案が主に精度向上の要因。 Nuscenesはクラス間精度の平均が評価指標になっているが、学習データ数がクラスによっては異常に少なく学習が進まない。例えば最も頻出する車クラスに対して人やanimalクラスは1/10から1/100しかない。 image.png

  • 提案 このようなデータセットインバランスに対応するため、点群データ中に少数クラスを恣意的に生成することで学習を進みやすくした。ネットワーク自体はほぼPointpillarsの応用で、データセット拡張により大幅な精度向上を実現した。また精度に貢献している提案として大きさなどが似ているクラス(人と自転車など)を"superclass"としてまず分類してから詳細クラスを分類する2-stepの分類を実行している。

image.png 特に自転車クラスは14倍も精度向上した。

点群系(3D)のベンチマーク

ModelNet40

image.png 一番基本的な3D系のベンチマーク。 90%精度が出てしまっているので、モデルがオーバーフィット気味でベンチマークの価値は現在あまりなくなった。MNIST的に学習時間も短いので最初に試すのはこれ。 https://paperswithcode.com/task/3d-object-classification

現在は3D capsulenetがリーダー。 image.png

ScanNetv2

現在の室内用3Dデータでは一番難しいタスク。 VoteNetが一位ですね。 https://paperswithcode.com/sota/3d-object-detection-on-scannetv2

image.png

自動運転系

nuScenes

image.png 現在では最も難しく、巨大なデータセット。

  • 大規模点群+カメラ画像のアノテーション結果
  • 20秒で区切られた1000シーンのデータ
  • ボストンとシンガポールの街中
  • 23クラスの結果

https://www.nuscenes.org/object-detection?externalData=all&mapData=all&modalities=Any

image.png ベンチマーク首位はMEGVIIでPointpillarsは結構精度は低い。 一方でPointpillarsは使いやすく、KaggleのLyftコンペではほとんどの上位陣が使った。 https://www.kaggle.com/c/3d-object-detection-for-autonomous-vehicles

KITTI

image.png

従来まで使われていたデータセット。 データ数はあまり多くないが、データセット形式が使いやすくスタートするには最適。 http://www.cvlibs.net/datasets/kitti/

PS

逐次最新論文等は自分のブログにまとめてます。

距離センサ入門(ステレオカメラ、プロジェクション、LiDAR)

Qiitaからのお引越し記事です。

目標

通常のカメラは物体の明るさ、色を抽出するのに対し、距離センサは物体までの距離をセンシングします。そのため3DカメラやDepth Sensorなどと呼ばれたりします。

距離を知ることは多様なアプリケーションにおいて重要であり、例えば自動運転では前方車両までの正確な距離を知ることは必須です。またゲームなどのアプリケーションでは人の動作などを距離センサで抽出するKinectが遊びの幅を広げました。またDaVinciといった手術ロボットでも患部の正確な距離を知るためにも距離センサは重要です。

身近なアプリケーションですと例えば部屋の3Dマッピングなどに使用できます。

部屋の寸法、距離感覚は写真や間取り図にするとイメージが湧きづらいのですが、3Dマッピングしてしまえば部屋そのものを感じることができます。 image.png

このようなマッピングは新型iPhone Proで簡単にPolycamなどのアプリを使ってできるのでもしデバイスを持っていたら試してみて下さい!

この記事では世の中に大きくどのような距離カメラがあるのか、それらの概要や特徴、使用されている製品をリストします。

例えば研究開発プロジェクトに使用する距離センサが選べるようにするのが目標です。

私はLiDARの専門家なのでカメラベースの手法にはあまり詳しくないのですが、使用頻度の高い距離センサであるステレオカメラやプロジェクションカメラについても記述しました。

また点群ディープラーニングについてはこちらをどうぞ。

aru47.hatenablog.com

ステレオカメラ

概要

スバルアイサイトなどに搭載されている市場に多く出回る距離センサです。

https://www.subaru.jp/eyesightowner/about/

アイサイトのしくみ

またこちらのWhitePaperも参考になります。

Obtaining Depth Information from Stereo Images

https://www.zmp.co.jp/knowledge/adas_dev/adas_sensor/adas_camera/adas_stereo

png.png

原理は人間がものを立体的にみるのと同じ原理です。

目を左右交互につむり、モニタを見ると左右にずれるのがわかりますか?

脳はこのずれが大きい物体ほど近くにあり、ずれが小さいほど遠くにあると認識してくれます。これがステレオカメラの原理であり、人間が使っている距離センサもステレオカメラがベースになっているといえます

ステレオカメラではピクセルがどれくらいずれた事から、物体までの距離を測ることができます。この際カメラ同士が離れれば離れているほど物体に対してピクセルずれは大きくなるため、正確な距離が図りやすいです。一方でカメラ同士が近いとほとんどピクセルズレが起こらないため、距離を測るのは難しくなります。画素数を向上させれば原理的には測距距離は長くなりますが、信号処理量が指数関数的に増加してしまうのが欠点です。

またステレオカメラ最大のチャレンジは、2つのカメラが同一の物体を見ているかどうかを判定することです。これには高度な画像処理テクニックが必要で、遠くの物体が本当に同一かどうかを当てるのはとても難しくなります。

image.png

Reference: Obtaining Depth Information from Stereo Images

またステレオビジョンを適応するには左右の画像間で高低差があってはいけません。 そのため前処理でカメラ歪除去と高低差除去のキャリブレーション(rectify)が必要です。

特徴

他の距離センサは専用部品(レーザなど)が必要な一方で、ステレオカメラは普通の市販カメラを2つ使うだけで実現できるため、コストが安いのが最大のメリットです。

ただ距離を測るために左右のカメラで同一の物体の同じ場所を見る(認識する)必要があり、遠くのものの距離を測るのは難しい。数式などは省略するが、アイサイトのようにカメラを離れて設置しないと遠くのものでは視差が生まれないため、遠くまで見たい場合は筐体が大型化してしまうのが欠点です。

使用製品

市販製品ではIntelのRealSenseがソフトウェア(SDK)もついてきて試しやすいです。

RealsenseD435ならアマゾンで24000円なのでお手軽に購入できます。

https://amzn.to/3tgO9zGamzn.to

RealsenseD435の出力例です。

  • 近距離(1-3m)ならば高い精度が取れる
  • カメラベースのため、解像度が高い
  • CPUでもリアルタイム(30FPS)で動作

のが特徴です。ホビー用途の距離センサならば一番の選択肢だと思います。

RealsenseD400系にはパターンプロジェクションも付いていますが、距離推定に用いているのはほぼステレオカメラです(プロジェクションを切ってもほぼ絵が変わらないためあくまで補助機能ぽいですね。)

パターンプロジェクションカメラ

概要

工業用の高性能3DカメラやiPhoneのFaceIDで使われているのがパターンプロジェクションカメラです。

物体に既知のパターンを投影し(パターンプロジェクション)、その歪み方からカメラと物体までの距離を信号処理によって導出します。

説明を聞いてもよくわからないが、下記の図を見るとわかりやすいかと思います。ヘルメットのような物体があるところにしましまのパターンを投影すると、物体の高さによってしましまが歪むことがわかります。このような歪みを読み取り解析することで、ヘルメットの三次元形状を知ることができます。

pattern.png

また下記の動画はiPhoneのFaceIDの仕組みについて解説してます。赤外線で顔中にパターンを放射し、顔の正確な形状を読み取ることができる。凹凸をみているのでFaceIDを騙すのは難しく、セキュリティを強固なものにしていますね。

Using An Infrared Camera To Show How Face ID Works

https://www.youtube.com/watch?v=g4m6StzUcOw maxresdefault.jpg

  • LiDARに比べると部品がシンプルなため、価格が抑えられるのが特徴。

  • 室内使用であれば非常に高い精度が得られる(mm~um精度)。

  • 一方で外乱の多い屋外使用は厳しい。

またパターンプロジェクションとステレオビジョンを両方使うアプローチは多いです。

ピッキングロボットで頻繁に使用される高精度3DカメラEnsensoではパターンプロジェクションとステレオビジョン両方を備えます。 ステレオビジョンが苦手なマッチングが取りにくい物体(例えば平坦な壁など)でも綺麗に点群を得られるのがメリットです。

使用製品

iPhone

Image result for iphone face id

iPhoneの顔認証(FaceID)はアクティブプロジェクションカメラで実現されています。

FaceIDが使われるたびに、iPhoneは赤外線のパターンを放射してます。

実は元の技術はMicrosoft KinectV1を開発していた企業(PrimeSense)をAppleが買収し、発展したものです。 関係者の間ではMiniKinectなどと言われています。。笑

ゾゾスーツ

https://zozo.jp/zozosuit/ 1570948182773.png

ゾゾスーツもスーツのドットをカメラで撮影し、距離を測っているという意味で技術的には似ている。ただゾゾスーツはドットを投光していないのでパッシブプロジェクションと言えるかもしれない。人の大体のサイズ感が測れれば良いためか、FaceIDのカメラに比べると点数は少なめ。

工業製品(Ensenso, キーエンス)

工業用途のセンサ出力例はあまりネットに出回ってないのですが、mm精度が得られRealsenseなどとは精度自体は比べ物になりません(そんなのホビー用途ではオーバースペックなんですが)

一つ数百万円します笑

工場内の検品やロボット製品に使われることが多いです。産業系の展示会に行くとこいつが付けられていることが多いので探してみて下さい笑

一方でプロジェクションを正確に読み取れるのは数メートル範囲内のみであり野外の使用などは厳しいです。基本的には室内用ですね。

キーエンス 3Dカメラ

Ensenso X36

Time of Flight LiDAR

Time of Flightの原理

カメラベースの距離センサ(ステレオカメラ、プロジェクション)とLiDARは原理が根本的に異なる。

一方でLiDARはTime of Flight(光の飛行時間)をベースにして距離を測ります。

原理としては単純で、下記の図のようにレーザ光を筐体から放ち、そのレーザが物体に反射して返ってくるまでの時間を計測する。もしレーザ光が10秒後に返ってきて光速を単純のため1m/sとすると、物体までの距離は

(10秒 * 1m/秒)/2 = 5m

と5m先に物体があることがわかります。このように飛行時間をベースにして距離を導出する手法をTime of Flightと呼びます。

実際の光速は108m/sと非常に早いため、光が返ってくるまでの時間は数ピコ秒、ナノ秒のオーダーなので時間を計測する回路には高い精度が求められます。

このようなレーザパルスの帰還時間を直接測定するタイプのLiDARはdirect Time of Flight sensorとも呼ばれます。

https://tech.nikkeibp.co.jp/atcl/nxt/column/18/00001/02023/?ST=nnm image2.png

特徴

LiDARの最大の特徴は

  • 高精度
  • 遠距離
  • 外乱に強い(野外で使用可)
  • 値段が高い

と挙げられます。強いパルスレーザ光を照射し、その帰還時間から直接距離を導出するため誤差が混入しづらく信頼性が高いのが特徴です。これらの特徴はカメラベースで実現するのは難しいため、過酷な条件でも動作が義務付けられる自動運転ではLiDARが主に遠方の物体検出で使われることが期待されています。

一方でスキャン機構、レーザ出射機、レーザ受光部と多くの専門素子が必要になるためカメラ型に対してコストは数倍~数十倍高くなってしまうのはデメリットです。

最近はLivoxなど10万以下のLiDARが多くのプロトタイプで使用され始めているので値段が高いから敬遠される時代は終わりつつあるかも知れません。

この図のような長距離(100-200m)の精微な点群(Point Cloud)でスキャンできるのはLiDARの最大のメリットです。

野外で遠距離までバッチリ点群を取れる距離センサはLiDARだけです。なので自動運転などで熱い期待を持たれています。(TeslaなどLiDARいらないよ派もいますが。。)

スキャン型LiDAR

https://www.gizmocrazed.com/2019/03/gates-backed-lumotive-upends-lidar-conventions-using-metamaterials/ image.png

LiDARのの上記のTime of flightの原理で1画素あたりの距離を取得することはできます。それでは絵として距離情報を得るためにはどうしたら良いのでしょう?そのための工夫としてLiDARにはスキャンという概念があります。

一つのアプローチはミラーを使い、出射するレーザ光を走査(スキャン)する方法です。

https://commons.wikimedia.org/wiki/File:LIDAR-scanned-SICK-LMS-animation.gif image.png

上記のアニメーションはミラーを使ったLiDARのスキャンをわかりやすく表しています。ミラーを回転させることでレーザ光を360度走査し、周辺環境の全ての情報を得ることができます。

このとき1画素ずつ情報を得ながら二次元的にスキャンするのが2Dラスタースキャンという方式で、縦画素を一気に取得しながら横方向にスキャンする方式を1Dスキャンと呼びます。両者とも距離は出ますが、1Dスキャン法が距離画像を得るために必要な時間が短いため、高いFPSを達成しやすいです。

一方でミラーを使うと(一眼レフカメラのように)LiDARの筐体は大きくなってしまいます。そのためミラーを使わずにMEMSミラーを使ったり、光学的にスキャンするOptical Phased Array LiDARなども開発が活発に進められており、将来的にLiDARの小型化や低コスト化は進むと見られています。

フラッシュ型LiDAR

またスキャンをせずにカメラのイメージセンサのように二次元画素情報を一度に取得するようなLiDARも開発されています。イメージセンサ全体を覆うようなレーザ光を出射し、それを受光するだけなので構造的には非常に単純です。そのためスキャン型に比べ低コストで実現可能です。

一方で課題はいくつかあり、 - 1画素あたりのレーザパワーが少ないため距離が短い - 外乱に弱い - 干渉などに弱く、ノイズが発生しやすい というのが欠点のため自動運転には適さないが、室内のロボットなどには活かせるかもという感じです。

製品

Velodyne Series

最も有名なToF LiDARはVelodyneの製品でないでしょうか。LiDAR製品を初めて世に出したメーカーで現在も帝王として君臨しています。

彼らの製品は数十万~数百万と非常に高額ですがそのクオリティは一級品。Velodyneを使っていない自動運転車はTeslaとWaymoくらいだと思います(Waymoは独自LiDARを使用)。ロボットの研究開発の現場でも画素数や精度が高いため、Veloが使われることが多いイメージです。

www.argocorp.com

VLP-16_2.jpg

追記 Livox Horizon

DJIの子会社であるLivox社が10万円を切りつつも高精度を達成するLiDARを発売しており、こちらがロボ系プロジェクトでは主流になりつつあります。

アマゾンで買えます笑

amzn.to

www.livoxtech.com

クオリティは凄まじく、SDKなどgithubで公開してくれてるので開発もしやすいです。Velodyneから乗り換えている人多数ですね。

iPhone

image.png iPhoneのフロントには実は結構昔からToF LiDAR(1画素)が搭載されています。 スキャンなどはしないので低コストなのでしょう。

画像で言うProximity Sensorがそうです。 通話する時、iPhoneの画面を顔に近づけると画面は自動的に切れます。これは顔とiphoneとの距離をセンシングしているからだと思います。

追記:iPhone, iPad ProはdToF LiDARを搭載しました。 詳しくは:こちら記事参照

iToF LiDAR

Azure Kinectなどに搭載されている技術です。

amzn.to

技術や原理はこちらの記事参照。

aru47.hatenablog.com

Maskをopencv使って縮小する

目的

このようなMask画像を画像に対して縮小したいというマニアックな事例の備忘録。

これを f:id:aru47:20210129144808p:plain

こうする f:id:aru47:20210129144910p:plain

パイプライン

マスクの中心を計算

# 重心を取得
m = cv2.moments(mask)
cx = int(m['m10'] // m['m00'])
cy = int(m['m01'] // m['m00'])
print(cx, cy)

中心に画像をオフセット

Affine変換で画像を平行移動させる。

# 中心にmaskをシフト
num_rows, num_cols = mask.shape[:2]
# Create translation matrix
offsetx = mask.shape[0]//2-cx
offsety = mask.shape[1]//2-cy
translation_matrix = np.float32([ [1,0,offsetx], [0,1,offsety] ])

# Image translation
mask_translation = cv2.warpAffine(mask, translation_matrix, (num_cols,num_rows))

f:id:aru47:20210129144755p:plain

mask全体を縮小

# maskを半分に縮小
mask2 = cv2.resize(mask_translation, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR)
mask_resized = cv2.copyMakeBorder(mask2, 200, 200, 200, 200, cv2.BORDER_CONSTANT, 0)

f:id:aru47:20210129144845p:plain

オフセット分を戻す

# オフセットを戻す
translation_matrix = np.float32([ [1,0,-offsetx], [0,1,-offsety] ])

# Image translation
mask_translation = cv2.warpAffine(mask_resized, translation_matrix, (num_cols,num_rows))
plt.imshow(mask_translation)

f:id:aru47:20210129144910p:plain

有名なDeep Learningの特許を調べてみた

目的

有名所のDNN特許を調べてみました。ほとんどがGoogleの特許ですがBatchNorm、transformer以外日本で登録されていないのが多いですね。 調べたところで力尽きてちゃんとクレームはトップ以外読んでません。随時リストはアップデートしていきます。

参考: https://www.reddit.com/r/MachineLearning/comments/c5mdm5/d_googles_patent_on_dropout_just_went_active_today/www.reddit.com

感想

Dropout,Batchnorm,transformerなど根幹特許を多くGoogleに抑えられていますが、基本的にはPatent Trollに対しての防衛でGoogleから権利行使することはないようです。(訴訟は今の所ない)

Tips

特許の状態は大きく分けて3つあります。

出願:特許庁に特許を提出した段階です。この段階では特許内容を他社が読むことは出来ません。

公開: 2年立つと特許は公開されます。この段階では特許内容を他社が読むことは出来ますが、まだ法的に特許は有効ではありません。

登録: 審査官が許可を出すと特許は登録され、特許として効力を持ちます。

ちなみに論文と同じで特許は審査官に拒絶されることもあり、最大2度ほど修正の機会が与えられます。

画像認識

Inception方式のDNN

patents.google.com クレーム:インセプション方式のDNN

Assignee:Google

Status:登録

日本:なし

Faster-RCNN

patents.google.com

qiita.com

クレーム: 2-stageの物体検出器(Fast-RCNN方式)

Assignee:Microsoft

Status:登録

日本:なし

学習系

hirotaka-hachiya.hatenablog.com

Dropout

patents.google.com クレーム:Dropout正則化を用いて学習されたモデル

Assignee:Google

Status:登録

日本:なし

Batchnorm

patents.google.com

トップクレーム範囲:BatchNormを用いて学習されたモデル

Assignee:Google

Status:登録

日本:あり

登録国がこの特許だけ異様に多いことからGの本気度が伺えます。

学習並列化

patents.google.com クレーム:並列化を用いて学習されたDNNモデル

Assignee:Google

Status:登録

日本:なし

蒸留

patents.google.com Assignee:Google

Status:登録

日本:なし

Neural Architecture Search (NAS)

patents.google.com

Assignee:Google

Status:登録

日本:なし

NLP

Transformer

patents.google.com

Assignee:Google

Status:US 登録

日本:あり

この特許も当たり前ですが出願国が多く、本気ですね。

word2vec

patents.google.com Assignee:Google

Status:登録

日本:なし

GAN

Spectral Normalization

patents.google.com Assignee:PFN

Status:公開

日本:あり

Pytorch高速化 (3) TensorRTで推論を10倍高速化

TLdr;

torch2trtというpytorchモデルをTensorRTに簡単に変換するライブラリを使い、Jetson nano+xavier上で画像認識とセグメンテーションの推論処理を10倍高速化できることを確認しました。

ただtorch2trtはカスタムモデルには対応していないため(resnetなどtorchvision標準モデルのみ)、自作モデルのTensorRT変換は大変だと思います。

他高速化シリーズ aru47.hatenablog.com

aru47.hatenablog.com

TensorRTとは

f:id:aru47:20201130141422p:plain https://amzn.to/3q9qrEK

https://amzn.to/37ndtL5

例えばJetsonNanoなどは安価に入手できるエッジデバイスだが搭載しているGPUはローエンドなため大きい画像をリアルタイムで処理するのは難しい。

一般的なpytorch推論は

model.eval()
with torch.no_grad():
        for (data, target, _, _) in tqdm(loader):
            # data = data.half().cuda() # 半精度推論
            logits = model(data)

と書けるが、これでも速度が不十分なことが多い。

TensorRTはnVidiaが提供している推論を高速化させるフレームワークである。モデルをTensorRTで走らせる事でエッジデバイスでも高速な推論が可能となる。

https://camo.qiitausercontent.com/d1d295922add81fb3bd160a2be4f091d2147b916/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3137313931352f62653963613665332d383261612d663163372d303663362d3138616136633363616264612e706e67

引用:http://on-demand.gputechconf.com/gtcdc/2017/presentation/dc7172-shashank-prasanna-deep-learning-deployment-with-nvidia-tensorrt.pdf

その機能の一つとして強力なものにグラフ最適化というものがある。 図はInceptionモジュールのものであるが、通常のpytorch等はconvrelubatchnormと3ステップ別々の計算を行う。 ここで入出力のテンソルは毎回メモリから読み書きする時間も必要であり、計算時間は長くなってしまう。

そこでTensorRTのグラフ最適化(レイヤフュージョン)はDNNではCNN, relu, batchnormと3ステップ計算が頻出することを利用し、その計算をまとめた専用レイヤーを用意する。 そうすることでデータ読み書きと演算はあたかも1度しか行わずにconvrelubatchnormの計算を実行することができる。reluやbatchnormは単純な加算、クリッピングで表現できるため計算をまとめることは容易である(カーネルを用意さえあれば)。

f:id:aru47:20201128114742p:plain

引用:http://on-demand.gputechconf.com/gtcdc/2017/presentation/dc7172-shashank-prasanna-deep-learning-deployment-with-nvidia-tensorrt.pdf

例えばresnet50では等価的にレイヤ数を1/4に削減している。 この最適化は非常に強力であり、大幅な高速化が可能となる。

またPytorchでは対応していないINT8のTensorCoreを使った計算もTensorRTならば可能である。 FP16に対しメモリ量が半分になるため、データ伝送も2倍高速化することが期待できる(実際に大幅に高速化可能)

またTensorRTはV100などの一般的なGPUでも高速化が可能であり、10倍ほどの高速化が公式スライドで報告されている。

f:id:aru47:20201128114416p:plain

TesnorRTを気軽に試す

TensorRTを自分のネットワークに試すにはPytorchモデルをONNXフォーマットに変換→ONNXのモデルをTensorRTで読み込むというフローがあるが、ONNX変換はクセが強く、気軽に試すのは難しい。

そのためnvidiaの出しているtorch2trtという変換ツールを使うことでONNXを介さすに直接PytorchモデルをTensorRTに変換できます。 ただtorch2trtは対応しているレイヤは標準CNNのものがほとんどで(Resnetなど)、カスタム関数などが入っていると上手く変換はできませんでした。 例えば物体認識モデルの変換は難しいです。その場合は公開されているONNXモデルを使うのが良いです。

github.com

以下レポでtensorRTをJetson上で試し、速度向上効果を見てみました。

TensorRT化はモデルと入力画像サイズを渡すことで簡単にできます。

 # define input
 input_size = [1, 3, 256, 256]
 x = torch.zeros(input_size).cuda()

# convert to tensorrt models
model_trt = torch2trt(model, [x], fp16_mode=True, int8_mode=True, max_batch_size=1) # 精度によってモード切り替え

詳しい使い方はレポかtorch2trtのページを読んでみて下さい。

github.com

画像認識

resnet18 resnet34 resnet50
Raw 11 12 16
FP32 3.8 5.6 9.9
FP16 2.1 3.3 4.4
INT8 1.7 2.7 3.0

Xavier上で数種類のネットワークの画像一枚に掛かる時間を調べました(ms)。rawはpytorchそのままで他はtensorRTで記載分解能で量子化したものです。NanoではInt8が何故か動きませんでした。 画像サイズは256x256.

画像セグメンテーション

f:id:aru47:20201128133902j:plain

fcn_resnet50 fcn_resnet101 deeplabv3_resnet50 deeplabv3_resnet101
Raw 200 344 281 426
FP32 173 290 252 366
FP16 36 57 130 151
INT8 21 32 97 108

こちらはセグメンテーションの結果です。画像サイズは512*512と大きく、量子化を積極的にやることで速度が大幅に上がることがわかりました。

Github Actionsでpypiのパッケージを発行

なぜgithub actionsで発行できると楽か

f:id:aru47:20201108162012p:plain

自作ライブラリ開発しているとすると普通ならコーディング、setup.pyを記述、pypiにtwineでアップロードという流れになります。 blog.amedama.jp

ただこのpypiへのアップロードが意外に面倒くさい。。!毎回pypiアカウントの認証などしなくてはならず、ライブラリ発行が億劫になるため出来るなら自動化したいものです。

Github actionsを使うとgithub上でリリースを追加することで自動的にpypiにアップロードすることができ非常に便利だったのでその方法を記述します。

やってみた

他レポの見様見真似でやってみた。

Pypiに書いてあるフローは以下であるが、微妙に今回参考にした設定とは異なる。

https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/

今回用いたライブラリは自分で開発しているkaggle用TTAライブラリであるodachです。

github.com

Github actionsの設定ファイルを追加

レポ下に.github/workflows/python-publish.yml

を追加します。

# https://github.com/kentaroy47/ODA-Object-Detection-ttA/blob/main/.github/workflows/python-publish.yml
name: Upload Python Package

on:
  release:
    types: [created]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.x'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install setuptools wheel twine
    - name: Build and publish
      env:
        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
      run: |
        # ソース配布型
        python setup.py sdist bdist_wheel
        twine upload dist/*

Pypiの認証情報をSecretsに追加

レポジトリのSettings以下のSecretsにpypiの認証情報を追加します。

PYPI.PASSWORDPIPY.USER_NAMEにそれぞれpypiのユーザ、パスワードを格納します。secretsの情報は管理者しか見られないため安心です。

f:id:aru47:20201108161856p:plain

Githubにリリースを追加

ここまででgithub actionsの準備は整いました。releaseを追加すればactionが走り、その結果をActions以下で確認できます。

f:id:aru47:20201108162144p:plain

無事に走ればpypiにもパッケージが追加されているのを確認できます。

pypi.org

f:id:aru47:20201108162311p:plain

Pytorch高速化 (2)Mixed Precision学習を試す

Qiitaからのお引越しです。

前編 aru47.hatenablog.com

TLDR; (2021/06/17)

resnet50でCIFAR10をFP16により学習を2倍高速化でき、メモリ使用量も半分にできる。

pytorch1.6からデフォルトでMixed Precision学習をサポートしており、画像認識なら大抵これで上手く学習できます。 一部例外として、swin transformerだとapexを使用したほうが精度が良い場合もありました。

このチュートリアル通りにコードを書くのがおすすめです。 pytorch.org

use_amp = True # ampをオンオフ

# Creates model and optimizer in default precision
model = Net().cuda()
optimizer = optim.SGD(model.parameters(), ...)

# Create a GradScaler once at the beginning of training.
scaler = torch.cuda.amp.GradScaler(enabled=use_amp)

for epoch in epochs:
    for input, target in data:
        optimizer.zero_grad()

        # Runs the forward pass with autocasting. 自動的にレイヤ毎に最適なビット精度を選択してくれる(convはfp16, bnはfp32等)
        # ベストプラクティスを選択してくれるため、便利。use_amp=Falseではfp32を使用する。
        with torch.cuda.amp.autocast(enabled=use_amp):
            output = net(input)
            loss = loss_fn(output, target)

        # Scales loss. 
        # Scalerはautocasting以下では呼ばないこと。
        # 
        scaler.scale(loss).backward()
        scaler.step(optimizer)
        scaler.update()
        opt.zero_grad()

実際のcifar10学習コード:

github.com

目的

RTX2080tiを手に入れたのでPytorchにてFP16学習を試す。 Tensorcoreを使うことで演算速度がFP32に対する大幅な高速化が(スペック的に)期待できる。 どれくらい早くなるか、pytorchでどう書けばFP16が使えるかなど記述する。

BatchNormはFP32なので正確にはMixed-precision trainingだ。

codes: https://github.com/kentaroy47/pytorch-cifar10-fp16

そもそもFP16って?

FP16とは俗にいう”半精度”と呼ばれる浮動小数点における数字の表現方法である。 コンピュータの内部では数字は2進数で保存されており、例えば"3"という数字は:

0011 # 0*8+0*4+1*2+1*1 = 3

といった形で保存されている。これは一般的な4bitの整数表現(4bit Int)という形である。 ただこれでは0から15までの数字しか表現できない。 更に負や小数などを表現するのに使われるのが浮動小数点(float)と呼ばれる方式である。 image.png

上記の図(Googleより)は32bit浮動小数点(FP32)と16bit浮動小数点(FP16)の形式を示す。 これらはコンピュータの数字表現で最も一般的なフォーマットでFP32が単精度と呼ばれ、メモリを節約したい場合に使われるFP16が半精度と呼ばれる。

特徴として: ・MSB(最も左側のビット)が正負を決める ・Exponent 8bitは小数点位置を決める指数部である。 ・Mantissaは整数部を決める。上記のInt方式と同様である。

image.png http://www.altima.jp/column/fpga_edison/bit_number_float.html

のように指数部をスケールすることで非常に幅広い範囲の数字を表現することができる。

FP16学習はDNNにどう関係するの?

1) メモリの節約 まずDNN学習時はGPUメモリ上にactivationやweightを保存し無くてはならない。 FP32方式でそれらのパラメータを保存するよりも、FP16で保存することで必要なメモリ量を半分にへらすことが出来る。

image.png nVidiaより。

2) 演算の高速化 次世代GPUはFP16を使うと演算速度が大幅に向上するTensorCoreが搭載されている。 そのためFP16で学習することでFP32時に対し数十倍の演算速度向上が期待できる(スペック上は)!

環境

Ubuntu 16.04 Pytorch 1.0 CUDA 10.0 cudnn 7.4 GPU RTX2080ti

学習環境

FP16学習repo →https://github.com/kentaroy47/pytorch-cifar10-fp16 CIFAR10の学習repoを元に改造した。

python train_cifar10.py --fp16

でResnet18で学習開始。

FP16化にあたりポイント

https://github.com/fastai/imagenet-fast/tree/master/cifar10 Fast-aiのFP16化レポを参考に学習コードを改造した。

他の参考は: Training with Half Precision https://discuss.pytorch.org/t/training-with-half-precision/11815 https://pytorch.org/docs/stable/tensors.html https://devblogs.nvidia.com/apex-pytorch-easy-mixed-precision-training/

PytorchではFP16(half)がサポートされており、簡単にFP16化が可能。

FastAIのモデル16FP化コードを使わせていただいた。 https://github.com/fastai/imagenet-fast/blob/master/cifar10/fp16util.py のコードを使い、FP16化した。

何をやっているかというと、 入力、CNNのレイヤ→FP16化 BatchNormレイヤ→FP32化 している。

class tofp16(nn.Module):
    def __init__(self):
        super(tofp16, self).__init__()

    def forward(self, input):
        return input.half()


def copy_in_params(net, params):
    net_params = list(net.parameters())
    for i in range(len(params)):
        net_params[i].data.copy_(params[i].data)


def set_grad(params, params_with_grad):

    for param, param_w_grad in zip(params, params_with_grad):
        if param.grad is None:
            param.grad = torch.nn.Parameter(param.data.new().resize_(*param.data.size()))
        param.grad.data.copy_(param_w_grad.grad.data)


def BN_convert_float(module):
# BatchNormのみFP32フォーマットにしないと性能が出ない。
# BatchNormレイヤを検索し、このレイヤのみFP32に設定。
    '''
    BatchNorm layers to have parameters in single precision.
    Find all layers and convert them back to float. This can't
    be done with built in .apply as that function will apply
    fn to all modules, parameters, and buffers. Thus we wouldn't
    be able to guard the float conversion based on the module type.
    '''
    if isinstance(module, torch.nn.modules.batchnorm._BatchNorm):
        module.float()
    for child in module.children():
        BN_convert_float(child)
    return module


def network_to_half(network):
    return nn.Sequential(tofp16(), BN_convert_float(network.half()))

正直このfp16util.pyをコピーし、network_to_halfをネットワークに対し適応すれば良い。

net = ResNet18()
from fp16util import network_to_half
net = network_to_half(net)

学習速度

resnet18

batch = 128 FP32時:1 epochあたり15sec メモリ資料量:2000MB

FP16時:1 epochあたり10sec メモリ使用量:1500MB

50%高速化。 更にバッチ数、モデルサイズを大きくすると。。?

resnet50

batch = 512 FP32時:1 epochあたり57sec メモリ資料量:21000MB

FP16時:1 epochあたり27sec メモリ使用量:11727MB @TitanRTX

  • メモリ使用量半分に
  • 2倍高速化

両者とも50epochでval.89%ほどに到達。 精度的には更にチューニングが必要で(250 epochで93%くらいはいくはず)、FP16が精度的に問題ないかはまだわからない。

TODO: ちゃんと狙い通り16FPになっているかなど確認する。

CIFAR10より更に大きいデータセット

CIFAR10は画像サイズが小さく、演算よりもメモリ通信やデータローダがボトルネックになってしまっている?50%くらいしか早くなっていない。。 Resnet50ではちゃんと二倍高速化。メモリ半分はかなり嬉しい。

知り合いによるとImagenet学習ではほぼ2倍の速度向上が得られたとのこと。 そのうち試したい。

Nvidia Apex

https://devblogs.nvidia.com/apex-pytorch-easy-mixed-precision-training/

Nvidia謹製のpytorch FP16学習ツールが公開されました。

続編で試します

qiita.com

推論高速化

aru47.hatenablog.com