From 685d1e3b330ff6605935ed4a33825bdeb97ced07 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Thu, 14 Dec 2017 04:27:05 +0000 Subject: [PATCH 1/3] Enable reshape_op to support dimension inference --- paddle/operators/reshape_op.cc | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/paddle/operators/reshape_op.cc b/paddle/operators/reshape_op.cc index 39bf2118d6..306dfa8069 100644 --- a/paddle/operators/reshape_op.cc +++ b/paddle/operators/reshape_op.cc @@ -34,21 +34,27 @@ class ReshapeOp : public framework::OperatorWithKernel { auto shape = ctx->Attrs().Get>("shape"); PADDLE_ENFORCE(shape.size() > 0, "Attr(shape) shouldn't be empty."); auto x_dims = ctx->GetInputDim("X"); - // TODO(qiao) change batch_size - for (size_t i = 1; i < shape.size(); ++i) { - PADDLE_ENFORCE(shape[i] > 0, - "Each dimension of Attr(shape) " - "must be positive except the first one."); - } - if (shape[0] < 0) { - shape[0] = x_dims[0]; + + std::vector neg_dims_idx; + for (size_t i = 0; i < shape.size(); ++i) { + PADDLE_ENFORCE(shape[i] > 0 || shape[i] == -1, + "Each dimension of Attr(shape) must be positive or -1."); + if (shape[i] == -1) { + neg_dims_idx.push_back(i); + PADDLE_ENFORCE(neg_dims_idx.size() <= 1, + "Only one dimension of Attr(shape) can be -1."); + } } + // capacity check int64_t capacity = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies()); int64_t in_size = framework::product(x_dims); - PADDLE_ENFORCE_EQ(capacity, in_size, + if (neg_dims_idx.size() == 1) { + shape[neg_dims_idx[0]] = in_size / (-capacity); + PADDLE_ENFORCE(shape[neg_dims_idx[0]] > 0, "The size of Input(X) mismatches with Attr(shape)."); + } // resize output std::vector shape_int64(shape.size(), 0); std::transform(shape.begin(), shape.end(), shape_int64.begin(), @@ -88,6 +94,9 @@ the tensor X into a 1-D tensor: [1, 2, 3, 4] +One dimension in the target shape can be set -1, and the real dimension +will be infered from the original shape of Input(X) and other +dimensions in the target shape. )DOC"); } }; From 0a75ed6f5bcad53b3622ee89d48a5613cc4ef396 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Thu, 14 Dec 2017 05:06:19 +0000 Subject: [PATCH 2/3] Add unit test for dimension inference in reshape_op --- paddle/operators/reshape_op.cc | 14 +++++++++----- python/paddle/v2/fluid/tests/test_reshape_op.py | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/paddle/operators/reshape_op.cc b/paddle/operators/reshape_op.cc index 306dfa8069..164f3104eb 100644 --- a/paddle/operators/reshape_op.cc +++ b/paddle/operators/reshape_op.cc @@ -42,19 +42,23 @@ class ReshapeOp : public framework::OperatorWithKernel { if (shape[i] == -1) { neg_dims_idx.push_back(i); PADDLE_ENFORCE(neg_dims_idx.size() <= 1, - "Only one dimension of Attr(shape) can be -1."); + "Only one dimension of Attr(shape) can be unknown."); } } - // capacity check int64_t capacity = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies()); int64_t in_size = framework::product(x_dims); if (neg_dims_idx.size() == 1) { - shape[neg_dims_idx[0]] = in_size / (-capacity); - PADDLE_ENFORCE(shape[neg_dims_idx[0]] > 0, - "The size of Input(X) mismatches with Attr(shape)."); + // dim infer + shape[neg_dims_idx[0]] = in_size / (-capacity); + // recalculate capacity + capacity = std::accumulate(shape.begin(), shape.end(), 1, + std::multiplies()); } + // capacity check + PADDLE_ENFORCE(capacity == in_size, + "The size of Input(X) mismatches with Attr(shape)."); // resize output std::vector shape_int64(shape.size(), 0); std::transform(shape.begin(), shape.end(), shape_int64.begin(), diff --git a/python/paddle/v2/fluid/tests/test_reshape_op.py b/python/paddle/v2/fluid/tests/test_reshape_op.py index 16bb6bb2af..18ee3aece6 100644 --- a/python/paddle/v2/fluid/tests/test_reshape_op.py +++ b/python/paddle/v2/fluid/tests/test_reshape_op.py @@ -17,5 +17,19 @@ class TestReshapeOp(OpTest): self.check_grad(["X"], "Out") +class TestReshapeOpDimInfer(OpTest): + def setUp(self): + self.op_type = "reshape" + self.inputs = {'X': np.random.random((10, 20)).astype("float32")} + self.attrs = {'shape': [4, -1, 5]} + self.outputs = {'Out': self.inputs['X'].reshape(self.attrs['shape'])} + + def test_check_output(self): + self.check_output() + + def test_check_grad(self): + self.check_grad(["X"], "Out") + + if __name__ == '__main__': unittest.main() From d37ed6cb13fc63aa2c81ece8de005bfbea4916be Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Fri, 15 Dec 2017 07:41:54 +0000 Subject: [PATCH 3/3] polish code in reshape_op --- paddle/operators/reshape_op.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/paddle/operators/reshape_op.cc b/paddle/operators/reshape_op.cc index 5baf459536..d82d828747 100644 --- a/paddle/operators/reshape_op.cc +++ b/paddle/operators/reshape_op.cc @@ -36,10 +36,13 @@ class ReshapeOp : public framework::OperatorWithKernel { auto x_dims = ctx->GetInputDim("X"); std::vector neg_dims_idx; + // set some dimension to -1 if it is unknown + const int unknown_size = -1; for (size_t i = 0; i < shape.size(); ++i) { - PADDLE_ENFORCE(shape[i] > 0 || shape[i] == -1, - "Each dimension of Attr(shape) must be positive or -1."); - if (shape[i] == -1) { + PADDLE_ENFORCE(shape[i] > 0 || shape[i] == unknown_size, + "Each dimension of Attr(shape) must be positive or %d.", + unknown_size); + if (shape[i] == unknown_size) { neg_dims_idx.push_back(i); PADDLE_ENFORCE(neg_dims_idx.size() <= 1, "Only one dimension of Attr(shape) can be unknown."); @@ -53,8 +56,7 @@ class ReshapeOp : public framework::OperatorWithKernel { // dim infer shape[neg_dims_idx[0]] = in_size / (-capacity); // recalculate capacity - capacity = std::accumulate(shape.begin(), shape.end(), 1, - std::multiplies()); + capacity = shape[neg_dims_idx[0]] * (-capacity); } // capacity check PADDLE_ENFORCE(capacity == in_size, @@ -98,9 +100,9 @@ the tensor X into a 2-D tensor: [[1, 2, 3, 4]] -One dimension in the target shape can be set -1, and the real dimension -will be infered from the original shape of Input(X) and other -dimensions in the target shape. +One dimension in the target shape can be set -1, representing that its +size is unknown. In this case, the real dimension will be infered from +the original shape of Input(X) and other dimensions in the target shape. )DOC"); } };