scope.md 6.5 KB
Newer Older
Y
Yu Yang 已提交
1
# Design of Scope in Paddle
Q
qiaolongfei 已提交
2

Y
Yu Yang 已提交
3
## Overview
Q
qiaolongfei 已提交
4

Y
Yibing Liu 已提交
5
Scope is an important concept in programming languages, which defines a program region that a set of bindings between names and entities applies. In a specific scope, a valid name is uniquely associated with an entity, such as a variable. And in another scope, this name may refer to other entity or nothing at all. It clearly restricts the visibility and validity of names in a program. Hence **Scope** is introduced to PaddlePaddle to manage variables in context. But different from the original abstract concept, Scope now becomes an object with two important attributes:
Q
qiaolongfei 已提交
6

Y
Yu Yang 已提交
7 8
- Scope is an association of a name to variable.
- Variables in a parent scope can be retrieved from local scope.
Y
Yibing Liu 已提交
9 10

A detailed explanation of these two attributes goes as following.
Q
qiaolongfei 已提交
11

Y
Yu Yang 已提交
12

Y
Yu Yang 已提交
13
## Scope is an association of a name to variable.
Y
Yu Yang 已提交
14

Y
Yu Yang 已提交
15
Scope is an association of a name to variable. All variables belong to `Scope`. You need to specify a scope to run a Net, i.e., `net.Run(&scope)`. One net can run in different scopes and update different variable in the scope.
16

Q
qiaolongfei 已提交
17

Y
Yu Yang 已提交
18
1. Scope only contains a map of a name to variable.
Q
qiaolongfei 已提交
19

Y
Yu Yang 已提交
20 21 22 23 24
   All parameters, data, states in a Net should be variables and stored inside a scope. Each op should get inputs and outputs to do computation from a scope, such as data buffer, state(momentum) etc.

1. Variable can only be created by Scope and a variable can only be got from Scope. User cannot create or get a variable outside a scope. This is a constraints of our framework, and will keep our framework simple and clear.

1. Scope only contains methods that are used to Create and Get Variables. Scope do not contain Operators and have no information to run them.
Y
Yu Yang 已提交
25
    `Net` is designed to drive the computation and Scope only contains a map of variables. There is no computation logic inside a `Scope`. Scope just handles the lifetime management of variables.
26 27 28 29
    - `Create` is used to create a Variable by its name and add the mapping relation.
    - `Get` is used to find a Variable by name.

1. Every variable only belongs to one certain Scope.
Q
qiaolongfei 已提交
30

Y
Yu Yang 已提交
31
   Variable can not belong to many scopes. If you want to use variables from parent scope, you can use `parent scope`.
Q
qiaolongfei 已提交
32

Y
Yu Yang 已提交
33
1. Scope should destruct all Variables inside it when itself is destructed. User can never store `Variable` pointer somewhere else. 
Q
qiaolongfei 已提交
34

Y
Yu Yang 已提交
35
   Because Variable can only be got from Scope. When destroying Scope, we also need to destroy all the Variables in it. If user store `Variable` pointer to private data member or some global variable, the pointer will be a invalid pointer when associated `Scope` is destroyed.
Q
qiaolongfei 已提交
36 37 38 39 40 41 42 43

```cpp
class Scope {
 public:
  Variable* CreateVariable(const std::string& name);
  const Variable* GetVariable(const std::string& name) const;

 private:
Q
qiaolongfei 已提交
44
    std::unordered_map<std::string, std::unique_ptr<Variable>> vars_;
Q
qiaolongfei 已提交
45 46
};
```
Y
Yu Yang 已提交
47

Q
qiaolongfei 已提交
48

Y
Yu Yang 已提交
49 50
## Parent scope and local scope

