Pooling.py 6.89 KB
Newer Older
yaoht's avatar
yaoht committed
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import numpy as np
import src.c2oObject as Node
import math
import copy


def get_pool_pads(layer):
    pad = layer.pooling_param.pad
    if pad != 0:
        pad_h = pad_w = pad
    else:
        if layer.pooling_param.pad_h != 0 and layer.pooling_param.pad_w != 0:
            pad_h = layer.pooling_param.pad_h
            pad_w = layer.pooling_param.pad_w
        else:
            pad_h = pad_w = 0
    pads = [0, 0, pad_h, pad_w, 0, 0, pad_h, pad_w]

    return pads


def calculate_pad_output_shape(input_shape, pads):
    pad_h = pads[2]
    pad_w = pads[3]
    output_shape = copy.deepcopy(input_shape[0])

    output_shape[2] = output_shape[2] + 2 * pad_h
    output_shape[3] = output_shape[3] + 2 * pad_w
    return [output_shape]


def create_pad_node(layer, node_name, input_name, output_name, input_shape):
    pads = get_pool_pads(layer)
    attributes = {"mode": "constant"}
    pad_input_name = input_name
    pad_output_name = output_name
    pad_output_shape = calculate_pad_output_shape(input_shape, pads)

    node = Node.c2oNode(layer, node_name, 'Pad', pad_input_name, pad_output_name, input_shape, pad_output_shape,
                        attributes)

    return node


def get_pool_attributes(layer, pool_type, input_shape):
    number = input_shape[0][0]
    channel = input_shape[0][1]
    height = input_shape[0][2]
    weight = input_shape[0][3]
    kernel_size = layer.pooling_param.kernel_size
    pad = layer.pooling_param.pad
    stride = layer.pooling_param.stride

    if pool_type == 'GlobalMaxPool' or pool_type == 'GlobalAveragePool':
        global_pooling = True
    else:
        global_pooling = False
    # pass kernel_shape
    if global_pooling:
        kernel_h = height
        kernel_w = weight
    else:
        if kernel_size != 0:
            kernel_h = kernel_w = kernel_size
        elif layer.pooling_param.kernel_h != 0 and layer.pooling_param.kernel_w != 0:
            kernel_h = layer.pooling_param.kernel_h
            kernel_w = layer.pooling_param.kernel_w
        else:
            kernel_h = 1
            kernel_w = 1
    kernel_shape = [kernel_h, kernel_w]
    # pass pad
    if pad != 0:
        pad_h = pad_w = pad
    else:
        if layer.pooling_param.pad_h != 0 and layer.pooling_param.pad_w != 0:
            pad_h = layer.pooling_param.pad_h
            pad_w = layer.pooling_param.pad_w
        else:
            pad_h = pad_w = 0
    pads = [pad_h, pad_w, pad_h, pad_w]
    # 由于 caffe 与 onnx 的 pad 的计算的原因,将 pad 属性,单独创建一个节点
    pads = [0, 0, 0, 0]
    # pass strides
    stride_h = stride_w = 1
    if stride != 1:
        stride_h = stride_w = stride
    else:
        if layer.pooling_param.stride_h != 0 and layer.pooling_param.stride_w != 0:
            stride_h = layer.pooling_param.stride_h
            stride_w = layer.pooling_param.stride_w
        else:
            stride_h = stride_w = 1
    strides = [stride_h, stride_w]

    # pass round_mode
    # caffe 上默认是使用 ceil 的,但是在 onnx 默认使用 floor
    # caffe definition
    #   enum RoundMode {
    #     CEIL = 0;
    #     FLOOR = 1;
    #   }
    # default Ceil = 0
    # onnx ceil_mode floor = 0, ceil = 1, default: floor = 0
    round_mode_ceil = 0
    round_mode_floor = 1
    round_mode = 0
    if layer.pooling_param.round_mode == 0:
        round_mode = round_mode_ceil
    elif layer.pooling_param.round_mode == 1:
        round_mode = round_mode_floor
    else:
        # wrong condition
        exit(-1)
    if round_mode == round_mode_ceil:
        ceil_mode = 1
    else:
        ceil_mode = 0

    attributes = {"kernel_shape": kernel_shape,
                  "strides": strides,
                  "pads": pads,
                  "ceil_mode": ceil_mode
                  }
    return attributes


