TensorFlowを扱う上ではテンソルの形状(shape)についての基礎知識は必須になります。

TensorFlowでは変数を作成するときも、変換するときにもshapeを使用するので、shapeの概念を知っておくことでTensorFlowを理解しやすくなり、デバッグも容易になります。

本記事では

  • TensorFlowでのshapeの意味について
  • 動的shapeと静的shapeにつて
  • tf.shapeの使い方
  • tf.get_shapeとの違い

について解説します。この記事を通して、shapeについて完璧に理解していきましょう。

テンソルのshapeとはなにか

以前、NumPyについて解説しているときに、ndarrayにshapeという概念があることを以下の記事で紹介しました。

NumPyのndarrayのインスタンス変数shapeの意味 /features/numpy-shape.html

基本的には、TensorFlowはndarrayのshapeと同様の意味を持ちます。仮に、以下のようにTensorをtf.contantを使用して呼び出してみましょう。

In [1]: import tensorflow as tf

In [2]: a = tf.constant(1, shape=[2, 3, 1])

In [3]: sess = tf.Session()

In [4]: sess.run(a)
Out[4]:
array([[[1],
        [1],
        [1]],

       [[1],
        [1],
        [1]]], dtype=int32)

In [5]: b = tf.constant([[1, 2], [3, 4]])

In [6]: b.shape
Out[6]: TensorShape([Dimension(2), Dimension(2)])

最初のaでは、shapeを2 × 3 × 1の要素が1であるTensorを作成しています。次のbでは2つの要素が入った2つのテンソルなので、2 × 2になります。

計算グラフ内の動的shapeについて

これまで見てきたshape計算グラフ外からみても計算グラフ内から見ても、明確に同一な値のshapeを持つNodeを作成しました。計算グラフの構築時にも実行時にも、作成したNodeのshapeは必ず指定したshapeになります。

しかしながら、shapeを指定したくない場合はどうすればいいのでしょうか。例えば、機械学習の文脈でバッチ学習するサイズを任意にしておきたい場合などが考えられます。推論時には、入力したいバッチサイズを動的に変更して、1枚でも1000枚でも画像認識をすることができるようにしたいはずです。

NumPyでは、一つ任意の値でshapeを整形したい場合、-1を利用する方法がありました。例えば、以下のように形状変換することができます。

In [1]: import numpy as np

In [2]: a = np.arange(15)

In [3]: b = np.reshape(a, [-1, 3])

In [4]: b
Out[4]:
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11],
       [12, 13, 14]])

n × 3のshapeを作成したい場合には、nに-1を指定するとNumPyが自動で5 × 3と計算してくれます。

同様にTensorFlowでは、以下のようにNoneを指定すると、具体的な数値を指定しないようにすることができます。

In [1]: import tensorflow as tf

In [2]: x = tf.placeholder(tf.int32, shape=[None, 3])

In [3]: sess = tf.Session()

In [4]: sess.run(x, feed_dict={x: [[1, 2, 3]]})
Out[4]: array([[1, 2, 3]], dtype=int32)

In [5]: sess.run(x, feed_dict={x: [[1, 2, 3], [2, 3, 4]]})
Out[5]:
array([[1, 2, 3],
       [2, 3, 4]], dtype=int32)

1度目の評価と2度目の評価ではバッチサイズが1と2で違った入力値を入れる。といった使用方法になります。

tf.shapeとget_shapeの違い

TensorFlowにはtf.shape.get_shapeという2つの似たような名前の関数が存在しています。

この2つは同じようで、少し異なります。違いは以下のようになります。

  1. tf.shape tf.shapeは前述した動的に変更されうるshapeに使用しましょう。バッチサイズや画像のサイズなどを計算する場合に使用します。

  2. .get_shape .get_shapeは変更されないshapeに使用します。

具体的には以下のようになります。

In [1]: import tensorflow as tf

In [2]: x = tf.placeholder(tf.int32, shape=[None, 3])

In [3]: batch_size = tf.shape(x)[0]

In [4]: sess = tf.Session()

In [5]: sess.run(batch_size, feed_dict={x: [[1, 2, 3], [1, 2, 3]]})
Out[5]: 2

In [6]: batch_size2 = x.get_shape()[0]
TypeError: Fetch argument Dimension(None) has invalid type...

一方で、決まったshapeであれば、get_shapeも使用することもできます。

In [1]: import tensorflow as tf

In [2]: y = tf.constant(1, shape=[2, 3, 4])

In [3]: y.get_shape()[0]
Out[3]: Dimension(2)

まとめ

本記事では、TensorFlowを扱う上では必須のshapeについて解説しました。

この記事を通して、shapeの使い方と、tf.shapeの使用方法についてマスターしてください。

参考