Y
Yu Yang 已提交
51
Just like [scope](https://en.wikipedia.org/wiki/Scope_(computer_science)) in programming languages, `Scope` in the neural network can also be a local scope. There are two attributes about local scope.
Y
Yu Yang 已提交
52

Y
Yu Yang 已提交
53
1.  We can create local variables in a local scope. When that local scope are destroyed, all local variables should also be destroyed.
Y
Yu Yang 已提交
54
2.  Variables in a parent scope can be retrieved from local scopes of that parent scope, i.e., when user get a variable from a scope, it will try to search this variable in current scope. If there is no such variable in the local scope, `scope` will keep searching from its parent, until the variable is found or there is no parent.
Y
Yu Yang 已提交
55 56 57

```cpp
class Scope {
Y
Yu Yang 已提交
58
 public:
Y
Yu Yang 已提交
59 60
  Scope(const std::shared_ptr<Scope>& scope): parent_(scope) {}

Y
Typo  
Yu Yang 已提交
61
  Variable* GetVariable(const std::string& name) const {
Y
Yu Yang 已提交
62 63 64 65
    Variable* var = GetVarLocally(name);
    if (var != nullptr) {
      return var;
    } else if (parent_ != nullptr) {
Y
Typo  
Yu Yang 已提交
66
      return parent_->GetVariable(name);
Y
Yu Yang 已提交
67 68 69 70 71
    } else {
      return nullptr;
    }
  }

Y
Yu Yang 已提交
72
 private:
Y
Yu Yang 已提交
73 74 75
  std::shared_ptr<Scope> parent_ {nullptr};
};
```
Y
Yu Yang 已提交
76

Y
Yu Yang 已提交
77
In `Scope` class, there is a private data member called `parent_`. `parent_` is a smart pointer to its parent scope. When user `Get` a variable by its `name`, the `name` will be searched inside the current scope. If the variable cannot be found locally and parent scope is not a `nullptr`, the variable will be searched inside that parent scope. `parent_` pointer's default value is `nullptr`. It means that the scope is a global scope when `parent_` is nullptr.
Y
Yu Yang 已提交
78

Y
Yu Yang 已提交
79
A local scope is very useful when we implement Recurrent Neural Network. Each timestep of an RNN should be a `Net`. Each `Net` of timestep (`StepNet` for short) should use an independent local scope. Just like variables in a while loop is inside a local scope in programming languages. By using a single `StepNet` and changing local scope, we can implement an RNN easily.
Y
Yu Yang 已提交
80

Y
Yu Yang 已提交
81
# Interface Design
Y
Yu Yang 已提交
82

Y
Yu Yang 已提交
83 84
```cpp
class Variable {
Y
Yu Yang 已提交
85
 private:
Y
Yu Yang 已提交
86 87 88 89 90
  Variable() = default;
  friend class Scope;
};

class Scope {
Y
Yu Yang 已提交
91
 private:
Y
Yu Yang 已提交
92 93
  Scope(const std::shared_ptr<Scope>& parent = nullptr);

Y
Yu Yang 已提交
94
 public:
Y
Yu Yang 已提交
95 96
  static std::shared_ptr<Scope> Create(const std::shared_ptr<Scope>& parent = nullptr);

Y
Yu Yang 已提交
97
  // return nullptr if not found.
98
  Variable* GetVariable(const std::string& name) const;
Y
Yu Yang 已提交
99 100 101 102

  // return Error if already contains same name variable.
  Error CreateVariable(const std::string& name);

Y
Yu Yang 已提交
103
 private:
Y
Yu Yang 已提交
104
  std::shared_ptr<Scope> parent_;
Y
Typo  
Yu Yang 已提交
105
  std::unordered_map<std::string, std::unique_ptr<Variable>> vars_;
Y
Yu Yang 已提交
106 107 108 109 110 111 112 113
};
```
## Only scope can create a variable

To ensure `only scope can create a variable`, we should mark `Variable`'s constructor as a private member function, and Scope is a friend class of Variable. And then only `CreateVariable` can construct `Variable`.

## When scope destroyed, all variables inside this scope should be destroyed together

114
The scope hold unique pointers for all variables. User can `GetVariable` from scope, but he should not hold this pointer as a member variable. Because when scope is destroyed, all variables inside this scope will be destroyed together.
Y
Yu Yang 已提交
115 116 117 118 119

## Sharing a parent scope

Local scope contains a `parent_` pointer. It is a linked-list for scopes. Using a `shared_ptr` because when a local scope is using, its parents cannot be destroyed.

Y
Yu Yang 已提交
120 121
Also, as the parent scope is a `shared_ptr`, we can only `Create()` a scope shared pointer. We cannot construct a scope variable, because it cannot be passed to other scope as `parent` pointer.

Y
Yu Yang 已提交
122 123 124
## Orthogonal interface

`GetVariable` will return `nullptr` when `name` is not found. It can be used as `Contains` method. `CreateVariable` will return a `Error` when there is a name conflict locally. Combine `GetVariable` and `CreateVariable`, we can implement `CreateOrGetVariable` easily.