-
Notifications
You must be signed in to change notification settings - Fork 5.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhance reshape #9008
Enhance reshape #9008
Changes from 4 commits
d3d16f7
1d4dfc0
a8cdd97
cf08185
c87d11a
a6e6424
eb12cbe
437f7a3
454b0a9
d4bb2ca
b7e83d2
c078ed4
4bfbc59
09743b6
5b8bb34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,53 +25,82 @@ class ReshapeOp : public framework::OperatorWithKernel { | |
: OperatorWithKernel(type, inputs, outputs, attrs) {} | ||
|
||
void InferShape(framework::InferShapeContext *ctx) const override { | ||
// input check | ||
PADDLE_ENFORCE(ctx->HasInput("X"), | ||
"Input(X) of ReshapeOp should not be null."); | ||
PADDLE_ENFORCE(ctx->HasOutput("Out"), | ||
"Output(Out) of ReshapeOp should not be null."); | ||
|
||
auto shape = ctx->Attrs().Get<std::vector<int>>("shape"); | ||
PADDLE_ENFORCE(shape.size() > 0, "Attr(shape) shouldn't be empty."); | ||
const std::vector<int> &shape = ctx->Attrs().Get<std::vector<int>>("shape"); | ||
PADDLE_ENFORCE(!shape.empty(), | ||
"The shape information must be set by Attr(shape)."); | ||
|
||
std::vector<int64_t> output_shape; | ||
auto x_dims = ctx->GetInputDim("X"); | ||
bool need_copy_dim = ValidateShape(shape, x_dims, output_shape); | ||
|
||
if (need_copy_dim) { | ||
// Some dimensions can only be determined during runtime. Here temporarily | ||
// set output tensor's shape the same as that of the input tensor. | ||
ctx->SetOutputDim("Out", x_dims); | ||
} else { | ||
ctx->SetOutputDim("Out", framework::make_ddim(output_shape)); | ||
|
||
// FIXME(caoying): When shape of the output tensor is determined during | ||
// runtime, LoD information of X will not passed to the output. | ||
if (shape[0] == x_dims[0]) { | ||
// Only pass LoD when the first dimension of output and Input(X) | ||
// are the same. | ||
ctx->ShareLoD("X", /*->*/ "Out"); | ||
} | ||
} | ||
} | ||
|
||
private: | ||
bool ValidateShape(const std::vector<int> &shape, | ||
const framework::DDim &input_dim, | ||
std::vector<int64_t> &output_shape) const { | ||
// only one dimension canbe set to -1, whose size will be automatically | ||
// infered. | ||
const int64_t unknown_index = -1; | ||
const auto in_size = framework::product(input_dim); | ||
const auto x_rank = input_dim.size(); | ||
|
||
bool need_dim_copy = false; | ||
std::vector<size_t> 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] == unknown_size, | ||
"Each dimension of Attr(shape) must be positive or %d.", | ||
unknown_size); | ||
if (shape[i] == unknown_size) { | ||
PADDLE_ENFORCE(shape[i] >= 0 || shape[i] == unknown_index, | ||
"Each input dimension of Attr(shape) must be positive, or " | ||
"only one input dimension can be -1."); | ||
if (shape[i] == unknown_index) { | ||
neg_dims_idx.push_back(i); | ||
PADDLE_ENFORCE(neg_dims_idx.size() <= 1, | ||
"Only one dimension of Attr(shape) can be unknown."); | ||
} else if (shape[i] == 0) { | ||
PADDLE_ENFORCE_LT( | ||
i, x_rank, | ||
"Only dimension less than rank of Input(X) can be set to 0."); | ||
need_dim_copy = true; | ||
} | ||
} | ||
PADDLE_ENFORCE_LE( | ||
neg_dims_idx.size(), 1, | ||
"Only one input dimension of Attr(shape) may be unknown."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
|
||
int64_t capacity = | ||
std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<int>()); | ||
int64_t in_size = framework::product(x_dims); | ||
if (neg_dims_idx.size() == 1) { | ||
// dim infer | ||
shape[neg_dims_idx[0]] = in_size / (-capacity); | ||
// recalculate capacity | ||
capacity = shape[neg_dims_idx[0]] * (-capacity); | ||
} | ||
// capacity check | ||
PADDLE_ENFORCE(capacity == in_size, | ||
"The size of Input(X) mismatches with Attr(shape)."); | ||
// resize output | ||
std::vector<int64_t> shape_int64(shape.size(), 0); | ||
std::transform(shape.begin(), shape.end(), shape_int64.begin(), | ||
output_shape.resize(shape.size(), 0); | ||
std::transform(shape.begin(), shape.end(), output_shape.begin(), | ||
[](int a) { return static_cast<int64_t>(a); }); | ||
auto out_dims = framework::make_ddim(shape_int64); | ||
ctx->SetOutputDim("Out", out_dims); | ||
if (shape[0] == x_dims[0]) { | ||
// Only pass LoD when the first dimension is equal between | ||
// output and input. | ||
ctx->ShareLoD("X", /*->*/ "Out"); | ||
|
||
// some dimension can only be determinted during runtime. | ||
if (need_dim_copy) return need_dim_copy; | ||
|
||
int64_t inferred_dim = 0; | ||
if (neg_dims_idx.size()) { | ||
int64_t capacity = std::accumulate(shape.begin(), shape.end(), 1, | ||
std::multiplies<int>()); | ||
inferred_dim = in_size / (-capacity); | ||
PADDLE_ENFORCE_EQ(inferred_dim * (-capacity), in_size, | ||
"Invalid shape is given."); | ||
output_shape[neg_dims_idx[0]] = inferred_dim; | ||
} | ||
return false; | ||
} | ||
}; | ||
|
||
|
@@ -81,9 +110,8 @@ class ReshapeOpMaker : public framework::OpProtoAndCheckerMaker { | |
: OpProtoAndCheckerMaker(proto, op_checker) { | ||
AddInput("X", "The input tensor of reshape operator."); | ||
AddOutput("Out", "The output tensor of reshape operator."); | ||
AddAttr<std::vector<int>>("shape", | ||
"(vector<int>) " | ||
"Target shape of reshape operator."); | ||
AddAttr<std::vector<int>>( | ||
"shape", "(std::vector<int>) Target shape of reshape operator."); | ||
AddAttr<bool>("inplace", | ||
"Change the source tensor's shape without copy memory.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
.SetDefault(true); | ||
|
@@ -99,7 +127,7 @@ and target shape = [1, 4], the reshape operator will transform | |
the tensor X into a 2-D tensor: [[1, 2, 3, 4]] | ||
|
||
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 | ||
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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to explain when the dimension can be set to 0 (necessary) and in-place reshape (optional) in the doc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,6 +70,7 @@ | |
'smooth_l1', | ||
'one_hot', | ||
'autoincreased_step_counter', | ||
'reshape', | ||
] | ||
|
||
|
||
|
@@ -3184,6 +3185,8 @@ def one_hot(input, depth): | |
The one-hot tensor or LodTensor, same as input. | ||
|
||
Examples: | ||
.. code-block:: python | ||
|
||
X is a LoDTensor: | ||
X.lod = [[0, 1, 4]] | ||
X.shape = [4, 1] | ||
|
@@ -3236,3 +3239,56 @@ def autoincreased_step_counter(counter_name=None, begin=1, step=1): | |
counter.stop_gradient = True | ||
|
||
return counter | ||
|
||
|
||
def reshape(x, shape, act=None, inplace=True, name=None): | ||
""" | ||
Gives a new shape to Tensor without changing its data. | ||
This layer takes a tensor as input and the attribute shape specifying the | ||
new shape. The shape attribute must be specified. At most one dimension of | ||
the new shape can be -1. In this case, the value is inferred from the size | ||
of the tensor and the remaining dimensions. A dimension could also be 0, | ||
in which case the actual dimension value is going to be copied from the | ||
input tensor. | ||
|
||
Args: | ||
input(variable): The input tensor. | ||
shape(list): The new shape. At most one dimension of the new shape can | ||
be -1. | ||
act (str): The non-linear activation to be applied to output variable. | ||
inplace(bool): If this flag is set true, a new output tensor is created | ||
whose data is copied from input x, otherwise the output | ||
shares data with input without copying. | ||
|
||
Returns(variable): The output tensor. | ||
|
||
Examples: | ||
.. code-block:: python | ||
|
||
Given a 2-D tensor X with shape [2 x 2], and the new shape: [1, 4]. | ||
The reshape layer will change tensor X into a 2-D tensor with | ||
shape [1 x 4] with its data unchanged. | ||
|
||
Given a 3-D tensor x with shape [2, 3, 4] and the new shape: [3, -1]. | ||
The reshape layer will change tensor X into a 2-D tensor with shape: | ||
[3 x 8] with its data unchanged. | ||
|
||
Given a 3-D tensor x with shape [2, 3, 8] and the new shape: | ||
[-1, 0, 2, 2]. The reshape layer will change tensor X into a 4-D tensor | ||
with shape [4, 3, 2, 2] with its data unchanged. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also need to refine this doc There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
|
||
""" | ||
|
||
if not (isinstance(shape, list) or isinstance(shape, tuple)): | ||
raise ValueError("Input shape must be a python lsit or tuple.") | ||
|
||
helper = LayerHelper("reshape", **locals()) | ||
reshaped = helper.create_tmp_variable(dtype=x.dtype) | ||
helper.append_op( | ||
type="reshape", | ||
inputs={"X": x}, | ||
attrs={"shape": shape, | ||
"inplace": inplace}, | ||
outputs={"Out": reshaped}) | ||
|
||
return helper.append_activation(reshaped) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
canbe
->can be
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.