LoginSignup
73
58

More than 5 years have passed since last update.

TensorFlow メソッド、shape をいじる系メモ

Posted at

TensorFlow で shapeを変えるようなメソッドをメモる。

shape を触る系のメソッド、だいたい複数同時に使うことが多く、いちいちページをいったりきたりして見るのが面倒になる。
メソッド単体がなにかわからない時はぐぐればいいけど、あのメソッドなんだっけって時とか組み合わせてどうなるんだっけってのがややこしい。一体今どんな shape なのかちゃんと把握したい。
あと、もう覚えちゃうことで複雑な shape に対する苦手意識を消し去りたい。

後半にちょっとだけ逆引きもいれた。

雑な部分もあるので間違ってるのがあったらご指摘ください。

TensorFlow バージョン

1.5 (多分 1.8 でも問題ないはず...)

メソッド

tf.reshape

tf.reshape(
    tensor: Tensor,
    shape: Tensor[int32, int64]],
    name: str=None
)

tensorで与えられた Tensor を、shapeで与えられたTensorShapeに変換する。
tensorの各 dimension の size の和と、shapeの合計が一緒じゃないとエラーになる。
shapeのどれかの要素に-1を与えると、 flat な1次元にする。性質上、1つの index にしか指定できない。
shape[]を与えると、スカラーにして返す。

公式Example

# tensor 't' is [1, 2, 3, 4, 5, 6, 7, 8, 9]
# tensor 't' has shape [9]
reshape(t, [3, 3]) ==> [[1, 2, 3],
                        [4, 5, 6],
                        [7, 8, 9]]

# tensor 't' is [[[1, 1], [2, 2]],
#                [[3, 3], [4, 4]]]
# tensor 't' has shape [2, 2, 2]
reshape(t, [2, 4]) ==> [[1, 1, 2, 2],
                        [3, 3, 4, 4]]

# tensor 't' is [[[1, 1, 1],
#                 [2, 2, 2]],
#                [[3, 3, 3],
#                 [4, 4, 4]],
#                [[5, 5, 5],
#                 [6, 6, 6]]]
# tensor 't' has shape [3, 2, 3]
# pass '[-1]' to flatten 't'
reshape(t, [-1]) ==> [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6]

# -1 can also be used to infer the shape

# -1 is inferred to be 9:
reshape(t, [2, -1]) ==> [[1, 1, 1, 2, 2, 2, 3, 3, 3],
                         [4, 4, 4, 5, 5, 5, 6, 6, 6]]
# -1 is inferred to be 2:
reshape(t, [-1, 9]) ==> [[1, 1, 1, 2, 2, 2, 3, 3, 3],
                         [4, 4, 4, 5, 5, 5, 6, 6, 6]]
# -1 is inferred to be 3:
reshape(t, [ 2, -1, 3]) ==> [[[1, 1, 1],
                              [2, 2, 2],
                              [3, 3, 3]],
                             [[4, 4, 4],
                              [5, 5, 5],
                              [6, 6, 6]]]

# tensor 't' is [7]
# shape `[]` reshapes to a scalar
reshape(t, []) ==> 7

tf.reduce_xxx

shape 変えるメソッドではなく list の計算系のメソッドだけど結果的に shape が変わる。
いっぱいある。

reduce_all, reduce_any, reduce_join, reduce_logsumexp, reduce_max
reduce_mean, reduce_min, reduce_prod, reduce_sum

reduce_sum の例で説明する。見てないけど演算が違うだけで使い方は他も一緒だと思う。

tf.reduce_sum(
    input_tensor: Tensor,
    axis: int=None,
    keepdims: bool=None,
    name: str=None,
    reduction_indices=None,
    keep_dims=None
)

reduction_indicesは duplicated でaxisを使う。
keep_dimsは duplicated でkeepdimsを使う(なんだこの差分)。

input_tensorで与えられた Tensor の各成分の和を計算する。
axis が与えられた場合、その次元について和を計算し、その次元が unstack された Tensor を返す。
keepdims=Trueにすると、元の次元を保持したまま和を計算する。例参照。

公式Example

x = tf.constant([[1, 1, 1], [1, 1, 1]])
tf.reduce_sum(x)  # 6
tf.reduce_sum(x, 0)  # [2, 2, 2]
tf.reduce_sum(x, 1)  # [3, 3]
tf.reduce_sum(x, 1, keepdims=True)  # [[3], [3]]
tf.reduce_sum(x, [0, 1])  # 6

tf.expand_dims

tf.expand_dims(
    input: Tensor,
    axis: int,
    name=None,
    dim: int=None
)

inputで与えられた Tensor について、axisで与えられた次元に1つ次元を追加する。
dimは duplicated で、axisを使う。
例えば single element を batch 化するのに使える。画像の例だと、[height, width, channels]axis=0に入れることで[1, height, width, channels]にするなど。
tf.tileと合わせて、「ある index に次元を増やして、その次元についてN個 tile する」みたいに使われることもある。
画像の例だと、

