人工知能とか犬とか

人工知能と犬に興味があります。しょぼしょぼ更新してゆきます。

2019年12月21日:OpenPoseの論文を読む 他2本

最近読んだ論文の中から印象に残ったものについて、一言コメントを書いてみます。

OpenPose: Real-time multi-person keypoint detection library for body, face, hands, and foot estimation

言わずと知れた姿勢推定モデルのOpenPoseですが、論文はちゃんと読んだことがなかったので、ちょっと読んでみっか、と思って読んでみました。きっかけは、『つくりながら学ぶ! PyTorchによる発展ディープラーニング』を読みながら、自分で再実装してみる、という作業をしているときに、どうもOpenPose周りの記述に誤りがあるらしいという情報をキャッチしたためです。そのへんの修正点は、githubのissueに上がっています。

つくりながら学ぶ! PyTorchによる発展ディープラーニング

つくりながら学ぶ! PyTorchによる発展ディープラーニング

OpenPoseの概要

論文の話に入る前に、OpenPoseの概要を示しておきます。

OpenPoseは、深層学習による、人間のキーポイント(関節など)を推定する、というモデルです。人間のキーポイント検出には大きく2通りのやり方があって、トップダウン方式とボトムアップ方式があります。前者は、人間がココにいるよ、という検出を行った後に、個々の人間の領域に対してキーポイントを求めていく、というやり方です。後者は、最初にココが左ひじっぽいよ、といったキーポイントを先に求めて、それらのキーポイントを集めて人間の骨格グラフにまとめる、というやり方です。OpenPoseは後者の方式をとっています。

f:id:wanchan-daisuki:20191221123612p:plain
OpenPoseのプロセス

ボトムアップ方式が問題になるのは、画像中に複数人写っている場合に、検出されたキーポイントをどう接続すれば良いのかがわからない、という点です。例えば上の一番左の画像(a)では2人の人物が写っていますが、理想的なキーポイント検出モデルができたとしても、左肘が2点、左肩が2点検出されてしまいます(b)。すると、これらの接続の方法は2通りあるため、どのような基準でキーポイント同士を接続するのかが問題となるというわけです。

OpenPoseでは、論文タイトルにもある Part Affinity Fields(PAFs)をつかってこの問題を解決します。PAFsは、上の図(c)のような結果を出力します。直感的には、「こっちの方に接続すべきポイントが有るよ」という情報を表すマップのようなものです。この情報をうまく使って、キーポイント同士をどう繋げば良いのかを決める、というのが本手法の最重要ポイントです。

コメント

で、OpenPoseの論文を読んでみようか、と思って調べてみたら、そもそもOpenPoseの論文には、2016年版2018年版とがあるという事実に気づきました。基本的なアイディアは同一ですが、ネットワークの構造が変わっていて、2018年版はより高速・高精度になっているようです。なお、『つくりながら学ぶ〜』では、2016年版が使われています。

f:id:wanchan-daisuki:20191221121253p:plain
2016年版。PAFsとキーポイントが一緒に繰り返し洗練されていく構造。

f:id:wanchan-daisuki:20191221121142p:plain
2018年版。PAFsを予測してからキーポイントを予測するという構造に変化している。

と、いうわけで、2018年版の論文を読んでみました。 物体検出系の論文でも思うことですが、こういう構造出力を行うアルゴリズムって、ニューラルネットワークの部分ではなく、前処理・後処理がめんどくさいな、という感想を持ちました。ネットワーク部分は30分もあれば実装できそうだけど、targetの作成や後処理はもっとかかるだろうな、という印象です。ここらへんの前処理や最適化アルゴリズムなども、サクッと実装できるだけの力を身に着けたいところです。

キーポイント出力のNMS

キーポイントの検出部分については出力されたヒートマップに対して、non maximum suppression(NMS)したよ、と書かれているのですが、具体的にどんな処理すんのよ? と疑問に思いました。PyTorchによる非公式の実装を見てみると、ヒートマップを1ピクセルずらした画像を用意して重ね合わせ、もとのヒートマップでの値が一番大きければLocal Maximumとみなせるので、それを出力するというとても簡単な処理がなされています。

PAFsの2部グラフ問題

また、どのPAFsを採用するのか、というグラフ構築の問題に対しては、以下の2つの処理を頑張って実装する必要があります。

  • エッジ候補に関して、PAFスコアを計算する。具体的にはエッジの候補の線上にあるピクセルをサンプルし、その点のPAFsの2次元ベクトルが、エッジとどれくらい同じ方向を向いているかをスコア化する、という処理を行います。
  • エッジ候補の中から一番確からしい構造になる組み合わせを選択する。完全に全エッジの組み合わせで問題を解くのはNP完全らしいので、1つのエッジに絞って、2部グラフとして考え、ハンガリアンアルゴリズムを使って2割当問題を解くそうです。

余談

人間の姿勢推定、つまりキーポイント推定は、torchvisionにすでに学習済みのものが実装済みです。こちらは、Mask R-CNNベースのものなので、OpenPoseのボトムアップ方式とは異なり、トップダウンの方式です。

pytorch.org

DewarpNet: Single-Image Document Unwarping With Stacked 3D and 2D Regression Networks

