ceil_modeとは
Pytorchの学習済モデルtorchvision.models.googlenetをKerasに移植してみようと思ったら、気になることが。
MaxPool2dのceil_modeってなんでしょか。
ドキュメントをみると、「Trueの場合、出力シェイプの計算でfloor(切り捨て)ではなくceil(切り上げ)を使う」とある。
torch.nn — PyTorch master documentation
ceil_mode – when True, will use ceil instead of floor to compute the output shape
以下は、torchvision.models.googlenetで最初にでてくるMaxPool2D。
# 入力は (112, 112, 64)
MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=True)
出力サイズを計算してみると、55.5
output\_shape = \frac{input\_shape + 2 \times padding - kernel\_size}{stride} + 1 \\
= \frac{112 + 2 \times 0 - 3}{2} + 1 = 55.5
torchsummaryで実際の出力サイズを見てみると、**(ch=64, 56, 56)**なので、確かに小数点以下を切り上げ(ceil)してるっぽい。
MaxPool2d-4 [-1, 64, 56, 56]
PyTorchの結果を確認してみる
kernel=(3,3), stride=(2,2)のMaxPool2dに以下の(10,10)サイズのサンプルデータを突っ込んで結果をみてみる。
サンプルデータ
import torch
import torch.nn as nn
>>> x = torch.arange(1, 101).view(1, 10, 10).float()
>>> x
tensor([[[ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.],
[ 11., 12., 13., 14., 15., 16., 17., 18., 19., 20.],
[ 21., 22., 23., 24., 25., 26., 27., 28., 29., 30.],
[ 31., 32., 33., 34., 35., 36., 37., 38., 39., 40.],
[ 41., 42., 43., 44., 45., 46., 47., 48., 49., 50.],
[ 51., 52., 53., 54., 55., 56., 57., 58., 59., 60.],
[ 61., 62., 63., 64., 65., 66., 67., 68., 69., 70.],
[ 71., 72., 73., 74., 75., 76., 77., 78., 79., 80.],
[ 81., 82., 83., 84., 85., 86., 87., 88., 89., 90.],
[ 91., 92., 93., 94., 95., 96., 97., 98., 99., 100.]]])
>>> x.shape
torch.Size([1, 10, 10])
ceil_mode = False
padding = 1
>>> nn.MaxPool2d((3,3), stride=2, padding=1, ceil_mode=False)(x)
# 出力サイズ (5, 5)
tensor([[[ 12., 14., 16., 18., 20.],
[ 32., 34., 36., 38., 40.],
[ 52., 54., 56., 58., 60.],
[ 72., 74., 76., 78., 80.],
[ 92., 94., 96., 98., 100.]]])
output\_shape = \frac{input\_shape + 2 \times padding - kernel\_size}{stride} + 1 \\
= \frac{10 + 2 \times 1 - 3}{2} + 1 = 5.5
小数点以下を切り捨てて、5.5 → 5
padding = 0
>>> nn.MaxPool2d((3,3), stride=2, padding=0, ceil_mode=False)(x)
# 出力サイズ (4, 4)
tensor([[[23., 25., 27., 29.],
[43., 45., 47., 49.],
[63., 65., 67., 69.],
[83., 85., 87., 89.]]])
output\_shape = \frac{input\_shape + 2 \times padding - kernel\_size}{stride} + 1 \\
= \frac{10 + 2 \times 0 - 3}{2} + 1 = 4.5
小数点以下を切り捨てて、4.5 → 4
ceil_mode = True
padding = 1
>>> nn.MaxPool2d((3,3), stride=2, padding=1, ceil_mode=True)(x)
# 出力サイズ (6, 6)
tensor([[[ 12., 14., 16., 18., 20., 20.],
[ 32., 34., 36., 38., 40., 40.],
[ 52., 54., 56., 58., 60., 60.],
[ 72., 74., 76., 78., 80., 80.],
[ 92., 94., 96., 98., 100., 100.],
[ 92., 94., 96., 98., 100., 100.]]])
output\_shape = \frac{input\_shape + 2 \times padding - kernel\_size}{stride} + 1 \\
= \frac{10 + 2 \times 1 - 3}{2} + 1 = 5.5
小数点以下を切り上げて、5.5 → 6
padding = 0
>>> nn.MaxPool2d((3,3), stride=2, padding=0, ceil_mode=True)(x)
# 出力サイズ (5, 5)
tensor([[[ 23., 25., 27., 29., 30.],
[ 43., 45., 47., 49., 50.],
[ 63., 65., 67., 69., 70.],
[ 83., 85., 87., 89., 90.],
[ 93., 95., 97., 99., 100.]]])
output\_shape = \frac{input\_shape + 2 \times padding - kernel\_size}{stride} + 1 \\
= \frac{10 + 2 \times 0 - 3}{2} + 1 = 4.5
小数点以下を切り上げて、4.5 → 5
ceil_modeのTrue/Falseでの違い
以下の出力サイズはいずれも(5, 5)となるが、違いは何か。
- padding=1, ceil_mode=False
- padding=0, ceil_mode=True
padding=1, ceil_mode=False
MaxPoolingの様子
出力
padding=0, ceil_mode=True
MaxPoolingの様子
パディングなしなので、左上からプーリングを行っている。
出力シェイプが切り上げされることにより、結果右と下のみパディングしたのと同じ結果となっている。
出力
Kerasの結果を確認してみる
kernel=(3,3), stride=(2,2)のMaxPool2dに以下の(10,10)サイズのサンプルデータを突っ込んで結果をみてみる。
KerasのMaxPooling2Dには、ceil_mode的なパラメータはない。
Kerasはいつも出力シェイプの計算結果を小数点以下切り捨てしている模様(Pytorchでいうところのceil_mode=False)。
サンプルデータ
Pytorchのときと同じく、10x10のデータを生成。
from tensorflow.keras.layers import MaxPooling2D
import numpy as np
x = np.arange(1, 101).reshape(1, 10, 10, 1).astype(np.float)
padding=1
Pytorchでの、padding=1, ceil_mode=Falseと同じ出力。
>>> out = MaxPooling2D((3,3), strides=(2,2))(ZeroPadding2D((1,1))(x))
>>> out = tf.transpose(out, perm=[0,3,1,2])
>>> with tf.Session() as sess:
>>> out_value = sess.run(out)
>>> print(out_value)
# 出力サイズ (5, 5)
[[[[ 12. 14. 16. 18. 20.]
[ 32. 34. 36. 38. 40.]
[ 52. 54. 56. 58. 60.]
[ 72. 74. 76. 78. 80.]
[ 92. 94. 96. 98. 100.]]]]
padding=0
Pytorchでの、padding=0, ceil_mode=Falseと同じ出力。
>>> out = MaxPooling2D((3,3), strides=(2,2))(x)
>>> out = tf.transpose(out, perm=[0,3,1,2])
>>> with tf.Session() as sess:
>>> out_value = sess.run(out)
>>> print(out_value)
# 出力サイズ (4, 4)
[[[[23. 25. 27. 29.]
[43. 45. 47. 49.]
[63. 65. 67. 69.]
[83. 85. 87. 89.]]]]
kerasで、ceil_mode=Trueと同じ出力を出すには?
ZeroPadding2Dは以下の様に設定すると、上下および左右に対しゼロパディングする。
ZeroPadding2D((1,1))(x)
以下のように、上と下、左と右、それぞれパディングの設定を変えることも可能。
(下と右にだけゼロパディングを適用している)
ZeroPadding2D(((0,1), (0,1)))(x)
下と右にだけゼロパディング適用することにより、ceil_mode=Trueと同じ出力を得ることができた。
>>> out = MaxPooling2D((3,3), strides=(2,2))(ZeroPadding2D(((0,1), (0,1)))(x))
>>> out = tf.transpose(out, perm=[0,3,1,2])
>>> with tf.Session() as sess:
>>> out_value = sess.run(out)
>>> print(out_value)
# 出力サイズ (5, 5)
[[[[ 23. 25. 27. 29. 30.]
[ 43. 45. 47. 49. 50.]
[ 63. 65. 67. 69. 70.]
[ 83. 85. 87. 89. 90.]
[ 93. 95. 97. 99. 100.]]]]