namespace
{
-
+// image size includes padding.
inline uint32_t compute_out_size(uint32_t image_size, uint32_t filter_size, uint32_t stride)
{
+ assert((image_size + stride - filter_size) % stride == 0);
return (image_size + stride - filter_size) / stride;
}
Buffer<RET_T> calc_conv2D(const loco::Conv2D *conv2d, const Buffer<IFM_T> *input_buf,
const Buffer<FIL_T> *filter_buf)
{
- // TODO Consider complex padding. This version works only when padding = {0,0,0,0}
- auto pad = conv2d->pad();
- assert(pad->bottom() == 0 && pad->top() == 0 && pad->right() == 0 && pad->left() == 0);
-
auto input_shape = input_buf->shape();
auto filter_shape = filter_buf->shape();
const uint32_t stride_width = conv2d->stride()->horizontal();
const uint32_t stride_height = conv2d->stride()->vertical();
- // loco invariant
- assert((input_height - filter_height) % stride_height == 0);
- assert((input_width - filter_width) % stride_width == 0);
-
// TODO Enable dilations. Let's set these to 1 for now.
const uint32_t dilation_width_factor = 1;
const uint32_t dilation_height_factor = 1;
- // TODO enable padding.
- const uint32_t pad_width = 0; // with padding, pad_width = conv2d->pad()->left();
- const uint32_t pad_height = 0; // with padding, pad_height = conv2d->pad()->top();
+ const uint32_t pad_top = conv2d->pad()->top();
+ const uint32_t pad_bottom = conv2d->pad()->bottom();
+
+ const uint32_t pad_left = conv2d->pad()->left();
+ const uint32_t pad_right = conv2d->pad()->right();
- const uint32_t output_height = compute_out_size(input_height, filter_height, stride_height);
- const uint32_t output_width = compute_out_size(input_width, filter_width, stride_width);
+ const uint32_t output_height =
+ compute_out_size(input_height + pad_top + pad_bottom, filter_height, stride_height);
+ const uint32_t output_width =
+ compute_out_size(input_width + pad_left + pad_right, filter_width, stride_width);
const uint32_t batches = input_shape.dim(0);
const uint32_t input_depth = input_shape.dim(3);
{
for (uint32_t out_channel = 0; out_channel < output_depth; ++out_channel)
{
- const int in_x_origin = (out_x * stride_width) - pad_width;
- const int in_y_origin = (out_y * stride_height) - pad_height;
+ const int in_x_origin = (out_x * stride_width) - pad_left;
+ const int in_y_origin = (out_y * stride_height) - pad_top;
RET_T total = static_cast<RET_T>(0);
using nncc::core::ADT::tensor::make_buffer;
using nncc::core::ADT::tensor::make_overlay;
-// TODO Add test for padding
void run_test(const float *ifm, const float *ker, const float *expected_ofm, const Shape &ifm_shape,
const Shape ker_shape, const Shape ofm_shape, const uint32_t stride_v,
- const uint32_t stride_h)
+ const uint32_t stride_h, const uint32_t pad_top = 0, const uint32_t pad_bottom = 0,
+ const uint32_t pad_left = 0, const uint32_t pad_right = 0)
{
auto g = loco::make_graph();
conv2d->ker(ker_enc);
conv2d->stride()->vertical(stride_v);
conv2d->stride()->horizontal(stride_h);
+ conv2d->pad()->top(pad_top);
+ conv2d->pad()->bottom(pad_bottom);
+ conv2d->pad()->left(pad_left);
+ conv2d->pad()->right(pad_right);
// run interpreter
locomotiv::NodeExecution::get().run(conv2d);
with tf.Session() as sess:
print(sess.run(out))
*/
-
TEST(NodeExecution_Conv2D, f32_1x5x5x1_calculation)
{
using nncc::core::ADT::tensor::Shape;
1, 1 // stride
);
}
+
+/* ifm and ofm are from the code below:
+tensorflow version : 1.12.0
+
+import tensorflow as tf
+
+ifm = tf.constant([-1.3653529, 0.4160791, 0.5059157, 0.7649683, 0.39364856,
+ -1.0164733, 1.506766, -1.1413091, 1.2766701, -0.9253511,
+ 1.3570246, 0.32089928, -0.9898171, 1.983792, -0.3423274,
+ -1.1901658, 1.2288222, -0.47401968, -0.01369802, 0.4136331,
+ 0.06960588, -0.16537654, -0.65015996, -0.555224, 0.7140603
+], shape=[1, 5, 5, 1])
+
+ker = tf.constant([2.3490515, -0.4572366, 0.05790535,
+ 0.3672005, 0.52679914, 0.74607974,
+ -1.7211207, 1.1174419, -0.59663385
+], shape=[3, 3, 1, 1])
+
+ofm = tf.nn.conv2d(ifm, ker, strides=[1, 1, 1, 1], padding='SAME')
+
+with tf.Session() as sess:
+ print(sess.run(ofm))
+*/
+TEST(NodeExecution_Conv2D, with_padding)
+{
+ using nncc::core::ADT::tensor::Shape;
+
+ const float ifm[] =
+ {
+ -1.3653529, 0.4160791, 0.5059157, 0.7649683, 0.39364856,
+ -1.0164733, 1.506766, -1.1413091, 1.2766701, -0.9253511,
+ 1.3570246, 0.32089928, -0.9898171, 1.983792, -0.3423274,
+ -1.1901658, 1.2288222, -0.47401968, -0.01369802, 0.4136331,
+ 0.06960588, -0.16537654, -0.65015996, -0.555224, 0.7140603
+ };
+
+ const float ker[] =
+ {
+ 2.3490515, -0.4572366, 0.05790535,
+ 0.3672005, 0.52679914, 0.74607974,
+ -1.7211207, 1.1174419, -0.59663385
+ };
+
+ const float ofm[] =
+ {
+ -2.443676, 4.2094254, -3.6403496, 4.8254814, -2.743059,
+ 2.5620093, -5.185688, -1.1470609, 4.54913, -2.1985974,
+ -0.5567835, 0.49045527, 2.5752437, -2.3383713, 4.455967,
+ -0.13562866, 2.9236434, 1.4019353, -3.0521483, 6.782954,
+ 0.5286269, -3.9317036, 2.285041, -1.0817666, -0.04901773
+ };
+
+ run_test(ifm, ker, ofm,
+ Shape{1, 5, 5, 1}, Shape{1, 3, 3, 1}, Shape{1, 5, 5, 1}, // shapes of input, ker, output
+ 1, 1, // stride
+ 1, 1, 1, 1 // padding
+ );
+}
// clang-format on