歪んだ書類をきっちり補正する、という研究です。OCRの精度も上がるよ、とのこと。

概要

本論文では、歪んだ書類を補正する、Dewarpという問題に取り組んでいます。

f:id:wanchan-daisuki:20191221134834p:plain
DewarpNetの出力

大きくは、DewarpNetと呼ばれるメインのネットワークと、Refinement NetworkというDewarpNetの結果をきれいに補正するネットワークの2つから成っています。

DewarpNet

DewarpNetの中には2つのサブネットワークがあります。それぞれ書類の面の3次元的な位置を推定するU-Net風のShape Networkと、その出力結果をもとに、補正済みの画像を入力画像のどこからサンプルしてくるのかを求めるTexture Mapping Networkです。

f:id:wanchan-daisuki:20191221142454p:plain
DewarpNetのプロセス

Refinement Network

DewarpNetだけだと、入力画像からサンプルしてきた画素を並べただけの画像が出力されるので、書類の歪みによって生じた陰などがOCRのときに問題になったりします。 というわけで、DewarpNetによって得られる結果を補正するRefinement Networkというのを提案しています。

まず、入力画像から法線ベクトル画像\hat{N}を推定するNEがあります。これ、上のShape Netとかなり親しい問題を解いているので、マルチタスク学習するといい感じになるのではと思いますが、この論文では別々のネットワークでやっています。NEもU-Net風の構造です。

DewarpNetのテクスチャマッピング\hat{B}をもとに、補正済み入力画像と補正済み法線ベクトル画像を作成します。この2枚の画像を用いて、書類の明るさ画像\hat{S}を推論します。明るさがわかれば補正済み入力画像の値から引いて上げると、更にきれいになった画像\hat{A}が得られる、というわけです。

f:id:wanchan-daisuki:20191221143611p:plain
Refinement Networkのプロセス

コメント

この論文が印象に残ったのは、データセットを自分たちで頑張って作っているという点です。上に上げた2つのネットワークの至るところで損失が計算されていることからもわかるように、これらの中間処理結果には明確なTargetがあります。例えば、Shape Networkは、\mathbb{R}^{3 \times H \times W}を出力しますが、これは書類の面に関する3次元的な位置を表します。これ、どうやってデータセット作るの? という感じですが、なかなか頑張っています。

f:id:wanchan-daisuki:20191221144144p:plain
データセットの構築

まず、左端の写真に何やら土台とその上に乗っかっている歪んだ書類が見えます。この土台は、8x8の64本のピンからなる制御装置です。これで上に乗っかっている書類を動かしながら、いろいろな角度からの歪んだ書類を上から撮影しています。 上についているカメラは深度カメラであるReal Senseです。Real Senseで撮影したデータから深度マップとRGB画像、Point Cloudが得られるわけですが、ここからいろいろな平滑化や補正の処理を行って、3次元的なMesh画像を推定しています。更に、せっかく得られたMesh画像なんだから、いろいろな書類のTextureや照明条件で合成データ作ろう、こともやっています。

自分は3次元のデータ処理はほとんどやったことがないのですが、なかなか手間がかかるけど合理的なことをやっているなあと、感心した次第です。

データセットは下記リポジトリで公開されてますが、現在はメンテナンス中のようです。機会があれば触ってみたいと思います。 github.com

データセットの作り方以外で面白いな、と思ったポイントは、Texture Mapping Networkで、CoordConvを使っていることです。CoordConvは非常に単純な手法で、2Dの畳み込み層の一種なのですが、単に入力された特徴量マップに、縦横の座標を表す2チャネルを結合してから畳み込みを行うというもので、これだけの単純な仕組みでいろいろなタスクで精度が向上するよ、というものだったと記憶しています。Texture Mapping Networkは、座標情報を推論するネットワークなので、なるほど、こういう場面だと確かに有効そうだな、と思いました。

Balanced Datasets Are Not Enough: Estimating and Mitigating Gender Bias in Deep Image Representations

画像認識データセットは、ラベルをバランスさせるだけじゃ不十分だよ、という研究です。ICCV2019に採択された、AllenNLPでおなじみの、Allen Institute for AIの論文です。

arxiv.org

概要

Qiitaに解説記事を書いたので、ごく簡単に済ませます。

qiita.com

データセットが(ラベルのような表面的に)持っているバイアスを定量化するDatasetLeakageと、モデルが持っているバイアスModelLeakageを定量化し、その差である、モデルによってもたらされたバイアスの増加分Bias amplificationと呼ばれる指標を提案しています。

この論文では、そのような指標を提案するのがメインで、ネットワークの提案には重きは置かれていません。Adversarial Debiasingという一連の手法があり、そのいくつかの例を実験で採用し、今回提案している指標で確かにバイアスが減っていると評価できるよね、という主張です。

f:id:wanchan-daisuki:20191221150732p:plain
Adversarial Debiasingの例

コメント

この分野の他の論文では、Dataset LeakageはDataset Bias、Model LeakageはRepresentation Biasと呼ばれています(間違っていたらごめんなさい)。正直、〜Biasという呼称よりも、〜Leakageという呼称の方が実態にあった呼び方のような気がするので、私も積極的に使っていきたいところです。

