栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Python

torch.nn.MaxPool2d Returns Wrong Shape With Ceil

Python 更新时间:发布时间: 百科书网 趣学号
问题描述

测试自家编译器的时候遇到了一个问题,同 github issue:Bug in max_pool2d with ceil_mode under certain conditions · Issue #45357 · pytorch/pytorch · GitHub

问题分析 step1:理解官方定义的output shape 计算公式 1. 官方文档

MaxPool2d — PyTorch 1.9.1 documentation

2. 关键信息

ceil_mode – when True, will use ceil instead of floor to compute the output shape

2. 公式简化

(1)K' = dilation[i] * (kernel_size[i] -1) + 1, 表示膨胀后的滑窗大小

 (2)pad_H = Hin + 2 * padding[0] 

上述公式简化成:out_H = ceil ( ( pad_H - K') / stride_h + 1) 

此公式默认最后至少存在一个滑窗,且滑窗的start在最后一个滑窗范围内,此公式忽略了一种情况,图示如下:

3. 结论一

pytorch文档描述错误,公式不可信

step2:从应用角度出发,推理pytorch实际公式可能是什么 1. ceil_mode理解

2. note部分理解

 字面上理解,如果滑窗的start在左边/上边padding 区域,或者input tensor区域,则允许其越界;

若滑窗的start在右边/下边padding区域,则忽略此滑窗

3. 推论一 

pytorch执行结果的outshape计算公式可能为:

ceil_mode类似模式outshape计算公式
FalseVALID

H_out = ceil( (pad_H -(K'-1) / stride_h)

           = ceil( (H_in+2*padding[0] - d_h * (kernel_h -1) -1) / stride_h) 

ps:公式推导过程见下

TrueSAME

H_out = ceil( pad_H/stride_h)

           = ceil( (H_in+2*padding[0]) / stride_h)

如果最后一个滑窗的start(起始行、列)在right/bottom padding区域,则该滑窗忽略;

if H_out%stride_h <=padding[0]:

        H_out = ceil( pad_H/stride_h) -1

==》 总结

H_out =  ceil( (H_in+padding[0]) / stride_h)

 step3:验证pytorch结果是否符合推论
import torch as t
import numpy as np
import warnings
warnings.filterwarnings("ignore")

case = ([1, 144, 4090], [13, 13], [15, 15], [2, 4], [1, 1], True)
'''
# cal shape(tf same):  (10.0, 274.0)
        out_w = ceil((4090+8)/15) = ceil(273+3/15),
        最后一个滑窗start=3,倒数第三行,属于padding区域 
# cal shape(ceil_mode=True,推论公式):  (10.0, 273.0)
# cal shape(pth doc without ceil_mode):  (10.0, 273.3333333333333)
# cal shape(pth doc ceil):  (10.0, 274.0)
# pytorch res shape:  torch.Size([1, 10, 273])
'''

# case = ([1, 131, 4095], [13, 13], [15, 15], [2, 4], [1, 1], True)
'''
# cal shape(tf same):  (9.0, 274.0)
# cal shape(ceil_mode=True,推论公式):  (9.0, 274.0)
# cal shape(pth doc without ceil_mode):  (9.133333333333333, 273.6666666666667)
# cal shape(pth doc ceil):  (10.0, 274.0)
# pytorch res shape:  torch.Size([1, 9, 274])
'''

# case = ([1, 6, 7], [1, 1], [2, 2], [0, 0], [1, 1], True)
'''
# cal shape(tf same):  (3.0, 4.0)
# cal shape(ceil_mode=True,推论公式):  (3.0, 4.0)
# cal shape(pth doc without ceil_mode):  (3.5, 4.0)
# cal shape(pth doc ceil):  (4.0, 4.0)
# pytorch res shape:  torch.Size([1, 3, 4])
'''

# case = ([1, 32, 32], [1, 1], [2, 2], [0, 0], [1, 1], True)
'''
# cal shape(tf same):  (16.0, 16.0)
# cal shape(ceil_mode=True,推论公式):  (16.0, 16.0)
# cal shape(pth doc without ceil_mode):  (16.5, 16.5)
# cal shape(pth doc ceil):  (17.0, 17.0)
# pytorch res shape:  torch.Size([1, 16, 16])
'''

# case = ([1, 142, 13], [10, 10], [15, 15], [5, 1], [1, 1], True)
'''
# cal shape(tf same):  (11.0, 1.0)
        out_h = ceil((142+10)/15+1) = ceil( 152/15+1) = ceil(10+2/15)
        最后一个滑窗start=2,倒数第2行,属于padding区域 
# cal shape(ceil_mode=True,推论公式):  (10.0, 1.0)
# cal shape(pth doc without ceil_mode):  (10.466666666666667, 1.3333333333333333)
# cal shape(pth doc ceil):  (11.0, 2.0)
# pytorch res shape:  torch.Size([1, 10, 1])
'''

# case = ([1, 110, 5], [15, 8], [15, 15], [7, 2], [1, 1], True)
'''
# cal shape(tf same):  (9.0, 1.0)
        out_h = ceil((110+14)/15) = ceil(8+4/15),
        最后一个滑窗start=4,倒数第4行,属于padding区域 
# cal shape(ceil_mode=True,推论公式):  (8.0, 1.0)
# cal shape(pth doc without ceil_mode):  (8.266666666666666, 1.0666666666666667)
# cal shape(pth doc ceil):  (9.0, 2.0)
# pytorch res shape:  torch.Size([1, 8, 1])
'''

# case = ([1, 109, 13], [5, 5], [8, 8], [2, 2], [1, 1], True)
'''
# cal shape(tf same):  (15.0, 3.0)  
       out_h = ceil((109+4)/8) = ceil( 113/8) = ceil(14+1),
       最后一个滑窗start=1,倒数第1行,属于padding区域 
       out_w = ceil((13+4)/8+1) =  = ceil(2+1), 
       最后一个滑窗start=1,倒数第1行,属于padding区域
# cal shape(ceil_mode=True,推论公式):  (14.0, 2.0) 
# cal shape(pth doc without ceil_mode):  (14.5, 2.5)
# cal shape(pth doc ceil):  (15.0, 3.0)
# pytorch res shape:  torch.Size([1, 14, 2])
'''

tensor_shape = case[0]
ceil_mode = case[5]

x = t.randn(tensor_shape)
res = t.nn.MaxPool2d(kernel_size=case[1], stride=case[2], padding=case[3],
               dilation=case[4], return_indices=False, ceil_mode=ceil_mode)(x)
# 膨胀后的kernel大小
new_fh = case[4][0] * (case[1][0] - 1) + 1
new_fw = case[4][1] * (case[1][1] - 1) + 1

# 根据pytorch文档公式计算,不考虑ceil_mode
out_h = (case[0][-2] + 2 * case[3][0] - new_fh) / case[2][0] + 1
out_w = (case[0][-1] + 2 * case[3][1] - new_fw) / case[2][1] + 1
# 根据推论计算ceil_mode=False, 同valid mode
out_h2 = np.ceil((case[0][-2] + 2 * case[3][0] - (new_fh - 1)) / case[2][0])
out_w2 = np.ceil((case[0][-1] + 2 * case[3][1] - (new_fw - 1)) / case[2][1])
# 根据same mode计算,不考虑最后一个滑窗start在padding area的情形
out_h3 = np.ceil((case[0][-2] + 2 * case[3][0]) / case[2][0])
out_w3 = np.ceil((case[0][-1] + 2 * case[3][1]) / case[2][1])
# 根据推理计算ceil_mode=True
out_h4 = np.ceil((case[0][-2] + case[3][0]) / case[2][0])
out_w4 = np.ceil((case[0][-1] + case[3][1]) / case[2][1])


if ceil_mode:
    print("# cal shape(tf same): ", (out_h3, out_w3))
    print("# cal shape(ceil_mode=True,推论公式): ", (out_h4, out_w4))
    out_h1 = np.ceil(out_h)
    out_w1 = np.ceil(out_w)
    print("# cal shape(pth doc without ceil_mode): ", (out_h, out_w))
    print("# cal shape(pth doc ceil): ", (out_h1, out_w1))
else:
    print("# cal shape(ceil_mode=False, 推理公式): ", (out_h2, out_w2))
    out_h1 = np.floor(out_h)
    out_w1 = np.floor(out_w)
    print("# cal shape(pth doc floor): ", (out_h1, out_w1))

print("# pytorch res shape: ", res.shape)

结论二:

以上面有限的例子,暂时可以证明:推理公式计算出的outshape与pytorch实际结果相同;

应是pytorch doc中计算公式以及ceil_mode参数说明描述有误。

ps: 此结论未经大量数据验证,不保证其准确性。

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/272860.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号