Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
PaddleDetection
提交
174a3b3e
P
PaddleDetection
项目概览
PaddlePaddle
/
PaddleDetection
1 年多 前同步成功
通知
696
Star
11112
Fork
2696
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
184
列表
看板
标记
里程碑
合并请求
40
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
PaddleDetection
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
184
Issue
184
列表
看板
标记
里程碑
合并请求
40
合并请求
40
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
174a3b3e
编写于
8月 08, 2017
作者:
Y
Yu Yang
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'develop' of github.com:baidu/Paddle into feature/uniform_random_op
上级
b861c019
d9f97b02
变更
28
隐藏空白更改
内联
并排
Showing
28 changed file
with
560 addition
and
97 deletion
+560
-97
Dockerfile
Dockerfile
+1
-1
doc/api/v2/config/layer.rst
doc/api/v2/config/layer.rst
+5
-0
doc/design/releasing_process.md
doc/design/releasing_process.md
+9
-0
paddle/framework/CMakeLists.txt
paddle/framework/CMakeLists.txt
+3
-0
paddle/framework/operator.cc
paddle/framework/operator.cc
+5
-5
paddle/framework/operator.h
paddle/framework/operator.h
+11
-11
paddle/framework/tensor.h
paddle/framework/tensor.h
+2
-2
paddle/framework/tensor_impl.h
paddle/framework/tensor_impl.h
+14
-13
paddle/framework/tensor_test.cc
paddle/framework/tensor_test.cc
+4
-2
paddle/gserver/layers/SubNestedSequenceLayer.cpp
paddle/gserver/layers/SubNestedSequenceLayer.cpp
+176
-0
paddle/gserver/tests/test_LayerGrad.cpp
paddle/gserver/tests/test_LayerGrad.cpp
+78
-0
paddle/operators/add_op.cc
paddle/operators/add_op.cc
+1
-2
paddle/operators/cross_entropy_op.cc
paddle/operators/cross_entropy_op.cc
+13
-12
paddle/operators/fill_zeros_like_op.cc
paddle/operators/fill_zeros_like_op.cc
+8
-8
paddle/operators/mean_op.cc
paddle/operators/mean_op.cc
+4
-4
paddle/operators/net_op.h
paddle/operators/net_op.h
+3
-3
paddle/operators/sgd_op.cc
paddle/operators/sgd_op.cc
+5
-5
paddle/operators/softmax_op.cc
paddle/operators/softmax_op.cc
+13
-13
paddle/parameter/Argument.cpp
paddle/parameter/Argument.cpp
+20
-0
paddle/parameter/Argument.h
paddle/parameter/Argument.h
+24
-0
paddle/platform/enforce.h
paddle/platform/enforce.h
+4
-13
paddle/platform/enforce_test.cc
paddle/platform/enforce_test.cc
+27
-1
paddle/scripts/docker/build.sh
paddle/scripts/docker/build.sh
+6
-1
python/paddle/trainer/config_parser.py
python/paddle/trainer/config_parser.py
+25
-0
python/paddle/trainer_config_helpers/layers.py
python/paddle/trainer_config_helpers/layers.py
+49
-0
python/paddle/trainer_config_helpers/tests/configs/file_list.sh
.../paddle/trainer_config_helpers/tests/configs/file_list.sh
+2
-1
python/paddle/trainer_config_helpers/tests/configs/protostr/test_seq_select_layers.protostr
...rs/tests/configs/protostr/test_seq_select_layers.protostr
+37
-0
python/paddle/trainer_config_helpers/tests/configs/test_seq_select_layers.py
...er_config_helpers/tests/configs/test_seq_select_layers.py
+11
-0
未找到文件。
Dockerfile
浏览文件 @
174a3b3e
...
@@ -47,7 +47,7 @@ ENV GOROOT=/usr/local/go GOPATH=/root/gopath
...
@@ -47,7 +47,7 @@ ENV GOROOT=/usr/local/go GOPATH=/root/gopath
# should not be in the same line with GOROOT definition, otherwise docker build could not find GOROOT.
# should not be in the same line with GOROOT definition, otherwise docker build could not find GOROOT.
ENV
PATH=${PATH}:${GOROOT}/bin:${GOPATH}/bin
ENV
PATH=${PATH}:${GOROOT}/bin:${GOPATH}/bin
# install glide
# install glide
RUN
curl
-q
https://glide.sh/get | sh
RUN
curl
-
s
-
q
https://glide.sh/get | sh
# git credential to skip password typing
# git credential to skip password typing
RUN
git config
--global
credential.helper store
RUN
git config
--global
credential.helper store
...
...
doc/api/v2/config/layer.rst
浏览文件 @
174a3b3e
...
@@ -257,6 +257,11 @@ seq_concat
...
@@ -257,6 +257,11 @@ seq_concat
.. autoclass:: paddle.v2.layer.seq_concat
.. autoclass:: paddle.v2.layer.seq_concat
:noindex:
:noindex:
sub_nested_seq
--------------
.. autoclass:: paddle.v2.layer.sub_nested_seq
:noindex:
Reshaping Layers
Reshaping Layers
================
================
...
...
doc/design/releasing_process.md
浏览文件 @
174a3b3e
...
@@ -11,6 +11,15 @@ Paddle每次发新的版本,遵循以下流程:
...
@@ -11,6 +11,15 @@ Paddle每次发新的版本,遵循以下流程:
*
编译这个版本的Ubuntu Deb包。如果失败,修复Ubuntu Deb包编译问题,Patch号加一,返回第二步。
*
编译这个版本的Ubuntu Deb包。如果失败,修复Ubuntu Deb包编译问题,Patch号加一,返回第二步。
*
使用Regression Test List作为检查列表,测试Docker镜像/ubuntu安装包的功能正确性
*
使用Regression Test List作为检查列表,测试Docker镜像/ubuntu安装包的功能正确性
*
如果失败,记录下所有失败的例子,在这个
`release/版本号`
分支中,修复所有bug后,Patch号加一,返回第二步
*
如果失败,记录下所有失败的例子,在这个
`release/版本号`
分支中,修复所有bug后,Patch号加一,返回第二步
*
编译这个版本的python wheel包,并发布到pypi。
*
由于pypi.python.org目前遵循
[
严格的命名规范PEP 513
](
https://www.python.org/dev/peps/pep-0513
)
,在使用twine上传之前,需要重命名wheel包中platform相关的后缀,比如将
`linux_x86_64`
修改成
`manylinux1_x86_64`
。
*
pypi上的package名称为paddlepaddle和paddlepaddle_gpu,如果要上传GPU版本的包,需要修改build/python/setup.py中,name: "paddlepaddle_gpu"并重新打包wheel包:
`python setup.py bdist_wheel`
。
*
上传方法:
```
cd build/python
pip install twine
twine upload dist/[package to upload]
```
4.
第三步完成后,将
`release/版本号`
分支合入master分支,并删除
`release/版本号`
分支。将master分支的合入commit打上tag,tag为
`版本号`
。同时再将
`master`
分支合入
`develop`
分支。最后删除
`release/版本号`
分支。
4.
第三步完成后,将
`release/版本号`
分支合入master分支,并删除
`release/版本号`
分支。将master分支的合入commit打上tag,tag为
`版本号`
。同时再将
`master`
分支合入
`develop`
分支。最后删除
`release/版本号`
分支。
5.
编译master分支的Docker发行镜像,发布到dockerhub。编译ubuntu的deb包,发布到github release页面
5.
编译master分支的Docker发行镜像,发布到dockerhub。编译ubuntu的deb包,发布到github release页面
6.
协同完成Release Note的书写
6.
协同完成Release Note的书写
...
...
paddle/framework/CMakeLists.txt
浏览文件 @
174a3b3e
...
@@ -35,6 +35,8 @@ add_dependencies(framework_py_proto framework_py_proto_init)
...
@@ -35,6 +35,8 @@ add_dependencies(framework_py_proto framework_py_proto_init)
cc_library
(
backward SRCS backward.cc DEPS net_op
)
cc_library
(
backward SRCS backward.cc DEPS net_op
)
cc_test
(
backward_test SRCS backward_test.cc DEPS backward
)
cc_test
(
backward_test SRCS backward_test.cc DEPS backward
)
if
(
WITH_PYTHON
)
cc_library
(
paddle_pybind SHARED
cc_library
(
paddle_pybind SHARED
SRCS pybind.cc
SRCS pybind.cc
DEPS pybind python backward
DEPS pybind python backward
...
@@ -46,3 +48,4 @@ cc_library(paddle_pybind SHARED
...
@@ -46,3 +48,4 @@ cc_library(paddle_pybind SHARED
recurrent_op
recurrent_op
uniform_random_op
uniform_random_op
fill_zeros_like_op
)
fill_zeros_like_op
)
endif
(
WITH_PYTHON
)
paddle/framework/operator.cc
浏览文件 @
174a3b3e
...
@@ -34,8 +34,8 @@ ExecutionContext::GetEigenDevice<platform::GPUPlace, Eigen::GpuDevice>() const {
...
@@ -34,8 +34,8 @@ ExecutionContext::GetEigenDevice<platform::GPUPlace, Eigen::GpuDevice>() const {
#endif
#endif
const
std
::
string
&
OperatorBase
::
Input
(
const
std
::
string
&
name
)
const
{
const
std
::
string
&
OperatorBase
::
Input
(
const
std
::
string
&
name
)
const
{
PADDLE_ENFORCE
(
in_out_idxs_
!=
nullptr
,
PADDLE_ENFORCE
_NOT_NULL
(
in_out_idxs_
,
"Input Output Indices could not be nullptr"
);
"Input Output Indices could not be nullptr"
);
auto
it
=
in_out_idxs_
->
find
(
name
);
auto
it
=
in_out_idxs_
->
find
(
name
);
PADDLE_ENFORCE
(
it
!=
in_out_idxs_
->
end
(),
"no key [%s] in in_out_idxs_"
,
PADDLE_ENFORCE
(
it
!=
in_out_idxs_
->
end
(),
"no key [%s] in in_out_idxs_"
,
name
);
name
);
...
@@ -49,7 +49,7 @@ const std::string& OperatorBase::Input(const std::string& name) const {
...
@@ -49,7 +49,7 @@ const std::string& OperatorBase::Input(const std::string& name) const {
}
}
std
::
vector
<
std
::
string
>
OperatorBase
::
Inputs
(
const
std
::
string
&
name
)
const
{
std
::
vector
<
std
::
string
>
OperatorBase
::
Inputs
(
const
std
::
string
&
name
)
const
{
PADDLE_ENFORCE
(
in_out_idxs_
!=
nullptr
,
"IO Idx could not be nullptr"
);
PADDLE_ENFORCE
_NOT_NULL
(
in_out_idxs_
,
"IO Idx could not be nullptr"
);
auto
input_format
=
GetAttr
<
std
::
vector
<
int
>>
(
"input_format"
);
auto
input_format
=
GetAttr
<
std
::
vector
<
int
>>
(
"input_format"
);
auto
offset
=
in_out_idxs_
->
at
(
name
);
auto
offset
=
in_out_idxs_
->
at
(
name
);
PADDLE_ENFORCE
(
input_format
.
at
(
static_cast
<
size_t
>
(
offset
)
+
1
)
<=
PADDLE_ENFORCE
(
input_format
.
at
(
static_cast
<
size_t
>
(
offset
)
+
1
)
<=
...
@@ -62,7 +62,7 @@ std::vector<std::string> OperatorBase::Inputs(const std::string& name) const {
...
@@ -62,7 +62,7 @@ std::vector<std::string> OperatorBase::Inputs(const std::string& name) const {
}
}
const
std
::
string
&
OperatorBase
::
Output
(
const
std
::
string
&
name
)
const
{
const
std
::
string
&
OperatorBase
::
Output
(
const
std
::
string
&
name
)
const
{
PADDLE_ENFORCE
(
in_out_idxs_
!=
nullptr
,
"InOut Indice could not be nullptr"
);
PADDLE_ENFORCE
_NOT_NULL
(
in_out_idxs_
,
"InOut Indice could not be nullptr"
);
auto
it
=
in_out_idxs_
->
find
(
name
);
auto
it
=
in_out_idxs_
->
find
(
name
);
PADDLE_ENFORCE
(
it
!=
in_out_idxs_
->
end
(),
"no key [%s] in in_out_idxs_"
,
PADDLE_ENFORCE
(
it
!=
in_out_idxs_
->
end
(),
"no key [%s] in in_out_idxs_"
,
name
);
name
);
...
@@ -76,7 +76,7 @@ const std::string& OperatorBase::Output(const std::string& name) const {
...
@@ -76,7 +76,7 @@ const std::string& OperatorBase::Output(const std::string& name) const {
}
}
std
::
vector
<
std
::
string
>
OperatorBase
::
Outputs
(
const
std
::
string
&
name
)
const
{
std
::
vector
<
std
::
string
>
OperatorBase
::
Outputs
(
const
std
::
string
&
name
)
const
{
PADDLE_ENFORCE
(
in_out_idxs_
!=
nullptr
,
"InOut Indice could not be nullptr"
);
PADDLE_ENFORCE
_NOT_NULL
(
in_out_idxs_
,
"InOut Indice could not be nullptr"
);
auto
output_format
=
GetAttr
<
std
::
vector
<
int
>>
(
"output_format"
);
auto
output_format
=
GetAttr
<
std
::
vector
<
int
>>
(
"output_format"
);
auto
offset
=
in_out_idxs_
->
at
(
name
);
auto
offset
=
in_out_idxs_
->
at
(
name
);
PADDLE_ENFORCE
(
output_format
.
at
(
static_cast
<
size_t
>
(
offset
)
+
1
)
<=
PADDLE_ENFORCE
(
output_format
.
at
(
static_cast
<
size_t
>
(
offset
)
+
1
)
<=
...
...
paddle/framework/operator.h
浏览文件 @
174a3b3e
...
@@ -167,15 +167,15 @@ class OperatorContext {
...
@@ -167,15 +167,15 @@ class OperatorContext {
template
<
typename
T
>
template
<
typename
T
>
const
T
*
Input
(
const
size_t
index
)
const
{
const
T
*
Input
(
const
size_t
index
)
const
{
auto
var
=
InputVar
(
index
);
auto
var
=
InputVar
(
index
);
PADDLE_ENFORCE
(
var
!=
nullpt
r
,
"Input(%d) should not be nullptr"
,
index
);
PADDLE_ENFORCE
_NOT_NULL
(
va
r
,
"Input(%d) should not be nullptr"
,
index
);
return
&
var
->
Get
<
T
>
();
return
&
var
->
Get
<
T
>
();
}
}
template
<
typename
T
>
template
<
typename
T
>
T
*
Output
(
const
size_t
index
)
const
{
T
*
Output
(
const
size_t
index
)
const
{
auto
var
=
OutputVar
(
index
);
auto
var
=
OutputVar
(
index
);
PADDLE_ENFORCE
(
PADDLE_ENFORCE
_NOT_NULL
(
var
!=
nullptr
,
var
,
"Output(%d) not be nullptr, which means variable [%s] does not "
"Output(%d) not be nullptr, which means variable [%s] does not "
"exist in scope"
,
"exist in scope"
,
index
,
op_
.
outputs_
[
index
]);
index
,
op_
.
outputs_
[
index
]);
...
@@ -185,14 +185,14 @@ class OperatorContext {
...
@@ -185,14 +185,14 @@ class OperatorContext {
template
<
typename
T
>
template
<
typename
T
>
const
T
*
Input
(
const
std
::
string
&
name
)
const
{
const
T
*
Input
(
const
std
::
string
&
name
)
const
{
auto
var
=
InputVar
(
name
);
auto
var
=
InputVar
(
name
);
PADDLE_ENFORCE
(
var
!=
nullpt
r
,
"Input(%s) should not be nullptr"
,
name
);
PADDLE_ENFORCE
_NOT_NULL
(
va
r
,
"Input(%s) should not be nullptr"
,
name
);
return
&
var
->
Get
<
T
>
();
return
&
var
->
Get
<
T
>
();
}
}
template
<
typename
T
>
template
<
typename
T
>
T
*
Output
(
const
std
::
string
&
name
)
const
{
T
*
Output
(
const
std
::
string
&
name
)
const
{
auto
var
=
OutputVar
(
name
);
auto
var
=
OutputVar
(
name
);
PADDLE_ENFORCE
(
var
!=
nullpt
r
,
"Output(%s) should not be nullptr"
,
name
);
PADDLE_ENFORCE
_NOT_NULL
(
va
r
,
"Output(%s) should not be nullptr"
,
name
);
return
var
->
GetMutable
<
T
>
();
return
var
->
GetMutable
<
T
>
();
}
}
...
@@ -204,9 +204,9 @@ class OperatorContext {
...
@@ -204,9 +204,9 @@ class OperatorContext {
std
::
transform
(
names
.
begin
(),
names
.
end
(),
std
::
back_inserter
(
res
),
std
::
transform
(
names
.
begin
(),
names
.
end
(),
std
::
back_inserter
(
res
),
[
&
](
const
std
::
string
&
sub_name
)
{
[
&
](
const
std
::
string
&
sub_name
)
{
auto
var
=
scope_
.
FindVar
(
sub_name
);
auto
var
=
scope_
.
FindVar
(
sub_name
);
PADDLE_ENFORCE
(
var
!=
nullptr
,
PADDLE_ENFORCE
_NOT_NULL
(
"MultiInput(%s:%s) should not be nullptr"
,
var
,
"MultiInput(%s:%s) should not be nullptr"
,
name
,
name
,
sub_name
);
sub_name
);
return
&
var
->
Get
<
T
>
();
return
&
var
->
Get
<
T
>
();
});
});
return
res
;
return
res
;
...
@@ -220,9 +220,9 @@ class OperatorContext {
...
@@ -220,9 +220,9 @@ class OperatorContext {
std
::
transform
(
names
.
begin
(),
names
.
end
(),
std
::
back_inserter
(
res
),
std
::
transform
(
names
.
begin
(),
names
.
end
(),
std
::
back_inserter
(
res
),
[
&
](
const
std
::
string
&
sub_name
)
{
[
&
](
const
std
::
string
&
sub_name
)
{
auto
var
=
scope_
.
FindVar
(
sub_name
);
auto
var
=
scope_
.
FindVar
(
sub_name
);
PADDLE_ENFORCE
(
var
!=
nullptr
,
PADDLE_ENFORCE
_NOT_NULL
(
"MultiOutput(%s:%s) should not be nullptr"
,
var
,
"MultiOutput(%s:%s) should not be nullptr"
,
name
,
name
,
sub_name
);
sub_name
);
return
var
->
GetMutable
<
T
>
();
return
var
->
GetMutable
<
T
>
();
});
});
return
res
;
return
res
;
...
...
paddle/framework/tensor.h
浏览文件 @
174a3b3e
...
@@ -127,8 +127,8 @@ class Tensor {
...
@@ -127,8 +127,8 @@ class Tensor {
memory
::
PODDeleter
<
T
,
Place
>
(
place
)),
memory
::
PODDeleter
<
T
,
Place
>
(
place
)),
place_
(
place
),
place_
(
place
),
size_
(
size
)
{
size_
(
size
)
{
PADDLE_ENFORCE
(
ptr_
!=
nullptr
,
"Insufficient %s memory to allocation."
,
PADDLE_ENFORCE
_NOT_NULL
(
ptr_
,
"Insufficient %s memory to allocation."
,
is_cpu_place
(
place_
)
?
"CPU"
:
"GPU"
);
(
is_cpu_place
(
place_
)
?
"CPU"
:
"GPU"
)
);
}
}
virtual
size_t
size
()
const
{
return
size_
;
}
virtual
size_t
size
()
const
{
return
size_
;
}
...
...
paddle/framework/tensor_impl.h
浏览文件 @
174a3b3e
...
@@ -14,17 +14,18 @@ limitations under the License. */
...
@@ -14,17 +14,18 @@ limitations under the License. */
#pragma once
#pragma once
#include "paddle/memory/memcpy.h"
#include "paddle/memory/memcpy.h"
#include "paddle/platform/enforce.h"
namespace
paddle
{
namespace
paddle
{
namespace
framework
{
namespace
framework
{
template
<
typename
T
>
template
<
typename
T
>
inline
void
Tensor
::
check_memory_size
()
const
{
inline
void
Tensor
::
check_memory_size
()
const
{
PADDLE_ENFORCE
(
holder_
!=
nullptr
,
PADDLE_ENFORCE
_NOT_NULL
(
"Tenosr holds no memory. Call Tensor::mutable_data first."
);
holder_
,
"Tenosr holds no memory. Call Tensor::mutable_data first."
);
PADDLE_ENFORCE
(
holder_
->
size
()
>=
product
(
dims_
)
*
sizeof
(
T
)
+
offset_
,
PADDLE_ENFORCE
_GE
(
holder_
->
size
(),
product
(
dims_
)
*
sizeof
(
T
)
+
offset_
,
"Tensor's dims_ is out of bound. Call Tensor::mutable_data "
"Tensor's dims_ is out of bound. Call Tensor::mutable_data "
"first to re-allocate memory."
);
"first to re-allocate memory."
);
}
}
template
<
typename
T
>
template
<
typename
T
>
...
@@ -51,9 +52,9 @@ inline T* Tensor::mutable_data(DDim dims, platform::Place place) {
...
@@ -51,9 +52,9 @@ inline T* Tensor::mutable_data(DDim dims, platform::Place place) {
template
<
typename
T
>
template
<
typename
T
>
inline
T
*
Tensor
::
mutable_data
(
platform
::
Place
place
)
{
inline
T
*
Tensor
::
mutable_data
(
platform
::
Place
place
)
{
static_assert
(
std
::
is_pod
<
T
>::
value
,
"T must be POD"
);
static_assert
(
std
::
is_pod
<
T
>::
value
,
"T must be POD"
);
PADDLE_ENFORCE
(
product
(
dims_
)
>
0
,
PADDLE_ENFORCE
_GT
(
product
(
dims_
),
0
,
"Tensor's numel must be larger than zero to call "
"Tensor's numel must be larger than zero to call "
"Tensor::mutable_data. Call Tensor::set_dim first."
);
"Tensor::mutable_data. Call Tensor::set_dim first."
);
/* some versions of boost::variant don't have operator!= */
/* some versions of boost::variant don't have operator!= */
size_t
size
=
product
(
dims_
)
*
sizeof
(
T
);
size_t
size
=
product
(
dims_
)
*
sizeof
(
T
);
if
(
holder_
==
nullptr
||
!
(
holder_
->
place
()
==
place
)
||
if
(
holder_
==
nullptr
||
!
(
holder_
->
place
()
==
place
)
||
...
@@ -120,11 +121,11 @@ inline void Tensor::CopyFrom(const Tensor& src,
...
@@ -120,11 +121,11 @@ inline void Tensor::CopyFrom(const Tensor& src,
template
<
typename
T
>
template
<
typename
T
>
inline
Tensor
Tensor
::
Slice
(
const
int
&
begin_idx
,
const
int
&
end_idx
)
const
{
inline
Tensor
Tensor
::
Slice
(
const
int
&
begin_idx
,
const
int
&
end_idx
)
const
{
check_memory_size
<
T
>
();
check_memory_size
<
T
>
();
PADDLE_ENFORCE
(
begin_idx
>=
0
,
"Slice begin index is less than zero."
);
PADDLE_ENFORCE
_GE
(
begin_idx
,
0
,
"Slice begin index is less than zero."
);
PADDLE_ENFORCE
(
end_idx
<=
dims_
[
0
],
"Slice end index is out of bound."
);
PADDLE_ENFORCE
_LE
(
end_idx
,
dims_
[
0
],
"Slice end index is out of bound."
);
PADDLE_ENFORCE
(
begin_idx
<
end_idx
,
PADDLE_ENFORCE
_LT
(
begin_idx
,
end_idx
,
"Begin index must be less than end index."
);
"Begin index must be less than end index."
);
PADDLE_ENFORCE
(
dims_
[
0
]
!=
1
,
"Can not slice a tensor with dims_[0] = 1."
);
PADDLE_ENFORCE
_NE
(
dims_
[
0
],
1
,
"Can not slice a tensor with dims_[0] = 1."
);
int
base
=
product
(
dims_
)
/
dims_
[
0
];
int
base
=
product
(
dims_
)
/
dims_
[
0
];
Tensor
dst
;
Tensor
dst
;
dst
.
holder_
=
holder_
;
dst
.
holder_
=
holder_
;
...
...
paddle/framework/tensor_test.cc
浏览文件 @
174a3b3e
...
@@ -36,7 +36,8 @@ TEST(Tensor, DataAssert) {
...
@@ -36,7 +36,8 @@ TEST(Tensor, DataAssert) {
}
catch
(
paddle
::
platform
::
EnforceNotMet
err
)
{
}
catch
(
paddle
::
platform
::
EnforceNotMet
err
)
{
caught
=
true
;
caught
=
true
;
std
::
string
msg
=
std
::
string
msg
=
"Tenosr holds no memory. Call Tensor::mutable_data first."
;
"holder_ should not be null
\n
Tenosr holds no memory. Call "
"Tensor::mutable_data first."
;
const
char
*
what
=
err
.
what
();
const
char
*
what
=
err
.
what
();
for
(
size_t
i
=
0
;
i
<
msg
.
length
();
++
i
)
{
for
(
size_t
i
=
0
;
i
<
msg
.
length
();
++
i
)
{
ASSERT_EQ
(
what
[
i
],
msg
[
i
]);
ASSERT_EQ
(
what
[
i
],
msg
[
i
]);
...
@@ -111,7 +112,8 @@ TEST(Tensor, ShareDataWith) {
...
@@ -111,7 +112,8 @@ TEST(Tensor, ShareDataWith) {
}
catch
(
paddle
::
platform
::
EnforceNotMet
err
)
{
}
catch
(
paddle
::
platform
::
EnforceNotMet
err
)
{
caught
=
true
;
caught
=
true
;
std
::
string
msg
=
std
::
string
msg
=
"Tenosr holds no memory. Call Tensor::mutable_data first."
;
"holder_ should not be null
\n
Tenosr holds no memory. Call "
"Tensor::mutable_data first."
;
const
char
*
what
=
err
.
what
();
const
char
*
what
=
err
.
what
();
for
(
size_t
i
=
0
;
i
<
msg
.
length
();
++
i
)
{
for
(
size_t
i
=
0
;
i
<
msg
.
length
();
++
i
)
{
ASSERT_EQ
(
what
[
i
],
msg
[
i
]);
ASSERT_EQ
(
what
[
i
],
msg
[
i
]);
...
...
paddle/gserver/layers/SubNestedSequenceLayer.cpp
0 → 100644
浏览文件 @
174a3b3e
/* 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 "Layer.h"
#include "paddle/math/Matrix.h"
#include "paddle/math/Vector.h"
#include "paddle/utils/Logging.h"
#include "paddle/utils/Stat.h"
namespace
paddle
{
class
SubNestedSequenceLayer
:
public
Layer
{
public:
explicit
SubNestedSequenceLayer
(
const
LayerConfig
&
config
)
:
Layer
(
config
)
{}
bool
init
(
const
LayerMap
&
layerMap
,
const
ParameterMap
&
parameterMap
)
override
;
void
forward
(
PassType
passType
)
override
;
void
backward
(
const
UpdateCallback
&
callback
=
nullptr
)
override
;
private:
/*
* This functions generates the indices of rows in a batch according to the
* indices of selected sub-sequence in each sequence.
*
* Examples:
* selectedIndices:
* [
* [0, 1, -1],
* [0, 1, 2],
* [0, -1, -1],
* [0, 2, 3],
* ]
* inputSeqInfo:
* [
* [0,3,4],
* [4,5,7,10,15],
* [15,20],
* [20,22,23,25,28]
* ]
*
* ths output is saved to private member rowIndice_;
* [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
* 16,17,18,19,20,21,22,23,24,25,26,27]
*/
void
calSelectedCols
(
const
MatrixPtr
selectedIndices
,
const
std
::
vector
<
std
::
vector
<
int
>>&
inputSeqInfo
);
// if the second input of this layer is on GPU memory, copy it to CPU memory.
MatrixPtr
selIdsCpu_
;
// reorganized sequenceStartPositions and subSequenceStartPositions
// into a 2d vector to facilitate the sequence selection process.
std
::
vector
<
std
::
vector
<
int
>>
inputSeqInfoVec_
;
// the final selected row indices in a batch,
// rowIdx_ and selectedRows_ actually share a same memory.
IVectorPtr
rowIndice_
;
std
::
vector
<
int
>
selectedRows_
;
};
REGISTER_LAYER
(
sub_nested_seq
,
SubNestedSequenceLayer
);
bool
SubNestedSequenceLayer
::
init
(
const
LayerMap
&
layerMap
,
const
ParameterMap
&
parameterMap
)
{
/* Initialize the basic parent class */
Layer
::
init
(
layerMap
,
parameterMap
);
CHECK_EQ
(
2U
,
inputLayers_
.
size
());
setNeedSequenceInfo
(
false
);
return
true
;
}
void
SubNestedSequenceLayer
::
calSelectedCols
(
const
MatrixPtr
selectedIndices
,
const
std
::
vector
<
std
::
vector
<
int
>>&
inputSeqInfo
)
{
selectedRows_
.
clear
();
std
::
vector
<
int
>
outSeqStartInfo
(
1
,
0
);
std
::
vector
<
int
>
outSubSeqStartInfo
(
1
,
0
);
size_t
seqNum
=
selectedIndices
->
getHeight
();
size_t
beamSize
=
selectedIndices
->
getWidth
();
for
(
size_t
i
=
0
;
i
<
seqNum
;
++
i
)
{
for
(
size_t
j
=
0
;
j
<
beamSize
;
++
j
)
{
if
(
selectedIndices
->
getElement
(
i
,
j
)
==
-
1.
)
break
;
int
selSubSeqIdx
=
selectedIndices
->
getElement
(
i
,
j
);
CHECK_GT
(
inputSeqInfoVec_
[
i
].
size
()
-
1
,
selSubSeqIdx
);
size_t
subSeqLen
=
inputSeqInfoVec_
[
i
][
selSubSeqIdx
+
1
]
-
inputSeqInfoVec_
[
i
][
selSubSeqIdx
];
for
(
size_t
k
=
0
;
k
<
subSeqLen
;
++
k
)
selectedRows_
.
push_back
(
inputSeqInfoVec_
[
i
][
selSubSeqIdx
]
+
k
);
outSubSeqStartInfo
.
push_back
(
outSubSeqStartInfo
.
back
()
+
subSeqLen
);
}
outSeqStartInfo
.
push_back
(
outSubSeqStartInfo
.
back
());
}
if
(
useGpu_
)
{
rowIndice_
=
IVector
::
create
(
selectedRows_
.
size
(),
useGpu_
);
rowIndice_
->
copyFrom
(
selectedRows_
.
data
(),
selectedRows_
.
size
());
}
else
{
rowIndice_
=
IVector
::
create
(
selectedRows_
.
data
(),
selectedRows_
.
size
(),
useGpu_
);
}
// create the sequence information for the output.
ICpuGpuVector
::
resizeOrCreate
(
output_
.
sequenceStartPositions
,
outSeqStartInfo
.
size
(),
false
);
output_
.
sequenceStartPositions
->
copyFrom
(
outSeqStartInfo
.
data
(),
outSeqStartInfo
.
size
(),
false
);
ICpuGpuVector
::
resizeOrCreate
(
output_
.
subSequenceStartPositions
,
outSubSeqStartInfo
.
size
(),
false
);
output_
.
subSequenceStartPositions
->
copyFrom
(
outSubSeqStartInfo
.
data
(),
outSubSeqStartInfo
.
size
(),
false
);
}
void
SubNestedSequenceLayer
::
forward
(
PassType
passType
)
{
Layer
::
forward
(
passType
);
const
Argument
&
inputSeq
=
getInput
(
0
);
CHECK
(
inputSeq
.
hasSubseq
())
<<
"The first input of SubNestSequence layer "
<<
"must be a nested sequence."
;
const
MatrixPtr
selectedIndices
=
getInputValue
(
1
);
CHECK_EQ
(
inputSeq
.
getNumSequences
(),
selectedIndices
->
getHeight
());
if
(
dynamic_cast
<
GpuMatrix
*>
(
selectedIndices
.
get
()))
{
/*
* Currently, the second input for this layer is generated by
* kmax_sequence_score_layer whose output is always stored on CPU,
* or a data_layer which canbe on GPU.
*
* If the second input is on GPU, copy it to CPU memory, because this
* input always uses very few memory, and operations related to it are
* all logic control, not computations.
*/
Matrix
::
resizeOrCreate
(
selIdsCpu_
,
selectedIndices
->
getHeight
(),
selectedIndices
->
getWidth
(),
false
/* trans */
,
false
/* useGpu */
);
selIdsCpu_
->
copyFrom
(
*
selectedIndices
);
}
else
{
selIdsCpu_
=
selectedIndices
;
}
Argument
::
reorganizeSeqInfo
(
inputSeq
.
sequenceStartPositions
,
inputSeq
.
subSequenceStartPositions
,
inputSeqInfoVec_
);
calSelectedCols
(
selIdsCpu_
,
inputSeqInfoVec_
);
resetOutput
(
selectedRows_
.
size
(),
getSize
());
getOutputValue
()
->
selectRows
(
*
getInputValue
(
0
),
*
rowIndice_
);
}
void
SubNestedSequenceLayer
::
backward
(
const
UpdateCallback
&
callback
)
{
MatrixPtr
inputSeqGrad
=
getInputGrad
(
0
);
MatrixPtr
outputGrad
=
getOutputGrad
();
if
(
inputSeqGrad
)
outputGrad
->
addToRows
(
*
inputSeqGrad
,
*
rowIndice_
);
}
}
// namespace paddle
paddle/gserver/tests/test_LayerGrad.cpp
浏览文件 @
174a3b3e
...
@@ -1899,6 +1899,84 @@ TEST(Layer, CropLayer) {
...
@@ -1899,6 +1899,84 @@ TEST(Layer, CropLayer) {
}
}
}
}
vector
<
real
>
randSampling
(
real
range
,
int
n
)
{
CHECK_GE
(
range
,
n
);
vector
<
real
>
num
(
range
);
iota
(
begin
(
num
),
end
(
num
),
0.
);
if
(
range
==
n
)
return
num
;
random_shuffle
(
begin
(
num
),
end
(
num
));
num
.
resize
(
n
);
sort
(
begin
(
num
),
end
(
num
));
return
num
;
}
TEST
(
Layer
,
SubNestedSequenceLayer
)
{
// layer size is not crutial for this layer,
// so use a small layer size in unittest
const
int
layerSize
=
4
;
const
int
maxSeqNum
=
50
;
const
int
maxSeqLen
=
50
;
const
int
maxBeamSize
=
32
;
srand
((
size_t
)(
time
(
NULL
)));
int
beamSize
=
1
+
(
rand
()
%
maxBeamSize
);
TestConfig
config
;
config
.
layerConfig
.
set_type
(
"sub_nested_seq"
);
config
.
layerConfig
.
set_name
(
"sub_nested_seq_layer"
);
config
.
layerConfig
.
set_size
(
layerSize
);
int
seqNum
=
1
+
(
rand
()
%
maxSeqNum
);
// sequence information for the first input, it is a nested sequence
vector
<
int
>
seqStartPos
(
seqNum
+
1
,
0
);
vector
<
int
>
subSeqStartPos
(
1
,
0
);
// selected indices
MatrixPtr
selectedIndices
=
Matrix
::
create
(
seqNum
,
beamSize
,
false
,
false
);
selectedIndices
->
one
();
selectedIndices
->
mulScalar
(
-
1.
);
real
*
indicesData
=
selectedIndices
->
getData
();
for
(
int
i
=
0
;
i
<
seqNum
;
++
i
)
{
int
subSeqNum
=
1
+
(
rand
()
%
maxSeqNum
);
for
(
int
j
=
0
;
j
<
subSeqNum
;
++
j
)
{
subSeqStartPos
.
push_back
(
subSeqStartPos
.
back
()
+
(
1
+
(
rand
()
%
maxSeqLen
)));
}
vector
<
real
>
selSeqs
=
randSampling
(
static_cast
<
real
>
(
subSeqNum
),
min
(
beamSize
,
subSeqNum
));
memcpy
(
indicesData
+
(
i
*
beamSize
),
selSeqs
.
data
(),
selSeqs
.
size
()
*
sizeof
(
real
));
seqStartPos
[
i
+
1
]
=
subSeqStartPos
.
back
();
}
MatrixPtr
seqInputPtr
=
Matrix
::
create
(
seqStartPos
.
back
(),
layerSize
,
false
,
false
);
seqInputPtr
->
randomizeUniform
();
config
.
inputDefs
.
push_back
({
INPUT_SELF_DEFINE_DATA
,
"nested_seq_input"
,
seqInputPtr
,
seqStartPos
,
subSeqStartPos
});
config
.
layerConfig
.
add_inputs
();
config
.
inputDefs
.
push_back
(
{
INPUT_SELF_DEFINE_DATA
,
"selected_indices"
,
selectedIndices
});
config
.
layerConfig
.
add_inputs
();
for
(
auto
useGpu
:
{
false
,
true
})
{
testLayerGrad
(
config
,
"sub_nested_seq"
,
/* batchSize */
seqNum
,
/* trans */
false
,
/* useGpu*/
useGpu
,
/* useWeight */
false
);
}
}
TEST
(
Layer
,
ClipLayer
)
{
TEST
(
Layer
,
ClipLayer
)
{
const
size_t
batchSize
=
128
;
const
size_t
batchSize
=
128
;
const
size_t
size
=
512
;
const
size_t
size
=
512
;
...
...
paddle/operators/add_op.cc
浏览文件 @
174a3b3e
...
@@ -22,8 +22,7 @@ class AddOp : public OperatorWithKernel {
...
@@ -22,8 +22,7 @@ class AddOp : public OperatorWithKernel {
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE_EQ
(
ctx
.
InputSize
(),
2
);
PADDLE_ENFORCE_EQ
(
ctx
.
InputSize
(),
2
);
PADDLE_ENFORCE_EQ
(
ctx
.
OutputSize
(),
1
);
PADDLE_ENFORCE_EQ
(
ctx
.
OutputSize
(),
1
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
0
)
!=
nullptr
&&
ctx
.
InputVar
(
1
)
!=
nullptr
,
PADDLE_ENFORCE_NOT_NULL
(
ctx
.
InputVar
(
0
),
"Inputs of AddOp must all be set"
);
"Inputs of AddOp must all be set"
);
PADDLE_ENFORCE
(
ctx
.
OutputVar
(
0
)
!=
nullptr
,
PADDLE_ENFORCE
(
ctx
.
OutputVar
(
0
)
!=
nullptr
,
"Outputs of AddOp must all be set"
);
"Outputs of AddOp must all be set"
);
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
()
==
ctx
.
Input
<
Tensor
>
(
1
)
->
dims
(),
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
()
==
ctx
.
Input
<
Tensor
>
(
1
)
->
dims
(),
...
...
paddle/operators/cross_entropy_op.cc
浏览文件 @
174a3b3e
...
@@ -20,18 +20,19 @@ namespace operators {
...
@@ -20,18 +20,19 @@ namespace operators {
class
OnehotCrossEntropyOp
:
public
OperatorWithKernel
{
class
OnehotCrossEntropyOp
:
public
OperatorWithKernel
{
protected:
protected:
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
2
,
PADDLE_ENFORCE_EQ
(
ctx
.
InputSize
(),
2
,
"Input size of OnehotCrossEntropyOp must be two"
);
"Input size of OnehotCrossEntropyOp must be two"
);
PADDLE_ENFORCE
(
ctx
.
OutputSize
()
==
1
,
PADDLE_ENFORCE_EQ
(
ctx
.
OutputSize
(),
1
,
"Output size of OnehotCrossEntropyOp must be one"
);
"Output size of OnehotCrossEntropyOp must be one"
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
0
)
!=
nullptr
&&
ctx
.
InputVar
(
1
)
!=
nullptr
,
PADDLE_ENFORCE_NOT_NULL
(
ctx
.
InputVar
(
0
),
"Inputs of OnehotCrossEntropyOp must all be set"
);
"0-th input of OnehotCrossEntropyOp should be set"
);
PADDLE_ENFORCE
(
ctx
.
OutputVar
(
0
)
!=
nullptr
,
PADDLE_ENFORCE_NOT_NULL
(
ctx
.
InputVar
(
1
),
"Outputs of OnehotCrossEntropyOp must all be set"
);
"1-th input of OnehotCrossEntropyOp should be set"
);
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
().
size
()
==
2
,
PADDLE_ENFORCE_NOT_NULL
(
ctx
.
OutputVar
(
0
),
"X's dimension must be 2."
);
"Outputs of OnehotCrossEntropyOp must all be set"
);
PADDLE_ENFORCE
(
ctx
.
Output
<
Tensor
>
(
0
)
->
dims
().
size
()
==
1
,
PADDLE_ENFORCE_EQ
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
().
size
(),
2
);
"label's dimension must be 1."
);
PADDLE_ENFORCE_EQ
(
ctx
.
Output
<
Tensor
>
(
0
)
->
dims
().
size
(),
1
,
"label's dimension must be 1."
);
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
({
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
()[
0
]});
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
({
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
()[
0
]});
}
}
};
};
...
...
paddle/operators/fill_zeros_like_op.cc
浏览文件 @
174a3b3e
...
@@ -20,14 +20,14 @@ namespace operators {
...
@@ -20,14 +20,14 @@ namespace operators {
class
FillZerosLikeOp
:
public
framework
::
OperatorWithKernel
{
class
FillZerosLikeOp
:
public
framework
::
OperatorWithKernel
{
protected:
protected:
void
InferShape
(
const
framework
::
InferShapeContext
&
ctx
)
const
override
{
void
InferShape
(
const
framework
::
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
1UL
,
PADDLE_ENFORCE
_EQ
(
ctx
.
InputSize
(),
1UL
,
"Input size of FillZerosLikeOp must be one."
);
"Input size of FillZerosLikeOp must be one."
);
PADDLE_ENFORCE
(
ctx
.
OutputSize
()
==
1UL
,
PADDLE_ENFORCE
_EQ
(
ctx
.
OutputSize
(),
1UL
,
"Output size of AddOp must be one."
);
"Output size of AddOp must be one."
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
0
)
!=
nullptr
,
PADDLE_ENFORCE
_NOT_NULL
(
ctx
.
InputVar
(
0
)
,
"Input of FillZerosLikeOp must be set."
);
"Input of FillZerosLikeOp must be set."
);
PADDLE_ENFORCE
(
ctx
.
OutputVar
(
0
)
!=
nullptr
,
PADDLE_ENFORCE
_NOT_NULL
(
ctx
.
OutputVar
(
0
)
,
"Output of FillZerosLikeOp must be set."
);
"Output of FillZerosLikeOp must be set."
);
ctx
.
Output
<
framework
::
Tensor
>
(
0
)
->
Resize
(
ctx
.
Output
<
framework
::
Tensor
>
(
0
)
->
Resize
(
ctx
.
Input
<
framework
::
Tensor
>
(
0
)
->
dims
());
ctx
.
Input
<
framework
::
Tensor
>
(
0
)
->
dims
());
}
}
...
...
paddle/operators/mean_op.cc
浏览文件 @
174a3b3e
...
@@ -20,10 +20,10 @@ namespace operators {
...
@@ -20,10 +20,10 @@ namespace operators {
class
MeanOp
:
public
OperatorWithKernel
{
class
MeanOp
:
public
OperatorWithKernel
{
protected:
protected:
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
1
,
"Input size of AddOp must be one"
);
PADDLE_ENFORCE
_EQ
(
ctx
.
InputSize
(),
1
,
"Input size of AddOp must be one"
);
PADDLE_ENFORCE
(
ctx
.
OutputSize
()
==
1
,
"Output size of AddOp must be one"
);
PADDLE_ENFORCE
_EQ
(
ctx
.
OutputSize
(),
1
,
"Output size of AddOp must be one"
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
0
)
!=
nullptr
&&
ctx
.
OutputVar
(
0
)
!=
nullptr
,
PADDLE_ENFORCE
_NOT_NULL
(
ctx
.
InputVar
(
0
),
"input should be set"
);
"Input/Output of MeanOp must be initialized.
"
);
PADDLE_ENFORCE_NOT_NULL
(
ctx
.
OutputVar
(
0
),
"output should be set
"
);
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
(
framework
::
make_ddim
({
1
}));
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
(
framework
::
make_ddim
({
1
}));
}
}
};
};
...
...
paddle/operators/net_op.h
浏览文件 @
174a3b3e
...
@@ -70,15 +70,15 @@ class NetOp : public framework::OperatorBase {
...
@@ -70,15 +70,15 @@ class NetOp : public framework::OperatorBase {
*/
*/
void
AddOp
(
const
std
::
shared_ptr
<
OperatorBase
>&
op
)
{
void
AddOp
(
const
std
::
shared_ptr
<
OperatorBase
>&
op
)
{
PADDLE_ENFORCE
(
!
add_op_done_
,
"Cannot AddOp when this network is sealed"
);
PADDLE_ENFORCE
(
!
add_op_done_
,
"Cannot AddOp when this network is sealed"
);
PADDLE_ENFORCE
(
op
!=
nullptr
,
"Cannot Insert Null op"
);
PADDLE_ENFORCE
_NOT_NULL
(
op
,
"Cannot Insert Null op"
);
ops_
.
push_back
(
op
);
ops_
.
push_back
(
op
);
}
}
void
InsertOp
(
size_t
pos
,
const
std
::
shared_ptr
<
OperatorBase
>&
op
)
{
void
InsertOp
(
size_t
pos
,
const
std
::
shared_ptr
<
OperatorBase
>&
op
)
{
PADDLE_ENFORCE
(
!
add_op_done_
,
PADDLE_ENFORCE
(
!
add_op_done_
,
"Cannot InsertOp when this network is sealed"
);
"Cannot InsertOp when this network is sealed"
);
PADDLE_ENFORCE
(
op
!=
nullptr
,
"Cannot Insert Null op"
);
PADDLE_ENFORCE
_NOT_NULL
(
op
,
"Cannot Insert Null op"
);
PADDLE_ENFORCE
(
pos
<=
ops_
.
size
(),
"Out of range"
);
PADDLE_ENFORCE
_LE
(
pos
,
ops_
.
size
(),
"Out of range"
);
ops_
.
insert
(
ops_
.
begin
()
+
pos
,
op
);
ops_
.
insert
(
ops_
.
begin
()
+
pos
,
op
);
}
}
...
...
paddle/operators/sgd_op.cc
浏览文件 @
174a3b3e
...
@@ -20,11 +20,11 @@ namespace operators {
...
@@ -20,11 +20,11 @@ namespace operators {
class
SGDOp
:
public
OperatorWithKernel
{
class
SGDOp
:
public
OperatorWithKernel
{
protected:
protected:
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
2
,
"Input size of SGDOp must be two"
);
PADDLE_ENFORCE
_EQ
(
ctx
.
InputSize
(),
2
,
"Input size of SGDOp must be two"
);
PADDLE_ENFORCE
(
ctx
.
OutputSize
()
==
1
,
"Output size of SGDOp must be one"
);
PADDLE_ENFORCE
_EQ
(
ctx
.
OutputSize
(),
1
,
"Output size of SGDOp must be one"
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
0
)
!=
nullptr
,
"inputs[0] mast be set"
);
PADDLE_ENFORCE
_NOT_NULL
(
ctx
.
InputVar
(
0
)
,
"inputs[0] mast be set"
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
1
)
!=
nullptr
,
"inputs[1] mast be set"
);
PADDLE_ENFORCE
_NOT_NULL
(
ctx
.
InputVar
(
1
)
,
"inputs[1] mast be set"
);
PADDLE_ENFORCE
(
ctx
.
OutputVar
(
0
)
!=
nullptr
,
"outputs[0] mast be set"
);
PADDLE_ENFORCE
_NOT_NULL
(
ctx
.
OutputVar
(
0
)
,
"outputs[0] mast be set"
);
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
()
==
ctx
.
Input
<
Tensor
>
(
1
)
->
dims
(),
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
()
==
ctx
.
Input
<
Tensor
>
(
1
)
->
dims
(),
"Two input of SGD Op's dimension must be same."
);
"Two input of SGD Op's dimension must be same."
);
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
());
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
());
...
...
paddle/operators/softmax_op.cc
浏览文件 @
174a3b3e
...
@@ -20,12 +20,12 @@ namespace operators {
...
@@ -20,12 +20,12 @@ namespace operators {
class
SoftmaxOp
:
public
OperatorWithKernel
{
class
SoftmaxOp
:
public
OperatorWithKernel
{
protected:
protected:
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
1UL
,
PADDLE_ENFORCE
_EQ
(
ctx
.
InputSize
(),
1UL
,
"Only one input is need for softmax"
);
"Only one input is need for softmax"
);
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
"X"
)
->
dims
().
size
()
==
2UL
,
PADDLE_ENFORCE
_EQ
(
ctx
.
Input
<
Tensor
>
(
"X"
)
->
dims
().
size
(),
2UL
,
"The input of softmax op must be matrix"
);
"The input of softmax op must be matrix"
);
PADDLE_ENFORCE
(
ctx
.
OutputSize
()
==
1UL
,
PADDLE_ENFORCE
_EQ
(
ctx
.
OutputSize
(),
1UL
,
"Only one output is need for softmax"
);
"Only one output is need for softmax"
);
ctx
.
Output
<
Tensor
>
(
"Y"
)
->
Resize
(
ctx
.
Input
<
Tensor
>
(
"X"
)
->
dims
());
ctx
.
Output
<
Tensor
>
(
"Y"
)
->
Resize
(
ctx
.
Input
<
Tensor
>
(
"X"
)
->
dims
());
}
}
};
};
...
@@ -43,13 +43,13 @@ class SoftmaxOpMaker : public OpProtoAndCheckerMaker {
...
@@ -43,13 +43,13 @@ class SoftmaxOpMaker : public OpProtoAndCheckerMaker {
class
SoftmaxOpGrad
:
public
OperatorWithKernel
{
class
SoftmaxOpGrad
:
public
OperatorWithKernel
{
protected:
protected:
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
3UL
,
PADDLE_ENFORCE
_EQ
(
ctx
.
InputSize
(),
3UL
,
"Input of SoftmaxOpGrad should be 3, X, Y, YG"
);
"Input of SoftmaxOpGrad should be 3, X, Y, YG"
);
PADDLE_ENFORCE
(
ctx
.
OutputSize
()
==
1UL
,
PADDLE_ENFORCE
_EQ
(
ctx
.
OutputSize
(),
1UL
,
"Output of SoftmaxOpGrad should be 1"
);
"Output of SoftmaxOpGrad should be 1"
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
"Y"
)
!=
nullptr
,
"Input(Y) should not be null"
);
PADDLE_ENFORCE
_NOT_NULL
(
ctx
.
InputVar
(
"Y"
)
,
"Input(Y) should not be null"
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
framework
::
GradVarName
(
"Y"
))
!=
nullptr
,
PADDLE_ENFORCE
_NOT_NULL
(
ctx
.
InputVar
(
framework
::
GradVarName
(
"Y"
))
,
"Input(Y@GRAD) should not be null"
);
"Input(Y@GRAD) should not be null"
);
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
"Y"
)
->
dims
()
==
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
"Y"
)
->
dims
()
==
ctx
.
Input
<
Tensor
>
(
framework
::
GradVarName
(
"Y"
))
->
dims
(),
ctx
.
Input
<
Tensor
>
(
framework
::
GradVarName
(
"Y"
))
->
dims
(),
"the shape of Input(0) and Input(1) should be the same"
);
"the shape of Input(0) and Input(1) should be the same"
);
...
...
paddle/parameter/Argument.cpp
浏览文件 @
174a3b3e
...
@@ -666,4 +666,24 @@ void Argument::subArgFrom(const Argument& input,
...
@@ -666,4 +666,24 @@ void Argument::subArgFrom(const Argument& input,
}
}
}
}
void
Argument
::
reorganizeSeqInfo
(
const
ICpuGpuVectorPtr
seqStartPos
,
const
ICpuGpuVectorPtr
subSeqStartPos
,
std
::
vector
<
std
::
vector
<
int
>>&
reorganizedSeqInfo
)
{
int
*
seqStarts
=
seqStartPos
->
getMutableData
(
false
);
int
*
subSeqStarts
=
subSeqStartPos
->
getMutableData
(
false
);
int
seqNum
=
seqStartPos
->
getSize
()
-
1
;
reorganizedSeqInfo
.
resize
(
seqNum
,
std
::
vector
<
int
>
());
int
seqIdx
=
0
;
for
(
size_t
i
=
0
;
i
<
subSeqStartPos
->
getSize
();
++
i
)
{
reorganizedSeqInfo
[
seqIdx
].
push_back
(
subSeqStarts
[
i
]);
if
(
subSeqStarts
[
i
]
==
seqStarts
[
seqIdx
+
1
])
{
seqIdx
++
;
if
(
seqIdx
==
seqNum
)
return
;
reorganizedSeqInfo
[
seqIdx
].
push_back
(
subSeqStarts
[
i
]);
}
}
}
}
// namespace paddle
}
// namespace paddle
paddle/parameter/Argument.h
浏览文件 @
174a3b3e
...
@@ -317,6 +317,30 @@ struct Argument {
...
@@ -317,6 +317,30 @@ struct Argument {
*/
*/
void
printValueString
(
std
::
ostream
&
stream
,
void
printValueString
(
std
::
ostream
&
stream
,
const
std
::
string
&
prefix
=
""
)
const
;
const
std
::
string
&
prefix
=
""
)
const
;
/**
* @brief reorganizeSeqInfo will reorganize sequenceStartPositions and
* subSequenceStartPositions into a 2 dimensional arrary: reorganizedSeqInfo.
*
* @param seqStartPos: sequenceStartPositions of an Argument.
* @param subSeqStartPos: subSequenceStartPositions of an Argument.
* @param the reorganized sequence start position information.
*
* Examples:
* seqStartPos: [0, 4, 15, 20, 28]
* subSeqStartPos: [0, 3, 4, 5, 7, 10, 15, 20, 22, 23, 25, 28]
* reorganizedSeqInfo:
* [
* [0,3,4],
* [4,5,7,10,15],
* [15,20],
* [20,22,23,25,28]
* ]
*/
static
void
reorganizeSeqInfo
(
const
ICpuGpuVectorPtr
seqStartPos
,
const
ICpuGpuVectorPtr
subSeqStartPos
,
std
::
vector
<
std
::
vector
<
int
>>&
reorganizedSeqInfo
);
};
};
}
// namespace paddle
}
// namespace paddle
paddle/platform/enforce.h
浏览文件 @
174a3b3e
...
@@ -187,25 +187,16 @@ inline void throw_on_error(T e) {
...
@@ -187,25 +187,16 @@ inline void throw_on_error(T e) {
__PADDLE_BINARY_COMPARE(__VAL0, __VAL1, <, >=, __VA_ARGS__)
__PADDLE_BINARY_COMPARE(__VAL0, __VAL1, <, >=, __VA_ARGS__)
#define PADDLE_ENFORCE_LE(__VAL0, __VAL1, ...) \
#define PADDLE_ENFORCE_LE(__VAL0, __VAL1, ...) \
__PADDLE_BINARY_COMPARE(__VAL0, __VAL1, <=, >, __VA_ARGS__)
__PADDLE_BINARY_COMPARE(__VAL0, __VAL1, <=, >, __VA_ARGS__)
#define PADDLE_ENFORCE_NOT_NULL(__VAL, ...) \
// if two values have different data types, choose a compatible type for them.
PADDLE_ENFORCE(nullptr != (__VAL), #__VAL " should not be null\n%s", \
template
<
typename
T1
,
typename
T2
>
paddle::string::Sprintf("" __VA_ARGS__));
struct
CompatibleType
{
static
const
bool
t1_to_t2
=
std
::
is_convertible
<
T1
,
T2
>::
value
;
typedef
typename
std
::
conditional
<
t1_to_t2
,
T2
,
T1
>::
type
type
;
};
#define __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, __CMP, __INV_CMP, ...) \
#define __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, __CMP, __INV_CMP, ...) \
PADDLE_ENFORCE(__COMPATIBLE_TYPE(__VAL0, __VAL1, __VAL0) \
PADDLE_ENFORCE(__VAL0 __CMP __VAL1, \
__CMP __COMPATIBLE_TYPE(__VAL0, __VAL1, __VAL1), \
"enforce %s " #__CMP " %s failed, %s " #__INV_CMP " %s\n%s", \
"enforce %s " #__CMP " %s failed, %s " #__INV_CMP " %s\n%s", \
#__VAL0, #__VAL1, std::to_string(__VAL0), \
#__VAL0, #__VAL1, std::to_string(__VAL0), \
std::to_string(__VAL1), \
std::to_string(__VAL1), \
paddle::string::Sprintf("" __VA_ARGS__));
paddle::string::Sprintf("" __VA_ARGS__));
#define __COMPATIBLE_TYPE(__VAL0, __VAL1, __VAL) \
typename paddle::platform::CompatibleType<decltype(__VAL0), \
decltype(__VAL1)>::type(__VAL)
}
// namespace platform
}
// namespace platform
}
// namespace paddle
}
// namespace paddle
paddle/platform/enforce_test.cc
浏览文件 @
174a3b3e
...
@@ -9,8 +9,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
...
@@ -9,8 +9,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
See the License for the specific language governing permissions and
limitations under the License. */
limitations under the License. */
#include "paddle/platform/enforce.h"
#include <memory>
#include "gtest/gtest.h"
#include "gtest/gtest.h"
#include "paddle/platform/enforce.h"
TEST
(
ENFORCE
,
OK
)
{
TEST
(
ENFORCE
,
OK
)
{
PADDLE_ENFORCE
(
true
,
"Enforce is ok %d now %f"
,
123
,
0.345
);
PADDLE_ENFORCE
(
true
,
"Enforce is ok %d now %f"
,
123
,
0.345
);
...
@@ -196,3 +198,27 @@ TEST(ENFORCE_LT, FAIL) {
...
@@ -196,3 +198,27 @@ TEST(ENFORCE_LT, FAIL) {
ASSERT_TRUE
(
in_catch
);
ASSERT_TRUE
(
in_catch
);
}
}
TEST
(
ENFORCE_NOT_NULL
,
OK
)
{
int
*
a
=
new
int
;
PADDLE_ENFORCE_NOT_NULL
(
a
);
delete
a
;
}
TEST
(
ENFORCE_NOT_NULL
,
FAIL
)
{
bool
in_catch
=
false
;
int
*
a
{
nullptr
};
try
{
PADDLE_ENFORCE_NOT_NULL
(
a
);
}
catch
(
paddle
::
platform
::
EnforceNotMet
error
)
{
in_catch
=
true
;
const
std
::
string
msg
=
"a should not be null"
;
const
char
*
what
=
error
.
what
();
for
(
size_t
i
=
0
;
i
<
msg
.
length
();
++
i
)
{
ASSERT_EQ
(
what
[
i
],
msg
[
i
]);
}
}
ASSERT_TRUE
(
in_catch
);
}
paddle/scripts/docker/build.sh
浏览文件 @
174a3b3e
...
@@ -33,6 +33,9 @@ Configuring cmake in /paddle/build ...
...
@@ -33,6 +33,9 @@ Configuring cmake in /paddle/build ...
-DWITH_AVX=
${
WITH_AVX
:-
OFF
}
-DWITH_AVX=
${
WITH_AVX
:-
OFF
}
-DWITH_GOLANG=
${
WITH_GOLANG
:-
OFF
}
-DWITH_GOLANG=
${
WITH_GOLANG
:-
OFF
}
-DWITH_SWIG_PY=ON
-DWITH_SWIG_PY=ON
-DWITH_C_API=
${
WITH_C_API
:-
OFF
}
-DWITH_PYTHON=
${
WITH_PYTHON
:-
ON
}
-DWITH_SWIG_PY=
${
WITH_SWIG_PY
:-
ON
}
-DCUDNN_ROOT=/usr/
-DCUDNN_ROOT=/usr/
-DWITH_STYLE_CHECK=
${
WITH_STYLE_CHECK
:-
OFF
}
-DWITH_STYLE_CHECK=
${
WITH_STYLE_CHECK
:-
OFF
}
-DWITH_TESTING=
${
WITH_TESTING
:-
OFF
}
-DWITH_TESTING=
${
WITH_TESTING
:-
OFF
}
...
@@ -49,7 +52,9 @@ cmake .. \
...
@@ -49,7 +52,9 @@ cmake .. \
-DWITH_GPU
=
${
WITH_GPU
:-
OFF
}
\
-DWITH_GPU
=
${
WITH_GPU
:-
OFF
}
\
-DWITH_AVX
=
${
WITH_AVX
:-
OFF
}
\
-DWITH_AVX
=
${
WITH_AVX
:-
OFF
}
\
-DWITH_GOLANG
=
${
WITH_GOLANG
:-
OFF
}
\
-DWITH_GOLANG
=
${
WITH_GOLANG
:-
OFF
}
\
-DWITH_SWIG_PY
=
ON
\
-DWITH_SWIG_PY
=
${
WITH_SWIG_PY
:-
ON
}
\
-DWITH_C_API
=
${
WITH_C_API
:-
OFF
}
\
-DWITH_PYTHON
=
${
WITH_PYTHON
:-
ON
}
\
-DCUDNN_ROOT
=
/usr/
\
-DCUDNN_ROOT
=
/usr/
\
-DWITH_STYLE_CHECK
=
${
WITH_STYLE_CHECK
:-
OFF
}
\
-DWITH_STYLE_CHECK
=
${
WITH_STYLE_CHECK
:-
OFF
}
\
-DWITH_TESTING
=
${
WITH_TESTING
:-
OFF
}
\
-DWITH_TESTING
=
${
WITH_TESTING
:-
OFF
}
\
...
...
python/paddle/trainer/config_parser.py
浏览文件 @
174a3b3e
...
@@ -2657,6 +2657,31 @@ class SubSequenceLayer(LayerBase):
...
@@ -2657,6 +2657,31 @@ class SubSequenceLayer(LayerBase):
self
.
create_bias_parameter
(
bias
,
size
)
self
.
create_bias_parameter
(
bias
,
size
)
@
config_layer
(
'sub_nested_seq'
)
class
SubNestedSequenceLayer
(
LayerBase
):
def
__init__
(
self
,
name
,
inputs
,
selected_indices
,
bias
=
False
,
**
xargs
):
if
isinstance
(
inputs
,
list
):
assert
len
(
inputs
)
==
1
,
(
'the first input of sub_nested_seq '
'layer is a single nested sequence.'
)
inputs
=
inputs
[
0
]
if
isinstance
(
selected_indices
,
list
):
assert
len
(
selected_indices
)
==
1
,
(
'the second input of '
'sub_nested_seq layer is a single layer which is a '
'set of selected indices.'
)
selected_indices
=
selected_indices
[
0
]
super
(
SubNestedSequenceLayer
,
self
).
__init__
(
name
,
'sub_nested_seq'
,
0
,
inputs
=
[
inputs
,
selected_indices
],
**
xargs
)
input_layer0
=
self
.
get_input_layer
(
0
)
size
=
input_layer0
.
size
self
.
set_layer_size
(
size
)
@
config_layer
(
'out_prod'
)
@
config_layer
(
'out_prod'
)
class
OuterProdLayer
(
LayerBase
):
class
OuterProdLayer
(
LayerBase
):
def
__init__
(
self
,
name
,
inputs
,
device
=
None
):
def
__init__
(
self
,
name
,
inputs
,
device
=
None
):
...
...
python/paddle/trainer_config_helpers/layers.py
浏览文件 @
174a3b3e
...
@@ -129,6 +129,7 @@ __all__ = [
...
@@ -129,6 +129,7 @@ __all__ = [
'prelu_layer'
,
'prelu_layer'
,
'gated_unit_layer'
,
'gated_unit_layer'
,
'crop_layer'
,
'crop_layer'
,
'sub_nested_seq_layer'
,
'clip_layer'
,
'clip_layer'
,
'slice_projection'
,
'slice_projection'
,
]
]
...
@@ -224,6 +225,7 @@ class LayerType(object):
...
@@ -224,6 +225,7 @@ class LayerType(object):
PRELU
=
'prelu'
PRELU
=
'prelu'
CROP_LAYER
=
'crop'
CROP_LAYER
=
'crop'
SUB_NESTED_SEQ
=
'sub_nested_seq'
CLIP_LAYER
=
'clip'
CLIP_LAYER
=
'clip'
@
staticmethod
@
staticmethod
...
@@ -6088,6 +6090,53 @@ def crop_layer(input, offset, axis=2, shape=None, name=None, layer_attr=None):
...
@@ -6088,6 +6090,53 @@ def crop_layer(input, offset, axis=2, shape=None, name=None, layer_attr=None):
size
=
l
.
config
.
size
)
size
=
l
.
config
.
size
)
@
wrap_name_default
()
@
layer_support
()
def
sub_nested_seq_layer
(
input
,
selected_indices
,
name
=
None
):
"""
The sub_nested_seq_layer accepts two inputs: the first one is a nested
sequence; the second one is a set of selceted indices in the nested sequence.
Then sub_nest_seq_layer trims the first nested sequence input according
to the selected indices to form a new output. This layer is useful in
beam training.
The example usage is:
.. code-block:: python
sub_nest_seq = sub_nested_seq_layer(input=[data, selected_indices])
:param input: A nested sequence.
:type input: LayerOutput
:param selected_indices: a set of sequence indices in the nested sequence.
:type input: LayerOutput
:param name: name of this layer.
:type name: basestring
:return: LayerOutput object.
:rtype: LayerOutput
"""
assert
isinstance
(
input
,
LayerOutput
),
(
'The first input of '
'sub_nested_seq_layer must be a Paddle layer.'
)
assert
isinstance
(
selected_indices
,
LayerOutput
),
(
'The second input of '
'sub_nested_seq_layer must be a Paddle layer.'
)
l
=
Layer
(
inputs
=
input
.
name
,
selected_indices
=
selected_indices
.
name
,
name
=
name
,
type
=
LayerType
.
SUB_NESTED_SEQ
)
return
LayerOutput
(
name
=
name
,
layer_type
=
LayerType
.
SUB_NESTED_SEQ
,
parents
=
input
,
size
=
l
.
config
.
size
)
@
wrap_name_default
(
"clip"
)
@
wrap_name_default
(
"clip"
)
def
clip_layer
(
input
,
min
,
max
,
name
=
None
):
def
clip_layer
(
input
,
min
,
max
,
name
=
None
):
"""
"""
...
...
python/paddle/trainer_config_helpers/tests/configs/file_list.sh
浏览文件 @
174a3b3e
...
@@ -7,6 +7,7 @@ test_rnn_group shared_fc shared_lstm shared_gru test_cost_layers_with_weight
...
@@ -7,6 +7,7 @@ test_rnn_group shared_fc shared_lstm shared_gru test_cost_layers_with_weight
test_spp_layer test_bilinear_interp test_maxout test_bi_grumemory math_ops
test_spp_layer test_bilinear_interp test_maxout test_bi_grumemory math_ops
test_seq_concat_reshape test_pad test_smooth_l1 test_multiplex_layer
test_seq_concat_reshape test_pad test_smooth_l1 test_multiplex_layer
test_prelu_layer test_row_conv test_detection_output_layer test_multibox_loss_layer
test_prelu_layer test_row_conv test_detection_output_layer test_multibox_loss_layer
test_recursive_topology test_gated_unit_layer test_clip_layer test_row_l2_norm_layer
)
test_recursive_topology test_gated_unit_layer test_clip_layer test_row_l2_norm_layer
test_seq_select_layers
)
export
whole_configs
=(
test_split_datasource
)
export
whole_configs
=(
test_split_datasource
)
python/paddle/trainer_config_helpers/tests/configs/protostr/test_seq_select_layers.protostr
0 → 100644
浏览文件 @
174a3b3e
type: "nn"
layers {
name: "input_seq"
type: "data"
size: 300
active_type: ""
}
layers {
name: "input"
type: "data"
size: 5
active_type: ""
}
layers {
name: "__sub_nested_seq_layer_0__"
type: "sub_nested_seq"
size: 300
active_type: ""
inputs {
input_layer_name: "input_seq"
}
inputs {
input_layer_name: "input"
}
}
input_layer_names: "input_seq"
output_layer_names: "__sub_nested_seq_layer_0__"
sub_models {
name: "root"
layer_names: "input_seq"
layer_names: "input"
layer_names: "__sub_nested_seq_layer_0__"
input_layer_names: "input_seq"
output_layer_names: "__sub_nested_seq_layer_0__"
is_recurrent_layer_group: false
}
python/paddle/trainer_config_helpers/tests/configs/test_seq_select_layers.py
0 → 100644
浏览文件 @
174a3b3e
#!/usr/bin/env python
#coding=utf-8
from
paddle.trainer_config_helpers
import
*
beam_size
=
5
data
=
data_layer
(
name
=
'input_seq'
,
size
=
300
)
selected_ids
=
data_layer
(
name
=
'input'
,
size
=
beam_size
)
sub_nest_seq
=
sub_nested_seq_layer
(
input
=
data
,
selected_indices
=
selected_ids
)
outputs
(
sub_nest_seq
)
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录