Representation Biasの問題は、Adversarial ExampleやDisentanglementなどの領域とも関係するチャレンジングな問題だと思っています。 Adversarial Debiasingに関しては、ちょっとした研究テーマを考えているので、来年早々にサーベイでもやってみようかなと思っています。

今回は以上です。

2019年11月の機械学習関連記事まとめ

2019年11月にはてブをつけた記事に関する振り返りです。

ICCV2019関連

10月末に、韓国ソウルにてICCV2019が行われました。 ICCVは、CV系の国際学会で、CVPRとかECCVなどと並んで世界トップクラスの研究が集まる学会です。

ICCV 2019 open access

そのICCVの論文を集めたページがこのページです。 ある程度オープンな国際学会では、こういう論文一覧ページが公開されるので、毎回楽しみにしています。

openaccess.thecvf.com

もっとも、論文の数が多すぎて、タイトルを一通り見るというのだけでも一苦労です。 自分の場合は、興味のあるキーワードがタイトルに含まれているものに絞ったり、Best Paperとして選出された論文に絞ったりしたうえで、abstractを読んで更に絞る、という手順で読むものを決めることにしています。

tamarott/SinGAN

ICCV2019でBest Paperに選ばれた論文の公式実装がgithubで公開されています。 単一の画像から生成モデルを作成するという手法で、様々なアプリケーションに使える、という非常に面白い論文でした。 このような形ですぐに実行できる形で公開してくれるというのは、本当にありがたいことです。

github.com

ICCV 2019 Tutorial: Everything You Need to Know to Reproduce SOTA Deep Learning Models

論文だけでなく、ワークショップやチュートリアルも、学会の見どころです。 このチュートリアルでは、SOTAの深層学習モデルのような、大規模な訓練が必要なときに、どのようにやれば良いのか、というノウハウが学べるようです。

iccv19.mxnet.io

私自身は、長くてもシングルGPUで4,5日程度訓練する程度までしかやったことがないので、こういうものをちゃんと読んで、もっと大規模な訓練もできるようになりたいなあ、と思っています。

素敵なジョークプログラム

今月印象に残ったジョークプログラムというか、ジョークプロジェクトを2つ挙げておきます。

Booksby.ai

AIによる、AIのための本屋さん、というジョークサイト。

booksby.ai

すべての本がAIによって生成され、すべてのレビューがAIによって書かれているという笑えるサイトです。 レビュアーの顔写真も生成された画像を使っています。 これらの本はamazon.comでペーパーバックとして実際に買うことすらできるようになっています。

裏でどういう手法やデータを使っているのかは、作者のブログで公開されています。

GitHub - gahjelle/pythonji: 🐍 - Write Python with Emojis

Pythonでは、スクリプト中でemojiを使うことができない、ということをネタにしたジョークライブラリです。

github.com

例えば、以下のようなかわいいコーディングが可能になります。

import pandas as 🐼

中身はどうなっているのかな、と確認してみると、emoji部分を適当な文字列に置き換えてcompile, execしているという感じで、まあそうだろうなという印象です。 このリポジトリ自体はジョークですが、READMEに記載してあるとおり、Pythonunicode対応が不足しているというのは案外重要な指摘なのかなとも思います。

以下のような日本語によるコーディングは可能なわけですから、emojiも使えるようになるべきではないか、という気もします。

import pandas as パンダ

その他の気になった記事

Baidu製DeepLearningフレームワーク:Paddle

github.com

Baiduが発表した、新しいDeepLearningのフレームワークです。 今更新しいDeepLearningのフレームワーク? という印象を最初は持ったのですが、公式のドキュメントがガッツリ中国語になっていて、国内の開発者たちがコミュニティを作りやすいフレームワークを用意したんだな、と理解しました。

機械学習の標準パフォーマンス計測フレームワーク:MLPerf

mlperf.org

前からあったようなのですが、機械学習のtrainingやinferenceのパフォーマンスを計測する標準のフレームワークのようです。 定期的に報告書が出されているようなので、毎回確認したいところです。

多言語QAデータセット:MLQA

github.com

Facebook AI Researchは、MLQAという本文抽出型の質問応答タスク用データセットを公開し、論文も公開しました。面白いのは、Cross Lingualを表明していることで、いくつかの言語に関するパラレルな質問応答のデータセットになっているという点です。日本語の質問応答システムを作るために、日本語の質問応答データセットを用意せずに、英語のデータセットを活用してモデルを訓練したい、といった課題を解決するための、ベンチマークとして使われることを想定しているようです。

Pyroが1.0になった

github.com

PyTorchベースの確率的プログラミングのライブラリPyroが1.0になりました。 以前PyMCなどのライブラリは使ったことがあって、非常に使いやすかったのですが、CPUでの計算を行っていたのでそこそこ複雑なベイズモデルを作ろうとするとなかなか結果が出るのに時間がかかっていました。 Pyro自体も前々から存在は知っていましたが、そもそもベイズモデルを構築する機会が無くて、なかなか試してみる機会を設けられませんでした。いい機会なので、今度触ってみたいと思います。

アンカーフリーの物体検出手法:RepPoints

arxiv.org

