diff --git a/doc/api/v2/config/networks.rst b/doc/api/v2/config/networks.rst
index 6e813ab1a8..048379cf01 100644
--- a/doc/api/v2/config/networks.rst
+++ b/doc/api/v2/config/networks.rst
@@ -125,3 +125,8 @@ simple_attention
:members: simple_attention
:noindex:
+dot_product_attention
+---------------------
+.. automodule:: paddle.v2.networks
+ :members: dot_product_attention
+ :noindex:
diff --git a/doc/design/executor.md b/doc/design/executor.md
new file mode 100644
index 0000000000..b5fb6c5c3c
--- /dev/null
+++ b/doc/design/executor.md
@@ -0,0 +1,23 @@
+# Executor Design Doc
+
+## Motivation
+
+We use executor to do the runtime evaluation of a `ProgramDesc`.
+
+## Overview
+
+An executor takes a `ProgramDesc`, a `block_id` and a `Scope`. The `ProgramDesc` is a list of blocks and each block contains the protobuf definition of all the parameters and operators. The `block_id` specifies the entrance block. And the `Scope` is the container of all the variable instance, which is persistent throughout different runs.
+
+### What does executor do?
+
+It evaluates all the operators in the `block_id`th block of a `ProgramDesc`.
+
+### What does executor NOT do?
+
+It does not do runtime optimization, meaning intelligently parse the dependency of each op a choose which one to be run and in which order they should be run.
+
+It does not do graph partitioning, meaning dividing the `ProgramDesc` into several small pieces and executing them on different devices.
+
+## Implementation
+
+`Executor` evaluates a `ProgramDesc`. Essentially, it instantiates Variables and Operators, then run all the operators in sequence. [[code]](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/executor.cc)
diff --git a/doc/design/images/feed_forward.png b/doc/design/images/feed_forward.png
new file mode 100644
index 0000000000..d312371a04
Binary files /dev/null and b/doc/design/images/feed_forward.png differ
diff --git a/doc/design/images/feed_forward_regularized.png b/doc/design/images/feed_forward_regularized.png
new file mode 100644
index 0000000000..677e99bfd9
Binary files /dev/null and b/doc/design/images/feed_forward_regularized.png differ
diff --git a/doc/design/images/l1_regularization.png b/doc/design/images/l1_regularization.png
new file mode 100644
index 0000000000..e1b9c7a44f
Binary files /dev/null and b/doc/design/images/l1_regularization.png differ
diff --git a/doc/design/images/l2_regularization.png b/doc/design/images/l2_regularization.png
new file mode 100644
index 0000000000..d5c2fcbc2c
Binary files /dev/null and b/doc/design/images/l2_regularization.png differ
diff --git a/doc/design/images/loss_equation.png b/doc/design/images/loss_equation.png
new file mode 100644
index 0000000000..14212ec8d3
Binary files /dev/null and b/doc/design/images/loss_equation.png differ
diff --git a/doc/design/infer_var_type.md b/doc/design/infer_var_type.md
new file mode 100644
index 0000000000..d9d5397bec
--- /dev/null
+++ b/doc/design/infer_var_type.md
@@ -0,0 +1,78 @@
+# Design Doc: InferVarType
+
+## The Problem Posed
+
+The variable in our design can hold variant types. Such as `LoDTensor` and `SelectedRows`. An operator should be able to inference the variable types of its output.
+
+For example, a `lookup table` operator takes two `LoDTensor`; one is a float tensor as the embedding table, the other is an int tensor as word ID. The gradient operator of `lookup table` will generate a `SelectedRows` as its output. A `sum` operator can take both `LoDTensor` and `SelectedRows` as its inputs and will generate a `LoDTensor` if any of its inputs is `LoDTensor`, otherwise, the `sum` operator will generate `SelectedRows` as its output.
+
+The variable type will be constant at runtime. Every variable's type can either be set by the user (input data and parameter) or be inferred by the operator in compile time.
+
+## Proposed Solution
+
+The `InferVarType` is a compile-time function which is registered to each operator. The inferface of that function is:
+
+
+```c++
+using InferVarTypeFN = std::function<
+ void (const OpDescBind& /*op_desc*/, BlockDescBind* /*block*/)>;
+```
+
+It takes an operator description as its input and will write the output variable type and store them in block description.
+
+The `InferVarTypeFN` will be registered in `OpInfo`, to replace `infer_var_type_` field. The `OpInfo` should be
+
+```cpp
+struct OpInfo {
+ InferVarTypeFN infer_var_type_;
+ ...
+};
+```
+
+The default `InferVarType` will set output type as `LoDTensor`. It can be done by `GetInferVarType()`.
+
+```cpp
+void DefaultInferVarType(const OpDescBind& op_desc, BlockDescBind* block) {
+ // set the output type of variable as `LoDTensor`.
+ // ...
+}
+
+struct OpInfo {
+ InferVarTypeFN infer_var_type_;
+ InferVarTypeFN GetInferVarType() const {
+ if (infer_var_type_) {
+ return infer_var_type_;
+ } else {
+ return DefaultInferVarType;
+ }
+ }
+};
+```
+
+## Register InferVarType
+
+We provide a thin base class for registering an `InferVarTypeFN`. To use a base class will ease the implementation of registry since we can detect the registry entry is an `InferVarTypeFN` or not.
+
+```cpp
+class VarTypeInferer {
+public:
+ virtual void operator()(const OpDescBind& op_desc, BlockDescBind* block) const = 0;
+}
+```
+
+Operator developers can write the specialize `VarTypeInferer` as follow.
+
+```cpp
+class SpecialVarTypeInferer : public VarTypeInferer {
+public:
+ virtual void operator()(const OpDescBind& op_desc, BlockDescBind* block) const {
+ // .. own logic
+ }
+}
+```
+
+Then user can register the `InferVarType` just like `GradOpDescMaker` and `OpInfoMaker`.
+
+```
+REGISTER_OPERATOR(some_op, OpType, SpecialVarTypeInferer, ...);
+```
diff --git a/doc/design/python_api.md b/doc/design/python_api.md
index 56ae1d925a..cb5fdc765b 100644
--- a/doc/design/python_api.md
+++ b/doc/design/python_api.md
@@ -179,40 +179,104 @@ init_attr={
`optimize_op_attrs` is not in the `VarDesc` message, but kept in the Python instance, as it will be used in the Python space when creating the optimize operator's `OpDesc`, and will be in the `OpDesc` message.
-## Layer Functions
+## Layer Function
-A layer is a Python function that creates some operators and variables. Layers simplify the work of application programmers.
+A layer is a Python function that creates some operators and variables. Layers simplify the work of application programmers.
-### Data Layer
+Layer functions take `Variable` and configuration parameters as its input and return the output variable(s).
+
+For example, `FullyConnected` take one or more variable as its input. The input could be input data or another layer's output. There are many configuration options for a `FullyConnected` layer, such as layer size, activation, parameter names, initialization strategies of parameters, and so on. The `FullyConnected` layer will return an output variable.
+
+
+### Necessity for reusing code between layer functions
+
+There are a lot of code that can be reused. Such as
+
+* Give the default value of configuration. e.g., default initialize strategy for parameters is uniform random with `min = -1.0`, `max = 1.0`. and default initialize strategy for bias is to fill zero.
+* Append the activation operator.
+* Create a temporary variable.
+* Create parameter.
+* Generate a unique name.
+* Add a bias.
+* ...
+
+A mechanism to reuse code between layer functions is necessary. It will be around [150 lines of code](https://github.com/PaddlePaddle/Paddle/pull/4724/files#diff-823b27e07e93914ada859232ae23f846R12) if we write a `FullyConnected` layer without any helper functions.
+
+
+
+### Comparision between global functions and helper class
+
+The `FullyConnected` layer will be as follow when we provide global functions:
```python
-def data_layer(name, type, column_name):
- block = the_current_program.glolal_block()
- var = block.create_global_var(
- name=name,
- shape=[None] + type.dims(),
- dtype=type.dtype)
- block.prepend_operator(block,
- type="Feed",
- inputs = None,
- outputs = [var],
- {column_name: column_name})
- return var
+def fc_layer(input, size, param_attr=None, bias_attr=None, act=None, name=None):
+ if name is None:
+ name = unique_name("fc")
+ input = multiple_input(input)
+ param_attr = default_param_attr(param_attr)
+ param_attr = multiple_param_attr(param_attr, len(input))
+
+ # mul
+ mul_results = []
+ for ipt, attr in zip(input, param_attr):
+ shape = ipt.shape[1:] + [size]
+ w = g_program.global_block().create_parameter(shape, ipt.dtype, name, attr)
+ tmp = create_tmp_var(name)
+ g_program.current_block().append_op("mul", {ipt, w}, {tmp})
+ mul_results.append(tmp)
+
+ # add sum
+ ...
+ # add bias
+ ...
+ # add activation
+ ...
+ return out
```
-The input to the feed operator is a special variable in the global scope, which is the output of [Python readers](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/design/reader/README.md).
+We can provide many helpers functions for layer developers. However, there are several disadvantages for global helper functions:
+
+1. We need a namespace for these methods, then layer developers can quickly figure out what method they can use.
+2. Global functions will force layer developers to pass its parameter time by time.
+
+So we provide a helper class, `LayerHelper`, to share code between layer functions. The `FullyConnected` Layer will be as follow.
+
+```python
+def fc_layer(input, size, param_attr=None, bias_attr=None, act=None, name=None):
+ helper = LayerHelper(locals()) # pass all parameter to LayerHelper
+
+ mul_results = []
+ for ipt, param in helper.iter_multiple_input_and_param():
+ w = helper.create_parameter(shape=ipt.shape[1:] + [size], dtype = ipt.dtype)
+ tmp = helper.create_tmp_variable()
+ helper.append_op('mul', {ipt, w}, {tmp})
+ mul_results.append(tmp)
+
+ pre_bias = helper.add_sum(mul_results)
+ pre_activation = helper.add_bias(pre_bias)
+ return helper.add_activation(pre_activation)
+```
+
+We not only use the fewer lines of code to write `fc_layer` but also make the code clearer to understand. At the same time, layer developers can figure out what function they can invoke by typing `helper.` in a python editor.
+
+
+### Implementation of layer helper
-### FC Layer
+We just keep all parameters of a layer function as a dictionary in layer helper as a private data member. Every method of layer helper will look up the dictionary after it is invoked. In that way, we can implement a layer helper for all layer functions even some layer does not contain some operator. For example, The `activation` is used by the FullyConnected layer or convolution layers, but a cross-entropy layer does not use it. The example code of `add_activation` are:
```python
-def fc_layer(input, size, ...):
- block = program.current_block()
- w = block.create_parameter(...)
- b = block.create_parameter(...)
- out = block.create_var()
- op = block.append_operator("FC", X=input, W=w, b=b, out=out)
- out.writer = op
- return out
+class LayerHelper(object):
+ def __init__(self, **kwargs): # kwargs is short for `keyword arguments`
+ self.kwargs = kwargs
+
+ def add_activation(self, input_var):
+ act = self.kwargs.get("act", None) # default value is None
+ if act is None: # do nothing if no act
+ return input_var
+
+ tmp = self.create_tmp_var(self)
+ self.append_op(type=act, input=input_var, output=tmp)
+ return tmp
```
## Optimizer
diff --git a/doc/design/regularization.md b/doc/design/regularization.md
new file mode 100644
index 0000000000..703a9fbdd4
--- /dev/null
+++ b/doc/design/regularization.md
@@ -0,0 +1,103 @@
+# Regularization in PaddlePaddle
+
+## Introduction to Regularization
+A central problem in machine learning is how to design an algorithm that will perform well not just on the training data, but also on new data. Many strategies are used by machine learning practitioners to reduce the test error, possibly at the expense of increased training error. These strategies are collectively known as **regularization**.
+
+### Parameter Norm Penalties
+Most common regularization approaches in deep learning are based on limiting the capacity of the models by adding a parameter norm penalty to the objective function `J`. This is given as follows:
+
+![](./images/loss_equation.png)
+
+The parameter `alpha` is a hyperparameter that weights the relative contribution of the norm penalty term, `omega`, relative to the standard objective function `J`.
+
+The most commonly used norm penalties are the L2 norm penalty and the L1 norm penalty. These are given as follows:
+
+##### L2 Regularization:
+![](./images/l2_regularization.png)
+
+##### L1 Regularization
+![](./images/l1_regularization.png)
+
+A much more detailed mathematical background of reguilarization can be found [here](http://www.deeplearningbook.org/contents/regularization.html).
+
+
+## How to do Regularization in PaddlePaddle
+
+On surveying existing frameworks like Tensorflow, PyTorch, Caffe, etc, it can be seen that there are 2 common approaches of doing regularization:
+
+1. Making regularization a part of the optimizer using an attribute like `weight_decay` that is used to control the scale of the L2 Penalty. This approach is used in PyTorch as follows:
+ ```python
+ opt = torch.optim.SGD(params, lr=0.2, weight_decay=0.2)
+ ```
+ At every optimization step, this code will add the gradient of the L2 Norm of the params to the gradient of the params with respect to the loss function. This can seen in the following code snippet:
+ ```python
+ if weight_decay != 0:
+ d_p.add_(weight_decay, p.data)
+ ```
+ This is a very restyrictive way of doing regularization and does not give the users enough flexibility.
+
+ **Advantages**:
+ - It is easy to implement for us.
+ - Faster execution of backward. However, it can be done manually by advanced users too.
+
+ **Disadvantages**:
+ - Not flexible for other regularizations such as L1/L0 regularization.
+ - Does not allow for different regularization coefficient for different parameters. For example, in most models, ony the weight matrices are regularized and the bias vectors are unregularized.
+ - Tightly coupled optimizer and regularization implementation.
+
+
+2. Adding regularization ops to the graph through Python API. This approach is used by Tensorflow and Caffe. Using this approach, we manually add regularization ops to the graph and then add the regularization loss to the final loss function before sending them to the optimizer.
+
+ **Advantages**:
+ - Allows for greater flexibility to the users of Paddle. Using this approach, the users can put different regularization to different parameters and also choose parameters that are not a part of regularization.
+ - Makes it easy for the users to customize and extend the framework.
+
+ **Disadvantages**:
+ - Implementation requires comprehensive design and time.
+
+## Proposal for Regularization in PaddlePaddle
+
+### Low-Level implementation
+
+In the new design, we propose to create new operations for regularization. For now, we can add 2 ops thgat correspond to the most frequently used regularizations:
+- L2_regularization_op
+- L1_regularization_op
+
+These ops can be like any other ops with their own CPU/GPU implementations either using Eigen or separate Cpu and GPU kernels. As the initial implementation, we can implement their kernels using Eigen following the abstraction pattern implemented for [Activation Ops](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/operators/accuracy_op.h). This abstraction pattern can make it very easy to implement new regularization schemes. other than L1 and L2 norm penalties.
+
+The idea of building ops for regularization is in sync with the refactored Paddle philosophy of using operators to represent any computation unit. The way these ops will be added to the computation graph, will be decided by the [layer functions](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/design/python_api.md#layer-function) in Python API.
+
+### Computation Graph
+
+Below is an example of a really simple feed forward neural network.
+
+![](./images/feed_forward.png)
+
+The Python API will modify this computation graph to add regularization operators. The modified computation graph will look as follows:
+
+![](./images/feed_forward_regularized.png)
+
+### Python API implementation for Regularization
+
+Using the low level ops, `L2_regularization_op` and `L1_regularization_op`, any user can add regularization to their computation graphs. However, this will require a lot of lines of code and we should design Python APIs that support regularization. An example of such an API can be seen in [Keras](https://keras.io/regularizers/). As per the PaddlePaddle [Python API design](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/design/python_api.md), the layer functions are responsible for creating operators, operator parameters and variables. Since regularization is a property of parameters, it makes sense to create these in the layer functions.
+
+#### Creation of Regularization ops
+There are two possibilities for creating the regularization ops:
+1. We create these ops immediately while building the computation graph.
+2. We add these ops in a lazy manner, just before the backward, similar to the way the optimization ops are added.
+
+The proposal is to add these ops in a lazy manner just before the backward pass.
+
+#### Storage of Regularization attributes
+
+Since we want to create the regularization ops in a lazy manner, the regularization attributes (type of regularization and weight of regularization penalty) can be stored as attributes of the [`Parameter`](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/framework/framework.py#L421) class. This is because regularization is a property of the parameters and storing regularization properties with Parameters also allows for shared parameters.
+
+#### High-level API
+
+In PaddlePaddle Python API, users will primarily rely on [layer functions](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/design/python_api.md#layer-function) to create neural network layers. Hence, we lso need to provide regularization functionality in layer functions. The design of these APIs can be postponed for later right now. A good reference for these APIs can be found in [Keras](https://keras.io/regularizers/) and also by looking at Tensorflow in [`tf.contrib.layers`](https://www.tensorflow.org/api_guides/python/contrib.layers).
+
+
+
+
+
+
diff --git a/doc/design/selected_rows.md b/doc/design/selected_rows.md
index 9e6f3b20cb..1a98839a95 100644
--- a/doc/design/selected_rows.md
+++ b/doc/design/selected_rows.md
@@ -1,6 +1,6 @@
# Design Doc: Selected Rows
-`SelectedRows` is a kind of sparse tensor data type, which is designed to support `embedding` operators. The gradient of embedding table is a sparse tensor. Only a few rows are non-zero values in that tensor. It is straightforward to represent the sparse tensor by the following sparse tensor data structure:
+`SelectedRows` is a type of sparse tensor data type, which is designed to support `embedding` operators. The gradient of embedding table is a sparse tensor. Only a few rows are non-zero values in this tensor. It is straight-forward to represent a sparse tensor by the following sparse tensor data structure:
```cpp
class SelectedRows {
@@ -11,7 +11,7 @@ class SelectedRows {
};
```
-The field `height_` shows the first dimension of `SelectedRows`. The `rows` are the indices of which rows of `SelectedRows` are non-zeros. The `value_` field is an N-dim tensor and shape is `[rows.size() /* NUM_ROWS */, ...]`, which supplies values for each row. The dimension of `SelectedRows` satisfies `[height_] + value_.shape[1:]`.
+The field `height_` is the first dimension of `SelectedRows`. The `rows` are the indices of the non-zero rows of `SelectedRows`. The `value_` field is an N-dim tensor of shape `[rows.size() /* NUM_ROWS */, ...]`, which supplies values for each row. The dimension of `SelectedRows` satisfies `[height_] + value_.shape[1:]`.
Suppose that a SelectedRows-typed variable `x` has many rows, but only two of them have values -- row 73 is `[1, 2]` and row 84 is `[3, 4]`, the `SelectedRows` representation would be:
@@ -25,7 +25,7 @@ x = SelectedRow {
## SelectedRows in Protobuf
-`SelectedRows` is a kind of `Variable`. `VarDesc` in protobuf should describe the `SelectedRows` information. Only the tensor dimension of a `SelectedRows` will be described in compile-time since the `rows_` and `value_` are related to training data.
+`SelectedRows` is a type of `Variable`. `VarDesc` in protobuf should describe the `SelectedRows` information. Only the tensor dimension of a `SelectedRows` will be described in compile-time because the `rows_` and `value_` are dependent on the training data.
So we use `TensorDesc` to unify `data_type` and `dims`. A LodTensorDesc contains a `TensorDesc` and `lod_level`. The description of `SelectedRows` is a Tensor description.
```proto
@@ -54,7 +54,7 @@ message VarDesc {
## InferShape for Selected Rows
-Just like `LoD` information, `InferShape` method will inference output tensor type as well. The operator should decide whether its output is a `SelectedRows` or `Dense` tensor.
+Just like `LoD` information, `InferShape` method will infer the output tensor type as well. The operator should decide whether its output is a `SelectedRows` or `Dense` tensor.
For example, the gradient operator of `TableLookup` will always generate `SelectedRows`. Its `InferShape` method should be like following
@@ -68,7 +68,7 @@ void TableLookupGrad::InferShape(context) {
## Sparse Operators
-There are several operators should be written to support `SelectedRows`. They are:
+There are several operators that need to be written to support `SelectedRows`. These are:
-1. Operators which generates `SelectedRows` gradient. e.g. Gradient of `TableLookupOp`.
+1. Operators which generate `SelectedRows` gradient. e.g. Gradient of `TableLookupOp`.
2. Optimize operators which support `SelectedRows` gradient. e.g. `SGD` or `AdaGrad` for `SelectedRows`. However, there should be only one `SGD` operator. `OpWithKernel::Run` should select a suitable kernel for both `dense` tensor or `SelectedRows`.
diff --git a/doc/howto/cross_compiling/cross_compiling_for_android_cn.md b/doc/howto/cross_compiling/cross_compiling_for_android_cn.md
index 90dc84718c..1fc58c37cc 100644
--- a/doc/howto/cross_compiling/cross_compiling_for_android_cn.md
+++ b/doc/howto/cross_compiling/cross_compiling_for_android_cn.md
@@ -1,9 +1,46 @@
# 构建Android平台上的PaddlePaddle库
-用户可通过交叉编译的方式,在用户熟悉的开发平台(Linux,Mac OS X和Windows)上编译Android平台上适用的PaddlePaddle库。
+用户可通过如下两种方式,交叉编译Android平台上适用的PaddlePaddle库:
+- 基于Docker容器的编译方式
+- 基于Linux交叉编译环境的编译方式
+
+## 基于Docker容器的编译方式
+Docker能在所有主要操作系统(包括Linux,Mac OS X和Windows)上运行,因此,使用基于Docker容器的编译方式,用户可在自己熟悉的开发平台上编译Android平台上适用的PaddlePaddle库。
+
+### 构建PaddlePaddle的Android开发镜像
+我们把PaddlePaddle的交叉编译环境打包成一个镜像,称为开发镜像,里面涵盖了交叉编译Android版PaddlePaddle库需要的所有编译工具。
+
+```bash
+$ git clone https://github.com/PaddlePaddle/Paddle.git
+$ cd Paddle
+$ docker build -t username/paddle-android:dev . -f Dockerfile.android
+```
+
+### 编译PaddlePaddle C-API库
+构建好开发镜像后,即可使用开发镜像来编译Android版PaddlePaddle C-API库。
+Android的Docker开发镜像向用户提供两个可配置的参数:
+
+| Argument | Optional Values | Default |
+|-----------------|-------------------------|---------|
+|`ANDROID_ABI` |`armeabi-v7a, arm64-v8a` | `armeabi-v7a` |
+|`ANDROID_API` |`>= 21` | `21` |
+
+- 编译`armeabi-v7a`,`Android API 21`的PaddlePaddle库
+```bash
+$ docker run -it --rm -v $PWD:/paddle -e "ANDROID_ABI=armeabi-v7a" -e "ANDROID_API=21" username/paddle-android:dev
+```
+
+- 编译`arm64-v8a`,`Android API 21`的PaddlePaddle库
+```bash
+$ docker run -it --rm -v $PWD:/paddle -e "ANDROID_ABI=arm64-v8a" -e "ANDROID_API=21" username/paddle-android:dev
+```
+
+执行上述`docker run`命令时,容器默认执行[paddle/scripts/docker/build_android.sh](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/scripts/docker/build_android.sh)脚本。该脚本中记录了交叉编译Android版PaddlePaddle库常用的CMake配置,并且会根据`ANDROID_ABI`和`ANDROID_API`自动构建独立工具链、进行编译和安装。由于arm64架构要求Android API不小于21。因此当`ANDROID_ABI=arm64-v8a`,`ANDROID_API<21`时,Docker容器中将默认使用`Android API 21`的编译工具链。用户可以参考下文**配置交叉编译参数**章节,根据个人的需求修改定制Docker容器所执行的脚本。编译安装结束之后,PaddlePaddle的C-API库将被安装到`$PWD/install_android`目录,所依赖的第三方库同时也被安装到`$PWD/install_android/third_party`目录。
+
+## 基于Linux交叉编译环境的编译方式
本文档将以Linux x86-64平台为例,介绍交叉编译Android平台上适用的PaddlePaddle库的方法和步骤。
-## 准备交叉编译环境
+### 准备交叉编译环境
从源码交叉编译PaddlePaddle,用户需要提前准备好交叉编译环境。Android平台上使用的C/C++交叉编译工具链为[Android NDK](https://developer.android.com/ndk/downloads/index.html?hl=zh-cn),用户可自行前往下载预编译好的版本,也可通过以下命令获取:
@@ -13,18 +50,27 @@ unzip -q android-ndk-r14b-linux-x86_64.zip
```
Android NDK中包含了所有Android API级别、所有架构(arm/arm64/x86/mips)需要用到的编译工具和系统库。用户可根据自己的编译目标架构、所需支持的最低Android API级别,构建[独立工具链](https://developer.android.google.cn/ndk/guides/standalone_toolchain.html?hl=zh-cn)。
-比如:
+
+- 构建`armeabi-v7a`、 `Android API 21`的独立工具链:
```bash
your/path/to/android-ndk-r14b-linux-x86_64/build/tools/make-standalone-toolchain.sh \
- --arch=arm --platform=android-21 --install-dir=your/path/to/my_standalone_toolchain
+ --arch=arm --platform=android-21 --install-dir=your/path/to/arm_standalone_toolchain
```
-此命令将在your/path/to/my_standalone_toolchain目录生成一套编译工具链,面向架构为32位ARM架构,支持的最小的Android API级别为21,使用的编译器为arm-linux-androideabi-gcc (GCC) 4.9。
+此命令将在`your/path/to/arm_standalone_toolchain`目录生成一套独立编译工具链,面向架构为32位ARM架构,支持的最小的Android API级别为21,支持编译器`arm-linux-androideabi-gcc (GCC) 4.9`和`clang 3.8`。
-注意:**PaddlePaddle要求使用的编译工具链所支持的Andoid API级别不小于21**。
+- 构建`arm64-v8a`、 `Android API 21`的独立工具链:
+```bash
+your/path/to/android-ndk-r14b-linux-x86_64/build/tools/make-standalone-toolchain.sh \
+ --arch=arm64 --platform=android-21 --install-dir=your/path/to/arm64_standalone_toolchain
+```
-## 配置交叉编译参数
+此命令将在`your/path/to/arm64_standalone_toolchain`目录生成一套独立编译工具链,面向架构为64位ARM64架构,支持的最小Android API级别为21,支持编译器`arm-linux-androideabi-gcc (GCC) 4.9`和`clang 3.8`。
+
+注意:**PaddlePaddle要求使用的编译工具链所支持的Android API级别不小于21**。
+
+### 配置交叉编译参数
CMake系统对交叉编译提供了支持[cmake-toolchains](https://cmake.org/cmake/help/v3.0/manual/cmake-toolchains.7.html#cross-compiling)。为了简化cmake配置,PaddlePaddle为交叉编译提供了工具链配置文档[cmake/cross_compiling/android.cmake](https://github.com/PaddlePaddle/Paddle/blob/develop/cmake/cross_compiling/android.cmake),以提供一些默认的编译器和编译参数相关配置。注意,从CMake 3.7版本开始,CMake官方对Android平台的交叉编译提供了通用的支持。PaddlePaddle若检测到用户使用的CMake版本不低于3.7时,将会将用户传进来的配置参数传递CMake系统,交由CMake系统本身来处理。有关参数配置的详细说明见[cmake-toolchains](https://cmake.org/cmake/help/v3.7/manual/cmake-toolchains.7.html#cross-compiling)。
@@ -36,32 +82,57 @@ CMake系统对交叉编译提供了支持[cmake-toolchains](https://cmake.org/cm
Android平台可选配置参数:
- `ANDROID_STANDALONE_TOOLCHAIN`,独立工具链所在的绝对路径,或者相对于构建目录的相对路径。PaddlePaddle的CMake系统将根据该值自动推导和设置需要使用的交叉编译器、sysroot、以及Android API级别;否则,用户需要在cmake时手动设置这些值。无默认值。
-- `ANDROID_ABI`,目标架构ABI。目前只支持`armeabi-v7a`,默认值为`armeabi-v7a`。
+- `ANDROID_TOOLCHAIN`,目标工具链。可设置`gcc/clang`,默认值为`clang`。
+ - CMake 3.7以上,将会始终使用`clang`工具链;CMake 3.7以下,可设置`ANDROID_TOOLCHAIN=gcc`以使用`gcc`工具链。
+ - Android官方提供的`clang`编译器要求系统支持`GLIBC 2.15`以上。
+- `ANDROID_ABI`,目标架构ABI。目前支持`armeabi-v7a`和`arm64-v8a`,默认值为`armeabi-v7a`。
- `ANDROID_NATIVE_API_LEVEL`,工具链的Android API级别。若没有显式设置,PaddlePaddle将根据`ANDROID_STANDALONE_TOOLCHAIN`的值自动推导得到。
-- `ANROID_ARM_MODE`,是否使用ARM模式。可设置`ON/OFF`,默认值为`ON`。
-- `ANDROID_ARM_NEON`,是否使用NEON指令。目前必须设置成`ON`,默认值为`ON`。
+- `ANROID_ARM_MODE`,是否使用ARM模式。
+ - `ANDROID_ABI=armeabi-v7a`时,可设置`ON/OFF`,默认值为`ON`;
+ - `ANDROID_ABI=arm64-v8a`时,不需要设置。
+- `ANDROID_ARM_NEON`,是否使用NEON指令。
+ - `ANDROID_ABI=armeabi-v7a`时,可设置`ON/OFF`,默认值为`ON`;
+ - `ANDROID_ABI=arm64-v8a`时,不需要设置。
其他配置参数:
+- `USE_EIGEN_FOR_BLAS`,是否使用Eigen库进行矩阵计算。可设置`ON/OFF`,默认值为`OFF`。
- `HOST_C/CXX_COMPILER`,宿主机的C/C++编译器。在编译宿主机版protoc可执行文件和目标机版OpenBLAS库时需要用到。默认设置成环境变量`CC`的值;若环境变量`CC`没有设置,则设置成`cc`编译器。
-一种常用的cmake配置如下:
+常用的cmake配置如下:
```bash
cmake -DCMAKE_SYSTEM_NAME=Android \
- -DANDROID_STANDALONE_TOOLCHAIN=your/path/to/my_standalone_toolchain \
+ -DANDROID_STANDALONE_TOOLCHAIN=your/path/to/arm_standalone_toolchain \
-DANDROID_ABI=armeabi-v7a \
-DANDROID_ARM_NEON=ON \
-DANDROID_ARM_MODE=ON \
+ -DUSE_EIGEN_FOR_BLAS=ON \
-DCMAKE_INSTALL_PREFIX=your/path/to/install \
-DWITH_C_API=ON \
-DWITH_SWIG_PY=OFF \
..
```
+```
+cmake -DCMAKE_SYSTEM_NAME=Android \
+ -DANDROID_STANDALONE_TOOLCHAIN=your/path/to/arm64_standalone_toolchain \
+ -DANDROID_ABI=arm64-v8a \
+ -DUSE_EIGEN_FOR_BLAS=OFF \
+ -DCMAKE_INSTALL_PREFIX=your/path/to/install \
+ -DWITH_C_API=ON \
+ -DWITH_SWIG_PY=OFF \
+ ..
+```
+
用户还可根据自己的需求设置其他编译参数。比如希望最小化生成的库的大小,可以设置`CMAKE_BUILD_TYPE`为`MinSizeRel`;若希望最快的执行速度,则可设置`CMAKE_BUILD_TYPE`为`Release`。亦可以通过手动设置`CMAKE_C/CXX_FLAGS_MINSIZEREL/RELEASE`来影响PaddlePaddle的编译过程。
-## 编译和安装
+**性能TIPS**,为了达到最快的计算速度,在CMake参数配置上,有以下建议:
+- 设置`CMAKE_BUILD_TYPE`为`Release`
+- 使用`clang`编译工具链
+- `armeabi-v7a`时,设置`USE_EIGEN_BLAS=ON`,使用Eigen进行矩阵计算;`arm64-v8a`时,设置`USE_EIGEN_FOR_BLAS=OFF`,使用OpenBLAS进行矩阵计算
+
+### 编译和安装
CMake配置完成后,执行以下命令,PaddlePaddle将自动下载和编译所有第三方依赖库、编译和安装PaddlePaddle预测库。
@@ -72,4 +143,4 @@ make install
注意:如果你曾经在源码目录下编译过其他平台的PaddlePaddle库,请先使用`rm -rf`命令删除`third_party`目录和`build`目录,以确保所有的第三方依赖库和PaddlePaddle代码都是针对新的CMake配置重新编译的。
-执行完安装命令后,`your/path/to/install`目录中会包含`include`和`lib`目录,其中`include`中包含C-API的头文件,`lib`中包含一个Android版本的库。自此,PaddlePaddle的已经安装完成,用户可将`your/path/to/install`目录下的生成文件用于深度学习相关Android App中,调用方法见C-API文档。
+执行完安装命令后,`your/path/to/install`目录中会包含`include`、`lib`和`third_party`目录,其中`include`中包含C-API的头文件,`lib`中包含若干个不同Android ABI的PaddlePaddle库,`third_party`中包含所依赖的所有第三方库。自此,PaddlePaddle的已经安装完成,用户可将`your/path/to/install`目录下的生成文件用于深度学习相关Android App中,调用方法见C-API文档。
diff --git a/go/pserver/client/client.go b/go/pserver/client/client.go
index 20d91e7703..e5187ce3df 100644
--- a/go/pserver/client/client.go
+++ b/go/pserver/client/client.go
@@ -137,7 +137,7 @@ func (c *Client) FinishInitParams() error {
return err
}
}
- return nil
+ return c.sel.Done()
}
// SendGrads sends gradients to parameter servers for updating
diff --git a/paddle/framework/CMakeLists.txt b/paddle/framework/CMakeLists.txt
index 9d039a54d6..8df92c4ad4 100644
--- a/paddle/framework/CMakeLists.txt
+++ b/paddle/framework/CMakeLists.txt
@@ -43,13 +43,6 @@ cc_library(backward SRCS backward.cc DEPS net_op)
cc_test(backward_test SRCS backward_test.cc DEPS backward recurrent_op device_context)
cc_library(executor SRCS executor.cc DEPS op_registry device_context scope framework_proto backward)
-set(EXECUTOR_TEST_OP elementwise_add_op gaussian_random_op feed_op fetch_op
- mul_op sum_op squared_l2_distance_op fill_constant_op sgd_op mean_op)
-if(WITH_GPU)
- nv_test(executor_test SRCS executor_test.cc DEPS executor ${EXECUTOR_TEST_OP})
-else()
- cc_test(executor_test SRCS executor_test.cc DEPS executor ${EXECUTOR_TEST_OP})
-endif()
cc_library(prune SRCS prune.cc DEPS framework_proto)
cc_test(prune_test SRCS prune_test.cc DEPS op_info prune recurrent_op device_context)
@@ -57,5 +50,7 @@ cc_test(prune_test SRCS prune_test.cc DEPS op_info prune recurrent_op device_con
cc_library(tensor_array SRCS tensor_array.cc DEPS lod_tensor)
cc_test(tensor_array_test SRCS tensor_array_test.cc DEPS tensor_array place)
+cc_test(var_type_inference_test SRCS var_type_inference_test.cc DEPS op_registry
+ proto_desc)
cc_library(selected_rows SRCS selected_rows.cc DEPS tensor)
cc_test(selected_rows_test SRCS selected_rows_test.cc DEPS selected_rows)
diff --git a/paddle/framework/attribute.cc b/paddle/framework/attribute.cc
index d6a2975aaa..29fe352ca4 100644
--- a/paddle/framework/attribute.cc
+++ b/paddle/framework/attribute.cc
@@ -19,19 +19,7 @@ limitations under the License. */
namespace paddle {
namespace framework {
-static ProgramDesc* g_program_desc = nullptr;
-
-ProgramDesc& GetProgramDesc() {
- if (g_program_desc == nullptr) {
- g_program_desc = new ProgramDesc();
- auto root_block = g_program_desc->mutable_blocks()->Add();
- root_block->set_idx(0);
- root_block->set_parent_idx(-1);
- }
- return *g_program_desc;
-}
-
-Attribute GetAttrValue(const OpDesc::Attr& attr_desc) {
+Attribute GetAttrValue(const OpDesc::Attr& attr_desc, ProgramDesc* program) {
switch (attr_desc.type()) {
case framework::AttrType::BOOLEAN: {
return attr_desc.b();
@@ -74,7 +62,9 @@ Attribute GetAttrValue(const OpDesc::Attr& attr_desc) {
return val;
}
case framework::AttrType::BLOCK: {
- return GetProgramDesc().mutable_blocks(attr_desc.block_idx());
+ PADDLE_ENFORCE(program != nullptr,
+ "Need to specify ProgramDesc when get a block attr");
+ return program->mutable_blocks(attr_desc.block_idx());
}
}
PADDLE_ENFORCE(false, "Unknown OpDesc::AttrDesc::type !");
diff --git a/paddle/framework/attribute.h b/paddle/framework/attribute.h
index d13530e340..9744662b8f 100644
--- a/paddle/framework/attribute.h
+++ b/paddle/framework/attribute.h
@@ -26,16 +26,13 @@ limitations under the License. */
namespace paddle {
namespace framework {
-
-ProgramDesc& GetProgramDesc();
-
template
inline AttrType AttrTypeID() {
Attribute tmp = T();
return static_cast(tmp.which() - 1);
}
-Attribute GetAttrValue(const OpDesc::Attr& attr_desc);
+Attribute GetAttrValue(const OpDesc::Attr& attr_desc, ProgramDesc* desc);
class AttrReader {
public:
@@ -120,6 +117,57 @@ class EnumInContainer {
std::unordered_set container_;
};
+template
+struct ExtractAttribute {
+ explicit ExtractAttribute(const std::string& attr_name)
+ : attr_name_(attr_name) {}
+
+ T* operator()(Attribute& attr) const {
+ T* attr_value = nullptr;
+ try {
+ attr_value = &boost::get(attr);
+ } catch (boost::bad_get& bad_get) {
+ PADDLE_THROW("Cannot get attribute %s by type %s, its type is %s",
+ attr_name_, typeid(T).name(), attr.type().name());
+ }
+ return attr_value;
+ }
+
+ const std::string& attr_name_;
+};
+
+// special handle bool
+// FIXME(yuyang18): Currently we cast bool into int in python binding. It is
+// hard to change the logic there. In another way, we should correct handle
+// if the user set `some_flag=1`.
+//
+// FIX ME anytime if there is a better solution.
+template <>
+struct ExtractAttribute {
+ explicit ExtractAttribute(const std::string& attr_name)
+ : attr_name_(attr_name) {}
+
+ bool* operator()(Attribute& attr) const {
+ if (attr.type() == typeid(int)) { // NOLINT
+ int val = boost::get(attr);
+ attr = static_cast(val);
+ } else if (attr.type() == typeid(float)) { // NOLINT
+ float val = boost::get(attr);
+ attr = static_cast(val);
+ }
+ bool* attr_value = nullptr;
+ try {
+ attr_value = &boost::get(attr);
+ } catch (boost::bad_get& bad_get) {
+ PADDLE_THROW("Cannot get attribute %s by type bool, its type is %s",
+ attr_name_, attr.type().name());
+ }
+ return attr_value;
+ }
+
+ const std::string& attr_name_;
+};
+
// check whether a certain attribute fit its limits
// an attribute can have more than one limits
template
@@ -171,9 +219,10 @@ class TypedAttrChecker {
attr_map[attr_name_] = val;
}
Attribute& attr = attr_map.at(attr_name_);
- T& attr_value = boost::get(attr);
+ ExtractAttribute extract_attr(attr_name_);
+ T* attr_value = extract_attr(attr);
for (const auto& checker : value_checkers_) {
- checker(attr_value);
+ checker(*attr_value);
}
}
diff --git a/paddle/framework/backward.cc b/paddle/framework/backward.cc
index e3d7dacd7f..fb552fe344 100644
--- a/paddle/framework/backward.cc
+++ b/paddle/framework/backward.cc
@@ -281,12 +281,16 @@ static void CreateGradVarInBlock(
auto ops = block_desc->AllOps();
for (size_t op_index = grad_op_start_index; op_index < ops.size();
++op_index) {
+ bool need_infer_shape = false;
ForEachVarName(ops[op_index]->Outputs(),
[&](const std::string& grad_var_name) {
if (block_desc->HasVar(grad_var_name)) {
return false;
}
- block_desc->Var(grad_var_name);
+ need_infer_shape = true;
+ auto var = block_desc->Var(grad_var_name);
+ // FIXME(qiao) infer the datatype
+ var->SetDataType(framework::DataType::FP32);
auto it = param_name_map.find(grad_var_name);
if (it == param_name_map.end()) {
return false;
@@ -298,12 +302,14 @@ static void CreateGradVarInBlock(
grad_record.op_idx_ = static_cast(op_index);
return false; /* not break */
});
+ if (need_infer_shape) {
+ ops[op_index]->InferShape(*block_desc);
+ }
}
}
std::vector> MakeOpGrad(
- const std::unique_ptr& op_desc,
- std::unordered_set* no_grad_vars,
+ const OpDescBind* op_desc, std::unordered_set* no_grad_vars,
std::unordered_map* grad_to_var) {
std::vector> grad_op_descs;
// All input gradients of forwarding operator do not need to calculate.
@@ -350,7 +356,7 @@ std::vector> MakeBlockBackward(
std::unordered_set* no_grad_vars,
std::unordered_map* grad_to_var) {
BlockDescBind* cur_block = program_desc.Block(block_idx);
- std::deque>& op_descs = cur_block->ops_;
+ std::vector op_descs = cur_block->AllOps();
std::unordered_map> dup_out_ops;
size_t grad_desc_idx = 0;
std::vector> backward_descs;
@@ -368,7 +374,7 @@ std::vector> MakeBlockBackward(
program_desc, step_block_idx, no_grad_vars, grad_to_var);
BlockDescBind* backward_block = program_desc.AppendBlock(*cur_block);
for (auto& ptr : backward_block_op_descs) {
- backward_block->ops_.push_back(std::move(ptr));
+ backward_block->AppendAllocatedOp(std::move(ptr));
}
op_grads[0]->SetBlockAttr("step_block", *backward_block);
}
@@ -425,17 +431,22 @@ ParamGradInfoMap AppendBackward(
const int root_block_idx = 0;
auto root_block = program_desc.Block(root_block_idx);
- auto& all_ops = root_block->ops_;
// insert fill one op for target
+ // TODO(qiao) add some check to the target.
std::string fill_one_op_out = GradVarName(target.Name());
+ std::vector target_shape_desc = target.Shape();
+ std::vector target_shape;
+ std::transform(target_shape_desc.begin(), target_shape_desc.end(),
+ std::back_inserter(target_shape),
+ [](int64_t dim) { return static_cast(dim); });
std::unique_ptr fill_one_op(
new OpDescBind("fill_constant", {}, {{"Out", {fill_one_op_out}}},
- {{"shape", std::vector{1}},
+ {{"shape", target_shape},
{"value", static_cast(1.0)},
- {"dataType", framework::DataType::FP32}}));
- all_ops.push_back(std::move(fill_one_op));
- size_t forward_op_num = all_ops.size();
+ {"data_type", framework::DataType::FP32}}));
+ root_block->AppendAllocatedOp(std::move(fill_one_op));
+ size_t forward_op_num = root_block->OpSize();
size_t forward_block_num = program_desc.Size();
// Insert backward operators
@@ -443,13 +454,22 @@ ParamGradInfoMap AppendBackward(
auto backward_op_descs = MakeBlockBackward(program_desc, root_block_idx,
&no_grad_var_names, &grad_to_var);
- std::unordered_map retv;
-
- // Create Variable
for (auto& ptr : backward_op_descs) {
- all_ops.push_back(std::move(ptr));
+ root_block->AppendAllocatedOp(std::move(ptr));
}
- root_block->Var(fill_one_op_out);
+ // Create Variable
+
+ // Create target gradient variable
+ std::unordered_map retv;
+
+ auto var = root_block->Var(fill_one_op_out);
+ // FIXME(qiao) infer the data type
+ var->SetDataType(framework::DataType::FP32);
+ var->SetShape(target.Shape());
+ auto& target_grad = retv[target.Name()];
+ target_grad.name_ = fill_one_op_out;
+ target_grad.block_idx_ = root_block_idx;
+ target_grad.op_idx_ = static_cast(forward_op_num);
// create grad_var for all blocks in this program
CreateGradVarInBlock(forward_op_num, grad_to_var, root_block, &retv);
diff --git a/paddle/framework/backward_test.cc b/paddle/framework/backward_test.cc
index 5302afcafb..10301f7e39 100644
--- a/paddle/framework/backward_test.cc
+++ b/paddle/framework/backward_test.cc
@@ -26,6 +26,20 @@ namespace framework {
using DeviceContext = platform::DeviceContext;
+class NoneOp : public framework::OperatorWithKernel {
+ public:
+ using framework::OperatorWithKernel::OperatorWithKernel;
+
+ protected:
+ void InferShape(framework::InferShapeContext *ctx) const override {}
+};
+
+template
+class NoneKernel : public framework::OpKernel {
+ public:
+ void Compute(const framework::ExecutionContext &context) const override {}
+};
+
class RowWiseAddOpMaker : public OpProtoAndCheckerMaker {
public:
RowWiseAddOpMaker(OpProto *proto, OpAttrChecker *op_checker)
@@ -215,19 +229,51 @@ class MinusOpMaker : public OpProtoAndCheckerMaker {
namespace f = paddle::framework;
namespace ops = paddle::operators;
using EnforceNotMet = paddle::platform::EnforceNotMet;
-REGISTER_OPERATOR(rowwise_add, f::NOP, f::RowWiseAddOpMaker,
+// rowwise_add
+REGISTER_OPERATOR(rowwise_add, f::NoneOp, f::RowWiseAddOpMaker,
f::RowWiseAddGradMaker);
-REGISTER_OPERATOR(rowwise_add_grad, f::NOP);
-REGISTER_OP(mul, f::NOP, f::MulOpMaker, mul_grad, f::NOP);
-REGISTER_OP(sigmoid, f::NOP, f::SigmoidOpMaker, sigmoid_grad, f::NOP);
-REGISTER_OP_WITHOUT_GRADIENT(nograd, f::NOP, f::NoGradOpMaker);
-REGISTER_OP_WITHOUT_GRADIENT(fill_zeros_like, f::NOP, f::FillZeroOpMaker);
-REGISTER_OP(sum, f::NOP, f::SumOpMaker, sum_grad, f::NOP);
+REGISTER_OP_CPU_KERNEL(rowwise_add,
+ f::NoneKernel);
+REGISTER_OPERATOR(rowwise_add_grad, f::NoneOp);
+REGISTER_OP_CPU_KERNEL(rowwise_add_grad,
+ f::NoneKernel);
+// mul
+REGISTER_OP(mul, f::NoneOp, f::MulOpMaker, mul_grad, f::NoneOp);
+REGISTER_OP_CPU_KERNEL(mul, f::NoneKernel);
+REGISTER_OP_CPU_KERNEL(mul_grad,
+ f::NoneKernel);
+// sigmoid
+REGISTER_OP(sigmoid, f::NoneOp, f::SigmoidOpMaker, sigmoid_grad, f::NoneOp);
+REGISTER_OP_CPU_KERNEL(sigmoid,
+ f::NoneKernel);
+REGISTER_OP_WITHOUT_GRADIENT(nograd, f::NoneOp, f::NoGradOpMaker);
+// fill_zeros_like
+REGISTER_OP_WITHOUT_GRADIENT(fill_zeros_like, f::NoneOp, f::FillZeroOpMaker);
+REGISTER_OP_CPU_KERNEL(fill_zeros_like,
+ f::NoneKernel);
+// sum
+REGISTER_OP(sum, f::NoneOp, f::SumOpMaker, sum_grad, f::NoneOp);
+REGISTER_OP_CPU_KERNEL(sum, f::NoneKernel);
+REGISTER_OP_CPU_KERNEL(sum_grad,
+ f::NoneKernel);
+// fc
REGISTER_OP_WITHOUT_GRADIENT(fc, f::FcOp, f::FcOpMaker);
-REGISTER_OP(many_output_op, f::NOP, f::ManyOutputOpMaker, many_output_op_grad,
- f::NOP);
-REGISTER_OP(mult_in_out, f::NOP, f::MultInOutOpMaker, mult_in_out_grad, f::NOP);
-REGISTER_OPERATOR(minus, f::NOP, f::MinusOpMaker, f::MinusGradOpDescMaker);
+// many_output_op
+REGISTER_OP(many_output_op, f::NoneOp, f::ManyOutputOpMaker,
+ many_output_op_grad, f::NoneOp);
+// mult_in_out
+REGISTER_OP(mult_in_out, f::NoneOp, f::MultInOutOpMaker, mult_in_out_grad,
+ f::NoneOp);
+REGISTER_OP_CPU_KERNEL(mult_in_out,
+ f::NoneKernel);
+REGISTER_OP_CPU_KERNEL(mult_in_out_grad,
+ f::NoneKernel);
+// minus
+REGISTER_OPERATOR(minus, f::NoneOp, f::MinusOpMaker, f::MinusGradOpDescMaker);
+REGISTER_OP_CPU_KERNEL(minus, f::NoneKernel);
+// scale
+REGISTER_OPERATOR(scale, f::NoneOp);
+REGISTER_OP_CPU_KERNEL(scale, f::NoneKernel);
TEST(Backward, simple_op_not_need_grad) {
auto fwd = f::OpRegistry::CreateOp(
@@ -449,20 +495,10 @@ TEST(Backward, linear_net_intermediate_variable_has_no_grad) {
EXPECT_EQ(bwd_net->ops_[2]->Outputs(all).size(), 0UL);
}
-// =================================== //
-
-f::ProgramDesc *GetNewProgramDesc() {
- auto *program_desc = new f::ProgramDesc();
- auto *root_block = program_desc->add_blocks();
- root_block->set_idx(0);
- root_block->set_parent_idx(-1);
- return program_desc;
-}
-
TEST(Backward, simple_single_op) {
- f::ProgramDesc *program_desc = GetNewProgramDesc();
- f::ProgramDescBind &program = f::ProgramDescBind::Instance(program_desc);
+ f::ProgramDescBind program;
f::BlockDescBind *block = program.Block(0);
+
f::OpDescBind *op = block->AppendOp();
op->SetType("rowwise_add");
op->SetInput("X", {"x"});
@@ -487,7 +523,7 @@ TEST(Backward, simple_single_op) {
EXPECT_EQ(grad_op->Output(f::GradVarName("b")),
std::vector({f::GradVarName("b")}));
- EXPECT_EQ(var_to_grad.size(), 2UL);
+ EXPECT_EQ(var_to_grad.size(), 3UL);
EXPECT_EQ(var_to_grad.at("b"), f::GradVarInfo(f::GradVarName("b"), 0, 2));
EXPECT_EQ(var_to_grad.at("x"), f::GradVarInfo(f::GradVarName("x"), 0, 2));
@@ -496,8 +532,7 @@ TEST(Backward, simple_single_op) {
}
TEST(Backward, default_attribute) {
- f::ProgramDesc *program_desc = GetNewProgramDesc();
- f::ProgramDescBind &program = f::ProgramDescBind::Instance(program_desc);
+ f::ProgramDescBind program;
f::BlockDescBind *block = program.Block(0);
f::OpDescBind *op = block->AppendOp();
op->SetType("mul");
@@ -523,8 +558,7 @@ TEST(Backward, default_attribute) {
}
TEST(Backward, simple_mult_op) {
- f::ProgramDesc *program_desc = GetNewProgramDesc();
- f::ProgramDescBind &program = f::ProgramDescBind::Instance(program_desc);
+ f::ProgramDescBind program;
f::BlockDescBind *block = program.Block(0);
f::OpDescBind *op1 = block->AppendOp();
op1->SetType("rowwise_add");
@@ -588,7 +622,7 @@ TEST(Backward, simple_mult_op) {
EXPECT_EQ(grad_op3->Output(f::GradVarName("b")),
std::vector({f::GradVarName("b3")}));
- EXPECT_EQ(var_to_grad.size(), 6UL);
+ EXPECT_EQ(var_to_grad.size(), 7UL);
EXPECT_EQ(var_to_grad.at("x1"), f::GradVarInfo(f::GradVarName("x1"), 0, 6));
EXPECT_EQ(var_to_grad.at("b1"), f::GradVarInfo(f::GradVarName("b1"), 0, 6));
EXPECT_EQ(var_to_grad.at("out1"),
@@ -607,8 +641,7 @@ TEST(Backward, simple_mult_op) {
}
TEST(Backward, intermedia_var_no_grad) {
- f::ProgramDesc *program_desc = GetNewProgramDesc();
- f::ProgramDescBind &program = f::ProgramDescBind::Instance(program_desc);
+ f::ProgramDescBind program;
f::BlockDescBind *block = program.Block(0);
f::OpDescBind *op1 = block->AppendOp();
op1->SetType("rowwise_add");
@@ -666,7 +699,7 @@ TEST(Backward, intermedia_var_no_grad) {
std::vector({f::GradVarName("out1")}));
EXPECT_EQ(grad_op4->Output(f::GradVarName("Y")), std::vector());
- EXPECT_EQ(var_to_grad.size(), 3UL);
+ EXPECT_EQ(var_to_grad.size(), 4UL);
EXPECT_EQ(var_to_grad.at("x1"), f::GradVarInfo(f::GradVarName("x1"), 0, 6));
EXPECT_EQ(var_to_grad.at("b1"), f::GradVarInfo(f::GradVarName("b1"), 0, 6));
EXPECT_EQ(var_to_grad.at("out1"),
@@ -678,8 +711,7 @@ TEST(Backward, intermedia_var_no_grad) {
}
TEST(Backward, var_no_grad) {
- f::ProgramDesc *program_desc = GetNewProgramDesc();
- f::ProgramDescBind &program = f::ProgramDescBind::Instance(program_desc);
+ f::ProgramDescBind program;
f::BlockDescBind *block = program.Block(0);
f::OpDescBind *op1 = block->AppendOp();
op1->SetType("mult_in_out");
@@ -744,7 +776,7 @@ TEST(Backward, var_no_grad) {
EXPECT_EQ(grad_op1->Output(f::GradVarName("H")),
std::vector({f::GradVarName("h1")}));
- EXPECT_EQ(var_to_grad.size(), 3UL);
+ EXPECT_EQ(var_to_grad.size(), 4UL);
EXPECT_EQ(var_to_grad.at("y1"), f::GradVarInfo(f::GradVarName("y1"), 0, 3));
EXPECT_EQ(var_to_grad.at("x1"), f::GradVarInfo(f::GradVarName("x1"), 0, 5));
EXPECT_EQ(var_to_grad.at("h1"), f::GradVarInfo(f::GradVarName("h1"), 0, 5));
@@ -755,8 +787,7 @@ TEST(Backward, var_no_grad) {
}
TEST(Backward, shared_var) {
- f::ProgramDesc *program_desc = GetNewProgramDesc();
- f::ProgramDescBind &program = f::ProgramDescBind::Instance(program_desc);
+ f::ProgramDescBind program;
f::BlockDescBind *block = program.Block(0);
f::OpDescBind *op1 = block->AppendOp();
op1->SetType("rowwise_add");
@@ -830,7 +861,7 @@ TEST(Backward, shared_var) {
EXPECT_EQ(grad_op1->Output(f::GradVarName("b")),
std::vector({f::GradVarName("b1")}));
- EXPECT_EQ(var_to_grad.size(), 5UL);
+ EXPECT_EQ(var_to_grad.size(), 6UL);
EXPECT_EQ(var_to_grad.at("b3"), f::GradVarInfo(f::GradVarName("b3"), 0, 4));
EXPECT_EQ(var_to_grad.at("y2"), f::GradVarInfo(f::GradVarName("y2"), 0, 5));
EXPECT_EQ(var_to_grad.at("out1"),
@@ -846,8 +877,7 @@ TEST(Backward, shared_var) {
}
TEST(Backward, half_backward) {
- f::ProgramDesc *program_desc = GetNewProgramDesc();
- f::ProgramDescBind &program = f::ProgramDescBind::Instance(program_desc);
+ f::ProgramDescBind program;
f::BlockDescBind *block = program.Block(0);
auto *op1 = block->AppendOp();
op1->SetType("minus");
@@ -863,7 +893,7 @@ TEST(Backward, half_backward) {
auto ops = block->AllOps();
ASSERT_EQ(3UL, ops.size());
- EXPECT_EQ(var_to_grad.size(), 1UL);
+ EXPECT_EQ(var_to_grad.size(), 2UL);
EXPECT_EQ(var_to_grad.at("a"),
f::GradVarInfo(f::GradVarName("a"), 0, forward_len + 1));
}
diff --git a/paddle/framework/block_desc.cc b/paddle/framework/block_desc.cc
index 47b75228cd..92ac302e46 100644
--- a/paddle/framework/block_desc.cc
+++ b/paddle/framework/block_desc.cc
@@ -19,11 +19,11 @@ namespace paddle {
namespace framework {
VarDescBind *BlockDescBind::Var(const std::string &name) {
- need_update_ = true;
auto it = vars_.find(name);
if (it != vars_.end()) {
return it->second.get();
}
+ need_update_ = true;
auto *var = new VarDescBind(name);
vars_[name].reset(var);
return var;
@@ -55,6 +55,11 @@ OpDescBind *BlockDescBind::AppendOp() {
return ops_.back().get();
}
+void BlockDescBind::AppendAllocatedOp(std::unique_ptr &&op_desc) {
+ need_update_ = true;
+ ops_.emplace_back(std::move(op_desc));
+}
+
OpDescBind *BlockDescBind::PrependOp() {
need_update_ = true;
ops_.emplace_front(new OpDescBind());
@@ -70,15 +75,19 @@ std::vector BlockDescBind::AllOps() const {
}
void BlockDescBind::Flush() {
+ for (auto &op_desc : ops_) {
+ op_desc->Flush();
+ }
+
if (need_update_) {
auto &op_field = *this->desc_->mutable_ops();
- op_field.Clear();
+ this->ClearPBOps();
op_field.Reserve(static_cast(ops_.size()));
for (auto &op_desc : ops_) {
op_field.AddAllocated(op_desc->Proto());
}
auto &var_field = *this->desc_->mutable_vars();
- var_field.Clear();
+ this->ClearPBVars();
var_field.Reserve(static_cast(vars_.size()));
for (auto &var_desc : vars_) {
var_field.AddAllocated(var_desc.second->Proto());
@@ -99,5 +108,21 @@ BlockDesc *BlockDescBind::Proto() {
return desc_;
}
+void BlockDescBind::ClearPBOps() {
+ auto ops = this->desc_->mutable_ops();
+ while (!ops->empty()) {
+ // we do not own the OpDesc, so release the ownership.
+ ops->ReleaseLast();
+ }
+}
+
+void BlockDescBind::ClearPBVars() {
+ auto vars = this->desc_->mutable_vars();
+ while (!vars->empty()) {
+ // we do not own the VarDesc, so release the ownership.
+ vars->ReleaseLast();
+ }
+}
+
} // namespace framework
} // namespace paddle
diff --git a/paddle/framework/block_desc.h b/paddle/framework/block_desc.h
index 9fb88f9632..5e1f10c1ae 100644
--- a/paddle/framework/block_desc.h
+++ b/paddle/framework/block_desc.h
@@ -36,6 +36,11 @@ class BlockDescBind {
BlockDescBind(ProgramDescBind *prog, BlockDesc *desc)
: prog_(prog), desc_(desc), need_update_(false) {}
+ ~BlockDescBind() {
+ this->ClearPBVars();
+ this->ClearPBOps();
+ }
+
int32_t ID() const { return desc_->idx(); }
int32_t Parent() const { return desc_->parent_idx(); }
@@ -52,17 +57,25 @@ class BlockDescBind {
OpDescBind *AppendOp();
+ void AppendAllocatedOp(std::unique_ptr &&op_desc);
+
OpDescBind *PrependOp();
std::vector AllOps() const;
+ size_t OpSize() const { return ops_.size(); }
+
+ OpDescBind *Op(int idx) { return ops_.at(idx).get(); }
+
void Flush();
BlockDesc *Proto();
- // FIXME(yuyang18): backward will access private data of BlockDesc.
- // Mark it public temporary. We can fix it later.
- public:
+ private:
+ void ClearPBOps();
+ void ClearPBVars();
+
+ private:
ProgramDescBind *prog_; // not_own
BlockDesc *desc_; // not_own
bool need_update_;
diff --git a/paddle/framework/details/op_registry.h b/paddle/framework/details/op_registry.h
index ed7c5f17b0..357ad21f39 100644
--- a/paddle/framework/details/op_registry.h
+++ b/paddle/framework/details/op_registry.h
@@ -18,6 +18,7 @@
#include "paddle/framework/op_info.h"
#include "paddle/framework/op_proto_maker.h"
#include "paddle/framework/operator.h"
+#include "paddle/framework/var_type_inference.h"
namespace paddle {
namespace framework {
@@ -26,7 +27,8 @@ namespace details {
enum OpInfoFillType {
kOperator = 0,
kOpProtoAndCheckerMaker = 1,
- kGradOpDescMaker = 2
+ kGradOpDescMaker = 2,
+ kVarTypeInference = 3
};
template
@@ -38,7 +40,9 @@ struct OpInfoFillTypeID {
? kOpProtoAndCheckerMaker
: (std::is_base_of::value
? kGradOpDescMaker
- : static_cast(-1)));
+ : (std::is_base_of::value
+ ? kVarTypeInference
+ : static_cast(-1))));
}
};
@@ -106,6 +110,17 @@ struct OpInfoFiller {
};
}
};
+
+template
+struct OpInfoFiller {
+ void operator()(const char* op_type, OpInfo* info) const {
+ info->infer_var_type_ = [](const OpDescBind& fwd_op, BlockDescBind* block) {
+ T inference;
+ inference(fwd_op, block);
+ };
+ }
+};
+
} // namespace details
} // namespace framework
diff --git a/paddle/framework/executor.cc b/paddle/framework/executor.cc
index 8e82e28bac..00caa6e1d5 100644
--- a/paddle/framework/executor.cc
+++ b/paddle/framework/executor.cc
@@ -64,99 +64,24 @@ void Executor::Run(const ProgramDesc& pdesc, Scope* scope, int block_id) {
auto& block = pdesc.blocks(block_id);
auto& device = device_contexts_[0];
- // Instantiate all the vars in the global scope
- for (auto& var : block.vars()) {
- scope->Var(var.name());
- }
-
Scope& local_scope = scope->NewScope();
- std::vector should_run = Prune(pdesc, block_id);
- PADDLE_ENFORCE_EQ(should_run.size(), static_cast(block.ops_size()));
- for (size_t i = 0; i < should_run.size(); ++i) {
- if (should_run[i]) {
- for (auto& var : block.ops(i).outputs()) {
- for (auto& argu : var.arguments()) {
- if (local_scope.FindVar(argu) == nullptr) {
- local_scope.Var(argu);
- }
- }
- }
- auto op = paddle::framework::OpRegistry::CreateOp(block.ops(i));
- op->Run(local_scope, *device);
+ for (auto& var : block.vars()) {
+ if (var.persistable()) {
+ scope->Var(var.name());
+ } else {
+ local_scope.Var(var.name());
}
}
- // TODO(tonyyang-svail):
- // - Destroy local_scope
-}
-
-std::vector Prune(const ProgramDesc& pdesc, int block_id) {
- // TODO(tonyyang-svail):
- // - will change to use multiple blocks for RNN op and Cond Op
-
- auto& block = pdesc.blocks(block_id);
- auto& ops = block.ops();
-
- bool expect_feed = true;
- for (auto& op_desc : ops) {
- PADDLE_ENFORCE(op_desc.type() != kFeedOpType || expect_feed,
- "All FeedOps are at the beginning of the ProgramDesc");
- expect_feed = (op_desc.type() == kFeedOpType);
- }
-
- bool expect_fetch = true;
- for (auto op_iter = ops.rbegin(); op_iter != ops.rend(); ++op_iter) {
- auto& op_desc = *op_iter;
- PADDLE_ENFORCE(op_desc.type() != kFetchOpType || expect_fetch,
- "All FetchOps must at the end of the ProgramDesc");
- expect_fetch = (op_desc.type() == kFetchOpType);
- }
-
- std::set dependent_vars;
- std::vector should_run;
- for (auto op_iter = ops.rbegin(); op_iter != ops.rend(); ++op_iter) {
- auto& op_desc = *op_iter;
-
- bool found_dependent_vars = false;
- for (auto& var : op_desc.outputs()) {
- for (auto& argu : var.arguments()) {
- if (dependent_vars.count(argu) != 0) {
- found_dependent_vars = true;
- }
- }
- }
-
- if (op_desc.type() == kFetchOpType || found_dependent_vars) {
- // erase its output to the dependency graph
- for (auto& var : op_desc.outputs()) {
- for (auto& argu : var.arguments()) {
- dependent_vars.erase(argu);
- }
- }
-
- // insert its input to the dependency graph
- for (auto& var : op_desc.inputs()) {
- for (auto& argu : var.arguments()) {
- dependent_vars.insert(argu);
- }
- }
-
- should_run.push_back(true);
- } else {
- should_run.push_back(false);
- }
+ for (auto& op_desc : block.ops()) {
+ auto op = paddle::framework::OpRegistry::CreateOp(
+ op_desc, const_cast(&pdesc));
+ op->Run(local_scope, *device);
}
// TODO(tonyyang-svail):
- // - check this after integration of Init
- // PADDLE_ENFORCE(dependent_vars.empty());
-
- // since we are traversing the ProgramDesc in reverse order
- // we reverse the should_run vector
- std::reverse(should_run.begin(), should_run.end());
-
- return should_run;
+ // - Destroy local_scope
}
} // namespace framework
diff --git a/paddle/framework/executor.h b/paddle/framework/executor.h
index 4e3bc2c0a5..793ee954e2 100644
--- a/paddle/framework/executor.h
+++ b/paddle/framework/executor.h
@@ -40,16 +40,5 @@ class Executor {
std::vector device_contexts_;
};
-/* @Brief
- * Pruning the graph
- *
- * @param
- * ProgramDesc
- *
- * @return
- * vector Same size as ops. Indicates whether an op should be run.
- */
-std::vector Prune(const ProgramDesc& pdesc, int block_id);
-
} // namespace framework
} // namespace paddle
diff --git a/paddle/framework/executor_test.cc b/paddle/framework/executor_test.cc
deleted file mode 100644
index e08d31e361..0000000000
--- a/paddle/framework/executor_test.cc
+++ /dev/null
@@ -1,348 +0,0 @@
-/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License. */
-
-#include "paddle/framework/executor.h"
-
-#include
-#include
-
-#include "gflags/gflags.h"
-#include "gtest/gtest.h"
-#include "paddle/framework/attribute.h"
-#include "paddle/framework/backward.h"
-#include "paddle/framework/block_desc.h"
-#include "paddle/framework/op_desc.h"
-#include "paddle/framework/op_registry.h"
-#include "paddle/framework/operator.h"
-
-USE_OP(elementwise_add);
-USE_OP(gaussian_random);
-USE_NO_KERNEL_OP(feed);
-USE_NO_KERNEL_OP(fetch);
-USE_OP(mul);
-USE_OP(sum);
-USE_OP(squared_l2_distance);
-USE_OP(fill_constant);
-USE_OP(mean);
-USE_OP(sgd);
-
-constexpr auto kFeedValueName = "feed_value";
-constexpr auto kFetchValueName = "fetch_value";
-
-using namespace paddle::platform;
-using namespace paddle::framework;
-
-void AddOp(const std::string& type, const VariableNameMap& inputs,
- const VariableNameMap& outputs, AttributeMap attrs,
- paddle::framework::BlockDescBind* block) {
- // insert output
- for (auto kv : outputs) {
- for (auto v : kv.second) {
- // <<<<<<< HEAD
- // auto var = block->Var(v);
- // var->SetType(VarDesc::LOD_TENSOR);
- // var->SetDataType(paddle::framework::DataType::FP32);
- // =======
- if (!block->HasVar(v)) {
- auto var = block->Var(v);
- var->SetDataType(paddle::framework::DataType::FP32);
- }
- // >>>>>>> origin/develop
- }
- }
-
- // insert op
- auto op = block->AppendOp();
- op->SetType(type);
- for (auto& kv : inputs) {
- op->SetInput(kv.first, kv.second);
- }
- for (auto& kv : outputs) {
- op->SetOutput(kv.first, kv.second);
- }
- op->SetAttrMap(attrs);
- op->CheckAttrs();
-}
-
-// Tensors in feed value variable will only be in CPUPlace
-// So we can memcpy the data from vector to feed_value
-template
-void SetFeedVariable(const std::vector>& inputs,
- const std::vector>& dims) {
- Variable* g_feed_value = GetGlobalScope().FindVar(kFeedValueName);
- auto& feed_inputs =
- *(g_feed_value->GetMutable>());
- size_t size = inputs.size();
- feed_inputs.resize(size);
- for (size_t i = 0; i < size; i++) {
- T* dst = feed_inputs[i].mutable_data(make_ddim(dims[i]), CPUPlace());
- memcpy(dst, inputs[i].data(), inputs[i].size() * sizeof(T));
- }
-}
-
-// Tensors in fetch value variable will only be in CPUPlace
-// So we can memcpy the data from fetch_value to vector
-template
-std::vector> GetFetchVariable() {
- Variable* g_fetch_value = GetGlobalScope().FindVar(kFetchValueName);
- auto& fetch_outputs =
- *(g_fetch_value->GetMutable>());
-
- size_t size = fetch_outputs.size();
- std::vector> result;
- result.reserve(size);
- for (size_t i = 0; i < size; i++) {
- std::vector tmp;
- tmp.resize(fetch_outputs[i].numel());
- memcpy(tmp.data(), fetch_outputs[i].data(),
- fetch_outputs[i].numel() * sizeof(T));
- result.push_back(tmp);
- }
-
- return result;
-}
-
-class ExecutorTesterRandom : public ::testing::Test {
- public:
- virtual void SetUp() override {
- int input_dim = 3, batch_size = 2, embed_dim = 5;
-
- auto temp_init_root_block = init_pdesc_.add_blocks();
- temp_init_root_block->set_idx(0);
- temp_init_root_block->set_parent_idx(-1);
- paddle::framework::ProgramDescBind& init_program =
- paddle::framework::ProgramDescBind::Instance(&init_pdesc_);
- paddle::framework::BlockDescBind* init_root_block = init_program.Block(0);
-
- AddOp("gaussian_random", {}, {{"Out", {"w1"}}},
- {{"dims", std::vector{input_dim, embed_dim}}}, init_root_block);
- AddOp("gaussian_random", {}, {{"Out", {"w2"}}},
- {{"dims", std::vector{embed_dim, input_dim}}}, init_root_block);
- AddOp("fetch", {{"Input", {"w1"}}}, {{"Out", {kFetchValueName}}},
- {{"col", 0}}, init_root_block);
- AddOp("fetch", {{"Input", {"w2"}}}, {{"Out", {kFetchValueName}}},
- {{"col", 1}}, init_root_block);
-
- // flush
- init_program.Proto();
-
- // run block
- auto temp_root_block = pdesc_.add_blocks();
- temp_root_block->set_idx(0);
- temp_root_block->set_parent_idx(-1);
- paddle::framework::ProgramDescBind& program =
- paddle::framework::ProgramDescBind::Instance(&pdesc_);
- paddle::framework::BlockDescBind* root_block = program.Block(0);
-
- // feed data
- inputs_.push_back({1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
- dims_.push_back({batch_size, input_dim});
- AddOp("feed", {{"Input", {kFeedValueName}}}, {{"Out", {"a"}}},
- {{"dims", std::vector{batch_size, input_dim}}, {"col", 0}},
- root_block);
-
- // forward
- AddOp("mul", {{"X", {"a"}}, {"Y", {"w1"}}}, {{"Out", {"b"}}}, {},
- root_block);
- AddOp("mul", {{"X", {"b"}}, {"Y", {"w2"}}}, {{"Out", {"a_out"}}}, {},
- root_block);
- AddOp("squared_l2_distance", {{"X", {"a"}}, {"Y", {"a_out"}}},
- {{"Out", {"l2_distance"}}, {"sub_result", {"l2_distance_sub"}}}, {},
- root_block);
- AddOp("mean", {{"X", {"l2_distance"}}}, {{"Out", {"mean_out"}}}, {},
- root_block);
-
- // backward
- auto target = VarDescBind("mean_out");
- AppendBackward(program, target, {});
-
- // update
- AddOp("fill_constant", {}, {{"Out", {"learning_rate"}}},
- {{"shape", std::vector{1}}, {"value", float(0.001)}},
- root_block);
- AddOp("sgd", {{"Param", {"w1"}},
- {"LearningRate", {"learning_rate"}},
- {"Grad", {"w1@GRAD"}}},
- {{"ParamOut", {"w1"}}}, {}, root_block);
- AddOp("sgd", {{"Param", {"w2"}},
- {"LearningRate", {"learning_rate"}},
- {"Grad", {"w2@GRAD"}}},
- {{"ParamOut", {"w2"}}}, {}, root_block);
-
- AddOp("fetch", {{"Input", {"w1"}}}, {{"Out", {kFetchValueName}}},
- {{"col", 0}}, root_block);
- AddOp("fetch", {{"Input", {"w2"}}}, {{"Out", {kFetchValueName}}},
- {{"col", 1}}, root_block);
- AddOp("fetch", {{"Input", {"l2_distance"}}}, {{"Out", {kFetchValueName}}},
- {{"col", 0}}, root_block);
-
- // flush
- program.Proto();
- }
-
- protected:
- ProgramDesc init_pdesc_;
- ProgramDesc pdesc_;
- std::vector> inputs_;
- std::vector> dims_;
-};
-
-class ExecutorTesterFeedAndFetch : public ::testing::Test {
- public:
- virtual void SetUp() override {
- auto temp_root_block = pdesc_.add_blocks();
- temp_root_block->set_idx(0);
- temp_root_block->set_parent_idx(-1);
-
- // wrap to BlockDescBind
- paddle::framework::ProgramDescBind& program =
- paddle::framework::ProgramDescBind::Instance(&pdesc_);
- paddle::framework::BlockDescBind* root_block = program.Block(0);
-
- std::vector dim{6};
-
- AddOp("feed", {{"Input", {kFeedValueName}}}, {{"Out", {"a"}}},
- {{"dims", dim}, {"col", 0}}, root_block);
- AddOp("feed", {{"Input", {kFeedValueName}}}, {{"Out", {"b"}}},
- {{"dims", dim}, {"col", 1}}, root_block);
- AddOp("fetch", {{"Input", {"a"}}}, {{"Out", {kFetchValueName}}},
- {{"col", 0}}, root_block);
- AddOp("fetch", {{"Input", {"b"}}}, {{"Out", {kFetchValueName}}},
- {{"col", 1}}, root_block);
-
- // flush
- program.Proto();
-
- std::vector vec1 = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
- std::vector vec2 = {4.0, 5.0, 6.0, 7.0, 8.0, 9.0};
- inputs_.push_back(vec1);
- inputs_.push_back(vec2);
- dims_.push_back({static_cast(vec1.size())});
- dims_.push_back({static_cast(vec2.size())});
- }
-
- protected:
- ProgramDesc pdesc_;
- std::vector> inputs_;
- std::vector> dims_;
-};
-
-#ifndef PADDLE_WITH_CUDA
-TEST_F(ExecutorTesterRandom, CPU) {
- std::vector places;
- CPUPlace cpu_place;
- places.push_back(cpu_place);
-
- // We have a global Scope and BuddyAllocator, and we must ensure
- // global BuddyAllocator is initialized before global Scope. Thus,
- // global Scope will deconstruct before BuddyAllocator. Otherwise,
- // "pointer being freed was not allocated" error will appear.
- paddle::memory::Used(cpu_place);
-
- std::unique_ptr executor(new Executor(places));
- executor->Run(init_pdesc_, &GetGlobalScope(), 0);
- SetFeedVariable(inputs_, dims_);
- executor->Run(pdesc_, &GetGlobalScope(), 0);
- std::vector> result = GetFetchVariable();
-}
-
-TEST_F(ExecutorTesterFeedAndFetch, CPU) {
- std::vector places;
- CPUPlace cpu_place;
- places.emplace_back(cpu_place);
-
- // We have a global Scope and BuddyAllocator, and we must ensure
- // global BuddyAllocator is initialized before global Scope. Thus,
- // global Scope will deconstruct before BuddyAllocator. Otherwise,
- // "pointer being freed was not allocated" error will appear.
- paddle::memory::Used(cpu_place);
-
- std::unique_ptr executor(new Executor(places));
-
- for (int batch_id = 0; batch_id < 3; batch_id++) {
- SetFeedVariable(inputs_, dims_);
- executor->Run(pdesc_, &GetGlobalScope(), 0);
- std::vector> result = GetFetchVariable();
- ASSERT_EQ(result.size(), inputs_.size());
- for (size_t i = 0; i < result.size(); ++i) {
- ASSERT_EQ(result[i].size(), inputs_[i].size());
- for (size_t j = 0; j < result[i].size(); ++j) {
- ASSERT_EQ(result[i][j], inputs_[i][j]);
- }
- }
- }
-}
-#else
-TEST_F(ExecutorTesterRandom, GPU) {
- std::vector places;
- GPUPlace gpu_place(0);
- places.push_back(gpu_place);
-
- // We have a global Scope and BuddyAllocator, and we must ensure
- // global BuddyAllocator is initialized before global Scope. Thus,
- // global Scope will deconstruct before BuddyAllocator. Otherwise,
- // "pointer being freed was not allocated" error will appear.
- // If paddle is compiled with GPU, both CPU and GPU BuddyAllocator
- // need to be used at first.
- paddle::memory::Used(CPUPlace());
- paddle::memory::Used(gpu_place);
-
- std::unique_ptr executor(new Executor(places));
-
- executor->Run(init_pdesc_, &GetGlobalScope(), 0);
- for (int batch_id = 0; batch_id < 3; batch_id++) {
- SetFeedVariable(inputs_, dims_);
- executor->Run(pdesc_, &GetGlobalScope(), 0);
- }
-}
-
-TEST_F(ExecutorTesterFeedAndFetch, GPU) {
- std::vector places;
- GPUPlace gpu_place(0);
- places.push_back(gpu_place);
- // We have a global Scope and BuddyAllocator, and we must ensure
- // global BuddyAllocator is initialized before global Scope. Thus,
- // global Scope will deconstruct before BuddyAllocator. Otherwise,
- // "pointer being freed was not allocated" error will appear.
- // If paddle is compiled with GPU, both CPU and GPU BuddyAllocator
- // need to be used at first.
- paddle::memory::Used(CPUPlace());
- paddle::memory::Used(gpu_place);
-
- std::unique_ptr executor(new Executor(places));
-
- for (int batch_id = 0; batch_id < 3; batch_id++) {
- SetFeedVariable(inputs_, dims_);
- executor->Run(pdesc_, &GetGlobalScope(), 0);
- std::vector> result = GetFetchVariable();
- PADDLE_ENFORCE_EQ(result.size(), inputs_.size());
- for (size_t i = 0; i < result.size(); ++i) {
- PADDLE_ENFORCE_EQ(result[i].size(), inputs_[i].size());
- for (size_t j = 0; j < result[i].size(); ++j) {
- PADDLE_ENFORCE_EQ(result[i][j], inputs_[i][j]);
- }
- }
- }
-}
-
-DECLARE_double(fraction_of_gpu_memory_to_use);
-
-int main(int argc, char** argv) {
- testing::InitGoogleTest(&argc, argv);
- // Use less GPU memory for unittest.
- FLAGS_fraction_of_gpu_memory_to_use = 0.25;
- return RUN_ALL_TESTS();
-}
-
-#endif
diff --git a/paddle/framework/feed_fetch_method.h b/paddle/framework/feed_fetch_method.h
new file mode 100644
index 0000000000..826d180bfc
--- /dev/null
+++ b/paddle/framework/feed_fetch_method.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License. */
+
+#pragma once
+#include "paddle/framework/scope.h"
+#include "paddle/framework/variable.h"
+
+namespace paddle {
+namespace framework {
+
+template
+void SetFeedVariable(const LoDTensor& input, const std::string& var_name,
+ size_t index) {
+ // If var_name Variable is not found in GlobalScope, a new variable will
+ // be created.
+ Variable* g_feed_value = GetGlobalScope().Var(var_name);
+ auto& feed_inputs =
+ *(g_feed_value->GetMutable>());
+ if (index >= feed_inputs.size()) {
+ feed_inputs.resize(index + 1);
+ }
+ // shared data with input tensor
+ feed_inputs[index].ShareDataWith(input);
+ // set lod
+ feed_inputs[index].set_lod(input.lod());
+}
+
+LoDTensor& GetFetchVariable(const std::string& var_name, size_t index) {
+ // Since we want to fetch LodTensor from a variable, the variable must
+ // be created alreadly.
+ Variable* g_fetch_value = GetGlobalScope().FindVar(var_name);
+ auto& fetch_outputs =
+ *(g_fetch_value->GetMutable>());
+ PADDLE_ENFORCE_LT(index, fetch_outputs.size());
+ return fetch_outputs[index];
+}
+
+} // namespace framework
+} // namespace paddle
diff --git a/paddle/framework/op_desc.cc b/paddle/framework/op_desc.cc
index 7f7cebb026..18fabe481d 100644
--- a/paddle/framework/op_desc.cc
+++ b/paddle/framework/op_desc.cc
@@ -239,5 +239,19 @@ void OpDescBind::InferShape(const BlockDescBind &block) const {
it->second(&ctx);
}
+void OpDescBind::InferVarType(BlockDescBind *block) const {
+ auto &info = OpInfoMap::Instance().Get(this->Type());
+ if (info.infer_var_type_) {
+ info.infer_var_type_(*this, block);
+ } else {
+ // all output type is LoDTensor by default
+ for (auto &out_pair : this->outputs_) {
+ for (auto &out_var_name : out_pair.second) {
+ block->Var(out_var_name)->SetType(VarDesc::LOD_TENSOR);
+ }
+ }
+ }
+}
+
} // namespace framework
} // namespace paddle
diff --git a/paddle/framework/op_desc.h b/paddle/framework/op_desc.h
index 73b5cf846f..313bf538ac 100644
--- a/paddle/framework/op_desc.h
+++ b/paddle/framework/op_desc.h
@@ -102,6 +102,8 @@ class OpDescBind {
void InferShape(const BlockDescBind &block) const;
+ void InferVarType(BlockDescBind *block) const;
+
void Flush();
private:
diff --git a/paddle/framework/op_info.h b/paddle/framework/op_info.h
index c504f69e30..e926180780 100644
--- a/paddle/framework/op_info.h
+++ b/paddle/framework/op_info.h
@@ -19,7 +19,6 @@
#include
#include "paddle/framework/attribute.h"
-#include "paddle/framework/op_desc.h"
#include "paddle/framework/type_defs.h"
#include "paddle/platform/macros.h"
@@ -31,6 +30,7 @@ struct OpInfo {
GradOpMakerFN grad_op_maker_;
OpProto* proto_{nullptr};
OpAttrChecker* checker_{nullptr};
+ InferVarTypeFN infer_var_type_;
bool HasOpProtoAndChecker() const {
return proto_ != nullptr && checker_ != nullptr;
diff --git a/paddle/framework/op_registry.cc b/paddle/framework/op_registry.cc
index 504afbd5db..c2f2438edf 100644
--- a/paddle/framework/op_registry.cc
+++ b/paddle/framework/op_registry.cc
@@ -43,12 +43,13 @@ static VariableNameMap ConvertOpDescVarsToVarNameMap(
return ret_val;
}
-std::unique_ptr OpRegistry::CreateOp(const OpDesc& op_desc) {
+std::unique_ptr OpRegistry::CreateOp(const OpDesc& op_desc,
+ ProgramDesc* program) {
VariableNameMap inputs = ConvertOpDescVarsToVarNameMap(op_desc.inputs());
VariableNameMap outputs = ConvertOpDescVarsToVarNameMap(op_desc.outputs());
AttributeMap attrs;
for (auto& attr : op_desc.attrs()) {
- attrs[attr.name()] = GetAttrValue(attr);
+ attrs[attr.name()] = GetAttrValue(attr, program);
}
return CreateOp(op_desc.type(), inputs, outputs, attrs);
diff --git a/paddle/framework/op_registry.h b/paddle/framework/op_registry.h
index 226e8ddcd4..d25b4abccb 100644
--- a/paddle/framework/op_registry.h
+++ b/paddle/framework/op_registry.h
@@ -45,18 +45,15 @@ class Registrar {
template
struct OperatorRegistrar : public Registrar {
- explicit OperatorRegistrar(const char* op_type) : op_type(op_type) {
+ explicit OperatorRegistrar(const char* op_type) {
PADDLE_ENFORCE(!OpInfoMap::Instance().Has(op_type),
"'%s' is registered more than once.", op_type);
static_assert(sizeof...(ARGS) != 0,
"OperatorRegistrar should be invoked at least by OpClass");
+ OpInfo info;
details::OperatorRegistrarRecursive<0, false, ARGS...>(op_type, &info);
OpInfoMap::Instance().Insert(op_type, info);
}
-
- const char* op_type;
-
- OpInfo info;
};
class OpRegistry {
@@ -77,21 +74,12 @@ class OpRegistry {
const VariableNameMap& outputs,
AttributeMap attrs);
- static std::unique_ptr CreateOp(const OpDesc& op_desc);
+ static std::unique_ptr CreateOp(const OpDesc& op_desc,
+ ProgramDesc* program);
static std::unique_ptr CreateOp(const OpDescBind& op_desc);
};
-template
-class OpRegistrar : public Registrar {
- public:
- explicit OpRegistrar(const char* op_type) { OpRegistrar(op_type, ""); }
- OpRegistrar(const char* op_type, const char* grad_op_type) {
- OpRegistry::RegisterOp(op_type,
- grad_op_type);
- }
-};
-
template
struct OpKernelRegistrarFunctor;
diff --git a/paddle/framework/op_registry_test.cc b/paddle/framework/op_registry_test.cc
index b860fe6cac..6289125d7c 100644
--- a/paddle/framework/op_registry_test.cc
+++ b/paddle/framework/op_registry_test.cc
@@ -74,7 +74,7 @@ TEST(OpRegistry, CreateOp) {
attr->set_type(paddle::framework::AttrType::FLOAT);
attr->set_f(scale);
- auto op = paddle::framework::OpRegistry::CreateOp(op_desc);
+ auto op = paddle::framework::OpRegistry::CreateOp(op_desc, nullptr);
paddle::framework::Scope scope;
paddle::platform::CPUDeviceContext dev_ctx;
op->Run(scope, dev_ctx);
@@ -95,7 +95,7 @@ TEST(OpRegistry, IllegalAttr) {
bool caught = false;
try {
- paddle::framework::OpRegistry::CreateOp(op_desc);
+ paddle::framework::OpRegistry::CreateOp(op_desc, nullptr);
} catch (paddle::platform::EnforceNotMet err) {
caught = true;
std::string msg = "larger_than check fail";
@@ -115,7 +115,7 @@ TEST(OpRegistry, DefaultValue) {
ASSERT_TRUE(op_desc.IsInitialized());
- auto op = paddle::framework::OpRegistry::CreateOp(op_desc);
+ auto op = paddle::framework::OpRegistry::CreateOp(op_desc, nullptr);
paddle::framework::Scope scope;
paddle::platform::CPUDeviceContext dev_ctx;
op->Run(scope, dev_ctx);
@@ -131,7 +131,7 @@ TEST(OpRegistry, CustomChecker) {
// attr 'test_attr' is not set
bool caught = false;
try {
- paddle::framework::OpRegistry::CreateOp(op_desc);
+ paddle::framework::OpRegistry::CreateOp(op_desc, nullptr);
} catch (paddle::platform::EnforceNotMet err) {
caught = true;
std::string msg = "Attribute 'test_attr' is required!";
@@ -149,7 +149,7 @@ TEST(OpRegistry, CustomChecker) {
attr->set_i(3);
caught = false;
try {
- paddle::framework::OpRegistry::CreateOp(op_desc);
+ paddle::framework::OpRegistry::CreateOp(op_desc, nullptr);
} catch (paddle::platform::EnforceNotMet err) {
caught = true;
std::string msg = "'test_attr' must be even!";
@@ -166,7 +166,7 @@ TEST(OpRegistry, CustomChecker) {
attr->set_name("test_attr");
attr->set_type(paddle::framework::AttrType::INT);
attr->set_i(4);
- auto op = paddle::framework::OpRegistry::CreateOp(op_desc);
+ auto op = paddle::framework::OpRegistry::CreateOp(op_desc, nullptr);
paddle::platform::CPUDeviceContext dev_ctx;
paddle::framework::Scope scope;
op->Run(scope, dev_ctx);
diff --git a/paddle/framework/operator_test.cc b/paddle/framework/operator_test.cc
index d7890ac8d0..c358f1a2b6 100644
--- a/paddle/framework/operator_test.cc
+++ b/paddle/framework/operator_test.cc
@@ -83,7 +83,7 @@ TEST(OperatorBase, all) {
paddle::platform::CPUDeviceContext device_context;
paddle::framework::Scope scope;
- auto op = paddle::framework::OpRegistry::CreateOp(op_desc);
+ auto op = paddle::framework::OpRegistry::CreateOp(op_desc, nullptr);
scope.Var("OUT1");
ASSERT_EQ(paddle::framework::op_run_num, 0);
op->Run(scope, device_context);
@@ -208,7 +208,7 @@ TEST(OpKernel, all) {
paddle::platform::CPUDeviceContext cpu_device_context;
paddle::framework::Scope scope;
- auto op = paddle::framework::OpRegistry::CreateOp(op_desc);
+ auto op = paddle::framework::OpRegistry::CreateOp(op_desc, nullptr);
ASSERT_EQ(paddle::framework::cpu_kernel_run_num, 0);
op->Run(scope, cpu_device_context);
ASSERT_EQ(paddle::framework::cpu_kernel_run_num, 1);
@@ -244,7 +244,7 @@ TEST(OpKernel, multi_inputs) {
scope.Var("y0")->GetMutable();
scope.Var("y1")->GetMutable();
- auto op = paddle::framework::OpRegistry::CreateOp(op_desc);
+ auto op = paddle::framework::OpRegistry::CreateOp(op_desc, nullptr);
op->Run(scope, cpu_device_context);
}
diff --git a/paddle/framework/program_desc.cc b/paddle/framework/program_desc.cc
index fcb7292884..df846f115a 100644
--- a/paddle/framework/program_desc.cc
+++ b/paddle/framework/program_desc.cc
@@ -18,27 +18,10 @@ limitations under the License. */
namespace paddle {
namespace framework {
-using ProgDescMap =
- std::unordered_map>;
-static ProgDescMap *g_bind_map = nullptr;
-
-ProgramDescBind &ProgramDescBind::Instance(ProgramDesc *prog) {
- if (g_bind_map == nullptr) {
- g_bind_map = new ProgDescMap();
- }
- auto &map = *g_bind_map;
- auto &ptr = map[prog];
-
- if (ptr == nullptr) {
- ptr.reset(new ProgramDescBind(prog));
- }
- return *ptr;
-}
-
BlockDescBind *ProgramDescBind::AppendBlock(const BlockDescBind &parent) {
- auto *b = prog_->add_blocks();
+ auto *b = prog_.add_blocks();
b->set_parent_idx(parent.ID());
- b->set_idx(prog_->blocks_size() - 1);
+ b->set_idx(prog_.blocks_size() - 1);
blocks_.emplace_back(new BlockDescBind(this, b));
return blocks_.back().get();
}
@@ -47,14 +30,14 @@ ProgramDesc *ProgramDescBind::Proto() {
for (auto &block : blocks_) {
block->Flush();
}
- return prog_;
+ return &prog_;
}
-ProgramDescBind::ProgramDescBind(ProgramDesc *prog) {
- prog_ = prog;
- for (auto &block : *prog->mutable_blocks()) {
- blocks_.emplace_back(new BlockDescBind(this, &block));
- }
+ProgramDescBind::ProgramDescBind() {
+ auto *block = prog_.mutable_blocks()->Add();
+ block->set_idx(0);
+ block->set_parent_idx(-1);
+ blocks_.emplace_back(new BlockDescBind(this, block));
}
} // namespace framework
} // namespace paddle
diff --git a/paddle/framework/program_desc.h b/paddle/framework/program_desc.h
index f29b1c54e7..514b62654d 100644
--- a/paddle/framework/program_desc.h
+++ b/paddle/framework/program_desc.h
@@ -26,7 +26,7 @@ class BlockDescBind;
class ProgramDescBind {
public:
- static ProgramDescBind &Instance(ProgramDesc *prog);
+ ProgramDescBind();
BlockDescBind *AppendBlock(const BlockDescBind &parent);
@@ -37,10 +37,7 @@ class ProgramDescBind {
ProgramDesc *Proto();
private:
- explicit ProgramDescBind(ProgramDesc *prog);
-
- // Not owned
- ProgramDesc *prog_;
+ ProgramDesc prog_;
std::vector> blocks_;
diff --git a/paddle/framework/scope.cc b/paddle/framework/scope.cc
index 8f8a53eec8..5bf5e91f25 100644
--- a/paddle/framework/scope.cc
+++ b/paddle/framework/scope.cc
@@ -65,16 +65,12 @@ void Scope::DropKids() {
kids_.clear();
}
-std::once_flag feed_variable_flag;
-
framework::Scope& GetGlobalScope() {
- static std::unique_ptr g_scope{nullptr};
- std::call_once(feed_variable_flag, [&]() {
- g_scope.reset(new framework::Scope());
- g_scope->Var("feed_value");
- g_scope->Var("fetch_value");
- });
- return *(g_scope.get());
+ static framework::Scope* g_scope = nullptr;
+ if (g_scope == nullptr) {
+ g_scope = new framework::Scope();
+ }
+ return *g_scope;
}
} // namespace framework
diff --git a/paddle/framework/selected_rows.h b/paddle/framework/selected_rows.h
index f9f563051e..cd90781371 100644
--- a/paddle/framework/selected_rows.h
+++ b/paddle/framework/selected_rows.h
@@ -10,6 +10,7 @@ See the License for the specific language governing permissions and
limitations under the License. */
#pragma once
+#include "paddle/framework/lod_tensor.h"
#include "paddle/framework/tensor.h"
namespace paddle {
@@ -34,9 +35,9 @@ class SelectedRows {
void set_height(int64_t height) { height_ = height; }
- const std::vector& rows() const { return rows_; }
+ const Vector& rows() const { return rows_; }
- void set_rows(const std::vector& rows) { rows_ = rows; }
+ void set_rows(const Vector& rows) { rows_ = rows; }
DDim GetCompleteDims() const {
std::vector dims = vectorize(value_->dims());
@@ -45,7 +46,10 @@ class SelectedRows {
}
private:
- std::vector rows_;
+ // Notice: rows can be duplicate. We can have {0, 4, 7, 0, 5, 7, 9} here.
+ // SelectedRows are simplely concated when adding together. Until a
+ // SelectedRows add a Tensor, will the duplicate rows be handled.
+ Vector rows_;
std::unique_ptr value_{nullptr};
int64_t height_;
};
diff --git a/paddle/framework/type_defs.h b/paddle/framework/type_defs.h
index 0d1564a751..00da728939 100644
--- a/paddle/framework/type_defs.h
+++ b/paddle/framework/type_defs.h
@@ -16,12 +16,18 @@
#include
#include