読者です 読者をやめる 読者になる 読者になる

KZKY memo

自分用メモ.

TensorFlow: Save and Restore Graph/Variable

TensorflowのSaverでsave/restoreされるのは,MetaGarph/Variableなどの情報.graphだけを永続化したい場合は,tf.train.write_graph/tf.import_graph_defを使う.

同じグラフを常に使う場合,単なるチェックポイントからの復帰は,これのみで可能.

Save and Restore Graph/Variable

元のグラフ,変数を保存後,別のグラフにimportして,RestoreしたVariableにアクセスするサンプル.

  1. Variableを作って
  2. Default Graphに Opを追加
  3. VariableをCollectionに追加しておいて,Restore後にアクセスしやすくしておく
  4. tf.train.Saver.saveでsave
  5. tf.Graphで別のグラフを作って,graph.as_default()でコンテキストをつくり
  6. saver.restore()
  7. ユーザ定義のcollectionへアクセスして,変数へアクセス
#!/usr/bin/env python

import tensorflow as tf
import datetime

def main():
    # Varialbes
    initial_v1 = tf.truncated_normal((10, 5), mean=0, stddev=0.1)
    #v1 = tf.Variable(initial_v1, name="v1")
    v1 = tf.get_variable("v1", initializer=initial_v1)

    initial_v2 = tf.truncated_normal((5, 3), mean=0, stddev=0.1)
    #v2 = tf.Variable(initial_v2, name="v2")
    v2 = tf.get_variable("v2", initializer=initial_v2)
    tf.add_to_collection("v2", v2)
    
    # Ops
    op_name = "prod"
    prod = tf.matmul(v1, v2, name=op_name)
    init_op = tf.initialize_all_variables()

    # Saver
    saver = tf.train.Saver([v1, v2])
    #saver = tf.train.Saver()

    with tf.Session() as sess:
        # Init variables
        sess.run(init_op)

        # Compute
        result0 = sess.run(prod)
        print result0
        print ""

        # Save variables
        now = datetime.datetime.now()
        save_path = "/tmp/{}-{}.ckpt".format(__file__, now)
        saver.save(sess, save_path, write_meta_graph=True)
        print "Model saved in {}".format(save_path)
        print ""
        
    # Restore graph to another graph (not default graph) and variables
    graph = tf.Graph()
    with graph.as_default():
        saver = tf.train.import_meta_graph("{}.meta".format(save_path))

        with tf.Session() as sess:
            saver.restore(sess, save_path)
            print "Variables restored"
            print ""

            print [var.name for var in tf.get_collection(tf.GraphKeys.VARIABLES)]
            print [var.eval() for var in tf.get_collection(tf.GraphKeys.VARIABLES)]

            print tf.get_collection("v2")[0].eval()
            #print tf.get_variable("v2:0") can NOT get
            print ""
            
if __name__ == '__main__':
    main()

正直,save/restore APIが使いにくいので,そのうち変更されるかもしれない... これのようにハックしている人もいる.

Docker Engine

今更ながら,Dockerを触ってみた.

なぜDockerなのか

一言で言うと,CI/CD (継続的インテグ/継続的ディリバリ)が簡単になるため.

Getting Started

やっぱり本家のGetting Startedをやるのが,基本を理解するのに一番わかり易い.
ここでやるのは,Machine, Swarm, Composeではない,Engineのみ.

Getting Startedをやる.

Installation

$ curl -fsSL https://get.docker.com/ | sh
  • in case of over-proxy
$ curl -fsSL https://get.docker.com/gpg | sudo apt-key add -

userをdokcer groupに登録して,デーモン登録してから

$ sudo usermod -aG docker $USER
$ sudo sysv-rc-conf --level 456 docker on

logout or reboot

Run whalesay image

$ docker run docker/whalesay cowsay boo
$ docker run docker/whalesay cowsay boo-boo
$ docker run docker/whalesay ls /
$ docker run docker/whalesay ls 

Build your own image

以下の手順で作る