物体検出の新しい手法RepPointsがMicrosoft Researchから発表されていたようです。Ng先生のThe Batchで知りました。 物体検出の手法といえば、事前に定義したアンカーにGroundTruthのバウンディングボックスを関連付けるという手法がメインなわけですが、この手法は、そのようなアンカーを使用しない手法なのだそうです。ざっくりと、クラスラベルに特徴的な見た目のfeatureを学習し、その領域(を代表する点)を囲むバウンディングボックスを生成することで検出結果にするという手法だと理解しているのですが、詳細はまだ確認できていないので、今月読んでみようと思っています。

pandasのバックエンドを高速化する:modin

github.com

pandasのDataframeの高速化のために、通常のpandasとは異なるバックエンドとしてRayやDaskを使用するというmodinというフレームワークの存在を知りました。 ドキュメントを見ると、Rayをバックエンドとする際の解説は詳しめですが、Daskをバックエンドとする際の解説はまだありません。最終的には、pandas風のAPIだけではなく、sqlite風のAPIも扱えるようにすることを目指しているみたいです。

新しい時系列データライブラリ:adtk

github.com

新しい時系列データの異常検知ライブラリが公開されていました。 ドキュメントが充実しており、これを読むだけでも時系列異常検知についてある程度理解ができるようになりそうです。Seasonalityがある場合や、Multivariateの場合などにも対応しつつ、独自にカスタムした異常検知関数も設定できるなど、一通りの機能が揃っているみたいです。

Facebook AI Researchの強化学習ライブラリ

github.com

FAIR製で、内部でも使用されているという強化学習と(Contextual)Banditアルゴリズムのためのライブラリです。 基礎的な手法や前処理などが予め用意されているみたいです。また、プラットフォームとしての機能も入っているようです。

githubの方のドキュメントはただの手順書で、どういう思想のライブラリなのか今ひとつ把握できませんでした。ホワイトペーパーもあるものの図などが無くてちょっと近寄りがたい雰囲気です。 公式ブログの記事が一番どのように使えば良いのかを説明してくれているような気がします。プラットフォームとしては、オンラインで教師データを収集しつつ、オフラインで訓練したモデルを適宜オンラインで使用するモデルとしてデプロイするというワークフローを比較的簡単に組める、ということなのでしょうか。これって、強化学習に限った話ではないと思っているので、どこまで汎用的に使えるのか、そもそも使いやすいのかなどを確認してみたいと思います。

今回はこのへんで。

最近読んだ論文 2019/11/16

仕事や趣味で読んだ論文について、ごく簡単に紹介していくという記事です。 毎週は難しいかもしれませんが、ちょくちょく書いていきます。

今回は3本の論文を紹介します。

Beyond Word Importance: Contextual Decomposition to Extract Interactions from LSTMs

LSTMに入力されるトークン列の範囲が出力に対してどの程度の影響を持っているのかを分析する手法であるContextual Decompositionを提案しています。

f:id:wanchan-daisuki:20191116105642p:plain
token単位での貢献
"Beyond Word Importance"というタイトルどおり、token列に対するSentiment Analysis について、token単位での出力に対する貢献はもちろん、token列の任意の範囲の貢献も出すことができるという手法です。 いわゆる「説明可能なAI」の流れに属する手法ですが、LIMEのような近似的な線形モデルを構築するのではなく、内部を流れる値を各入力要素の貢献の和に分解することで説明しようという手法です。

Attentionを使った手法などで各tokenが出力(ネガティブ/ポジティブ)にどのように貢献しているのかを可視化する方法はありますが、本手法の特徴は、連続するtoken列の範囲に対して貢献を出力することができます。

f:id:wanchan-daisuki:20191116110755p:plain
連続するtoken範囲の貢献を可視化

手法の肝となるのは、LSTMのセル間を流れる h_t c_tを、token範囲の効果とそれ以外の効果に分離するということです。 f:id:wanchan-daisuki:20191116111853p:plain

こんな分離どうやってやるの、という感じなのですが、LSTMの特性をうまく利用して頑張って分離しているようなのですが、ちょっと読んだ程度ではなかなか理解できません。

また、非線形の変換であるtanhシグモイド関数は、以下のような特性をもつ関数L_{tanh}などを考えることで、各要素の和に対する変換を、各要素に対する返還の和へと分離します。 f:id:wanchan-daisuki:20191116112350p:plain

こんな便利な関数は実際には存在しませんので、L_{tanh}, L_{\sigma}は、入力された値に応じて動的に決まることになります。このような分離は、シャープレイ値の近似であると論文中では説明されています。 f:id:wanchan-daisuki:20191116114134p:plain

公式の実装がgithubで公開されていますが、対応しているPyTorchのバージョンが古く、最新のバージョンで動かすためには、少し変更する必要があります。 github.com

もっとも、本手法の後継論文の実装が2つ、別のリポジトリでActiveになっていますので、ちょっと触ってみてたいということであればこれらのほうが良いでしょう。 前者は、本論文で提案した手法をConv層やReLUなどにも使えるように拡張し、階層的な説明を可能にするというもの。後者は、Contextual Decompositionによって得られた説明に対して、修正を施すことで、モデルを改善できるという手法のようです。

