提交 079b8243 编写于 作者: Y ying

follow comments.

上级 b915fde9
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
### 使用流程 ### 使用流程
使用 C-API 分为:准备工作和预测程序开发两部分。 使用 C-API 分为:准备预测模型和预测程序开发两部分。
- 准备 - 准备预测模型
1. 将神经网络模型结构进行序列化。 1. 将神经网络模型结构进行序列化。
- 调用C-API预测时,需要提供序列化之后的网络结构和训练好的模型参数文件。 - 调用C-API预测时,需要提供序列化之后的网络结构和训练好的模型参数文件。
1. 将PaddlePaddle训练出的模型参数文件(多个)合并成一个文件。 1. 将PaddlePaddle训练出的模型参数文件(多个)合并成一个文件。
...@@ -14,18 +14,17 @@ ...@@ -14,18 +14,17 @@
- **注意**:以上两种方式只需选择其一即可。 - **注意**:以上两种方式只需选择其一即可。
- 调用 PaddlePaddle C-API 开发预测序 - 调用 PaddlePaddle C-API 开发预测序
1. 初始化PaddlePaddle运行环境。 1. 初始化PaddlePaddle运行环境。
1. 创建神经网络的输入,组织输入数据。
1. 加载模型。 1. 加载模型。
1. 创建神经网络的输入,组织输入数据。
1. 进行前向计算,获得计算结果。 1. 进行前向计算,获得计算结果。
1. 清理。 1. 清理。
本文档以手写数字识别任务为例,介绍如何使用 C-API 进行预测,完整代码请查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)
这里我们以手写数字识别任务为例,介绍如何使用 C-API 进行预测,完整代码请查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) ### 准备预测模型
运行目录下的 `python mnist_v2.py` 可以使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。脚本中的模型定义了一个简单的含有[两个隐层的全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。训练好的模型默认保存在当前运行目录下的`models`目录中。下面,我们将调用 C-API 加载训练好的模型进行预测。
通过在终端执行`python mnist_v2.py`
### 外部准备 运行[目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)下的 `mnist_v2.py` s可以使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。脚本中的模型定义了一个简单的含有[两个隐层的全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。训练好的模型默认保存在当前运行目录下的`models`目录中。下面,我们将调用 C-API 加载训练好的模型进行预测。
1. 序列化神经网络模型配置 1. 序列化神经网络模型配置
...@@ -52,8 +51,7 @@ ...@@ -52,8 +51,7 @@
代码示例如下: 代码示例如下:
```python ```python
from paddle.utils.merge_model import merge_v2_model from paddle.utils.merge_model import merge_v2_modelss
from mnist_v2 import network from mnist_v2 import network
net = network(is_infer=True) net = network(is_infer=True)
...@@ -70,54 +68,65 @@ ...@@ -70,54 +68,65 @@
### 编写预测代码 ### 编写预测代码
#### step 1. 初始化及加载模型 #### step 1. 初始化PaddlePaddle运行环境
使用C-API第一步需首先调用[`paddle_init`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/main.h#L27) 初始化PaddlePaddle运行环境。接口接受两个参数:参数的个数和参数。
1. 初始化PaddlePaddle运行环境。 下面的代码片段在初始化PaddlePaddle运行环境时指定不使用GPU:
```c
// Initalize the PaddlePaddle runtime environment.
char* argv[] = {"--use_gpu=False"};
CHECK(paddle_init(1, (char**)argv));
```
1. 加载训练好的模型。 ```c
// Initalize the PaddlePaddle runtime environment.
char* argv[] = {"--use_gpu=False"};
CHECK(paddle_init(1, (char**)argv));
```
这里需要介绍C-API使用中的一个重要概念:Gradient Machine。概念上,在 PaddlePaddle 内部,一个GradientMachine类的对象管理着一组计算层(PaddlePaddle Layers)来完成前向和反向计算,并处理与之相关的所有细节。特别的,在调用C-API预测时只需进行前向计算。这篇文档的之后部分我们会使用`gradient machine`来特指调用PaddlePaddle C-API创建的GradientMachine类的对象。 下面的代码片段在初始化PaddlePaddle运行环境时指定了两个参数:不使用GPU和[使用MKLDNN](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/design/mkl/mkldnn.md)
每一个 `gradient machine` 都会管理维护一份训练好的模型,模型可以通过以下两种方式获取: ```c
1. 从磁盘加载;这时`gradient machine`会独立拥有一份训练好的模型; char* argv[] = {"--use_gpu=False", "--use_mkldnn=True"};
1. 共享自其它`gradient machine`的模型;这种情况多出现在使用多线程预测时; CHECK(paddle_init(2, (char**)argv));
```
下面的代码片段创建 `gradient machine`,并从指定路径加载训练好的模型。 #### step2. 加载模型
```c 这里介绍C-API使用中的一个重要概念:Gradient Machine。概念上,在 PaddlePaddle 内部一个GradientMachine类的对象管理着一组计算层(PaddlePaddle Layers)来完成前向和反向计算,并处理与之相关的所有细节。在调用C-API预测时,只需进行前向计算而无需调用反向计算。这篇文档的之后部分我们会使用`gradient machine`来特指调用PaddlePaddle C-API创建的GradientMachine类的对象。
// Read the binary configuration file generated by `convert_protobin.sh`
long size;
void* buf = read_config(CONFIG_BIN, &size);
// Create the gradient machine for inference. 每一个 `gradient machine` 都会管理维护一份训练好的模型,下面是两种最常用的模型加载方式:
paddle_gradient_machine machine;
CHECK(paddle_gradient_machine_create_for_inference(&machine, buf, (int)size));
// Load the trained model. Modify the parameter MODEL_PATH to set the correct 1. 从磁盘加载:这时`gradient machine`会独立拥有一份训练好的模型;
// path of the trained model. 1. 共享自其它`gradient machine`的模型:这种情况多出现在使用多线程预测时,通过多个线程共享同一个模型来减少内存开销。可参考[此示例](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/multi_thread/main.c)
CHECK(paddle_gradient_machine_load_parameter_from_disk(machine, MODEL_PATH));
``` 下面的代码片段创建 `gradient machine`,并从指定路径加载训练好的模型。
```c
// Read the binary configuration file generated by `convert_protobin.sh`
long size;
void* buf = read_config(CONFIG_BIN, &size);
// Create the gradient machine for inference.
paddle_gradient_machine machine;
CHECK(paddle_gradient_machine_create_for_inference(&machine, buf, (int)size));
// Load the trained model. Modify the parameter MODEL_PATH to set the correct
// path of the trained model.
CHECK(paddle_gradient_machine_load_parameter_from_disk(machine, MODEL_PATH));
```
##### 注意事项 ##### 注意事项
1. 以上代码片段使用“仅序列化神经网络结构”的方式加载模型,需要同时指定模型参数存储的路径。 1. 以上代码片段使用“仅序列化神经网络结构”的方式加载模型,需要同时指定模型参数存储的路径。
- 使用PaddlePaddle V2 API训练,模型中所有可学习参数会被存为一个压缩文件,需要手动进行解压,将它们放在同一目录中,C-API不会直接加载 V2 API 存储的压缩文件。 - 使用PaddlePaddle V2 API训练,模型中所有可学习参数会被存为一个压缩文件,需要手动进行解压,将它们放在同一目录中,C-API不会直接加载 V2 API 存储的压缩文件。
1. 如果使用`merge model`方式将神经网络结构和训练好的参数序列化到一个文件,请参考此[示例](https://github.com/PaddlePaddle/Mobile/blob/develop/Demo/linux/paddle_image_recognizer.cpp#L59) 1. 如果使用`merge model`方式将神经网络结构和训练好的参数序列化到一个文件,请参考此[示例](https://github.com/PaddlePaddle/Mobile/blob/develop/Demo/linux/paddle_image_recognizer.cpp#L59)
1. 加载模型有多种方式,也可以在程序运行过程中再加载另外一个模型。
#### step 2. 创建神经网络输入,组织输入数据 #### step 2. 创建神经网络输入,组织输入数据
基本使用概念: 基本使用概念:
- 在PaddlePaddle内部,神经网络中一个计算层的输入/输出被组织为一个 `Argument` 结构体,如果神经网络有多个输入或者多个输入,每一个输入/输入都会对应有自己的`Argument` - 在PaddlePaddle内部,神经网络中一个计算层的输入输出被组织为一个 `Argument` 结构体,如果神经网络有多个输入或者多个输出,每一个输入/输出都会对应有自己的`Argument`
- `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 - `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。
-`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 -`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。
*注:本文档使用的示例任务手写数字识别不涉及一维整型数组作为输入,因此,本文档仅讨论二维稠密矩阵作为输入的情形。更多输入数据格式请参考输入/输出数据一节的内容。* *注:本文档使用的示例任务手写数字识别不涉及一维整型数组作为输入,因此,本文档仅讨论二维稠密矩阵作为输入的情形。更多输入数据格式请参考输入/输出数据一节的内容。*
这篇文档的之后部分会使用`argument`**特指** PaddlePaddle C-API中神经网的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 这篇文档的之后部分会使用`argument`**特指** PaddlePaddle C-API中神经网的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。
于是,在组织神经网络输入,获取输出时,需要思考完成以下工作: 于是,在组织神经网络输入,获取输出时,需要思考完成以下工作:
1. 为每一个输入/输出创建`argument` 1. 为每一个输入/输出创建`argument`
......
## 编译 PaddlePaddle 链接 ## 编译 PaddlePaddle 预测
### 概述 ### 概述
使用 C-API 进行预测依赖于将 PaddlePaddle 核心代码编译成链接库,只需在编译时指定编译选项:`-DWITH_C_API=ON`。同时,**建议将:`DWITH_PYTHON`,`DWITH_SWIG_PY`,`DWITH_GOLANG`,均设置为`OFF`**,以避免链接不必要的库。其它编译选项按需进行设定。 使用 C-API 进行预测依赖于将 PaddlePaddle 核心代码编译成链接库,只需在编译时需配制下面这些编译选项:
必须配置选项:
- `WITH_C_API`,必须配置为`ON`
推荐配置选项:
- `WITH_PYTHON`,推荐配置为`OFF`
- `WITH_SWIG_PY`,推荐配置为`OFF`
- `WITH_GOLANG`,推荐设置为`OFF`
可选配置选项:
- `WITH_GPU`,可配置为`ON/OFF`
- `WITH_MKL`,可配置为`ON/OFF`
对推荐配置中的选项建议按照设置,以避免链接不必要的库。其它可选编译选项按需进行设定。
下面的代码片段从github拉取最新代码,配制编译选项(需要将PADDLE_ROOT替换为PaddlePaddle预测库的安装路径):
```shell ```shell
INSTALL_PREFIX=/path/of/capi/ PADDLE_ROOT=/path/of/capi
PADDLE_ROOT=/path/of/paddle_source/ git clone https://github.com/PaddlePaddle/Paddle.git
cmake $PADDLE_ROOT -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \ cd Paddle
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=$PADDLE_ROOT \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DWITH_C_API=ON \ -DWITH_C_API=ON \
-DWITH_SWIG_PY=OFF \ -DWITH_SWIG_PY=OFF \
-DWITH_GOLANG=OFF \ -DWITH_GOLANG=OFF \
-DWITH_PYTHON=OFF \ -DWITH_PYTHON=OFF \
-DWITH_MKLML=OFF \ -DWITH_MKL=OFF \
-DWITH_MKLDNN=OFF \ -DWITH_GPU=OFF \
-DWITH_GPU=OFF \ ..
...
``` ```
在上面的代码片段中,`PADDLE_ROOT` 表示 PaddlePaddle 源码所在目录,生成Makefile文件后执行:`make && make install`。成功执行后,使用CAPI所需的依赖(包括:(1)编译出的PaddlePaddle 链接和头文件;(2)第三方链接库和头文件)均会存放于`INSTALL_PREFIX`目录中。
编译成功后在 `INSTALL_PREFIX` 下会看到如下目录结构(包括了编译出的PaddlePaddle头文件和链接库,以及第三方依赖链接库和头文件(如果需要,由链接方式决定)): 执行上述代码生成Makefile文件后,执行:`make && make install`。成功编译后,使用C-API所需的依赖(包括:(1)编译出的PaddlePaddle预测库和头文件;(2)第三方链接库和头文件)均会存放于`PADDLE_ROOT`目录中。
编译成功后在 `PADDLE_ROOT` 下会看到如下目录结构(包括了编译出的PaddlePaddle头文件和链接库,以及第三方依赖链接库和头文件(如果需要,由链接方式决定)):
```text ```text
├── include ├── include
...@@ -38,29 +57,62 @@ cmake $PADDLE_ROOT -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \ ...@@ -38,29 +57,62 @@ cmake $PADDLE_ROOT -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \
├── lib ├── lib
│   ├── libpaddle_capi_engine.a │   ├── libpaddle_capi_engine.a
│   ├── libpaddle_capi_layers.a │   ├── libpaddle_capi_layers.a
│   ├── libpaddle_capi_shared.dylib │   ├── libpaddle_capi_shared.so
│   └── libpaddle_capi_whole.a │   └── libpaddle_capi_whole.a
└── third_party └── third_party
├── ...... ├── gflags
│   ├── include
│   │   └── gflags
│   │   ├── gflags_completions.h
│   │   ├── gflags_declare.h
│   │   ...
│   └── lib
│   └── libgflags.a
├── glog
│   ├── include
│   │   └── glog
│   │   ├── config.h
│   │   ...
│   └── lib
│   └── libglog.a
├── openblas
│   ├── include
│   │   ├── cblas.h
│   │   ...
│   └── lib
│   ...
├── protobuf
│   ├── include
│   │   └── google
│   │   └── protobuf
│   │   ...
│   └── lib
│   └── libprotobuf-lite.a
└── zlib
├── include
│   ...
└── lib
...
``` ```
### 链接方式说明 ### 链接说明
目前提供三种链接方式: 目前提供三种链接方式:
1. 链接`libpaddle_capi_shared.so` 动态库 1. 链接`libpaddle_capi_shared.so` 动态库
- 使用 PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_shared.so`时,需注意: - 使用 PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_shared.so`时,需注意:
1. 如果编译时指定编译CPU版本,且使用`OpenBLAS`矩阵库,在使用CAPI开发预测程序时,只需要链接`libpaddle_capi_shared.so`这一个库。 1. 如果编译时指定编译CPU版本,且使用`OpenBLAS`数学库,在使用C-API开发预测程序时,只需要链接`libpaddle_capi_shared.so`这一个库。
1. 如果是用编译时指定CPU版本,且使用`MKL`矩阵库,由于`MKL`库有自己独立的动态库文件,在使用PaddlePaddle CAPI开发预测程序时,需要自己链接MKL链接库。 1. 如果是用编译时指定CPU版本,且使用`MKL`数学库,由于`MKL`库有自己独立的动态库文件,在使用PaddlePaddle C-API开发预测程序时,需要自己链接MKL链接库。
1. 如果编译时指定编译GPU版本,CUDA相关库会在预测程序运行时动态装载,需要将CUDA相关的库设置到`LD_LIBRARY_PATH`环境变量中。 1. 如果编译时指定编译GPU版本,CUDA相关库会在预测程序运行时动态装载,需要将CUDA相关的库设置到`LD_LIBRARY_PATH`环境变量中。
- 这种方式最为简便,链接相对容易,**在无特殊需求情况下,推荐使用此方式** - 这种方式最为简便,链接相对容易,**在无特殊需求情况下,推荐使用此方式**
2. 链接静态库 `libpaddle_capi_whole.a` 2. 链接静态库 `libpaddle_capi_whole.a`
- 使用PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_whole.a`时,需注意: - 使用PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_whole.a`时,需注意:
1. 需要指定`-Wl,--whole-archive`链接选项。 1. 需要指定`-Wl,--whole-archive`链接选项。
1. 需要显式地链接 `gflags``glog``libz``protobuf` 等第三方库,可在`INSTALL_PREFIX\third_party`下找到。 1. 需要显式地链接 `gflags``glog``libz``protobuf` 等第三方库,可在`PADDLE_ROOT/third_party`下找到。
1. 如果在编译 C-API 时使用OpenBLAS矩阵库,需要显示地链接`libopenblas.a` 1. 如果在编译 C-API 时使用OpenBLAS数学库,需要显示地链接`libopenblas.a`
1. 如果在编译 C-API 是使用 MKL 矩阵库,需要显示地链接 MKL 的动态库。 1. 如果在编译 C-API 是使用MKL数学库,需要显示地链接MKL的动态库。
3. 链接静态库 `libpaddle_capi_layers.a``libpaddle_capi_engine.a` 3. 链接静态库 `libpaddle_capi_layers.a``libpaddle_capi_engine.a`
- 使用PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_whole.a`时,需注意: - 使用PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_whole.a`时,需注意:
......
...@@ -4,6 +4,6 @@ PaddlePaddle C-API ...@@ -4,6 +4,6 @@ PaddlePaddle C-API
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
compile_paddle_lib.md compile_paddle_lib_cn.md
organization_of_the_inputs.md organization_of_the_inputs_cn.md
a_simple_example.md a_simple_example_cn.md
...@@ -260,6 +260,8 @@ ...@@ -260,6 +260,8 @@
</table> </table>
</html> </html>
<br> <br>
### 输出数据 ### 输出数据
PaddlePaddle中一个计算层的输出数据组织方式和输入数据组织方式完全相同。一个输出数据同样被组织为一个`argument``argument`通过`paddle_matrix``paddle_ivector`存数数据,如果输出是一个序列,那么会携带有`sequence_start_positions`信息。调用C-API相关接口,读取需要的结果即可。 PaddlePaddle中一个计算层的输出数据组织方式和输入数据组织方式完全相同。一个输出数据同样被组织为一个`argument``argument`通过`paddle_matrix``paddle_ivector`存数数据,如果输出是一个序列,那么会携带有`sequence_start_positions`信息。调用C-API相关接口,读取需要的结果即可。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册