1. create directory
2. create/write Dockerfile in the diretory
3. build image from the Dockerfile

  • (push the image to DockerHub)
  • (run the image as a container)

Dockerfile

FROM docker/whalesay:latest  # ベースとなるimage
RUN apt-get -y update && apt-get install -y fortunes  # commitされるコマンド
CMD /usr/games/fortune -a | cowsay  # default action, 1つだけ,複数あると最後のみ有効

を作って

docker build -t docker-whale ${Created Directory}

でimageができる.

確認

docker images

Create Docker Hub account & repository

dokcer hubにアクセスして作成すればいい.

Tag and Push your image

docker-whaleという名前のレポジトリができているので,namspace = docker hub accountをつける

docoker tag ${image id} ${namespace}/docker-whale:latest

pushする

$ dokcer login --username=${your account}
$ docker push ${your account}/docker-whale:latest

pullする前に,rmi

$ docker rmi docker-whale ${your account}/docker-whale

pullする

$ docker pull ${your account}/docker-whale

確認

$ dokcer images

基本の理解を深める

Docker ハンズオンをみて,一部やってみるのがいい.

所感

git likeに使えていい.Dockerfileはgitで管理すればいいし,imageはDokcer Hubで管理すればいい.LXCよりは断然dokcerだと感じた.

あとは,公式Referenceを見ながらやるのがいいと思う.

ちょっと古いが,知らぬはエンジニアの恥。今さら聞けない【コンテナ/仮想化技術】11選に,この手の技術の表面的な説明が網羅されているので,参考になる.

用語

  • image: dockerのimage
  • container: imageの具体化
  • repository: 複数imageを束ねる入れ物で,tagで複数imageをversioningする
  • registory: repositoryの登録場所 (docker hubとか自分用のhubとか)
  • commit: 変更されたcotainerからimageを作成する
  • build: dockerfileからimageを作る, dockerfileはあるdirの下にいて,dirを指定する

Computational Graphの実装概要

TF, CNTK, Theano, Caffe, Chainer等,巷にDNNライブラリはあふれている.DNNライブラリとはいかないまでも,自分でComputational Graphを実装してみたいと前から思っていたので,書いてみた.インターフェイスとしてすら,まだ全部出来ているわけではない.

概要

3つのクラス

  • ComputationalGraph
  • Vertex
  • Edge

がある.

設計思想

こんな感じに積み木を積み上げるように使いたい.

v_in = Vertex("x")
v1 = Edge(name="e0")(v_in)
v2 = Edge(name="e1")(v1)
v3 = Edge(name="e2")(v2, v1)  # fork and concate
v4 = Edge(name="e3")(v3)
v5 = Edge(name="e4")(v4)
...
v_in.forward()
v_out.backward()

学習パラメータとグラフは分離させたいので,EdgeについてるパララメータもVertexとして扱いたい.すなわち,Edgeにパラメータを持たせないことで,グラフとパラメータの分離ができて,グラフを動的に構築しながら,foward/backwardができる.

DAGのSourcesでfowardってやって,DAGのDistinationsでbackwardてやると,それぞれ,inference, backpropをしてくれるようにしたい.

すなわち,v.foward() -> e.foward() -> v.forward() -> e.foward() ... な感じで連鎖的に,自分のfowardを呼んで,子供のfowardを呼んで,その子供のfowardを呼んで,というようにしたい.backwardはその逆で,自分のbackwardを呼んで,親のbackwordを呼んでといった感じ.

ComputationalGraph

Directed Acyclic Graphそののもで, G=(V, E), VertexとEdgeを持つ

Vertex

Edgeを通る前,および,通った後の入れ物として機能するコンテナ

Edge

任意のVertciesを受け取って,何かしらの操作をして,Vertexを返す

