post Image
Microsoft の Deep Learning フレームワーク「CNTK」が Python 対応になって格段に使いやすくなってた話

CNTK ってご存知でしょうか?すでに何かしらの Deep Learning フレームワークを使ったことがある方にとっては、「MS が出してるってことくらいは知ってるけど・・・」という方が大半だと思います。

実際、周りに聞いてみると、まだバージョンが上がる前の DSL で書かれている記事などを見た人には習得コストが高いのでは?と思われていたり、そもそも CNTK に関する日本語のドキュメントが少なくいまいちどういう特徴があるのかわからないって人も結構多いようです。

そこで、本記事では、Python 対応になってより使いやすくなった CNTK について、その特徴や他のフレームワークとの差異などをキーワードを散りばめながら紹介してみようと思います。


“Open-source, Cross-platform Toolkit”


オープンな開発環境

オープンソースで GitHub上で開発が進んでます。


クロスプラットフォームな開発環境

Linux、Windows で利用可能です。

Linux 向けに Docker も用意されています。


多様なインターフェース

現在サポートされているのは、以下になります。

  • C++
  • Brainscript
  • Python (NEW!)

2016年10月に Version 2.0 に上がって ついに Python も対応しました!(涙)

機械学習を使うエンジニアは Python ユーザーが多いと思うので、C++ はともかく Brainscript で使えます!とか言われても 100 人中 132 人くらいは「ぶれいんすくりぷと・・・??」となるはず。しかし、Python が対応したおかげで一気に習得コストが低くなったんじゃないかと思います。


“Scalability”

CNTK の一番の魅力は、やはり何といってもスケーラビリティだと思います。

Speed Comparison

Microsoft Research Blog

上図は、各フレームワークによる学習速度の比較になります (※)。全結合な 4 層ニューラルネットワークを、一秒あたり何フレーム処理できるかを比較しています。GPU が1つの場合は各フレームワークでほとんど変わらない速度ですが、複数マシン/複数 GPU だと、CNTK が他よりも圧倒的に高いのがわかります。

※ 2015年12月当時の比較

benti_after.png

“Benchmarking State-of-the-Art Deep Learning Software Tools”

また、こちらは、全結合・AlexNet・ResNet・LSTM におけるベンチマークです(※)。

CNTK は全結合と LSTM において最も高速なパフォーマンスを出しているのがわかります。ちなみに、AlexNet は Caffe、そして ResNet は Torch が最速となりました。

なお、このベンチマークでは、CNTK はすべてのモデルにおいて Deep Learning フレームワークのパイオニアであり、一番有名な TensorFlow よりも高いパフォーマンスを出しているということもわかります。(これはかなり意外でした…)

※ 2016年8月当時の比較


“Efficient network authoring”

CNTK では、ニューラルネットワークをできるだけ効率よく簡単に作るために、以下のような工夫がされています。

  • シンプルな構成要素を組み合わせることで、どんなニューラルネットワークでも構築できるような設計 (composability)
  • 関数オブジェクトとしてのニューラルネットワークを扱う

例えば、2つの隠れ層を持つ順伝播型ニューラルネットワークを数式で以下のように表現するとします。

after.png

CNTKでは、数式と似たような形で書くことができます。

h1 = Sigmoid(W1  * x  + b1)

h2 = Sigmoid(W2 * h1 + b2)
P = Softmax(Wout * h2 + bout)

ce = CrossEntropy(y, P, tag='criterion')

CNTK では、ニューラルネットワークを関数オブジェクトとして扱うため、とても巨大で複雑な “関数” であるニューラルネットワーク (ディープニューラルネットワークも含む) を記述するのに非常に相性がいいと言えます。そして、Python が対応になったおかげで、さらに簡潔にニューラルネットワークの構造を書くことができます。

例えば、以下は画像認識システムでよく使われる畳み込みニューラルネットワーク (CNN) のネットワークのサンプルイメージです。

CNN.png

上のようなネットワーク構成を CNTK でストレートに書くとしたら、



def create_basic_model(input, out_dims):

net = Convolution((5,5), 32, init=glorot_uniform(), activation=relu, pad=True)(input)
net = MaxPooling((3,3), strides=(2,2))(net)

net = Convolution((5,5), 32, init=glorot_uniform(), activation=relu, pad=True)(net)
net = MaxPooling((3,3), strides=(2,2))(net)

net = Convolution((5,5), 64, init=glorot_uniform(), activation=relu, pad=True)(net)
net = MaxPooling((3,3), strides=(2,2))(net)

net = Dense(64, init=glorot_uniform())(net)
net = Dense(out_dims, init=glorot_uniform(), activation=None)(net)

return net

こんな感じで書けます。

しかし、これだと、隠れ層が増えるたびにコードが増え、冗長になってしまいます。

