オープンソースのDeep Learning Framework「Chainer」

秋葉拓哉氏(以下、秋葉):ChainerとChainerMNについても少し紹介したいと思います。時間がやばいので駆け足でいきます。まずChainerというフレームワークを簡単に紹介したいと思います。

ChainerはオープンソースのDeep Learning Frameworkで、我々Preferred Networksが開発をリードしております。Chainerの細かい話をするとまた長くなっちゃうんですけれども、この「Define-by-Run」という考え方を最初にFrameworkに取り入れた、かなり先進的なDeep Learning Frameworkでした。最近はPyTorchっていうライブラリが出てきて、彼らもこの「Define-by-Run」のアプローチにもとづいていたりします。

もっと最近では、TensorFlowがEager modeというのを作ったんですけれども、彼らも我々が論文で提案した「Define-by-Run」という言葉を明確にブログで使ってくれたりしていて、「Define-by-Run」のフォロワーがどんどん世界的に出てきているわけです。今のところ、Chainerのデザインがかなりの先駆けとなって、多くのフレームワークが後追いしている状況になりつつあります。

これはさっきもご紹介したとおり、ChainerMNは同期型の分散深層学習を実装しています。まずChainerMNがなにかというと、ChainerMNというのはChainerのアドオンパッケージです。Chainerに加えてChainerMNをインストールすると分散学習が使えるようになります。MNというのはMulti-nodeの意味ですね。

Chainerを普通に使ってるとForward・Backward・Optimizeができるんですが、ChainerMNはそこに通信のステップを挟めるよというパッケージですね。

通信にはNVIDIA NCCLというライブラリを使っています。これはNVIDIAの公式に公開してくれている通信用のライブラリですね。これは非常に使いやすいのですが、とくに使う人はいないと思います。

ChainerMNは非常に気軽に使っていただけると言いたいところなのですが、依存ライブラリの準備がやや面倒くさいですね。MPIという分散計算用のライブラリと、NVIDIA NCCLをインストールしてもらうと、ChainerMNが使えるようになります。ChainerMN自体のインストールはpipインストールでできます。

インストールしてもらったら、コードの変更は実はそんなに必要なくって、Chainerの普通の学習コードに対してこういったコミュニケーターを作ったり。

Optimizeの直前にAll-Reduceを挟むための処理を入れたり。

こういったものをユーザー側で変更していただくと、ChainerMNが使えるようになります。

分散深層学習はここ1~2年で大躍進を遂げた

最後にちょっと速度と精度という話をしたいと思います。最初にも言ったと思うんですけど、実は、分散深層学習って、ただGPUをいっぱい使えばできるという話ではぜんぜんないんですね。とくにわかりやすい例がこれです。分散深層学習をすると精度が落ちてしまうという話があったんです。

この話はけっこう深層学習の世界の動きの速さを物語っているかなと思っていて。そもそも、分散深層学習でGPUをいっぱい使って並列化すると精度が落ちてしまうという話がコンセンサスになったのも、一昨年ぐらいかなというイメージです。

(それまでは)みんななんとなく観測していて、「こういうconfigurationだと精度が落ちるっぽい」というのがだんだん集まってきて、それが明確にコンセンサスになってきて、「じゃあこの問題、みんなでがんばって解こう」となったのが2016年ぐらいかなという感じですね。実際に2017年の最初の時期、1月ぐらいにそういったものの代表作の論文が発表されたりしました。

それが2017年の間にだいぶ進歩しちゃって、2017年末には、それこそ我々のように1,024台のGPUを使っても高い精度が保てるといった発見がどんどん出てきて、今となっては分散深層学習してもかなり高い精度が出せるというのがコンセンサスになりました。

ちょっと時間がないので詳細は省略するんですけど、こんな感じですね。

縦軸・エラーなので下にあるほどいいんですけれども、8GPUくらいのときには精度が80パーセントぐらい。だからエラー率が20パーセントちょいまでいくんだけど、これがそのままのconfigurationでGPUを256個使うと、エラーが50パーセントぐらいまでしか落ちない。かなりひどいという。こういった現象が多数観測されていました。

これはなにが原因かということもいろいろ議論されていて、いろんな仮説も出たりして、そういった論文が例えばICLRというニューラルネットワークの学会で発表されたりもしてきたんですね。それを受けて、みんないろいろな提案をして、NIPSなどでいろいろと論文が出て、それをどうすればいいかというのが続々と出てきていて、そういった知見が積み重なって。我々も実際にそういった知見のうちの一部を開発しているんですけれども、そういったものを積み上げてImageNetの学習が1時間になり、そしてゆくゆくは15分になるというように、この世界は進歩してきました。

ちょっと最後のほうは駆け足になってしまったのですが、時間なので一旦私の発表を終わりにしたいと思います。まとめとしては、今日は分散深層学習について発表させていただきました。まず空気感というのをすごく感じ取っていただけたらうれしいなと思うのですが、ここ1〜2年ですごい大躍進ができたというのが、当事者の自分としても非常におもしろかったなと思います。