制約

  • DAGである
  • Edgeの入力/出力はVertex, Vetexの入力/出力はEdge
  • Multi Graphでない, 複数Edgeが同じVertex間に存在してはならない
  • Edgeは,複数のvertexを入力として良いが,出力vertexは1つ. すなわち,Edgeにおける,入力Vertex:出力Vertex=1-n:1
  • Vertexは,複数のEdgeに入ってい良いし,全く入らなくても良い (=Distination). すなわち,Vertexにおける,入力Edge:出力Edge=1:0-n
  • forward時,Edgeに複数入力がある場合は,全部の入力が揃うまで,子供Veretexのfowardは呼べない.
  • backward時,Vertexに複数入力がある場合は,全部の入力が揃うまで,親Edgeののbackwardは呼べない.

残件 (順不同)

これ以上やらないと思うが,

  • Activationの実装
  • Convolutionの実装
  • Computational Graph上にヘルパー関数forward/backwardの実装
  • Optimizerの実装
  • ComputatinalGraph上のグラフ操作系のヘルパー関数いろいろ

コード

Computational Graphにある.
サンプルは,ここにある.

所感

思ったより,Fork/Concateがややこしく,Edge.foward, Vertex.backwardがややこしくなっている.この実装がベストプラクティスなのか不明.現状,1 processで1graphが制約になっているのも不満.

mコマンドを触ってみた

mコマンド(mcmd)というものが耳に入ったのでひと通り触ってみた.
数億件くらいのCSVデータを効率的に集計処理できる

mコマンドとは

  • join, uniq, sort, count, sum, aveなどの集計処理に使える
  • in/outのフォーマットはCSV
  • パイプが使えるので,他のunixコマンドと組み合わせられる
  • 1台の標準PCでも数億件行のデータを効率よく処理できる

NYSOL

大規模データの解析に関する様々な大学やプロジェクトでの研究成果を

広く産業界に還元する目的で構築されたソフトウェアツールの総称およびそのプロジェクト活動である。

の元で配布しているらしいので,日本人くらいしか使っていないかもしれないことに注意.

インストール (Ubuntu14.04)

依存関連で足りなかったもの(他にもg++とかあるが)

実際のインストールは,Donwloadから最新版を持ってきて,

$ ./configure
$ make
$ sudo make install

laptopで,1.0hrくらいかかった.この記事を書いている時点での最新版は,2.4.

shared libraryを認識させる

$ sudo ldconfig

注意点

CSV

  • 全行同じフィールド数
  • 1行の最大バイト数はデフォルト,1MB (10MBまで拡張可能)
  • 最終レコードでも改行は必要
  • カンマを含むデータはダブルクオーツで囲む
  • ダブルクオーツを含むデータは,ダブルクオーツで囲む,かつデータ中のダブルクオーツにダブルクオーツをつける
  • 改行を含むデータもダブルクオーツで囲む

mchkcsvコマンドでチェック可能.

データ型

まとめるより,これを見る.インプットの全てのデータは文字列で表現されていることに注意.

項目指定

いろいろある.直感にあっているので,まとめるよりこれを見る.

パラメータ指定

まとめるより,これを見る.結構直感的.

  • i: input file
  • o: output file
  • f: field for in:out
  • s: sort key
  • k: key of "group by"

コマンド例

  • mcmdは,mxxxというコマンド郡の総称.mxxxがいっぱいある.
  • サンプルデータは,DLしたtar.gzの中のdatasetディレクトリに入っている

ここで紹介されているコマンドを練習として扱う.

sort
$ msortf i=uci/iris.csv f=SepalLength
cut
$ mcut i=uci/iris.csv f=SepalLength,SepalWidth,Species
cut, uniq, then cal length
$ mcut i=uci/iris.csv f=Species |                     
muniq k=Species | 
mcal c='length($s{Species})' a=Species_len o=uci/Species_len.csv
join
$ mjoin i=uci/iris.csv m=uci/Species_len.csv k=Species f=Species_len
cut, then count
$ mcut i=uci/iris.csv f=Species | mcount k=Species a=count_Species
select with condition
$ msel i=uci/iris.csv c='$s{Species}=="setosa" || $s{Species}=="versicolor"' 
$ msel i=uci/iris.csv c='$s{Species}=="setosa" && ${SepalLength}>5'

