Python: TCP Server
基本
Cのsokcet programmingとほとんど同じ
Server側
- create socket
- bind socket to (host, port)
- socket listens with qsize
- socket accepts connection
- socket recv msg from a client
- socket send msg to a client
Client側
- create socket
- socket connects to a server
- socket send msg to a server
- socket recv msg from a server
Workerを使う
シングルスレッドだと処理が重いので,Workerプロセスを使う
Main process
- create socket
- bind socket to (host, port)
- socket listens with qsize
- pass socket to child process
Child process
- socket accepts connection
- socket recv msg from a client
- socket send msg to a client
- msgを処理したらconn.shutdown, conn.close
今回はこうしているだけで,connを切らない方がいい場合もある.
コード
参考
- http://docs.python.jp/2.7/library/socket.html
- http://momijiame.tumblr.com/post/22314461916/python-%E3%81%A7%E3%83%9E%E3%83%AB%E3%83%81%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E5%9E%8B-tcp-%E3%82%B5%E3%83%BC%E3%83%90%E3%82%92%E6%9B%B8%E3%81%8F%E7%B7%B4%E7%BF%92
- http://memo.saitodev.com/home/python_network_programing/
Python: Manager
基本
何ができるか
Manager は別のプロセス間で共有されるデータの作成方法を提供します。マネージャオブジェクトは 共有オブジェクト を管理するサーバプロセスを制御します。他のプロセスはプロキシ経由で共有オブジェクトへアクセスすることができます。
Managerは,
- 共有オブジェクトを管理するサーバプロセス
- 他のプロセス(over network)はプロキシ経由で共有オブジェクトにアクセス可能
という機能を機能を提供するといえる.
serverもclientもmanager classは作るが,
- sever側は,manager.get_server().serve_forever()
- client側は,manager.connect()
ManagerがSyncManagerのaliasになっていて,Localで基本的な共有オブジェクトを提供しているので,自分で作成したcallable object (とか)を使わないなら,Managerを使っておけばいい.ただし,Managerは引数を取らないので,引数を指定する場合は,SyncManager.分散前提なら,BaseManagerを拡張した独自,Managerを作った方がいい.
あくまで,共有オブジェクトを管理管理するサーバであるので,中央集権的な感じだろう.
なので,pythonで中央主権的な(簡易)分散システムを作成するなら,Managerを使えばいい.
コード
Tensor Flow: Whitepaper
TensorFlow Whitepaperを2015/11/17に読んだ時のまとめ
Abstract
- いろんなデバイスで動く
- モバイイルから
- 大規模GPU Clusterまで
- Apache2.0 Licence
1 Introduction
2 Programming Model and Basic Concepts
- ノードが可能な挙動はNaiadに似ている
- その挙動は
- 状態を保つ
- 状態の更新
- 状態の永続化
- ブランチング
- ルーピング
- ノード
- 0 to nのインプットをとれる,およびアウトプットを出せる
- operation (実際の計算のこと)を行う
- エッジ
- この上をテンソルが流れる
- スペシャルエッジ (control dependency)
- このエッジのソースノードまでの実行はこのエッジのディスティネーションノードの実行より先に行わなければならない
- ordering enforcementに使える
Operations and Kernels
Session
- セッションのなかでグラフ作って実行したりする
- 基本この中でrunする
Varialbe
- persistent stateをもつ
- (パラメータありの演算ノードと思っておけばいい)
3 Implementation
Devices
- Workerは1以上のdeviceに対して責任を追う
3.1 Single Device Execution
- グラフノード上における実行順序の説明
- 依存関係をカウントしておく
- A -> B, A -> Cの依存だとAのカウントは2
- Bが実行されたら,Aのカウントは1にディクリメントされる
- カウントが0になったら実行queueに入れる
- 実行queueのにある要素の取り出し順序は不定
3.2 Multi-Device Execution
3.2.1 Node Placement
- グラフが構築されると,computationはset of devicesにマップされる
- このマップするアルゴは(経験から求めた)コストベースで,絶賛検討開発中
3.2.2 Cross-Device Communication
- メモリ最適化のために,依存関係が複数ある場合におけるreceive nodesの数が1つになるようにしている.(参考ダイアグラムはwhite paper参照)
- 違うデバイス上のグラフノードのスケジューリングはworkerの責務で,マスターにさせない,マスターはrunリクエストを発行するのみ.
3.3 Distributed Execution
- send/recieve nodesのコミュニケーションはTCPとかRDMA
Fault Tolerance
- failureの判断は
- send/recieve nodesのコミュニケーションの失敗
- master to workerのhealth check.
- faiureすると,初めからやり直しだけど,checkpointがあるとそこから再開
4. Extensions
4.1 Gradient Computation
4.2 Partial Execution
- run methodはsubgraphの実行も可能
- graph 途中からデータを入れるも可能
- graph 途中のデータを取り出すことも可能
4.3 Device Constraints
どのnodeをどのdeviceで計算を実行させるか可能
4.4 Control Flow
以下のコントロールが可能
- cyclic dataflow graph
- swith/mergeで,subgraphの実行をskip
- enter/leave/next iterationでiteration
- if-conditionals/while-loops
4.5 Input Operations
- filenamesを受け取って,tensorを返す,input nodeの設置
4.6 Queues
- input data prefetch
- gradientsをグルーピングして,その複雑なコンビーネーションを計算する
- RNNにおいて,複数センテンスを読みとって,大体同じ長さになるようなシークエンスのbinをいくつか作る
- shuffling queueもあってデータshufflingに使える
4.7 Containers
- Varialbeのbacking store
5 Optimizations
工夫の話
5.1 Common Subexpression Elimination
- computation graphは最終的に同じ計算になることが多いので,同じインプットでオペレーションタイプのコピーは正規化して,一意にしている
5.2 Controlling Data Communication and Memory Usage
- Receive nodeのスタートは気をつけていて,コントロールエッジを挟んでおき,その前のノードの計算が終わるまでスタートを待っている
5.3 Asynchronous Kernels
- non-blocking kernelもある
- 例えば,Receive, Enqueu, Dequeue
5.4 Optimized Libraries for Kernel Imple- mentations
- 今提供しているkernelはほとんど既存の最適化されたライブラリのthinラッパー
- BLAS, cuBLAS, cuda-convnet, cuDNNとか
- Eigenをndarrayを取り扱えるように拡張している
5.5 Lossy Compression
- ノード間でデータ転送をするとき,32bit to 16bitのlossyな変換をしている
- IEEE 16bitでなくて,32-bitIEEE 794を使っている
6 Status and Experience
DistBeliefからTensorFlowにportingしたときのレッスン
特にまとめない
7 Common Programming Idioms
Large-Scale Distributed Systemの話で出てくる一般的な用語とその説明
- Data Parallel SDG synchronouslyできる
- Data Parallel SDG asynchronouslyできる
- Model Parallelできる
- Concurrent Steps for Model Computation Pipeline (Data Parallel SDG asynchronouslyが同じdevicesで行われる)できる
8 Performance
まだないよ
9 Tools
9.1 TensorBoard: Visualization of graph structures and summary statistics
- いい感じに可視化してくれる
- 構築したcomputational graphをdrill downでみれる
- レイヤーにおけるパラメータ,グラディエントの変化履歴をみれる
10 FutureWork
- subgraphを完全に取り出せる
- jit compiler
- computation 2 device mapping algoの改善
- node scheduling algoの改善
11 RelatedWork
- chainer, theanoのようにsymbolic differenciation
- stateful parameter serverはnodeとして超簡単に表現可能
- Naiadのように,1つの最適化されたdataflow graphがすべての計算を表し,そのグラフに関するキャッシュ情報は各デバイス上にのせて,コミュニケーションオーバーヘッドを最小化.なので,RAMがたくさん欲しい.
12 Conclusions
OSSにしたから是非使ってくれ.
TensorFlow: Getting Started
Basic Usage
Install
- cuda 7.0 (gpu使いたい場合)
$ wget "http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1404/x86_64/cuda-repo-ubuntu1404_7.0-28_amd64.deb" $ sudo dpkg -i cuda-repo-ubuntu1404_7.0-28_amd64.deb $ sudo apt-get update $ sudo apt-get install cuda $ sudo apt-get install
7.0でないと怒られた.というか今はcuda=7.0, cudnn=6.5 v2のみをサポートしているよう.
- tensorflow 6.0
$ sudo pip install "six==1.9.0" $ sudo pip install --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow-0.6.0-cp27-none-linux_x86_64.whl # versionは適宜変更
実際に使うまでの2 phases
- construction phase
- that assembles a graph
- execution phase
- uses a session to execute ops in the graph
Session
- Computational graphが実行される場所のようなもの
- graph上のオペレーション(演算)をデバイス(cpu, gpu)にマップする
Varialbe
- Stateを持つように扱える
- 詳細は,コード参照
- 普通はこっち (chainer varialbe にみえる)
- よく使うのは
- variable.eval(): 評価
- variable.value(): Tensor object
- variable.op: Operation
Fetches
- Fetchesはsess.run([fetche, ..])といれる.
- input: list of varialbe, then output: list of nparray
- input: variable, then output: np.type
Feeds
- sessの中でデータを関数に食わせるという意味でfeedだとおもう.
- tf.placeholder(...)を使う
- こっちはtheano function like
サンプルコードはここ
Graphの作り方は,Theanoに似ているかな.
Interactive Usage
ipythonで使う場合
- 1. interactive session
- 2. variable作る
- 3. variable.initializer.run()
- 4. varialbe.eval()
こんな感じで評価する(結構めんどい)
こうすることで,sessionの中で保持するvarialbeを保持する必要がなくなる
sess = tf.InteractiveSession() weights = tf.Variable(tf.random_normal([784, 200], stddev=0.35), name="weights") weights.initializer.run() weights.eval()
を使う,
Hello TensorFlow
TensorFlow
Googleが出したFlow Graph or Computational Graph 上で数値計算を行えるOSS.
本命どこがOSSでだしてきた.界隈ではゲームのルールが変わるかも.
何ができるか
何が嬉しいか
他ライブラリと比較するという観点で4点.
1. Computational Graphおよびエッジ,ノード,学習履歴等,詳細の可視化が可能
現時点では,
- 学習したモデルをディスクに吐いて
- 吐いた先のディレクトリを指定したWeb Serverを立ち上げる
- エッジ,ノード,学習履歴等,詳細がブラウザから見れる
そのうち,ブラウザから学習まで実行するとかでてきそう.
2. Distributed Learning
ここの例を見る感じだとタスクを指定するときに,URIを指定しているので,分散学習が可能.
今のところ,明確なTutorialがないように思えるが, NIPS2015 TutorialにてLarge-Scale Distributed Systems for Training Neural NetworksのTutorialがあるので,その辺で,Visual Object Recognitionがcomming soonでなくなってImagenetをGoogleNetで学習させる例が来るのだろう.
3. SparseTensor
SparseTensorがあるよう.すなわち,0-paddingして,DenseTensorを使わなくてよいということ.
PythonにおけるShared Memory
前提
pythonはGILの影響でmulti thread programmingでcpu-bound jobが早くならない.
なので,multiprocessingを使うしかない.
CPythonのmultiprocessingはforkなので,unixならcopy-on-write.なので,globで定義したデータなら,Read-onlyに限り,特段気にしないで共有メモリでタスクがパラレルに使えるはずというのは勘違いのよう.この場合は,ref-countが変わって結局コピーが起こるそう.
結局のところ(multiprocessing documentにあるように), shared memory (Value, Array, RawArray)を使って,データを受け渡すしかないよう.Queue/Pipe経由ではなく,os.forkの前に受け渡す.Arrayはdefaultでlockがあるので,lockを気にしないならRawArrayを使う.
Read-onlyなタスク
タスクは,pairwise-similarity (pairwise-innerproduct)を使い,愚直にnumpyで2 loopsを回す.(10000, 100)の行列で,2回10000のloopでdot productをとる.
ここは,numbaを使う,こんなことやらずに,numpyで計算方法の工夫をするなどが考えられるが,それは,out-of-scope.numbaは,python: 行列演算の高速化を参考にされたい.
行いたい比較検証は下記のとおり
1. 1processでnumpyの計算を行う
2. sharedctypes.RawArray + np.ctypeslibで,2と同じことを行う
3. sharedmemを使う
これらで計算速度を比較する
それぞれの検証結果のコードはここ
各実験を10回実行した時の平均,パラでやっている場合は,最大値をとってからの平均.
結果
Exp. index | Elapsed time (sec) |
---|---|
1 | 123.8867863655 |
2 | 32.0695706606 |
3 | 129.2636404037 |
Writeありなタスク
タスクは,Read-onlyなタスクと同じで,pairwise-similarity (pairwise-innerproduct)だが,結果を共有メモリに書き出す.
行いたい比較検証は下記の通り
1. sharedctypes.Array + np.ctypeslibで書き込まれるobjectを作る
2. sharedctypes.RawArray + np.ctypeslibで書き込まれるobjectを作る
3. sharedmemで書き込まれるobjectを作る
元のデータは,Read-onlyなタスクでRawArray > sharememだったので,RawArrayで共有する.
これらで計算速度を比較する
それぞれの検証結果のコードはここ
各実験を10回実行した時の平均,パラでやっている場合は,最大値をとってからの平均.
結果
Exp. index | Elapsed time (sec) |
---|---|
1 | 37.442804575 |
2 | 37.6960495472 |
3 | 38.0742048979 |
まとめ
ReadもWriteも予想どおりスピードは,#concurrency倍になったが,sharedmemをreadソースとして使った時になぜか,baseの実験(read1)と同じ実行時間になってしまった..., 中身が,mmap.mmapなのが原因なのか...?
それにしては,baseの実験(read1)と同じくらいの実行時間になってしまったことが気がかり...
参考
- http://stackoverflow.com/questions/17785275/share-large-read-only-numpy-array-between-multiprocessing-processes
- http://briansimulator.org/sharing-numpy-arrays-between-processes/
- http://stackoverflow.com/questions/5549190/is-shared-readonly-data-copied-to-different-processes-for-python-multiprocessing/5550156#5550156
- http://stackoverflow.com/questions/14941729/will-os-fork-use-copy-on-write-or-do-a-full-copy-of-the-parent-process-in-pyth
Chainerをさわってみた
自分用のまとめなのでDNN知っていたり,Keras, Caffe, Torchとか他のDNN Libraryを知っている人は,公式Docmentを読んだほうがいい.
Github Star数的にはCaffe > Keras >= Torch > Chainer (ただし,chainerを見ているのは日本人くらいだろうから,結構多いとおもう)だが,今からからまともに触るならChainerがアーキ的にイケているという話を効いたのでやってみた.というか,Define-by-Runのおかげで,RNNが簡単に書けるのがイケている.
この記事を書いていた時点ではv1.3だったが,すでにv1.4になっている.
概要
PFN/PFIが作っているDeep Learning Frameworkでpythonで使う.
大まかな特徴は2つだと思う
GPU Array backendはPyCUDA/CuPy
- 他のtheano-based DNN libraryはCudaNdarray
- ただし,PyCUDAのフィーチャーがlow-levelすぎるとのことで,彼らはCuPyで置き換えようとしている.PyCUDA numpy interfaceのsubset. v1.3.0から置き換わっているよう.
Define-by-Run
多くのdeep learning libraryはDefine-and-Runでありnetwork (computational graphと言った方がいいかも)を一度defineしたらfixedなので,動的にnetwork構造を変更できないが,Chainerはできる.Define-by-Runは実際のforward-computationのときにon-the-flyでnetwork構造を定義する.そのため,柔軟なことが売りなのだと思う.多分,on-the-flyでnetwork構造を書けるようにすると,RNNの実装が簡単なんだと思う.
比較をみるともう少し詳細な違いがわかる.この表を見ると,大きなdisadvantageとしては,FFT-based Convolutionがないくらい.Stateful RNNs (LSTMのことか?)もあるかもしれないが,Kerasにもあるはず.
特徴ではないかもしれないが,low-level interfaceを公開してくれていて,使う側が柔軟な対応がでいるようになっていると思う.Define-by-Runの範疇かな.
Installation
$ sudo pip install chainer
基本
Variable
Parameterized functions
- fucntions moduleにあるconnection.pyとか.
- Affine, ConvolutionとかLayerのことだと思う
FunctionSet
- DNNにおけるレイヤーセットのような感じ.モデルということ.
- parameterized functionを入れる.activation functionは入れない
- FunctionSet.parameters and FunctionSet.gradientsですべてのレイヤーに対するoperationが可能
Optimizer
- sgd, momentum, adagrad, adadelta, adam, rmspropとかが入っている
- 基本的な使い方はmnist sampleを見たほうがいい
- .dot形式のgraphも書く機能があるよう
ここまでの例
ここ
Mnist Sample
注意点
Towards Chainer v1.5のp.6-9, 19によると, v1.5からLow-level Interface (FunctionSet/Optimizer)が変わるかもしれないので,v1.5からまともに触りだしてもいいかも.
進行が大分早く,半年後にはここに書いた内容がほぼ意味を成さなくなってくる可能性があるので注意.