今回はTensorFlowで最も重要な用語、Graph(グラフ),Tensor(テンソル),Operation(オペレーション),Session(セッション)を理解すべく、整理してみます。
Tensorflowの設計思想は、かなり独特です。
それをきちんと理解しておかないと前へすすめませんからね。
本当なら、数式とかを交えていくと、カッコ良いのですが、自分はたくさんの数式を見ると目眩がする方の人なので、極力数式に頼らない、自分なりの方法で謙虚に整理していこうと思います。
じゃあ、まずはグラフから。
Graph(グラフ)
Graphは、データとデータの関連を定義して、まとめて扱うための単位の呼び名みたいです。
プログラムでなんかの処理をする時には、定数や変数で表されるデータを使って、何かの処理をして、その結果を収めたデータを返すみたいなことをします。
それをデータとデータの関連として視覚化するとこんな感じになるんですかね。
普通1回足し算して終わりなんてことはなく、その結果を使って別のことをするみたいに、複数の関連が発生します。
それらも個別ではなく、ひとまとめにして扱うわけですね。
この辺までは、なんとなく理解してたんですが、pythonのコードの中には、Graphという単語がでてこないので、いつの間にか意識しなくなってました。(for Javaの方には、Graphというクラスが、がっつりでてきますが)
それで、このまとめて管理する意味合いを含めて、なんかぼやけた感じで理解していて、「そもそも何故こういう管理の仕方をするのか」という疑問を感じてなかったのが、間違いのもとでした。
で、今回は調べてみました。
どうも、こういう風に、ひとまとめにして扱うのは、効率的に処理するための方法論みたいです。
例えば、行列演算みたいな重たい機能を、C/C++等の別言語で書いたものをpythonから利用するような処理を、ひとまとめにしない従来のやり方で行うと、多くのオーバーヘッドがかかるみたいです。
そのオーバーヘッドは、GPU、または分散環境で計算を実行し、データ転送が高コストになる時に、特にパフォーマンスに大きな影響を与えるようなんですね。
なので、tensorflowでは、単一の高コストな操作をPythonから独立して実行します。
そういう完全にPythonの外で実行する操作を相互作用のグラフとして管理することで、これを回避するアプローチをとっているそうです。
なるほどですね。
Graph(グラフ)を、人間が管理しやすくするために用意されたものなのかな?という偏った観点だけで理解しようとすると、混乱した理由がわかりました。
Tensor(テンソル)
上記で言うデータの部分は、tensorflowでは、すべてをTensor(テンソル)と呼ぶ独自のデータ構造であつかうことになっています。
Tensor(テンソル)は、汎用的に様々なデータ構造を表現できるように工夫されたN次元の配列かLISTだと考えれば良いみたいです。
普通のプログラミング言語だと、大きさだけを持つスカラ型(n=243みたいな)や配列型([1,2,3]とか)、マトリックス型([[1,2,3][5,6,7]]とか)のように別々に管理していたものを、ひとつのデータ構造で汎用的に表現できるようにした感じですね。
Tensor(テンソル)はインタフェースで、それぞれの実体がスカラや配列などとして扱えるようになるみたいに、JAVAの感覚でイメージすると、わりとしっくりきました。
Tensor(テンソル)自体が実体ではないとイメージできれば、どういうデータ構造として使うかを実行時に決める必要があるのも、理解できます。
当然、利用する処理の側で、対象の「Tensor(テンソル)」がどのデータ構造で定義されているのかを認識できる指標が必要です。
そうしないと、どうしていいかわからないですから。
その役目を果たすのが「Rank」「Shape」「Type」ということです。
RankとShapeで、どんなデータ構造と形状をしているのかを示して、Typeでそこに収められているデータ型を示す感じですかね。
まあ、当面、3Dの座標の画像などを扱う予定はないので、とりあえず、Rank 0:スカラ、1:配列、2:行列を覚えとけば良いかなと思います。
扱えるTYPEは、以下表のどれかです。
pythonのTypeとの関連付けは、こちらの本家サイトを見て確認すればいいです。
こう理解すれば、例えば Rankの異なるTensor(テンソル)の演算とかはできないだろうなというのも、普通に予想できます。
だって、一次配列と二次配列の変数を同じものとして計算しようとすると普通のプログラム言語でもエラーになりますもんね。
operation(オペレーション)
ここが、一番に混乱した部分です。
混乱した理由は、参照した資料に、明らかに同じ部分が、「ノード」とか「オペレーション」とか「テンソル」とか違う名前で書かれていたからです。
前にでてきたGraph(グラフ)は、グラフ理論の「グラフ」のことだという暗黙の理解があって、グラフ理論の用語で示すのが適切な場合と、tensorflowの用語で示す方が適切な場合があるから、そうなっているということに、気づければよかったのですが、ここが文系人間で普段から数学に慣れ親しんでこなかった弊害ですね。
だから、先にグラフ理論の「グラフ」の用語の再確認からします。
グラフの簡単な例はこんな感じです。
グラフは関連を表現するものなので、このA、B、C、Dを仮に鉄道の駅としたら、そこを結ぶ線は線路だとかに置き換えて考えると、イメージしやすいですね。
グラフ理論の用語では、グラフを構成する上記のA,B,C,Dのようなパーツをnode(ノード:頂点、節点)、その間を結ぶ線を、edge(エッジ:辺、枝)と呼びます。
で、前の方で書いたtensorflowのデータ(ようするにTensor)の関連を書いた図を見てみると、これもGraph(グラフ)なわけです。
そう考えれば、「A」や「B」や「結果」の部分はグラフ理論で言う「ノード」にあたります。
で、tensorflowでは、この「ノード」のことを「Operation」と呼びます。
かつ、これらの「ノード=Operation」の実体はといえば、Tensor(テンソル)オブジェクトでもあるわけです。
これがわかれば、「tensorflowのGraphは、Tensorオブジェクトのノードで構成される。tensorflowでは、このノードのことを、Operationと呼ぶ」という、本などに書いてある説明が、十分理解できますね。
Session(セッション)
セッションはグラフを実行すると、前に書きましたが、厳密には違います。
セッションオブジェクトを作って、runで引数になったOperationを実行します。
擬似コードだと、こんな感じです。
session = tf.InteractiveSession()
session.run(operation)
Operationは、python上では単なる変数だったり、定数だったりするので、若干違和感がありますが、tensorflowでは、単なる定数であっても「その値を出力するOperationである」と考えるので、これでいいわけです。
ちなみに、Sessionには、Session() と、 InteractiveSession() の2種類があります。
できることは同じなのですが、使い方が違います。
Session()の方は使うとき、以下例のように明示的にSessionオブジェクトを渡す必要がありますし、Session()を呼ぶ前に、Graph(グラフ)の構築をしておく必要があるみたいです。
a = tf.constant(5.0)
b = tf.constant(6.0)
c = a * b
with tf.Session() as sess:
print(sess.run(c))
ですが、 InteractiveSession()は、デフォルトセッションとして自身をインストールするので、以下例のように、 InteractiveSession()をよんでから、Graphを順次構築していけばよく、With句も使う必要がありません。
sess = tf.InteractiveSession()
a = tf.constant(5.0)
b = tf.constant(6.0)
c = a * b
print(sess.run(c))
sess.close()
InteractiveSession() の流れをよく見ると、対話型のシェルなんかで処理する時には非常に都合がよい形になっているのがわかります。
逆に、Session()のやり方だと、対話型シェルではやりづらいですよね。
ただ、Session()の方のやり方の方が、session(セッション)を複数作って別々に実行させたりもできるなど、柔軟な感じはします。
まあ、やりたいことに都合のいい方を使いなさい的にとらえておけばよいのかなと理解しました。
ということで、GraphとTensorとOperationとSessionの意味合いを整理してみました。
個人的には、ここがわかるだけで、だいぶ見通しがよくなったなと感じてます。
やっぱ、基本は大事ですね。
TensorFlow関連カテゴリの一覧はこちらです。