所感

実験結果をCSV形式で吐いて,それを解析するときに,grep, cut, sort, uniq, awk, sedとかのコマンドの組み合わせで処理するよりは便利かもしれない.ただし,全行同じフィールド数って言うのが結構辛い制約なので,正直,書捨て集計をする場合は,grep, cut, sort, uniq, awk, sedに慣れていたら,必要ないと思ってしまった.

「そんなことはない!こういうユースケースでは超使える」という意見がある人は,教えてくださいm(_ _ )m.

中規模データでロジックが必要な場合はnumpy, scipy, pandas使えばいいし,大規模データでロジックが必要な場合はSpark使えば良い.

個人的には,引き出しの一つとしてとっておく.

Dygraphs メモ

基本

Tutorialに書いてあることがすべて

  • html
<script type="text/javascript"
  src="//cdnjs.cloudflare.com/ajax/libs/dygraph/1.1.1/dygraph-combined.js"></script>
  • js
g2 = new Dygraph(
    document.getElementById("graphdiv2"),
    "temperatures.csv",  // CSV 形式ならfileでなくてもOK
    {}          // options
  );
  • csvの指定にfile:///が使えないことに注意
  • csvがhttpスキーマでアクセスできても,htmlをfile:///からアクセスする場合は,ajaxエラー
  • CSV 形式ならfileでなくてもOKなのでXHRでデータ持ってきて入れることができる
  • jsfiddleのresultが動かん
    • dataがうまく読めてない感じ
  • sample貼り付けて自分のwebserverでhostすると動く
  • サンプルが結構載っている

TensorFlow: Sequence-to-Sequence Models

sequence 2 sequenceの学習の話.この記事を書いている時点で,TFのversionは,0.6.

データのダウンロード

cd tensorflow/models/rnn/translate
python translate.py --data_dir [your_data_directory]

で,English to Frenchのデータセットのダウンロードが始まる.20GBくらいある.

seq2seqのライブラリは,ここにある.ただし実態は,こっち

このチュートリアル自体のコードは,ここ

Sequence-to-Sequence Basics

まずは,RNN Encoder-Decoderの話から.RNN Encoder-Decoderは,Encoder (RNN)でインプットをEncodeし,fixed-length vectorにして,それをDecoder (RNN)のインプットにする.DecoderもRNNであって,そのfixed-length vectorをRNNのセルがいつも見に行く.たぶん,一番シンプルなAttentionと考えることもできると思う.しかし,fixed-length vectorしか見ないので,Input Vecotr SequenceのどこがOutput Vector Sequenceに効くのかよく分わからないので,Attentionの概念がでてくるのだと思う.EncoderとDecoderでは,パラメータをシェアしていてもいいし,一般的には,違うセル(異なるパラメータセット)を使ってもいい.

Attentionの細かい説明は,Bahdanu et al., 2014に書いてある.簡単にいうと,AttentionはRNN Encoder-Decoderにおいて,Decoderが,ステップの度に,Encoderの出力を覗き見ること.Bahdanu et al., 2014では,sequenceのannotationを覗き見ている.annotationは,forward hidden stateとbackward hidden stateを結合したものと定義している.そうすることで,word_iの周辺情報をみることができるので良いとのこと(BiRNNだと思う).


f:id:KZKY:20160216231837p:plain

言語に限らず,画像関連でも使用される.これでは,フィルターを使ってどこを見るかをシークエンシャルに決めることで,Attention.これもAttentionで,次にどこみるかを決める.

これによると,RNNの次(というか延長でもう来ている)は,Attention!

Attentionのコード自体は,これの431行目あたりに書いてあって,decoder inputの度に呼ばれて,結合されている(ここの456行目からあたり).

TensorFlow seq2seq Library

基本これ.

outputs, states = basic_rnn_seq2seq(encoder_inputs, decoder_inputs, cell)

encoder_inputs, decoder_inputs, およびrnn cellを受け取って,decoderにおけるすべての,output, stateを返す.