A Topological Loss Function for Deep-Learning based Image Segmentation using Persistent Homology

なんとなくタイトルから、面白そうだと思ったので読んでみた論文です。 本論文は、セグメンテーション結果に対して、パーシステントホモロジーを用いた正則化手法を提案しています。

真のセグメンテーション結果がわからない場合でも、トポロジカルな性質(Betti数)さえわかっていれば、セグメンテーションモデルの出力から計算できる微分可能な値であるPersistence barcodeのbirth/deathを用いて損失を計算できる、ということのようです。

f:id:wanchan-daisuki:20191116132330p:plain

f:id:wanchan-daisuki:20191116132538p:plain
persistence barcodeは、赤が結合部位の出現と消失を、緑が穴の出現とその消失を表す

実用例として、学習済みモデルの出力結果を補正するために使う方法と、半教師あり学習に使う方法が示されています。半教師あり学習に使うケースは、ラベルなしデータに対してトポロジカルな性質が事前にわかっているとし、現実的なユースケースではすべて同一であることが求められるようです。 このようなケースはやや特殊なドメイン(この論文では心臓の写真なので、必ず心室の壁がリング状になる)でのみ有効なようにも思いますが、ドメインによっては強力な正則化や後処理の手段になりそうです。

f:id:wanchan-daisuki:20191116130833p:plain

自分はトポロジーについて超ぼんやりとした理解しかしていないので、これから色々勉強していこうとしているところですが、以下のようなリポジトリ(論文もある)があったりして、道具は徐々に整備されてきているようです。

github.com

SinGAN: Learning a Generative Model from a Single Natural Image

ICCV2019のBest Paperに選ばれた論文です。 単一の画像で作るGANということで、どういうことなんだろうと思って読んでみた論文です。 今週、Qiitaに解説記事を書いてみましたので、詳しくはそちらを御覧ください。

モデル自体はそこまで複雑なところは無いのに、いろいろな応用があるというのが、非常に面白い論文でした。特にアニメーション生成はすごく面白いと思ったので、論文を読んだあとに公式リポジトリのサンプルを動かしてみました。教師データに用意する画像が一枚で済むこともあり、訓練時間は1時間程度ですし、GANを実際に動かしながら学びたいという人にとっての入り口としても良いと思いました。

f:id:wanchan-daisuki:20191116125221g:plain f:id:wanchan-daisuki:20191116125251g:plain

2019年10月の機械学習関連記事まとめ

趣旨

すこし趣向を変えて、ブログを再開してみようと思います。 自分は最近、毎週水曜日にいくつかの情報源をもとにネットを巡回し、面白そうな記事を見つけたときにははてなブックマークでコメントを書くことにしています。

とはいえ、ブックマーク+かんたんなコメント程度では、あまり意味がないと思っていて、定期的に見直す場を作ってみようと思いました。

そういうわけで、毎月頭に、先月自分がつけたブックマークを整理する、という記事を上げていこうと思います。

深層学習手法のデモ

深層学習は、自分で触ってみて「へーこんなことができるんだ」感動すると、俄然興味が湧いてくるものだと思います。

とはいえ、論文を自分で実装するというのは、なかなか骨が折れる。そのため、githubで公開されているソースコードや、デモサイトなんかが用意されていると、たいへん嬉しいものです。

というわけで、公式で用意されているデモを2つと、自分でデモを作るときに役立つかもしれない情報2つを紹介します。

CTRL - A Conditional Transformer Language Model for Controllable Generation

github.com

salesforceのテキスト生成モデルです。訓練するときにジャンルのようなコードを埋め込んで学習することで、それらしい文章が生成できるようになているらしいです。Colabで実行できるデモがあり、実在しないURLからそこに含まれる文字列をヒントに、それっぽい記事を生成する様子が見れます。

colab.research.google.com

10月末に、huggingface/transformersというtransformerベースの手法やpretrainedモデルを集めるコードベースに組み込まれたようです。

FUNITのデモ(GANIMAL)

www.nvidia.com

NVIDIA AI PLAYGROUNDにFUNITで構築したモデルのデモが公開されました。動物の顔画像を生成するGANなので、その名も「GANIMAL」。

f:id:wanchan-daisuki:20191104165452p:plain
FUNITのデモ

現状では、入力画像を選択できるだけなので、FUNITのキモであるFew-shot(変換先の画像が数枚あれば良い)という特性が活かせていないデモになっているのが残念です。

streamlit

github.com MLツールを簡単に作れるという売り文句のライブラリ。創業者がGoogleX出身ということもあり、注目度は高いです。 一通り触ってみて感じたのは、特に凝ったレイアウトが不要であるプロトタイプに最適なライブラリだなあ、という感想です。

個人的にポイントが高いのが、各種可視化ライブラリにデフォルトで対応しており、jupyterで普段から使っている可視化ライブラリのコードを流用できるという点です。あんまり使う機会が無いかもしれませんが、graphviz対応とかも良い感じです。

グラフだけでなく、地図や動画、音声もサクッと埋め込めます。

Python & OpenGL for Scientific Visualization

www.labri.fr

PythonOpenGLを使った科学計算で使う可視化についてのまとめ、というより教科書です。リッチな可視化を作ろうとすると、先程のstreamlitのようなものからスクラッチへの移行を検討する必要があるそうです。