いろんな話がある中の1つとして、「同期型と非同期型どっちがいいのかな?」といった話を取り上げてこの世界の難しさを見てもらったり、ChainerMNの使い方と仕組みについて簡単に触れたりさせていただきました。私からの発表は以上になります。どうもありがとうございました。

(会場拍手)

なぜ非同期型よりも同期型の精度が高いのか

司会者:ありがとうございました。ご質問がある方、挙手をお願いします。

質問者1:ありがとうございました。Syncの質問なんですけど、同期型が非同期型よりも精度がよくなる理由ってなぜなのでしょうか?

秋葉:ご質問ありがとうございます。同期型が非同期型より精度がよくなる理由というのは、仮説なのですが、Gradient Stalenessというものが関係していると言われています。さっきも説明にちょっと出てきたとおり、非同期型では、パラメータサーバにおけるパラメータが最新で、それがどんどん入れ替わるんです。なので、ワーカーから見ると計算してる間にパラメータが変わっちゃってるんですね。

ですけど、そのあとで自分が計算し終わった勾配をプッシュするんですよ。そうするとどうなるかというと、今のモデルからするとその勾配はもう古いんですね。それがStale Gradientと呼ばれる「腐った勾配」です。パラメータに対して、勾配の鮮度がちょっと落ちている。

そういうものは、モデルに対してあまり改善できなかったり、場合によってはモデルを壊してしまったりといった影響があると言われていて、これが非同期型の問題だとされています。ご質問ありがとうございます。

質問者1:ありがとうございます。

司会者:そのほかご質問のある方?

質問者2:ありがとうございます。やっぱりこのStalenessはあると思うんですけれど、たぶん、パラメータサーバに世代みたいな概念を導入して、8世代前のものだったらその更新の影響度を落とすみたいな感じの研究もあると思うのですが。

秋葉:あります。

質問者2:その場合にもやっぱりあまり改善されないのでしょうか?

秋葉:そうですね。うーん……コミュニティからは、本質的に解決しているとは見られていないという感じですかね。たぶん「多少はよくなる」というイメージなんだと思います。

あと、僕の1つの仮説としては、実は精度が落ちているのは単に非同期型のせいではなくて、非同期型のほうがパラメータ……というかハイパーパラメータですね。この実験設定が、より難しくなっちゃってる。それで、適切な実験設定を探せていないがゆえに精度が落ちている部分もあると考えています。

同期型って処理が非常にシンプルなので、なにが起こるかわかるし、あとは例えば計算機の速度とかによって学習の様子が変わらないんですね。非同期型の場合は、ネットワークがどのぐらい速いかとか、ほかのマシンがネットワークを今使ってたか使ってなかったかによってStalenessも変わってきちゃうんですよ。やっぱり、そういった現象によってけっこう調整が難しくなっちゃってる。そういった部分が1つあるかなと思っています。

質問者2:ありがとうございます。

高速化するにあたり一番苦労した部分は?

質問者3:1つ目は、大規模化ということでけっこうシステムプログラミングで苦労されたという話があったんですけれども、それはMPIぐらいのレベルなのですか? それとももっと下のほうの、例えばパケットを投げるところのハードウェアレベルなのですか? それとももう少し上の、例えばPythonレベルで同期化するみたいな? どのレベルで一番時間がかかるというか、チューニングが重要になるのでしょうか。

秋葉:非常にうれしい質問で、結局「最終的には全部です」というのが答えですね。というのも、別に僕らはこういった記録をつくるためにChainerを作っているのではなく、リサーチャーが使うために作っているものです。なので、使いやすさというのがファーストプライオリティなんですね。それゆえにインターフェースはすべてPythonで書かれています。

なので、そういったPythonからくるオーバーヘッドというのはあるけど、それはこういった記録をつくる上ではちゃんと潰していかないといけないので、Pythonの皮を持ちつつ中を高速化する。そういった部分で、例えばPythonの中のメソッドコールの仕様であったり、そういったものを把握してうまくキャッシュしたり、そういったこともやる。

それからMPIとかのレベルでももちろんやりますね。実はMPIはそんなに使ってなくて、先ほど言ったNVIDIA NCCLというライブラリが通信の主な部分を担うんですけれども、MPIが初期化をしたりするし、NCCLもMPIとかなり近い存在でInfiniBandのVerbsとかを叩くんですけれども、こういったようなやつらを使う。その使い方や呼び方を工夫するというのもあるし。

あと一番つらいのは、結局こいつらのデバッグをしないといけないんです。例えばNVIDIA NCCLって、NVIDIAさんの中でも1,000GPU規模での実験をしてなかったんですね。僕らが初めてNVIDIA NCCLをこの1,000のオーダーにのせたと。実際にその実験の過程でNVIDIA NCCLのバグがいっぱい出てきて、しかも僕らはソースコードを持っていないんですよ。だからがんばってエスパーして、これを直すと。

