Edited at

Tensorflow - padding = VALID/SAMEの違いについて

More than 3 years have passed since last update.


はじめに

Tensorflowの畳込み層やプーリング層のパラメータの一つpadding

これについて迷ったので備忘までに記述します


畳み込み、プーリング層からの出力テンソル次元数

例えば下記の様なコードがあったとします


cnn.py

# 重みを標準偏差0.1の正規分布で初期化

def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)

# バイアスを標準偏差0.1の正規分布で初期化
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)

# 畳み込み層の作成
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

# プーリング層の作成
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='SAME')
# 入力を28x28x3に変形
x_images = tf.reshape(images_placeholder, [-1, 28, 28, 3])

# 畳み込み層1の作成
with tf.name_scope('conv1') as scope:
W_conv1 = weight_variable([5, 5, 3, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_images, W_conv1) + b_conv1)

# プーリング層1の作成
with tf.name_scope('pool1') as scope:
h_pool1 = max_pool_2x2(h_conv1)

# 畳み込み層2の作成
with tf.name_scope('conv2') as scope:
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

# プーリング層2の作成
with tf.name_scope('pool2') as scope:
h_pool2 = max_pool_2x2(h_conv2)


このようなネットワーク定義があり、仮にゼロパディングをしていないとすると、以下の様な計算を経て、最終出力が得られると考えられます

※計算式: (inputのx(y)軸の要素数 - filter(weight)のx(y)軸の要素数) / strideの要素数 + 1

1. 入力イメージ => 28x28x3
2. conv1 => (28 - 5) / 1 + 1 = 24
3. pool1 => (24 - 2) / 2 + 1 = 12
4. conv2 => (12 - 5) / 1 + 1 = 8
5. pool2 => (8 - 2) / 2 + 1 = 4 =====> よって 4x4x64 のテンソルが得られる

しかし、このコードでは最終的に7x7x64の結果が得られています

これが「padding='SAME'」というパラメータの役割ですね

実際の計算式は以下のとおりとなっています

※計算式: (inputのx(y)軸の要素数) / strideの要素数

1. 入力イメージ => 28x28x3
2. conv1 => (28) / 1 = 28
3. pool1 => (28) / 2 = 14
4. conv2 => (14) / 1 = 14
5. pool2 => (14) / 2 = 7 =====> よって 7x7x64 のテンソルが得られる

つまりフィルタ分を補うようにゼロパディングされているというわけです

逆に「padding='VALID'」というパラメータでは以下の様な計算をすると考えられます

※計算式: (inputのx(y)軸の要素数 - filter(weight)のx(y)軸の要素数 + 1) / strideの要素数

1. 入力イメージ => 28x28x3
2. conv1 => (28 - 5 + 1) / 1 = 24
3. pool1 => (24 - 2 + 1) / 2 = 11.5 => 12 (ceil処理のため)
4. conv2 => (12 - 5 + 1) / 1 = 8
5. pool2 => (8 - 2 + 1) / 2 = 3.5 => 4 =====> よって 4x4x64 のテンソルが得られる


マニュアル

具体的にどういう計算式になるかというのは以下に記載しています

https://www.tensorflow.org/versions/r0.7/api_docs/python/nn.html#convolution

padding='SAME'

out_height = ceil(float(in_height) / float(strides[1]))

out_width = ceil(float(in_width) / float(strides[2]))

padding='VALID'

out_height = ceil(float(in_height - filter_height + 1) / float(strides[1]))

out_width = ceil(float(in_width - filter_width + 1) / float(strides[2]))


結論

全結合層においては最終プーリング層からの次元数を指定する必要があるので、どう計算されているかが非常に重要になります

まあSAMEを指定すれば単純にstrideの割り算で済むので、こちらを使ってあげればいいのかなあというざっくりとした感想ですかね