[height, width, channels] ->
tf.expand_dims(images, 0) ->
[1, height, width, channels] ->
tf.tile(images, [batch_size, 1, 1, 1]) ->
[batch_size, height, width, channels]

公式Example

# 't' is a tensor of shape [2]
tf.shape(tf.expand_dims(t, 0))  # [1, 2]
tf.shape(tf.expand_dims(t, 1))  # [2, 1]
tf.shape(tf.expand_dims(t, -1))  # [2, 1]

# 't2' is a tensor of shape [2, 3, 5]
tf.shape(tf.expand_dims(t2, 0))  # [1, 2, 3, 5]
tf.shape(tf.expand_dims(t2, 2))  # [2, 3, 1, 5]
tf.shape(tf.expand_dims(t2, 3))  # [2, 3, 5, 1]

tf.tile

tf.tile(
    input: Tensor,
    multiples: Tensor[int32, int64],
    name=None
)

inputで与えられた Tensor を、multiplesで与えられた1次元ベクトル分敷き詰めた Tensor を返す。
input.dims(i)multiples[i]が対応する。
上述の通り、tf.expand_dimsと合わせて使うことも。

batch_size を他から取ってきて同じ数 tile することが多そう。下記はexpand_dimsした後に batch_size 分 tile する例。

encoder_state = ...  # [batch_size, ...]
new_state = ...  # [1, hidden_unit_num]
new_state = tf.expand_dims(new_state, 0)  # [1, 1, hidden_unit_num]
new_state = tf.tile(new_state, [encoder_state.shape[0], 1, 1])  # [batch_size, 1, hidden_unit_num]

Example

# 't' is a tensor of [a b c d]
tf.tile(t, [2])
# [a b c d a b c d]

# 't2' is a tensor of shape [2, 3, 5]
tf.shape(tf.tile(t2, [3, 1, 1]))  
# [6, 3, 5]

tf.concat

tf.concat(
    values: Tensor,
    axis: Tensor[int],
    name='concat'
)

concat する。
ここらへんの axis と出力の対応がまだ頭の中ですぐにつかない。。

公式Example

t1 = [[1, 2, 3], [4, 5, 6]]
t2 = [[7, 8, 9], [10, 11, 12]]
tf.concat([t1, t2], 0)  # [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
tf.concat([t1, t2], 1)  # [[1, 2, 3, 7, 8, 9], [4, 5, 6, 10, 11, 12]]

# tensor t3 with shape [2, 3]
# tensor t4 with shape [2, 3]
tf.shape(tf.concat([t3, t4], 0))  # [4, 3]
tf.shape(tf.concat([t3, t4], 1))  # [2, 6]

t1 = [[[1, 2], [2, 3]], [[4, 4], [5, 3]]]
t2 = [[[7, 4], [8, 4]], [[2, 10], [15, 11]]]
tf.concat([t1, t2], -1)
[[[ 1,  2,  7,  4],
  [ 2,  3,  8,  4]],

 [[ 4,  4,  2, 10],
  [ 5,  3, 15, 11]]]

tf.squeeze

tf.squeeze(
    input: Tensor,
    axis: Optional[List[int]]=None,
    name=None,
    squeeze_dims=None
)

size が1の dimension を消す。もしくは、axisが指定された場合は、size が 1 の指定された次元を消す。size が 1 でない場合はエラーになる。
squeeze_dims はduplicatedでaxisを使う。

公式Example

# 't' is a tensor of shape [1, 2, 1, 3, 1, 1]
tf.shape(tf.squeeze(t))  # [2, 3]

# 't' is a tensor of shape [1, 2, 1, 3, 1, 1]
tf.shape(tf.squeeze(t, [2, 4]))  # [1, 2, 3, 1]

tf.split

tf.split(
    value: Tensor,
    num_or_size_splits: Union[Tensor[int], Tensor[List[int]]],
    axis=0,
    num=None,
    name='split'
)

num_or_size_splitsint のスカラーが与えられた場合は、その数で value を split する。
num_or_size_splitsList[int] が与えられた場合は、各次元が対応する List の値になるように value を split する。
axis で指定した次元について行う。
size_splits の形から outputs の数が推測できない場合は、 num に指定する。

公式Example

# 'value' is a tensor with shape [5, 30]
# Split 'value' into 3 tensors with sizes [4, 15, 11] along dimension 1
split0, split1, split2 = tf.split(value, [4, 15, 11], 1)
tf.shape(split0)  # [5, 4]
tf.shape(split1)  # [5, 15]
tf.shape(split2)  # [5, 11]
# Split 'value' into 3 tensors along dimension 1
split0, split1, split2 = tf.split(value, num_or_size_splits=3, axis=1)
tf.shape(split0)  # [5, 10]

tf.stack