(会場笑)

けっこう大変だったんです。「どうやらこのリソースが足りていないっぽいぞ」とか言ってulimitで上げてやったり、そういう変なことをいっぱいやって、なんとかこれを動かすということをやったりしました。なので、かなりいろんなレベルで高速化しています。もちろんGPUの計算の部分も高速化する必要がありますね。そういった部分もやっていますし、全面的にボトルネックを潰したという感じになります。

分散に関する質問

質問者3:あともう1個確認させてください。今回分散されたというのは、ハードウェアで計算エレメントは分散されたみたいですけれども、マトリクスというか係数行列は全体システムの中でだいたい1個という感じなんですよね? アップデートのタイミングで多少ずれはあるかもしれないですけれども。

秋葉:そうですね。基本的には通信を行うタイミングというのが、アップデートの直前の勾配を共有する部分だけなんですね。例えばマトリクスというのは、ニューラルネットワークのForwardの計算・Backwardの計算で行列積みたいな計算がいっぱい出てくるんですけど、そういった部分は基本的に1GPUの中に閉じています。

だから、各GPUはほぼ同じ計算をしているんですけど、処理している対象のデータが違う。得られた勾配の部分だけを通信する。これがData Parallelなアプローチですね。なので、おっしゃるとおりで、基本的にはそういったMatrix Multiplicationとかは1つのGPUの中に閉じています。

質問者3:ありがとうございます。

司会者:はい。あと1〜2件でしたら。さっき手を挙げた……。

質問者4:じゃあ、すいません、たいへん基本的な質問で恐縮なんですけれども。

秋葉:大丈夫です。

質問者4:この分散処理をする場合は、1GPU、1レコード、1exampleというのが基本なんですか? それとも1GPUに、例えば32ミニバッチみたいなことはやらないんですか?

秋葉:ご質問ありがとうございます。おっしゃるとおりで、基本的には1GPUについて、例えばミニバッチサイズ32とかですね。今日の説明ではDeep Learningを知っている人がどのぐらいいるかわからなかったので、ミニバッチという概念を導入するのをちょっとサボりまして、1GPUであたかも1サンプルをやっているかのような説明をしました。なので、実際には1GPUでちゃんとGPUのutilizationを上げられるようにある程度バッチサイズを上げています。

質問者4:ありがとうございます。

秋葉:ありがとうございます。

深層学習には速いネットワークが必要になる

質問者5:ありがとうございます。うちの会社にGPUが8枚載ったサーバが2台ありまして、それをあんまり有効活用できていなくて、ほとんど誰も使っていないんですよね。

(会場笑)

質問者5:機械学習で遊んでみたいなと思ってるんですけれども、素人がとりあえず使う場合に、どこを重点的にチューニングしたらうまく16倍になるぐらいまでスケールできるかというところをちょっと。

秋葉:ご質問ありがとうございます。この質問の回答は「なかなか簡単じゃない」というのが正直なところですね。やっぱりDeep Learningって、そもそも1GPUでも、けっこうちゃんと中身までわかってないと学習がすごく遅くなっちゃったりすることって多いですよね。

例えば、GPUのutilizationをちゃんと上げられないような計算が中に入っちゃってて、そういった計算が足を引っ張ってちゃんとした性能が出ないとか、そういった部分はよくあるんですよね。ほかにもCPUとGPUの間の通信がすごく遅いとか、やっぱりいろんな要素が入ってくるので、速度がなかなか出ないコードってよくあります。

ほかにもやっぱりディスクI/Oとか、jpegのデコードが遅いとか、いろいろなケースがあって。それに加えてChainerMNを使うと、そのインターコネクトでネットワークの話が入ってくるのでさらに複雑になってくるんですね。

今日は使わなかったスライドなんですけど、こういう場で僕がよく話しているのはやっぱりこれなんですね。

ChainerMNを使って性能が出なかったと言われるのは、だいたいこれ(が原因)なんですよ。

ネットワークもつながっていればいいという問題じゃなくて、2つのマシンの間がけっこう速いネットワークでつながっている必要があるんですね。基本的には深層学習って高性能な計算を要求するんですけど、計算、その高性能な計算についていくだけの速いネットワークがないといけなくて。

具体的には、InfiniBandと呼ばれたりするような、ちょっと特殊なハイパフォーマンスコンピューティング向けのネットワークとか、そういったものを入れるとだいぶスケールすると。逆にそういったものでつながっていないとけっこう厳しいかなというところはあります。ご質問ありがとうございます。

司会者:それではお時間ですので、質疑を以上とさせていただきまして、続きは懇親会でよろしくお願いします。ご質疑ご討論に参加いただきまして、ありがとうございます。

秋葉:ありがとうございました。

(会場拍手)