Decoderのインプットは,普通,教師を使うのだが,学習時でもモデルをロバストにする目的やシークエンスの生成時には,Decoderの前のアウトプットを現在のインプットに使用する.

シンボルがin/outの場合は,embeddingが用意されている.

outputs, states = embedding_rnn_seq2seq(
encoder_inputs, decoder_inputs, cell,
num_encoder_symbols, num_decoder_symbols,
output_projection=None, feed_previous=False)

言語処理のように,symbolが非常に多い場合は,Sampled Softmaxを使う.それも用意されていて,ouput_projectionに (W, b)を入れればいい.

Neural Translation Model

Sampled softmax and output projection

Sampled Softmaxの話.num_symbolが512(default value)を超えない場合は,普通のSoftmaxを使った方が良いという話.

Bucketing and padding

Sequencesはそれぞれ,長さが異なるので,理想的には,すべての長さに対応するgraphを作りたいが,それは非効率的.長さが違う場合は,paddingをしたりするが,一番長いsequenceに合わせると,短いsequenceは過度にpaddingされてしまう.なので,妥協案として,bucketing and paddingをする.

これの766行目あたりからをみるとbucket毎に,モデルを作っている様に見える.これらをこれの161行目あたりで,bucket毎(モデル毎)でlossのgradとっている.(全データが全部のバケットに入っている様に見えるのは気のせいだろうか...)

Let's Run It

取り敢えず動かしたいなら,DLが終わった後,

python translate.py \
  --data_dir [your_data_directory] --train_dir [checkpoints_directory] \
  --en_vocab_size=40000 --fr_vocab_size=40000 \

代替,batch-size 64で,340K steps くらいすると,bucket0, bucket1はperplexity的にいい感じになってきて,予測ができる.

python translate.py \
  --data_dir [your_data_directory] --train_dir [checkpoints_directory] \
  --size=256 --num_layers=2 --steps_per_checkpoint=50 \

Reading model parameters from /tmp/translate.ckpt-340000
>  Who is the president of the United States?
 Qui est le président des États-Unis ?

What Next?

上記はseq2seqだったけど,同じモデルが,seq2treeのようにparserとしても使える.Vinyals & Kaiser et al., 2015は,それ系のタスクでstate-of-the-artらしい.

TensorFlow: Python API: Transformation

記事を書いている時点では,v6.0なので注意

Casting

  • tf.string_to_number
  • tf.to_double
  • tf.to_float
  • tf.to_bfloat16
  • tf.to_int32
  • tf.to_int64
  • tf.cast

特に言及なし.

sample codes

Shapes and Shaping

  • tf.shape
  • tf.size
  • tf.rank
  • tf.reshape
  • tf.squeeze
  • tf.expand_dims

rankはTensorの次元数を返す

squeezeは,dim=1のテンソルの次元を削除.

expand_dimsは,dim=1のテンソルの次元を追加. batchsize=1のサンプルを作るのによく使用される.

sample codes

Slicing and Joining

  • tf.slice
  • tf.split
  • tf.tile
  • tf.pad
  • tf.concat
  • tf.pack
  • tf.unpack
  • tf.reverse_sequence
  • tf.reverse
  • tf.transpose
  • tf.gather
  • tf.dynamic_partition
  • tf.dynamic_stitch

reverse_sequenceは,逆向きにデータを突っ込むorBiRNNのために用意されているような感じを受ける.サンプル(sequenceとなっている前提)毎に,sequcneを反転させる.

gatherは,もともとのtensorから必要なindeciesのみ集める.

dynamic_stitchは,n個のTensorを指定したindicesでつなぎ合わせる感じ.

indiciesが2d-tensorの場合は,

merged[indices[m][i], ...] = data[m][i, ...]

のようになるので,

  • len(data) = indices.shape[0]
  • data[j][0] = indices.shape[1] for any j
  • indicesのエレメントは,0: len(data) * len(data[j].shape[0])で, uniqueな値を取る

が条件のようではある.

sample codes