研究・論文

今月面白そうと思った研究をさらっと紹介します。ちゃんと論文を読んだわけではなく、Abstractなどをざっと眺めただけなので、間違っていたらごめんなさい。

Expanding scene and language understanding with large-scale pre-training and a unified architecture - Microsoft Research

www.microsoft.com

テキストとビジョンを統合したBERTのようなUnified Encoder-Decoderの開発。画像中の物体RoI系列とテキスト系列をそのままBERT風のEncoder-Decoderにぶちこんで、マスクされたテキストの穴埋めを予測できるよう事前学習します。事前学習済みモデルをもとに、VQAやImage-Captioningにファインチューニングするらしいです。

Semi-Supervised and Semi-Weakly Supervised ImageNet Models

github.com

教師あり学習と弱教師あり学習を組み合わせて(Semi-Weakly)、10億枚規模のデータセットに対して画像分類モデルを学習するという研究。ここまで大きなデータセットになってくると、自分で手を出すことは無いと思うのだが、中身のロジックくらいは知っておきたいので、後日論文を読む予定(半教師ありと弱教師ありを組み合わせるってどういうこと???)。 torch.hubから学習済みモデルをロードできるようになっています。

Gate Decorator: Global Filter Pruning Method for Accelerating Deep Convolutional Neural Networks

arxiv.org

Andrew NgのThe Batchで紹介されていた、モデルの軽量化手法。各チャネルのスケーリングをするGate機構を導入し、それが0に近いチャネルを削除する、というのが基本的な方針のようです。

ImageNet(ResNet)の計算コストを55%削減・精度は0.67%減、CIFAR(ResNet)の計算コストを70%削減・精度は0.03%増という結果のようです。

facebookresearch/SlowFast

github.com

動画行動検出手法のPythonコードベース。FAIR製。 以下の手法がまとまっているようだ。

  • SlowFast
  • SlowOnly
  • C2D
  • I3D
  • Non-local Network

その他のブックマーク

SpeechBrain: A PyTorch-based Speech Toolkit.

speechbrain.github.io

PyTorchベースの音声認識ツールキットをつくりましょうというプロジェクト。スポンサーにDOLBYがいるのが本気度高そうです。

Hydra

cli.dev facebook製のpython用config管理ツール。少し眺めた程度なので、何ができるのか実はよくわかっていません。うまく使いこなせれば、pythonのargparse周りをきれいに整理できそうな予感があります。 Specializing configurationを見る限り、機械学習のハイパーパラメータをyamlファイルにまとめておく、という使い方もできるみたいです。

BrachioGraph

brachiograph.readthedocs.io

わずか14ユーロ(1800円くらい?)で作るお絵かきマシン。