先ほども言ったように CNTK は Composability (組み合わせ可能であること) に非常に重点を置いてるため、以下のようにもっとシンプルに効率良く書くことができます。

def create_model(input, out_dims):

with default_options(activation=relu):
model = Sequential([
For(range(3), lambda i: [
Convolution((5,5), [32,32,64][i], init=glorot_uniform(), pad=True),
MaxPooling((3,3), strides=(2,2))
]),
Dense(64, init=glorot_uniform()),
Dense(out_dims, init=glorot_uniform(), activation=None)
])

return model(input)

Sequential() を用いて、横一直線に結合している各層を一纏めにしたり、複数の層で同じ活性化関数やバイアスなどを使いたい場合は default_options() を用いたりして、一つ前の実装例よりも効率よくネットワークを書くことができています。

また、CNTK では一般的に利用される CNN や ResNet, RNN といったコンポーネントは API (関数) として用意されているため (※)、“ニューラルネットワークそのものをある程度理解してる方”にとって非常に楽ができるような仕組みとなっています。 

※ 一番下の[付録] として代表的な Python API for CNTK を書いたのでよかったらどんなものが用意されているのか確認してみてください。


“Efficient execution”

CNTK を使うメリットの一つは訓練時の学習速度の速さや複数の GPU を効率的に活用できるなどの高いパフォーマンス性にあることは上で述べたとおりですが、それらは以下のような CNTK ならではの機能があるためです。

  • グラフ表現に変換して学習
  • 独自の並列学習アルゴリズム


グラフ表現に変換して学習

h1 = Sigmoid(W1  * x  + b1)

h2 = Sigmoid(W2 * h1 + b2)
P = Softmax(Wout * h2 + bout)

ce = CrossEntropy(y, P, tag='criterion')

先ほども例に取り上げた、2つの隠れ層を持つ順伝播型ニューラルネットワークを再掲します。CNTK 内部では、 上のスクリプトをまず以下のような、Computation Graph と呼ばれる表現に変換します。

ce_after.png

このとき、グラフ上のノードは関数となっており、エッジが値となります。このように先にグラフ表現に変換することで、

  • 自動微分による学習
  • 事前に最適化することで、実行時のメモリ・計算コストの削減

を実現することができます。


独自の並列学習アルゴリズムの搭載


Block-Momentum SGD

block_after.png

Microsoft Research Blog

CNTK Version1.5 から Block-Momentum SGD という技術を実装しています。

これにより、大幅に GPU 間での通信コストを削除でき、精度を維持したまま、複数 GPU で高速化することが可能になります。

実際、上図のタスクにおいては、64 個の GPU クラスタで実行したら、1 個の場合と比べて パフォーマンスは 50 倍以上になっています。


1-bit SGD

GPU で分散処理する場合にボトルネックとなるのは通信コストです。 しかし、CNTKは 1-bit SGD という、勾配を 1bit まで量子化することで通信コストの削除が可能な技術を実装しています。

そのため、並列処理をする際に通信コストを大幅に削減することができるように、結果的に、ニューラルネットワークの訓練時の学習速度を向上させることができます。


おわりに

いかがでしたでしょうか。 CNTK の特徴をポイントを抑えて紹介しましたが、意外と知らなかったこともあったのではないでしょうか。

今回は CNTK とは何か?に焦点を置いたので、環境構築については触れませんでしが、python 対応になったことで 環境構築もかなり楽になりました。また、Jupyter Notebook ベースの Tutorial がとても充実してるので、この記事を見て CNTK に少しでも興味を持った方はTutorial で CNTK に触ってみてください! 環境構築や Tutorial に関してわからないことがあれば、MS の Deep Learning Framework CNTK で画風変換~もしも小学生の自分にゴッホを描かせたら? や、MicrosoftのDeep Learningライブラリ「CNTK」チュートリアル あたりが詳しく書かれているのそちらをご覧ください。

この記事を見て、「CNTKちょっと触ってみようかな〜」という人が少しでも増えると嬉しいです!:)


 [付録] Python API for CNTK の例

  • basic blocks:

    • LTSM(), GRU(), RNNUnit()
    • ForwardDeclaration(), Tensor[], SparseTensor[], Sequence[], SequenceOver[]

  • layers:

    • Dence(), Embedding()
    • Convolution(), Convolution1D(), convolution3D(), Deconvolution()
    • MaxPooling(), AveragePooling(), GlobalMaxPooling(), GlobalAveragePooling(), MaxUnpooling()
    • BatchNormalization(), LayerNormalization()
    • Dropout(), Activation()
    • Label()

  • composition:

    • Sequential(), For()
    • ResNetBlock(), SequentialClique()

  • sequences:

    • Delay(), PastValueWindows()
    • Recurrence(), RecurrencFrom(), Fold(), UnfoldFrom()

  • models:

    • AttentionModel()


『 Python 』Article List