shapes.py 4.9 KB
Newer Older
1 2 3 4 5
import math
from collections import namedtuple

from .errors import KaffeError

6 7 8
Tensor4DShape = namedtuple('Tensor4DShape',
                           ['batch_size', 'channels', 'height', 'width'])

9 10
Tensor3DShape = namedtuple('Tensor3DShape', ['batch_size', 'data1', 'data2'])

11 12 13 14 15 16 17 18
Tensor2DShape = namedtuple('Tensor2DShape', ['batch_size', 'data'])

ScalarShape = namedtuple('ScalarShape', ['batch_size'])


def make_tensor(batch_size, d1=None, d2=None, d3=None):
    if d3 is not None:
        return Tensor4DShape(batch_size, d1, d2, d3)
19 20
    elif d1 is not None and d2 is not None:
        return Tensor3DShape(batch_size, d1, d2)
21 22 23 24 25 26 27
    elif d1 is not None and d2 is None:
        return Tensor2DShape(batch_size, d1)
    elif d1 is None and d2 is None and d3 is None:
        return ScalarShape(batch_size)
    else:
        raise NotImplementedError('invalid params for make_tensor %s' \
                % (str((batch_size, d1, d2, d3))))
28 29 30


def get_filter_output_shape(i_h, i_w, params, round_func):
31 32 33 34 35 36 37 38
    dila_h = getattr(params, 'dila_h', 1)
    dila_w = getattr(params, 'dila_w', 1)

    o_h = (i_h + 2 * params.pad_h -
           (dila_h * (params.kernel_h - 1) + 1)) / float(params.stride_h) + 1
    o_w = (i_w + 2 * params.pad_w -
           (dila_w * (params.kernel_w - 1) + 1)) / float(params.stride_w) + 1

39 40 41 42 43 44 45 46 47 48 49
    return (int(round_func(o_h)), int(round_func(o_w)))


def get_strided_kernel_output_shape(node, round_func):
    assert node.layer is not None
    input_shape = node.get_only_parent().output_shape
    o_h, o_w = get_filter_output_shape(input_shape.height, input_shape.width,
                                       node.layer.kernel_parameters, round_func)
    params = node.layer.parameters
    has_c_o = hasattr(params, 'num_output')
    c = params.num_output if has_c_o else input_shape.channels
50
    return make_tensor(input_shape.batch_size, c, o_h, o_w)
51 52 53 54 55 56 57 58 59 60 61 62


def shape_not_implemented(node):
    raise NotImplementedError


def shape_identity(node):
    assert len(node.parents) > 0
    return node.parents[0].output_shape


def shape_scalar(node):
63
    return make_tensor(1, 1, 1, 1)
64

65 66
def shape_crop(node):
    raise KaffeError('crop function had been defined in customer_layers')
67 68 69 70

def shape_data(node):
    if node.output_shape:
        # Old-style input specification
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
        shape = node.output_shape
    else:
        try:
            # New-style input specification
            shape = map(int, node.parameters.shape[0].dim)
        except:
            # We most likely have a data layer on our hands. The problem is,
            # Caffe infers the dimensions of the data from the source (eg: LMDB).
            # We want to avoid reading datasets here. Fail for now.
            # This can be temporarily fixed by transforming the data layer to
            # Caffe's "input" layer (as is usually used in the "deploy" version).
            # TODO: Find a better solution for this.
            raise KaffeError(
                'Cannot determine dimensions of data layer.\n'
                'See comments in function shape_data for more info.')
    return shape
87 88 89 90


def shape_mem_data(node):
    params = node.parameters
91
    return make_tensor(params.batch_size, params.channels, params.height,
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
                       params.width)


def shape_concat(node):
    axis = node.layer.parameters.axis
    output_shape = None
    for parent in node.parents:
        if output_shape is None:
            output_shape = list(parent.output_shape)
        else:
            output_shape[axis] += parent.output_shape[axis]
    return tuple(output_shape)


def shape_convolution(node):
    return get_strided_kernel_output_shape(node, math.floor)


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
def shape_deconvolution(node):
    assert node.layer is not None
    input_shape = node.get_only_parent().output_shape
    h_i = input_shape.height
    w_i = input_shape.width

    params = node.layer.kernel_parameters
    p_h = params.pad_h
    p_w = params.pad_w

    dila_h = params.dila_h
    dila_w = params.dila_w

    k_h = params.kernel_h
    k_w = params.kernel_w

    s_h = params.stride_h
    s_w = params.stride_w

    h_o = (h_i - 1) * s_h - 2 * p_h + dila_h * (k_h - 1) + 1
    w_o = (w_i - 1) * s_w - 2 * p_w + dila_w * (k_w - 1) + 1

    params = node.layer.parameters
    has_c_o = hasattr(params, 'num_output')
    c = params.num_output if has_c_o else input_shape.channels
    return make_tensor(input_shape.batch_size, c, h_o, w_o)


138
def shape_pool(node):
139 140 141 142 143
    global_pool = getattr(node.layer.parameters, 'global_pooling', False)
    if global_pool:
        input_shape = node.get_only_parent().output_shape
        return make_tensor(input_shape.batch_size, input_shape.channels, 1, 1)

144 145 146 147 148 149
    ceil_mode = getattr(node.layer.parameters, 'ceil_mode', True)
    if ceil_mode is True:
        method = math.ceil
    else:
        method = math.floor
    return get_strided_kernel_output_shape(node, method)
150 151 152 153


def shape_inner_product(node):
    input_shape = node.get_only_parent().output_shape
154
    return make_tensor(input_shape.batch_size, node.layer.parameters.num_output)