[https://brachiograph.readthedocs.io/en/latest/images/brachiograph-with-pencil.jpg:image=https://brachiograph.readthedocs.io/en/latest/images/brachiograph-with-pencil.jpg]

この手作り感がたまらない。パーツを集めて作ってみたいですが、そんな暇はあるのでしょうか(汗)

LVISデータセット

www.lvisdataset.org MS-COCOをベースにした、インスタンスセグメンテーションの大規模データセットです。例をみるとわかりますが、かなり細かい部分まで丁寧にアノテーションがなされていることがわかります。

機械学習論文の再現性のためのチェックリスト

Andrew NgのThe Batchで紹介されていた、NeurIPs2019のCall for Papersでも参照されている、機械学習論文の再現性(Reproduction)のためのチェックリストです。

https://www.cs.mcgill.ca/~jpineau/ReproducibilityChecklist.pdf

月に何本か論文を読み、ときに実装する身としては、このくらいの要件はきちんと満たした論文だけになってほしいなあと思う次第です。

できれば、github等で、すべての実験のソースコードを公開してほしいところですが、実験って結構ぐちゃぐちゃになりながらなんとか成果が出るって感じのものがほとんどだと思うので、それをまた整理してgithubに置くのはしんどいのかも、と思ったりもします。

以上

2019年になった。

あけましておめでとうございます。

2019年になったようです。

そういえば最近更新できていないな、と思い出しました。

せっかくなので、今年の目標というか、方向性のようなものをふんわりと書き留めておこうと思います。

このブログは『人工知能とか犬とか』というタイトルのブログなのですが、PyTorchの入門記事ばかりで、犬関係の記事が無いということに気づきました。このままではタイトル詐欺になってしまうので、犬関係の記事を書いていこうと思います。

あと、PyTorchも1.0になって、記事の内容が若干古くなっているので、全体的に更新していこうかなと思います。 PyTorch本体だけではなく、周辺ライブラリにも突っ込んでいこうかなと思っています。

自分は研究者ではなく、ただの趣味でやっているので、色々なジャンルの入門レベルを味見する感じでやっていこうと思います。

そんなわけで、このブログの今後の方針はこんな感じです。

  • 犬系の書籍や論文を読んで、それとなくまとめてみる。
  • PyTorchを中心に色々な分野に(入門程度に)突っ込んでみる。

今年はいい年になるといいなあ(╹◡╹)

PyTorchのDatasetとDataLoader

概要

PyTorchのチュートリアルData Loading and Processing Tutorial をやってみて、DatasetとDataLoaderの使い方を学ぶのです。

DatasetとDataLoader

そもそも、深層学習で用いる教師データは、以下のような処理を必要とします。

  • データの読み込み
  • データの前処理
  • データ拡張
  • データの順番をシャッフルする
  • ミニバッチを作成する
  • シングルマシン or 分散システム中の複数のGPUにミニバッチを配布する

ここらへんの処理を簡単に実現できるのが、DdatasetとDataLoaderです。

Dataset

抽象基底クラスtorch.utils.data.Datasetを継承して、以下のメソッドを実装すれば、独自のDatasetを定義できます。

  • __len__: データセットに含まれる全要素の数を返す。
  • __getitem__: i番目のサンプルをdataset[i]という形で取得できるようにするためのメソッド。
class FaceLandmarksDataset(Dataset):
    """Face Landmarks dataset."""

    def __init__(self, csv_file, root_dir, transform=None):
        self.landmarks_frame = pd.read_csv(csv_file)
        self.root_dir = root_dir
        self.transform = transform

    def __len__(self):
        return len(self.landmarks_frame)

    def __getitem__(self, idx):
        img_name = os.path.join(self.root_dir,
                                self.landmarks_frame.iloc[idx, 0])
        image = io.imread(img_name)
        landmarks = self.landmarks_frame.iloc[idx, 1:].as_matrix()
        landmarks = landmarks.astype('float').reshape(-1, 2)
        sample = {'image': image, 'landmarks': landmarks}

        if self.transform:
            sample = self.transform(sample)

        return sample

これは、以下のようにiterableなオブジェクトとして扱うことができます。

face_dataset = FaceLandmarksDataset(
    csv_file='faces/face_landmarks.csv',
    root_dir='faces/')

for i, sample in enumerate(face_dataset):
    print(sample['image'].shape, sample['landmarks'].shape)
    
    if i > 4:
        break
(324, 215, 3) (68, 2)
(500, 333, 3) (68, 2)
(250, 258, 3) (68, 2)
(434, 290, 3) (68, 2)
(828, 630, 3) (68, 2)
(402, 500, 3) (68, 2)

DataLoader

Datasetは単体では、ただのiterableなオブジェクトに見えますが、DataLoaderと組み合わせることで、最初に挙げた処理を効率的に記述することができます。

まずはバッチサイズが1のミニバッチを生成するDataLoaderを作ってみます。

face_dataset = FaceLandmarksDataset(
    csv_file='faces/face_landmarks.csv',
    root_dir='faces/')
dataloader = DataLoader(face_dataset, batch_size=1,
                        shuffle=True, num_workers=4)

for i_batch, sample_batched in enumerate(dataloader):
    print(i_batch, sample_batched['image'].size(),
          sample_batched['landmarks'].size())
    
    if i_batch > 4:
        break
0 torch.Size([1, 500, 365, 3]) torch.Size([1, 68, 2])
1 torch.Size([1, 333, 500, 3]) torch.Size([1, 68, 2])
2 torch.Size([1, 500, 335, 3]) torch.Size([1, 68, 2])
3 torch.Size([1, 402, 500, 3]) torch.Size([1, 68, 2])
4 torch.Size([1, 334, 500, 3]) torch.Size([1, 68, 2])
5 torch.Size([1, 333, 500, 3]) torch.Size([1, 68, 2])

このように、ミニバッチをDatasetから簡単に作成することができました。上記の例では、imageのサイズが全てバラバラなので、このままでは、複数のサンプルのimageをまとめてミニバッチを作ることはできません。ミニバッチを作るためには、Datasetの__getitem__内でサンプルであるimageのサイズを揃える前処理が必要です。

TransformとCompose

チュートリアルでは、callableなRescaleとRandomCroopというクラスを作成していますが、特にパラメータが必要なければ、sampleを引数として受け取り、変更されたsampleを返す関数でも問題ありません。

def flip_img(sample):
    """
    画像だけを左右反転
    """
    image, landmarks = sample['image'], sample['landmarks']
    image = image[:, ::-1]  # 画像データを左右反転
    return {'image': image, 'landmarks': landmarks}

また、複数のTransformをまとめて、順次処理のパイプラインにすることもできます。 torchvision.transforms.Composeは、以下のように使用できます。

composed = transforms.Compose([
    flip_img,
    Rescale(256),
    RandomCrop(224),
    ToTensor()
])

ComposeをDatasetの__getitem__中で使用することで、ミニバッチ中の画像サイズの大きさを揃えることができます。これでミニバッチにもできますね。

transformed_dataset = FaceLandmarksDataset(
    csv_file='faces/face_landmarks.csv',
    root_dir='faces/',
    transform=transforms.Compose([  # transform引数にcomposeを与える
       Rescale(256),
       RandomCrop(224),
       ToTensor()
    ]))

dataloader = DataLoader(transformed_dataset, batch_size=4,
                        shuffle=True, num_workers=4)

for i_batch, sample_batched in enumerate(dataloader):
    print(i_batch, sample_batched['image'].size(),
          sample_batched['landmarks'].size())

    if i_batch == 3:
        break
0 torch.Size([4, 3, 224, 224]) torch.Size([4, 68, 2])
1 torch.Size([4, 3, 224, 224]) torch.Size([4, 68, 2])
2 torch.Size([4, 3, 224, 224]) torch.Size([4, 68, 2])
3 torch.Size([4, 3, 224, 224]) torch.Size([4, 68, 2])

(おまけ)DataLoaderのcollate_fn

DataLoaderにはcollate_fnという引数を指定できます。実装をみてみると、リストとして処理されるミニバッチ内のサンプルを、最後にTensorに変換する処理を行っていることがわかります。

今ひとつ使い所がわかりませんが、1つのサンプルからデータを抽出してミニバッチを作成するときなどに使うのかもしれません。

まとめ

チュートリアルをもとに、DatasetとDataLoaderの使い方を確認してきました。

DatasetとDataLoaderは適切に使えれば、データの前処理をきれいに書くことができる便利なクラスです。Datasetは、最初にデータを一括して読み込んでインメモリで処理したいとか、毎回ディスクから画像を読み込んでデータ拡張をしたいといった様々なケースに対応可能なようになっています。

効率的な処理ときれいなコードのために、習得しておきたいところです。

PyTorchの分散環境学習

概要

PyTorchのチュートリアルに、分散環境での学習に関する記事がある。 自分の家のサーバーはシングルGPUなのだが、最近少々不満を感じてきている。

クラウドでの分散学習を見越して、今後のために勉強しておくことにしたのです。

なお、特に実装はありません。

分散学習の構成要素

torch.distributedによって、マルチプロセスやクラスターでの実行、要は並列化を用意におこなえるようになります。 以下ではその構成要素を整理しています。

バックエンド

PyTorchでは、torch.multiprocessingという並列処理の機能がありますが、これは単一のマシン内での並列化にとどまります。一方で、torch.distributedは、以下のような並列処理用バックエンドを使った大規模クラスタでの並列処理をサポートします。

  • TCP:CPUしかサポートしていない。初期実装向け?
  • Gloo:CPUとGPU共にサポート。多分一番メジャー?
  • NCCL:Gloo同様だが、このチュートリアル作成時に追加されたらしい。まだ詳しいことはわからないが、NVIDIA謹製だけあって、GPU間のやり取りは高速な模様。
  • MPI:CPUとGPU共にサポートしているが、PyTorchのバイナリファイルにMPI用の実装が含まれていないので、自分で再コンパイルする必要があるらしい。

process, rank, group, world, size

用語の整理。

  • process:分散環境で動作する個々のプロセス。
  • rank:個々のprocessを指示するインデックス。Masterのprocessは0で固定みたい。
  • group:Collectiveを行う範囲を指定するprocessのサブセット。
  • world:全process。
  • size:groupやworldに含まれるprocessの数。

通信方法

Point-to-Point

process間でTensorをやり取りをする方法です。

send/recv

dist.senddist.recvによって、ノード間でTensorを送受信します。

isend/irecv

dist.isenddist.irecvによって、ノード間で非同期に、Tensorを送受信します。受信内容の反映は、非同期の送受信が完了するのをwait()してあげて、初めて保証されます。

Collective

複数のノード間でTensorを配布したり集計したりする方法です。

  • dist.scatter(tensor, src, scatter_list, group)srcのprocessからgroup内の各processにscatter_listで指定したTensorを送付し、各processのtensorに格納する。
  • dist.gather(tensor, dst, gather_list, group)group内の各processからtensorを集め、dstprocessのgather_listに格納する。
  • dist.all_gather(tensor_list, tensor, group)group内の各processからtensorを集め、group内の全processのtensor_listに格納する。
  • dist.broadcast(tensor, src, group)group内のsrcprocessのtensorを各processのtensorに格納する。
  • dist.reduce(tensor, dst, op, group)group内の各processのtesnsorを集めて、opで指定された処理を行い、dstprocessのtensorに格納する。
  • dist.all_reduce(tensor, op, group)group内の各processのtensorを集めて、opで指定された処理を行い、各processのtensorに格納する。

  • dist.barrier(group):groupに含まれるprocessがこのポイントに至るまで待機する。

Operation

dist.reducedist.all_reduceで指定するop、つまりOperationには、以下の4つがあります。すべて要素ごとに計算は行われます。

  • dist.reduce_op.SUMtensorの要素ごとの和
  • dist.reduce_op.PRODUCTtensorの要素ごとの積
  • dist.reduce_op.MAXtensorの要素ごとの最大値
  • dist.reduce_op.MINtensorの要素ごとの最小値

共有ファイルシステム

group内のprocess間で、同一のファイルを参照したり上書きしたりしながら処理を進めることがあります。競合が発生しないために、fcntlによるロックをサポートしているファイルシステムを採用する必要があるそうです。普通の分散ファイルシステムなら普通にサポートしていそうですが、どうなんでしょうかね?

まとめ

PyTorch TutorialのWriting Distributed Applications with PyTorchを読んで、まとめてみました。

クラウド上のリソースを使った分散学習で少しでも学習時間が減らせればと思って手を出そうとしているのですが、趣味でやっているので、お財布と要相談ですね…。