# 计算输出维度
def get_pooling_output_shape(input_shape, layer, attributes, with_indices=False):
    number = input_shape[0][0]
    channel = input_shape[0][1]
    kernel_shape = attributes["kernel_shape"]
    kernel_h = kernel_shape[0]
    kernel_w = kernel_shape[1]
    pads = attributes["pads"]
    strides = attributes["strides"]
    stride_h = strides[0]
    stride_w = strides[1]
    ceil_mode = attributes["ceil_mode"]
    pad_h = pads[2]
    pad_w = pads[3]
    height = input_shape[0][2]
    width = input_shape[0][3]

    if ceil_mode == 1:
        # ceil
        pooled_height = int(math.ceil((height + 2 * pad_h - kernel_h) / stride_h)) + 1
        pooled_width = int(math.ceil((width + 2 * pad_h - kernel_w) / stride_w)) + 1
    else:
        # floor
        pooled_height = int(math.floor((height + 2 * pad_h - kernel_h) / stride_h)) + 1
        pooled_width = int(math.floor((width + 2 * pad_h - kernel_w) / stride_w)) + 1

    if pad_h != 0 or pad_w != 0:
        if ((pooled_height - 1) * stride_h) >= (height + pad_h):
            pooled_height = pooled_height - 1
        if ((pooled_width - 1) * stride_w) >= (width + pad_w):
            pooled_width = pooled_width - 1
    if kernel_h == 0:
        kernel_h = kernel_w = 1
    if with_indices:
        output_shape = [[number, channel, pooled_height, pooled_width],
                        [number, channel, pooled_height, pooled_width]]
    else:
        output_shape = [[number, channel, pooled_height, pooled_width]]
    return output_shape


def pooling_type(layer):
    pool_value = layer.pooling_param.pool
    global_value = layer.pooling_param.global_pooling
    if pool_value == 0 and global_value is True:
        return 'GlobalMaxPool'
    elif pool_value == 1 and global_value is True:
        return 'GlobalAveragePool'
    elif pool_value == 0 and global_value is False:
        return 'MaxPool'
    elif pool_value == 1 and global_value is False:
        return 'AveragePool'
    else:
        print("unsupport pooling!")
        exit(-1)


# 构建节点
def create_pooling_node(layer, nodename, inname, outname, input_shape):
    pool_type = pooling_type(layer)
    node = None
    attributes = get_pool_attributes(layer, pool_type, input_shape)
    with_indices = True if len(outname) == 2 else False
    output_shape = get_pooling_output_shape(input_shape, layer, attributes, with_indices=with_indices)

    # 判断是池化种类,最大池化、平均池化
    if pool_type == 'GlobalMaxPool':
        node = Node.c2oNode(layer, nodename, "GlobalMaxPool", inname, outname, input_shape, output_shape, dict={})
    elif pool_type == 'MaxPool':
        node = Node.c2oNode(layer, nodename, "MaxPool", inname, outname, input_shape, output_shape, dict=attributes)
    elif pool_type == 'GlobalAveragePool':
        node = Node.c2oNode(layer, nodename, "GlobalAveragePool", inname, outname, input_shape, output_shape,
                            dict={})
    elif pool_type == 'AveragePool':
        node = Node.c2oNode(layer, nodename, "AveragePool", inname, outname, input_shape, output_shape,
                            dict=attributes)
    # Layers[i].pooling_param.pool==2为随机池化
    assert (node is not None)
    return node