-
Notifications
You must be signed in to change notification settings - Fork 27
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
Add a padding operator. #95
Conversation
@mrbeann Is this PR ready for code review? You can add either Sam or me (or both) as reviewers. |
I think this is ready for review. But it seems I do not have permission. Can you or Sam review it? @xyzsam @yaoyuannnn |
You should be able to add reviewers since you already created the PR. |
Also can you resolve the issue in the CI build? |
@yaoyuannnn Hi Yao, I resolve the CI problem. But add a reviewer can only be done for the owner of this repo according to the following answer, |
Thanks! That's interesting. In GitLab, the author can select reviewers so I thought GitHub would be the same. Maybe I will review this PR tonight. Thanks for contributing to the SMAUG project :) |
.gitignore
Outdated
@@ -0,0 +1,3 @@ | |||
build/ | |||
experiments/ | |||
smaug/operators/padding_op_test.h |
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.
Why putting this file here?
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.
Yes, please remove this file from this PR. You are welcome to send a separate PR with a .gitignore if you like, but if so, it should only contain build products, not other code (like padding_op_test.h).
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.
Mainly to facilitate my development. But I think adding the initial two lines would be beneficial to this repo (the third line is meaningless now). But I can delete the file if you think it doesn't make sense.
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.
See #96
@@ -1,38 +1,39 @@ | |||
#include "smaug/core/backend.h" | |||
#include "smaug/operators/batch_norm_op.h" | |||
#include "smaug/operators/concat_op.h" |
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.
Code rearrangement generally is confusing :) Moving forward, please move this to a separate PR.
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.
I definitely appreciate you sorting all the includes in alphabetical order! It's certainly cleaner than before. But yes, refactoring and code cleanups should be done separately if it's not related to the main PR purpose.
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.
I'll fix this
smaug/core/backend.cpp
Outdated
const unsigned kEltwiseOpHw = 0x0003; | ||
const unsigned kBatchNormHw = 0x0004; | ||
const unsigned kPoolingHw = 0x0005; | ||
const unsigned kConvolutionHw = 0x0001; // 0x0001; |
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.
I can't remember exactly, but we do want these accelerator blocks to be traced with different accelerator IDs. @xyzsam for his perspective.
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.
Yes, leave this unchanged here; there is an argument for both sides but regardless, it should not be done in this PR.
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.
This is based on the advice @xyzsam gives for another problem. But it is not relevant to this PR. I'll delete it.
@@ -1,56 +1,57 @@ | |||
#include <iostream> |
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.
Again, it'd better to put the code rearrangement into a separate PR.
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.
I'll fix this.
smaug/operators/padding_op.h
Outdated
} | ||
|
||
/** Set the number of padders of the Tensor along each dimension. */ | ||
void setPadder(const int& val) { |
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.
Why not make this support different padding size on different dimensions like TensorFlow (https://www.tensorflow.org/api_docs/python/tf/keras/layers/ZeroPadding2D)? Judging from the implementation, I think this is for 2D input tensor? If so, it's better to rename it to Padding2DOp.
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.
I think it would be relatively straightforward to make this support tensors with any number of dimensions and arbitrary padding size along all of them, so we should do that instead. The copyTensorRegion
API should work regardless.
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.
Good idea. I'll rename the operation.
smaug/operators/padding_op.h
Outdated
namespace smaug { | ||
|
||
/** \ingroup Operators | ||
* \brief Pad a given tensor in different dimension. |
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.
I think this is meant to pad on H and W dimensions. Let's make this more explicit.
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.
Also, mention that the padding on either side of all dimensions is assumed to be equal (e.g. 1 element on both left and right sides).
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.
Okay!
smaug/operators/padding_op.h
Outdated
int getPadder() { return padder; } | ||
|
||
void run() override { | ||
Tensor* input = getInput(0); |
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.
clang-format?
smaug/operators/padding_op.h
Outdated
std::vector<int> destOrigin; | ||
if (input->getShape().getLayout() == DataLayout::NCHW){ | ||
destOrigin = std::vector<int>({0, 0, padder, padder}); | ||
} |
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.
clang-format
smaug/operators/padding_op.h
Outdated
} | ||
std::vector<int> srcOrigin = std::vector<int>({0, 0, 0, 0}); | ||
std::vector<int> regionSize = inputDims; | ||
copyTensorRegion(output, input, destOrigin, srcOrigin, regionSize); |
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.
Glad this API gets used here :)
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.
Yeah, thanks for your efforts. Hope I use it correctly. :)
smaug/operators/padding_op.h
Outdated
// Optional but recommended function to verify operator parameters. | ||
bool validate() override { | ||
if (padder < 0){ | ||
return false; |
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.
clang-format.
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.
Thank you for your PR! A few high level comments:
- Run clang-format on your code. For all the files you changed, run
clang-format -i -style=file filename.cpp
. - Move unrelated code out of this PR (refactoring, re-ordering includes, depthwise conv). You can put them in a separate PR.
I will add some more information about contributing code to the repo for future users. As you can see, you are the first external user to contribute to smaug. We really appreciate it :)
.gitignore
Outdated
@@ -0,0 +1,3 @@ | |||
build/ | |||
experiments/ | |||
smaug/operators/padding_op_test.h |
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.
Yes, please remove this file from this PR. You are welcome to send a separate PR with a .gitignore if you like, but if so, it should only contain build products, not other code (like padding_op_test.h).
@@ -1,38 +1,39 @@ | |||
#include "smaug/core/backend.h" | |||
#include "smaug/operators/batch_norm_op.h" | |||
#include "smaug/operators/concat_op.h" |
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.
I definitely appreciate you sorting all the includes in alphabetical order! It's certainly cleaner than before. But yes, refactoring and code cleanups should be done separately if it's not related to the main PR purpose.
#include "smaug/operators/smv/smv_less_op.h" | ||
#include "smaug/operators/smv/smv_greater_op.h" | ||
#include "smaug/operators/smv/smv_tanh_op.h" | ||
#include "smaug/operators/softmax_op.h" |
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.
(Also, if this was meant to sort the header includes, these three should not be at the bottom :] )
smaug/core/backend.cpp
Outdated
const unsigned kEltwiseOpHw = 0x0003; | ||
const unsigned kBatchNormHw = 0x0004; | ||
const unsigned kPoolingHw = 0x0005; | ||
const unsigned kConvolutionHw = 0x0001; // 0x0001; |
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.
Yes, leave this unchanged here; there is an argument for both sides but regardless, it should not be done in this PR.
smaug/core/types.proto
Outdated
@@ -3,79 +3,80 @@ syntax = "proto3"; | |||
package smaug; | |||
|
|||
enum DataType { | |||
UnknownDataType = 0; |
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.
+1
smaug/operators/padding_op.h
Outdated
// set output size? | ||
} | ||
|
||
auto getPadder() { return padder; } |
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.
Two issues here:
- Avoid
auto
return types; it makes the API unnecessarily hard to read and understand. The reader should not need to look for wherepadder
is defined to know what this returns. - The name "padder" suggests that it is an object doing padding, when in fact it is just your padding size. So this API (and the class member) should be named "padding_size".
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.
Thank you for your advice.
smaug/operators/padding_op.h
Outdated
} | ||
|
||
/** Set the number of padders of the Tensor along each dimension. */ | ||
void setPadder(const int& val) { |
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.
I think it would be relatively straightforward to make this support tensors with any number of dimensions and arbitrary padding size along all of them, so we should do that instead. The copyTensorRegion
API should work regardless.
smaug/operators/padding_op.h
Outdated
Tensor* input = getInput(0); | ||
Tensor* output = getOutput(0); | ||
int ndims = input->ndims(); | ||
std::vector<int> inputDims = input->getShape().dims(); |
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.
All these vectors should be const ref since you don't need to modify them.
smaug/operators/padding_op.h
Outdated
namespace smaug { | ||
|
||
/** \ingroup Operators | ||
* \brief Pad a given tensor in different dimension. |
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.
Also, mention that the padding on either side of all dimensions is assumed to be equal (e.g. 1 element on both left and right sides).
smaug/python/ops/array_ops.py
Outdated
name: Name of the operator. | ||
|
||
Returns: | ||
A paded version of the input tensor. |
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.
padded
Hi, I Just fix all the problems. BTW some includes are sorted after using the |
smaug/operators/padding_op.h
Outdated
* ,dimk_backward> | ||
*/ | ||
void setPaddingSize(RepeatedField<google::protobuf::int32> const& val) { | ||
std::vector<double> paddingSize(val.begin(), val.end()); |
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.
Why converting to double here?
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.
I change it to int.
smaug/operators/padding_op.h
Outdated
|
||
void setPaddingSize(std::vector<int> const& val) { paddingSize = val; } | ||
|
||
std::vector<int> getPaddingSize() { return paddingSize; } |
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.
This should be a const member function.
auto paddingOp = | ||
new PaddingOp<ReferenceBackend>("padding", workspace()); | ||
paddingOp->setInput(input, 0); | ||
paddingOp->setPaddingSize({ 0, 0, 0, 0, 1, 1, 1, 1 }); |
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.
Can we have a test for asymmetric padding like padding size of 1 on the left and 2 on the right since it's supported?
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.
I add it now.
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.
Thanks!
smaug/python/ops/array_ops.py
Outdated
|
||
Args: | ||
input_tensor: Input tensor. | ||
padding_size: A list that contains number of values padded to each dimension. |
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.
I think this docstring needs to be more explicit: padding_size is in the format of {dim0_begin, dim0_end, dim1_begin, dim1_end, ...}.
smaug/operators/padding_op.h
Outdated
* ,dimk_backward> | ||
*/ | ||
void setPaddingSize(RepeatedField<google::protobuf::int32> const& val) { | ||
std::vector<int> paddingSize(val.begin(), val.end()); |
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.
Sorry I didn't make it clear in my comment. This defines a new variable. Can you copy from the repeated field like:
paddingSize.assign(val.begin(), val.end());
smaug/operators/padding_op.h
Outdated
|
||
void setPaddingSize(std::vector<int> const& val) { paddingSize = val; } | ||
|
||
const std::vector<int> getPaddingSize() { return paddingSize; } |
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.
I meant to make this a const member function, not a const return type:
std::vector<int> getPaddingSize() const { return paddingSize; }
auto paddingOp = | ||
new PaddingOp<ReferenceBackend>("padding", workspace()); | ||
paddingOp->setInput(input, 0); | ||
paddingOp->setPaddingSize({ 0, 0, 0, 0, 1, 1, 1, 1 }); |
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.
Thanks!
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.
Thanks!
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.
Please update the PR description to include more detail about this operator. For example:
- Mention that the operator supports padding in all dimensions and asymmetric padding.
- Implemented in software only, no accelerated version available.
- Example Python code.
- How this was tested.
smaug/operators/padding_op.h
Outdated
* The paddingSize is orgainized as <dim1_forward, dim1_backward, ... | ||
* ,dimk_backward> | ||
*/ | ||
void setPaddingSize(RepeatedField<google::protobuf::int32> const& val) { |
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.
This should be const RepeatedField&
, not RepeatedField const&
.
Also, what does "forward" and "backward" mean?
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.
I'll update this.
smaug/operators/padding_op.h
Outdated
|
||
int getPadder() { return padder; } | ||
std::vector<int> getPaddingSize() const { return paddingSize; } |
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.
return const std::vector& to avoid making a copy of paddingSize.
smaug/operators/padding_op.h
Outdated
Tensor* input = getInput(0); | ||
Tensor* output = getOutput(0); | ||
int ndims = input->ndims(); | ||
const std::vector<int> inputDims = input->getShape().dims(); |
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.
both inputDims
and outputDims
should be const-ref.
smaug/operators/padding_op.h
Outdated
output->fillData(vf.data(), vf.size()); | ||
std::vector<int> destOrigin, paddingBegin, srcOrigin; | ||
for (int i = 0; i < ndims; i++) { | ||
paddingBegin.push_back(paddingSize[2 * i]); |
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.
use paddingSize.at(2*i)
instead; this will throw an exception if you go out of bounds, whereas paddingSize[]
will attempt to add an element at that position.
smaug/operators/padding_op.h
Outdated
paddingBegin.push_back(paddingSize[2 * i]); | ||
srcOrigin.push_back(0); | ||
} | ||
destOrigin = std::vector<int>(paddingBegin); |
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.
There's no need to declare destOrigin earlier and then re-initialize it here - that causes the vector to be constructed twice. Just declare it here directly: std::vector<int> destOrigin = ...
.
smaug/operators/padding_op.h
Outdated
dims[1] += 2*padder; | ||
dims[2] += 2*padder; | ||
for (int i = 0; i < ndims; i++) { | ||
dims[i] += (paddingSize[2 * i] + paddingSize[2 * i + 1]); | ||
} | ||
TensorShape shape( | ||
dims, input->getShape().getLayout(), Backend::Alignment); | ||
Tensor* output = new Tensor(name, shape); | ||
workspace->addTensor(output); | ||
outputs.at(0) = output; |
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.
Same here - use enums instead of hardcoded constants.
smaug/operators/padding_op.h
Outdated
return Operator::validate(); | ||
} | ||
|
||
enum { kInputs, kNumInputs }; |
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.
nit: kInput
instead of kInputs
(you only have one), and likewise for outputs.
smaug/operators/padding_op_test.cpp
Outdated
std::vector<float> expected_output{ | ||
0, 0, 0, // input 0, chan 0, row -1 | ||
0, 0, 0, // input 0, chan 0, row 0 | ||
0, 0, 0, // input 0, chan 1, row 3 |
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.
Do you mean row 1?
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.
Yes, I modified it.
smaug/python/ops/array_ops.py
Outdated
"""Construct a tensor by padding a given tensor. | ||
|
||
Args: | ||
input_tensor: Input tensor. | ||
padder: A int value that represents the padding dimension | ||
padding_size: A list in the format of {dim0_begin, dim0_end, dim1_begin, dim1_end, ...} that |
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.
Add a note that the order of dimensions must align with the data layout of input_tensor.
Also, ensure that this line doesn't exceed 80 characters in length.
smaug/python/ops/array_ops.py
Outdated
else: | ||
raise ValueError("Only support layout as NHWC or NCHW") | ||
if len(padding_size) != 2 * len(src_dims): | ||
raise ValueError("The padding_size's dimension must be two times as the input_tensor") |
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.
nit: "len(padding_size) should be 2x input_tensor.shape.dims" is clearer.
I just update the PR description. Please check if it is okay now. Thanks! |
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.
Almost done, just two more small fixes, then we can merge. Thank you for your patience and your contributions!!!
smaug/operators/padding_op.h
Outdated
srcOrigin.push_back(0); | ||
} | ||
destOrigin = std::vector<int>(paddingBegin); | ||
std::vector<int> destOrigin = std::vector<int>(paddingBegin); |
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.
Sorry, I should have caught this earlier. Why build paddingBegin
and then copy it into destOrigin
? Why not just use paddingBegin directly?
smaug/operators/padding_op.h
Outdated
srcOrigin.push_back(0); | ||
} | ||
destOrigin = std::vector<int>(paddingBegin); | ||
std::vector<int> destOrigin = std::vector<int>(paddingBegin); | ||
std::vector<int> regionSize = inputDims; |
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.
Same here - no need to make a copy of inputDims, just use it directly. I think you're trying to make it more clear what each vector is representing in the copyTensorRegion call but there's no need since the API documents it very clearly already.
Thanks for your efforts! I just update the code. |
The implemented operator supports padding in all dimensions and asymmetric padding. And it is implemented in software only, no accelerated version is available. The code is tested in both CPP and python API. In the CPP test, I test it with different input sizes (2D, 4D) and different padding patterns (asymmetric, symmetric). The python API testing is mainly focused on it can work in a graph.
Example Python code.
Fixes issue #94.