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_splits
に int
のスカラーが与えられた場合は、その数で value
を split する。
num_or_size_splits
に List[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_dims
や tf.tile
にも別の例あり。