-
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
Design doc for operator attribute #2606
Design doc for operator attribute #2606
Conversation
doc/design/attribute.md
Outdated
```cpp | ||
class OperatorBase { | ||
public: | ||
Error InitializeAttribute(const AttributeReader& attrs) = 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.
virtual
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.
Oops.
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.
doc/design/attribute.md
Outdated
explicit AttributeReader(const AttributeMap& attrs) : attrs_(attrs) {} | ||
|
||
template <typename T> | ||
Error __must_check Get(const std::string& attributeName, T* attr) const; |
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.
what will attr
point to if Error happens?
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.
The attr is always pointing to an outside variable. The pointer is not modified in Get
method.
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.
oh, so the value T* attr
point to just not change, is that right?
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.
The pointer attr
is will never be changed inside Get
method. The value *attr
will not be changed if an error occurred. The value *attr
will be set to correct value when there is no error.
doc/design/attribute.md
Outdated
public: | ||
Error InitializeAttribute(const AttributeReader& attrs) { | ||
auto err = attrs.Get<float>("scale", &scale_); | ||
if (!err.isOK() && err != "Attribute Not Found") { |
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.
What will happen if err == "Attribute Not Found"
?
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.
If the error is Attribute Not Found
, then the default value is used.
The default value is set in private data member, like float scale_{1.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.
I think If the error is Attribute Not Found, then the default value is used.
should be added to comment.
Another thing is that remind the user to set a default value for their attributes.
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.
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.
LGTM!
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.
It seems that the writing could be shortened. Here is my trial:
An operator could have attributes. For example,
CosineOp
could have a float typed attributescale
, which changes the output range from [-1,1] to [-scale,scale].Attributes is defined by a name and a type. An instance of an attribute has a value of that type.
As part of the network description, attribute need to be serialized. So we need a protobuf message that describes an attribute, say
AttributeProto
.An operator could list its attributes in a data member
std::map<string/*name*/, AttributeProto*>
.
doc/design/attribute.md
Outdated
@@ -0,0 +1,125 @@ | |||
# Design Doc about operator attribute | |||
|
|||
## background |
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.
Background
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.
doc/design/attribute.md
Outdated
@@ -0,0 +1,125 @@ | |||
# Design Doc about operator attribute |
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.
Design Doc: Operator Attributes
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.
doc/design/attribute.md
Outdated
|
||
## background | ||
|
||
In a neural network, each operator could contain some configurable attributes. For example, a cosine similarity operator may contain an attribute named `scale`. The default cosine similarity returns a value in range [-1.0, 1.0]. But the user can set range scale manually, e.g., user set `scale=5.0`, then that cosine operator will return a value in the range [-5.0, 5.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.
In a neural network, each operator could contain some configurable attributes.
==>
Operators have attributes.
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.
doc/design/attribute.md
Outdated
|
||
In a neural network, each operator could contain some configurable attributes. For example, a cosine similarity operator may contain an attribute named `scale`. The default cosine similarity returns a value in range [-1.0, 1.0]. But the user can set range scale manually, e.g., user set `scale=5.0`, then that cosine operator will return a value in the range [-5.0, 5.0]. | ||
|
||
The configurable attributes could be various types. Some operators need `float` value to configure; some need `string` value. We need a data structure to represent different types. |
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.
be of various types
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.
doc/design/attribute.md
Outdated
|
||
In a neural network, each operator could contain some configurable attributes. For example, a cosine similarity operator may contain an attribute named `scale`. The default cosine similarity returns a value in range [-1.0, 1.0]. But the user can set range scale manually, e.g., user set `scale=5.0`, then that cosine operator will return a value in the range [-5.0, 5.0]. | ||
|
||
The configurable attributes could be various types. Some operators need `float` value to configure; some need `string` value. We need a data structure to represent different types. |
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.
The configurable attributes could be various types. Some operators need
float
value to configure; some needstring
value. We need a data structure to represent different types.
==>
Each attributes has a name and a type. We are going to describe them in a protobuf message.
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.
doc/design/attribute.md
Outdated
|
||
The configurable attributes could be various types. Some operators need `float` value to configure; some need `string` value. We need a data structure to represent different types. | ||
|
||
Each operator contains different configurable attributes. The names of attributes are not same. We need an associate map from attribute name to attribute value for `Operator`. |
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.
Each operator contains different configurable attributes. The names of attributes are not same. We need an associate map from attribute name to attribute value for
Operator
.
==>
We plan to list attributes of an operator in an associate map:
map<string, Attribute*>
.
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.
In protobuf, the type of Attributes
is a map<string, Attribute> attrs;
But in each op class, it stores simple strong typed values as member data.
class CosineOp {
public:
void Init(const AttributeReader& reader) {
scale_ = reader.Get<float>("scale")
}
private:
float scale_;
};
If we extract scale
from protobuf map every time when we use it, it is very slow.
doc/design/attribute.md
Outdated
``` | ||
|
||
There are two methods in `AttributeReader`: `Get` and `GetArray`. `GetArray` is used for `ListValue`, and `Get` is used for the rests. The user should invoke either of them when he wants to get an Attribute value from `AttributeMap`. | ||
|
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.
The ListValue message is defined as:
message ListValue {
repeated int32 ints = 1;
repeated float floats = 2;
repeated string strings = 3;
}
And GetArray method only has one template parameter T. So, how the GetArray get ListValue with various type.
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.
Only one of ListValue
field should be set. The protobuf 3 currently cannot make repeated
field inside a oneof
. So just make all ListValue a single message.
The tensorflow has a similar implementation
0865ca4
to
84843d5
Compare
* Simplify background description. * Not using Error as return value, use PADDLE_ENFORCE
84843d5
to
e3a63d7
Compare
…ute_design_for_op
679bf73
to
9bcd74c
Compare
}; | ||
|
||
/// Implementation of Contain | ||
namespace details { |
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 named details
?
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.
Because we need define some function that should not be used by the user but can only be defined in the header file.
So mark them in namespace details
could tell the user not to use them directly.
paddle/framework/attribute_reader.h
Outdated
* mismatch or not contains that name. | ||
*/ | ||
template <typename T> | ||
bool Contain(const std::string& name) const; |
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.
It seems that most lib name this Contians
https://docs.oracle.com/javase/7/docs/api/java/util/List.html#contains(java.lang.Object)
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.
reader.GetArray("listInt", &actual); | ||
reader.GetArray("listStr", &actualStr); | ||
ASSERT_EQ(expected, actual); | ||
ASSERT_EQ(expectedStr, actualStr) |
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.
need ";" at the end of this line
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 found it. Done.
Add copy right and comments Update design doc
9bcd74c
to
911113d
Compare
std::copy(field.begin(), field.end(), std::back_inserter(*vec)); \ | ||
} | ||
|
||
ATTR_GETARRAY_IMPL(int, ints); |
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.
to be with the same name style with ATTR_READER_GETVALUE_IMPL
and ATTR_READER_ISTYPE_IMPL
ATTR_GETARRAY_IMPL => ATTR_READER_GETARRAY_IMPL
* throw | ||
*/ | ||
template <typename T> | ||
T Get(const std::string& name) const; |
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.
just discuss, do we need a
T Get(const std::string& name, const T& default) const;
to set a default value
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 see the design doc, people can use Contains
to set a default value. So this is not a problem.
} | ||
|
||
oneof value { | ||
ListValue list = 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.
The oneof directive doesn't seem help much here -- it can locate a field in value
but it cannot help us locate a field in ListValue
. If our C++ code would still have to check which field is the one in ListValue
, it would be easier just use the old plain syntax:
syntax = "proto3";
message Attribute {
enum Type {
INTS = 0;
FLOATS = 1;
STRINGS = 2;
INT = 3;
FLOAT = 4;
STRING = 5;
}
Type type = 1;
repeated int32 ints = 2;
repeated float floats = 3;
repeated string strings = 4;
int32 int = 5;
float float = 6;
string string = 7;
}
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.
syntax = "proto3";
message Attribute {
repeated int32 ints = 1;
repeated float floats = 2;
repeated string strings = 3;
optional int32 int = 4;
optional float float = 5;
optinoal string string = 6;
}
Maybe the Type
is not useful, because we could use optional
field and has_xxx()
to detect AttributeType.
|
||
There are two frameworks implement `Attribute` concept in `protobuf`. They are [`caffe2`](https://github.com/caffe2/caffe2/blob/master/caffe2/proto/caffe2.proto#L98) and [`tensorflow`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/framework/attr_value.proto#L16). | ||
|
||
* Caffe2 uses `proto2` syntax. It treats all attributes as a list, and each attribute contains a `name`. Each time caffe2 read an attribute is searching a variable in a list. It is slow if the number of attributes is large. Caffe2 also mark all field as `optional`. It doesn't ensure `one of` attribute value is set. |
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.
Surprise to know that Caffe2 searches attributes in a protobuf list. The right way is to load from protobuf field repeated Attrbribute attrs = x;
into C++ data structure map<string, Attribute>
and search in the C++ data structure.
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.
It's my mistake, Caffe2 is loading all attribute into memory first.
} | ||
``` | ||
|
||
In `OperatorDescription` message, there should be a field like this: |
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.
OperatorDescription => OperatorDesc ?
|
||
```cpp | ||
using AttributeMap = google::protobuf::Map<std::string, Attribute>; | ||
class AttributeReader { |
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.
AttributeReader => Attributes
This is not a reader, it holds and searches attributes as well.
T Get(const std::string& name) const; | ||
|
||
// Get attribute with a array of type T, which name is `name` | ||
template <typename T> |
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.
What if an attribute was marked int in the protobuf message, but the user tries to retrieve its string value? How can we check and find such kind of errors?
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.
Because we use optional
, we can detect which type is set in protobuf, and assert fail when type mismatch.
// Is that `name` attribute with type T in map or not. | ||
// T could be int, float, string and std::vector of them | ||
template <typename T> | ||
bool Contains(const std::string& name) const; |
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.
Contains => IsType
For example,
if (IsType<std::vector<string>>("name")) {
...
}
Actually, maybe it would be easier to have Has
and Type
instead
class Attributes {
public:
bool Has(const std::string& name) const {
return attrs_.find(name) != attrs_.end();
}
const std::type_info& Type() const(const std::string& name) const {
PADDLE_ENFORCE(Has(name));
switch (attrs_[name].type()) {
case paddle::framework::Attribute::INTS:
return typeid(int);
...
}
}
};
class CosineOp : public OperatorBase { | ||
public: | ||
void InitializeAttribute(const AttributeReader& attrs) { | ||
if (attrs.Contains<float>("scale")) { |
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 doesn't look like a smart idea -- every time we change an attribute in the private:
section, we must also change this function accordingly.
}; | ||
``` | ||
|
||
`InitializeAttribute` will be invoked by `CreateOperator`. Since `InitializeAttribute ` could throw an EnforceNotMet, a `unique_ptr` is used to make code exception-safe. |
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.
It seems that this document is not complete. I cannot find the part describing macros that define attributes of an operator?
``` | ||
|
||
`InitializeAttribute` will be invoked by `CreateOperator`. Since `InitializeAttribute ` could throw an EnforceNotMet, a `unique_ptr` is used to make code exception-safe. | ||
|
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, I am curious about how the Python API can fill in the protobuf field OperatorDesc::attrs
, so that the C++ code could load.
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.
we will discuss more about operator attr and registry
Closed because it has been done. |
The design doc for operator attribute. Maybe here is better for review.