Skip to content

Commit e3a63d7

Browse files
committed
Update Attribute Design
* Simplify background description. * Not using Error as return value, use PADDLE_ENFORCE
1 parent 0d9b9d3 commit e3a63d7

File tree

1 file changed

+21
-38
lines changed

1 file changed

+21
-38
lines changed

doc/design/attribute.md

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
1-
# Design Doc about operator attribute
1+
# Design Doc: Operator Attributes
22

3-
## background
3+
## Background
44

5-
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].
5+
An operator could have attributes. For example, CosineOp could have a float typed attribute scale, which changes the output range from [-1,1] to [-scale,scale]. The default value of scale is `1.0`.
66

7-
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.
7+
Attributes is defined by a name and a type. An instance of an attribute has a value of that type.
88

9-
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`.
9+
As part of the network description, attribute need to be serialized. So we need a protobuf message that describes an attribute, say `Attribute`.
1010

11-
Also as we want to use `protobuf` to serialize and deserialize our model, we need to implement the attribute value and the associated map from attribute name to attribute value in `protobuf`.
12-
13-
In conclusion, there are four things we know as background.
14-
15-
1. We need an attribute type for Operator.
16-
1. That attribute type could represent different types.
17-
1. That attribute value should be associated with an attribute name, like a map<string, Attribute>.
18-
1. We need to implement them in `protobuf`.
11+
An operator could parse the Attribute and save them into its private data member.
1912

2013
## Protobuf Implementation
2114

@@ -65,12 +58,14 @@ class AttributeReader {
6558
public:
6659
explicit AttributeReader(const AttributeMap& attrs) : attrs_(attrs) {}
6760

61+
// return false if attribute is not found.
62+
// For typemismatch, it just called `ENFORCE` and throw exception.
6863
template <typename T>
69-
Error __must_check Get(const std::string& attributeName, T* attr) const;
64+
bool Get(const std::string& attributeName, T* attr) const;
7065

66+
// return false if attribute is not found.
7167
template <typename T>
72-
Error __must_check GetArray(const std::string& attributeName,
73-
std::vector<T>* array) const;
68+
bool GetArray(const std::string& attributeName, std::vector<T>* array) const;
7469

7570
private:
7671
const AttributeMap& attrs_;
@@ -86,40 +81,28 @@ Each operator stores its attributes. For faster attribute access, we should not
8681
```cpp
8782
class OperatorBase {
8883
public:
89-
virtual Error InitializeAttribute(const AttributeReader& attrs) = 0;
84+
virtual void InitializeAttribute(const AttributeReader& attrs) = 0;
9085
};
9186
9287
class CosineOp : public OperatorBase {
9388
public:
94-
Error InitializeAttribute(const AttributeReader& attrs) {
95-
auto err = attrs.Get<float>("scale", &scale_);
96-
97-
// ignore AttributeNotFound because scale_ is default = 1.0
98-
if (!err.isOK() && err != "Attribute Not Found") {
99-
return err;
100-
}
101-
if (scale_ <= 0.0f) {
102-
return Error("Scale of cosine op should be larger than 0.0");
103-
}
104-
return Error(); // OK;
89+
void InitializeAttribute(const AttributeReader& attrs) {
90+
attrs.Get<float>("scale", &scale_);
91+
PADDLE_ENFORCE(scale_ > 0.0f, "Scale of consine op should be larger than 0.0");
10592
}
10693
10794
private:
10895
float scale_ {1.0};
10996
};
11097
```
11198

112-
When `NetworkBase` invokes `CreateOperator(const OperatorDescription& desc)`, it create an operator first. Then `CreateOperator` will invoke `InitializeAttribute` and returns error code. The implementation of `CreateOperator` could be
99+
When `NetworkBase` invokes `CreateOperator(const OperatorDescription& desc)`, it create an operator first. Then `CreateOperator` will invoke `InitializeAttribute·. The implementation of `CreateOperator` could be
113100

114101
```cpp
115-
Error CreateOperator(const OperatorDescription& desc, OperatorBase** ptr) {
116-
*ptr = OperatorRegister.create(desc.type(), desc.inputs(), desc.outputs());
117-
Error err = (*ptr) -> InitializeAttribute(desc.attrs());
118-
if (!err.isOK()) {
119-
delete (*ptr);
120-
}
121-
return err;
102+
std::unique_ptr<OperatorBase> CreateOperator(const OperatorDescription& desc) {
103+
std::unique_ptr<OperatorBase> op(OperatorRegister.Create(
104+
desc.type(), desc.inputs(), desc.outputs()));
105+
op->InitializeAttribute(AttributeReader(desc.attrs()));
106+
return std::move(op);
122107
}
123108
```
124-
125-
`InitializeAttribute` will validation the user's configuration, and might return an `Error`. It is clearer to invoke the method `InitializeAttribute` and return an `Error` than let each operator's constructor implement this logic because the constructor cannot return a value.

0 commit comments

Comments
 (0)