tf.stack(
    values: Tensor,
    axis: int=0,
    name: str='stack'
)

ランク R の Tensor のリストをくっつけて、ランク (R+1) の Tensor にする。
axis が与えられた場合は、その次元についてくっつける。デフォは0。例参照。

公式Example

x = tf.constant([1, 4])
y = tf.constant([2, 5])
z = tf.constant([3, 6])
tf.stack([x, y, z])  # [[1, 4], [2, 5], [3, 6]] (Pack along first dim.)
tf.stack([x, y, z], axis=1)  # [[1, 2, 3], [4, 5, 6]]

tf.unstack

tf.unstack(
    value: Tensor,
    num: int=None,
    axis: int=0,
    name: str='unstack'
)

tf.stack の逆で、ランク R の Tensor の指定された次元で分割して、ランク (R-1) の Tensor のリストを作る。
(A, B, C, D) という Tensor が value で与えられた時、
axis=0 で unstack すると、 value[i, :, :, :] のリスト(それぞれの shape は(B, C, D))が返る。
axis=1で unstack すると、value[:, i, :, :]のリスト(それぞれの shape は(A, C, D))が返る。

tf.shape と合わせて使うことで、shape の size を取得できる。よく使う。

# A is a tensor with shape [5, 2, 3]
shape_a = tf.shape(A)  # [5, 2, 3]
tf.unstack(shape_a)
# 5, 2, 3
# 一次元のリストだったので、分けて取れる

tf.transpose

tf.transpose(
    a,
    perm=None,
    name='transpose',
    conjugate=False
)

第二引数のpermで元の行列であるaの順序を与える。

値が移動前の index で、 その値のあるindex が移動後の index .
perm=[0, 2, 1] と与えたときのperm[1]=2は、index=1だったやつがindex=2に移動するのではなく、index=2だったやつがindex=1に移動する。4次元以上だとややこしいので注意。

公式Example

x = tf.constant([[1, 2, 3], [4, 5, 6]])
tf.transpose(x)  # [[1, 4]
                 #  [2, 5]
                 #  [3, 6]]

# Equivalently
tf.transpose(x, perm=[1, 0])  # [[1, 4]
                              #  [2, 5]
                              #  [3, 6]]

# If x is complex, setting conjugate=True gives the conjugate transpose
x = tf.constant([[1 + 1j, 2 + 2j, 3 + 3j],
                 [4 + 4j, 5 + 5j, 6 + 6j]])
tf.transpose(x, conjugate=True)  # [[1 - 1j, 4 - 4j],
                                 #  [2 - 2j, 5 - 5j],
                                 #  [3 - 3j, 6 - 6j]]

# 'perm' is more useful for n-dimensional tensors, for n > 2
x = tf.constant([[[ 1,  2,  3],
                  [ 4,  5,  6]],
                 [[ 7,  8,  9],
                  [10, 11, 12]]])

# Take the transpose of the matrices in dimension-0
# (this common operation has a shorthand `matrix_transpose`)
tf.transpose(x, perm=[0, 2, 1])  # [[[1,  4],
                                 #   [2,  5],
                                 #   [3,  6]],
                                 #  [[7, 10],
                                 #   [8, 11],
                                 #   [9, 12]]]

逆引き

Tensor の shape のサイズを取得する

shape をとるには、A.get_shape() もしくは tf.shape(A) の2つの方法がある。
前者だと、データ依存せず静的に分かるものしか取れない。例えば placeholder で None を指定していると取れない。
後者は operation なので、tf.session で計算すれば取得できる。が、取得したものも Tensor なので unstack するとスカラーとして持てる。

# A is a tensor with shape [5, 2, 3]
shape_a = tf.shape(A)  # [5, 2, 3]
dim1, dim2, dim3 = tf.unstack(shape_a)
# Tensor(5), Tensor(2), Tensor(3) がそれぞれ入る
# shape_a が一次元のTensorだったので、 unstack すると分けて取れる

ある Tensor を新たにN個分複製した一次元大きな Tensor を作る

例えば、 shape が [10, 16, 128] の Tensor A があるとする。128次元の特徴を16個(channelのように)持つデータが10個(batch_size)あるようなイメージ。
今、 Tensor B の shape が [1, 128] だとして、これと A の各データを内積とったり足したりした上でいろいろ操作して...みたいなことをするとなると、まとめて計算するために B を10個分複製して [10, 1, 128] にしたい。そういうとき。

まず tf.expand_dims して、その次元を tf.tile する。

encoder_state = ...  # [batch_size, max_length, hidden_unit_num]
new_state = ...  # [1, hidden_unit_num]
new_state = tf.expand_dims(new_state, 0)  # [1, 1, hidden_unit_num]
new_state = tf.tile(new_state, [encoder_state.shape[0], 1, 1])  # [batch_size, 1, hidden_unit_num]

本記事の tf.expand_dimstf.tile にも別の例あり。

73
58
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
73
58