LoginSignup
3
2

More than 3 years have passed since last update.

【Pytorch】MaxPool2dのceil_mode

Last updated at Posted at 2020-06-30

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の様子

image.png

出力

image.png

padding=0, ceil_mode=True

MaxPoolingの様子

パディングなしなので、左上からプーリングを行っている。
出力シェイプが切り上げされることにより、結果右と下のみパディングしたのと同じ結果となっている。

image.png

出力

image.png

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)

image.png

以下のように、上と下、左と右、それぞれパディングの設定を変えることも可能。
(下と右にだけゼロパディングを適用している)

ZeroPadding2D(((0,1), (0,1)))(x)

image.png

下と右にだけゼロパディング適用することにより、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.]]]]
3
2
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
3
2