Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Crayon鑫
Paddle
提交
65880f7e
P
Paddle
项目概览
Crayon鑫
/
Paddle
与 Fork 源项目一致
Fork自
PaddlePaddle / Paddle
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
1
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
Paddle
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
1
Issue
1
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
65880f7e
编写于
8月 01, 2017
作者:
Q
qiaolongfei
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'develop' of
https://github.com/PaddlePaddle/Paddle
into scope
上级
5d134a03
61ebacbc
变更
20
隐藏空白更改
内联
并排
Showing
20 changed file
with
289 addition
and
185 deletion
+289
-185
paddle/cuda/src/hl_cuda_sequence.cu
paddle/cuda/src/hl_cuda_sequence.cu
+1
-2
paddle/framework/operator.cc
paddle/framework/operator.cc
+2
-2
paddle/framework/operator.h
paddle/framework/operator.h
+106
-75
paddle/framework/operator_test.cc
paddle/framework/operator_test.cc
+28
-4
paddle/math/tests/test_matrixCompare.cpp
paddle/math/tests/test_matrixCompare.cpp
+60
-0
paddle/operators/add_op.cc
paddle/operators/add_op.cc
+10
-11
paddle/operators/add_op.h
paddle/operators/add_op.h
+6
-5
paddle/operators/cross_entropy_op.cc
paddle/operators/cross_entropy_op.cc
+9
-9
paddle/operators/cross_entropy_op.h
paddle/operators/cross_entropy_op.h
+8
-8
paddle/operators/mul_op.cc
paddle/operators/mul_op.cc
+7
-9
paddle/operators/mul_op.h
paddle/operators/mul_op.h
+5
-7
paddle/operators/rowwise_add_op.cc
paddle/operators/rowwise_add_op.cc
+7
-7
paddle/operators/rowwise_add_op.h
paddle/operators/rowwise_add_op.h
+4
-6
paddle/operators/sgd_op.cc
paddle/operators/sgd_op.cc
+8
-9
paddle/operators/sgd_op.h
paddle/operators/sgd_op.h
+5
-5
paddle/operators/sigmoid_op.cc
paddle/operators/sigmoid_op.cc
+5
-7
paddle/operators/sigmoid_op.h
paddle/operators/sigmoid_op.h
+4
-5
paddle/operators/softmax_op.cc
paddle/operators/softmax_op.cc
+7
-9
paddle/operators/softmax_op.h
paddle/operators/softmax_op.h
+4
-4
paddle/operators/type_alias.h
paddle/operators/type_alias.h
+3
-1
未找到文件。
paddle/cuda/src/hl_cuda_sequence.cu
浏览文件 @
65880f7e
...
...
@@ -269,8 +269,7 @@ void hl_sequence2batch_copy_padding(real* batch,
int
blockDimY
=
CUDA_BLOCK_SIZE
/
blockDimX
;
dim3
threads
(
blockDimX
,
blockDimY
);
int
gridDimX
=
(
maxSequenceLength
*
blockDimX
+
CUDA_BLOCK_SIZE
-
1
)
/
CUDA_BLOCK_SIZE
;
int
gridDimX
=
(
maxSequenceLength
+
blockDimY
-
1
)
/
blockDimY
;
int
gridDimY
=
numSequences
;
dim3
grid
(
gridDimX
,
gridDimY
);
...
...
paddle/framework/operator.cc
浏览文件 @
65880f7e
...
...
@@ -20,7 +20,7 @@ namespace paddle {
namespace
framework
{
template
<
>
Eigen
::
DefaultDevice
*
Kernel
Context
::
GetEigenDevice
<
Eigen
::
DefaultDevice
*
Execution
Context
::
GetEigenDevice
<
platform
::
CPUPlace
,
Eigen
::
DefaultDevice
>
()
const
{
return
device_context_
.
get_eigen_device
<
Eigen
::
DefaultDevice
>
();
}
...
...
@@ -28,7 +28,7 @@ Eigen::DefaultDevice* KernelContext::GetEigenDevice<
#ifndef PADDLE_ONLY_CPU
template
<
>
Eigen
::
GpuDevice
*
Kernel
Context
::
GetEigenDevice
<
platform
::
GPUPlace
,
Eigen
::
GpuDevice
>
()
const
{
Execution
Context
::
GetEigenDevice
<
platform
::
GPUPlace
,
Eigen
::
GpuDevice
>
()
const
{
return
device_context_
.
get_eigen_device
<
Eigen
::
GpuDevice
>
();
}
#endif
...
...
paddle/framework/operator.h
浏览文件 @
65880f7e
...
...
@@ -31,22 +31,9 @@ limitations under the License. */
namespace
paddle
{
namespace
framework
{
template
<
typename
T
>
struct
EigenDeviceConverter
;
template
<>
struct
EigenDeviceConverter
<
platform
::
CPUPlace
>
{
using
EigenDeviceType
=
Eigen
::
DefaultDevice
;
};
#ifndef PADDLE_ONLY_CPU
template
<>
struct
EigenDeviceConverter
<
platform
::
GPUPlace
>
{
using
EigenDeviceType
=
Eigen
::
GpuDevice
;
};
#endif
class
OperatorBase
;
class
InferShapeContext
;
class
ExecutionContext
;
/**
* OperatorBase has the basic element that Net will call to do computation.
* Only CreateOperator from OpRegistry will new Operator directly. User
...
...
@@ -112,46 +99,127 @@ class OperatorBase {
std
::
shared_ptr
<
std
::
unordered_map
<
std
::
string
,
int
>>
in_out_idxs_
;
};
class
Kernel
Context
{
class
Operator
Context
{
public:
KernelContext
(
const
OperatorBase
*
op
,
const
Scope
&
scope
,
const
platform
::
DeviceContext
&
device_context
)
:
op_
(
*
op
),
scope_
(
scope
),
device_context_
(
device_context
)
{}
OperatorContext
(
const
OperatorBase
*
op
,
const
Scope
&
scope
)
:
op_
(
*
op
),
scope_
(
scope
)
{}
size_t
InputSize
()
const
{
return
op_
.
inputs_
.
size
();
}
const
Variable
*
Input
(
int
index
)
const
{
return
scope_
.
FindVar
(
op_
.
inputs_
[
index
]);
size_t
OutputSize
()
const
{
return
op_
.
outputs_
.
size
();
}
const
Variable
*
InputVar
(
const
size_t
index
)
const
{
return
scope_
.
FindVar
(
op_
.
inputs_
.
at
(
index
));
}
Variable
*
Output
(
in
t
index
)
const
{
return
scope_
.
FindVar
(
op_
.
outputs_
[
index
]
);
Variable
*
Output
Var
(
const
size_
t
index
)
const
{
return
scope_
.
FindVar
(
op_
.
outputs_
.
at
(
index
)
);
}
const
Variable
*
Input
(
const
std
::
string
&
name
)
const
{
const
Variable
*
Input
Var
(
const
std
::
string
&
name
)
const
{
return
scope_
.
FindVar
(
op_
.
Input
(
name
));
}
const
Variable
*
Output
(
const
std
::
string
&
name
)
const
{
Variable
*
OutputVar
(
const
std
::
string
&
name
)
const
{
return
scope_
.
FindVar
(
op_
.
Output
(
name
));
}
const
std
::
vector
<
const
Variable
*>
Inputs
(
const
std
::
string
&
name
)
const
{
const
std
::
vector
<
const
Variable
*>
MultiInputVar
(
const
std
::
string
&
name
)
const
{
auto
names
=
op_
.
Inputs
(
name
);
std
::
vector
<
const
Variable
*>
res
;
res
.
reserve
(
names
.
size
());
std
::
transform
(
names
.
begin
(),
names
.
end
(),
res
.
begin
(
),
names
.
begin
(),
names
.
end
(),
std
::
back_inserter
(
res
),
[
this
](
const
std
::
string
&
name
)
{
return
scope_
.
FindVar
(
name
);
});
return
res
;
}
const
std
::
vector
<
const
Variable
*>
Outputs
(
const
std
::
string
&
name
)
const
{
std
::
vector
<
const
Variable
*>
MultiOutputVar
(
const
std
::
string
&
name
)
const
{
auto
names
=
op_
.
Outputs
(
name
);
std
::
vector
<
const
Variable
*>
res
;
res
.
reserve
(
names
.
size
());
std
::
transform
(
names
.
begin
(),
names
.
end
(),
res
.
begin
(
),
names
.
begin
(),
names
.
end
(),
std
::
back_inserter
(
res
),
[
this
](
const
std
::
string
&
name
)
{
return
scope_
.
FindVar
(
name
);
});
return
res
;
}
template
<
typename
T
>
const
T
*
Input
(
const
size_t
index
)
const
{
return
&
(
InputVar
(
index
)
->
Get
<
T
>
());
}
template
<
typename
T
>
T
*
Output
(
const
size_t
index
)
const
{
return
OutputVar
(
index
)
->
GetMutable
<
T
>
();
}
template
<
typename
T
>
const
T
*
Input
(
const
std
::
string
&
name
)
const
{
return
&
(
InputVar
(
name
)
->
Get
<
T
>
());
}
template
<
typename
T
>
T
*
Output
(
const
std
::
string
&
name
)
const
{
return
OutputVar
(
name
)
->
GetMutable
<
T
>
();
}
template
<
typename
T
>
const
std
::
vector
<
const
T
*>
MultiInput
(
const
std
::
string
&
name
)
const
{
auto
names
=
op_
.
Inputs
(
name
);
std
::
vector
<
const
T
*>
res
;
res
.
reserve
(
names
.
size
());
std
::
transform
(
names
.
begin
(),
names
.
end
(),
std
::
back_inserter
(
res
),
[
this
](
const
std
::
string
&
name
)
{
return
&
scope_
.
FindVar
(
name
)
->
Get
<
T
>
();
});
return
res
;
}
template
<
typename
T
>
std
::
vector
<
const
T
*>
MultiOutput
(
const
std
::
string
&
name
)
const
{
auto
names
=
op_
.
Outputs
(
name
);
std
::
vector
<
const
T
*>
res
;
res
.
reserve
(
names
.
size
());
std
::
transform
(
names
.
begin
(),
names
.
end
(),
std
::
back_inserter
(
res
),
[
this
](
const
std
::
string
&
name
)
{
return
scope_
.
FindVar
(
name
)
->
GetMutable
<
T
>
();
});
return
res
;
}
const
OperatorBase
&
op_
;
const
Scope
&
scope_
;
};
class
InferShapeContext
:
public
OperatorContext
{
public:
InferShapeContext
(
const
OperatorBase
*
op
,
const
Scope
&
scope
)
:
OperatorContext
(
op
,
scope
)
{}
};
template
<
typename
T
>
struct
EigenDeviceConverter
;
template
<>
struct
EigenDeviceConverter
<
platform
::
CPUPlace
>
{
using
EigenDeviceType
=
Eigen
::
DefaultDevice
;
};
#ifndef PADDLE_ONLY_CPU
template
<>
struct
EigenDeviceConverter
<
platform
::
GPUPlace
>
{
using
EigenDeviceType
=
Eigen
::
GpuDevice
;
};
#endif
class
ExecutionContext
:
public
OperatorContext
{
public:
ExecutionContext
(
const
OperatorBase
*
op
,
const
Scope
&
scope
,
const
platform
::
DeviceContext
&
device_context
)
:
OperatorContext
(
op
,
scope
),
device_context_
(
device_context
)
{}
template
<
typename
PlaceType
,
typename
DeviceType
=
typename
EigenDeviceConverter
<
PlaceType
>::
EigenDeviceType
>
...
...
@@ -159,38 +227,23 @@ class KernelContext {
platform
::
Place
GetPlace
()
const
{
return
device_context_
.
GetPlace
();
}
const
OperatorBase
&
op_
;
const
Scope
&
scope_
;
const
platform
::
DeviceContext
&
device_context_
;
};
class
OpKernel
{
public:
/**
*
Kernel
Context is the only parameter of Kernel Run function.
*
Execution
Context is the only parameter of Kernel Run function.
* Run will get input/output variables, state such as momentum and
* device resource such as CUDA stream, cublas handle, etc. from
*
Kernel
Context. User should construct it before run the Operator.
*
Execution
Context. User should construct it before run the Operator.
*/
virtual
void
Compute
(
const
Kernel
Context
&
context
)
const
=
0
;
virtual
void
Compute
(
const
Execution
Context
&
context
)
const
=
0
;
virtual
~
OpKernel
()
{}
};
template
<
typename
T
>
struct
VarToTensor
{};
template
<>
struct
VarToTensor
<
Tensor
*>
{
Tensor
*
operator
()(
Variable
*
var
)
{
return
var
->
GetMutable
<
Tensor
>
();
}
};
template
<>
struct
VarToTensor
<
const
Tensor
*>
{
const
Tensor
*
operator
()(
Variable
*
var
)
{
return
&
var
->
Get
<
Tensor
>
();
}
};
class
OperatorWithKernel
:
public
OperatorBase
{
public:
struct
OpKernelKey
{
...
...
@@ -216,10 +269,14 @@ class OperatorWithKernel : public OperatorBase {
using
OpKernelMap
=
std
::
unordered_map
<
OpKernelKey
,
std
::
unique_ptr
<
OpKernel
>
,
OpKernelHash
>
;
void
InferShape
(
const
Scope
&
scope
)
const
{
InferShape
(
InferShapeContext
(
this
,
scope
));
}
void
Run
(
const
Scope
&
scope
,
const
platform
::
DeviceContext
&
dev_ctx
)
const
final
{
auto
&
opKernel
=
AllOpKernels
().
at
(
type_
).
at
(
OpKernelKey
(
dev_ctx
));
opKernel
->
Compute
(
Kernel
Context
(
this
,
scope
,
dev_ctx
));
opKernel
->
Compute
(
Execution
Context
(
this
,
scope
,
dev_ctx
));
}
static
std
::
unordered_map
<
std
::
string
/* op_type */
,
OpKernelMap
>&
...
...
@@ -228,34 +285,8 @@ class OperatorWithKernel : public OperatorBase {
return
g_all_op_kernels
;
}
void
InferShape
(
const
Scope
&
scope
)
const
final
{
std
::
vector
<
const
Tensor
*>
ins
;
VarNamesToTensors
(
scope
,
inputs_
,
&
ins
);
std
::
vector
<
Tensor
*>
outs
;
VarNamesToTensors
(
scope
,
outputs_
,
&
outs
);
InferShape
(
ins
,
outs
);
};
private:
template
<
typename
T
>
void
VarNamesToTensors
(
const
Scope
&
scope
,
const
std
::
vector
<
std
::
string
>&
var_names
,
std
::
vector
<
T
>*
container
)
const
{
container
->
reserve
(
var_names
.
size
());
VarToTensor
<
T
>
convert
;
for
(
auto
&
name
:
var_names
)
{
auto
var
=
scope
.
FindVar
(
name
);
if
(
var
!=
nullptr
)
{
container
->
push_back
(
convert
(
var
));
}
else
{
container
->
push_back
(
nullptr
);
}
}
}
protected:
virtual
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>&
inputs
,
const
std
::
vector
<
Tensor
*>&
outputs
)
const
=
0
;
virtual
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
=
0
;
};
}
// namespace framework
...
...
paddle/framework/operator_test.cc
浏览文件 @
65880f7e
...
...
@@ -73,6 +73,7 @@ TEST(OperatorBase, all) {
auto
op
=
paddle
::
framework
::
OpRegistry
::
CreateOp
(
op_desc
);
scope
.
NewVar
(
"OUT1"
);
ASSERT_EQ
(
paddle
::
framework
::
op_run_num
,
0
);
op
->
InferShape
(
scope
);
op
->
Run
(
scope
,
device_context
);
ASSERT_EQ
(
paddle
::
framework
::
op_run_num
,
1
);
}
...
...
@@ -97,14 +98,13 @@ static int cpu_kernel_run_num = 0;
class
OpWithKernelTest
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>&
inputs
,
const
std
::
vector
<
Tensor
*>&
outputs
)
const
override
{}
void
InferShape
(
const
framework
::
InferShapeContext
&
ctx
)
const
override
{}
};
template
<
typename
T1
,
typename
T2
>
class
CPUKernelTest
:
public
OpKernel
{
public:
void
Compute
(
const
Kernel
Context
&
ctx
)
const
{
void
Compute
(
const
Execution
Context
&
ctx
)
const
{
std
::
cout
<<
"this is cpu kernel"
<<
std
::
endl
;
std
::
cout
<<
ctx
.
op_
.
DebugString
()
<<
std
::
endl
;
cpu_kernel_run_num
++
;
...
...
@@ -149,13 +149,31 @@ class OpKernelTestMultiInputsProtoAndCheckerMaker
class
CPUKernalMultiInputsTest
:
public
OpKernel
{
public:
void
Compute
(
const
Kernel
Context
&
ctx
)
const
{
void
Compute
(
const
Execution
Context
&
ctx
)
const
{
auto
xs
=
ctx
.
op_
.
Inputs
(
"xs"
);
ASSERT_EQ
(
xs
.
size
(),
3UL
);
ASSERT_EQ
(
xs
[
0
],
"x0"
);
ASSERT_EQ
(
xs
[
1
],
"x1"
);
ASSERT_EQ
(
xs
[
2
],
"x2"
);
auto
inVar0
=
ctx
.
MultiInputVar
(
"xs"
);
ASSERT_EQ
(
inVar0
.
size
(),
3
);
auto
intVar1
=
ctx
.
InputVar
(
"k"
);
ASSERT_NE
(
intVar1
,
nullptr
);
auto
outVar0
=
ctx
.
MultiOutputVar
(
"ys"
);
ASSERT_EQ
(
outVar0
.
size
(),
2
);
auto
inTensor0
=
ctx
.
MultiInput
<
Tensor
>
(
"xs"
);
ASSERT_EQ
(
inTensor0
.
size
(),
3
);
auto
intTensor1
=
ctx
.
Input
<
Tensor
>
(
"k"
);
ASSERT_NE
(
intTensor1
,
nullptr
);
auto
outTensor0
=
ctx
.
MultiOutput
<
Tensor
>
(
"ys"
);
ASSERT_EQ
(
outTensor0
.
size
(),
2
);
auto
k
=
ctx
.
op_
.
Input
(
"k"
);
ASSERT_EQ
(
k
,
"k0"
);
...
...
@@ -233,6 +251,12 @@ TEST(OpKernel, multi_inputs) {
paddle
::
platform
::
CPUDeviceContext
cpu_device_context
;
paddle
::
framework
::
Scope
scope
;
scope
.
NewVar
(
"x0"
)
->
GetMutable
<
Tensor
>
();
scope
.
NewVar
(
"x1"
)
->
GetMutable
<
Tensor
>
();
scope
.
NewVar
(
"x2"
)
->
GetMutable
<
Tensor
>
();
scope
.
NewVar
(
"k0"
)
->
GetMutable
<
Tensor
>
();
scope
.
NewVar
(
"y0"
)
->
GetMutable
<
Tensor
>
();
scope
.
NewVar
(
"y1"
)
->
GetMutable
<
Tensor
>
();
auto
op
=
paddle
::
framework
::
OpRegistry
::
CreateOp
(
op_desc
);
op
->
Run
(
scope
,
cpu_device_context
);
...
...
paddle/math/tests/test_matrixCompare.cpp
浏览文件 @
65880f7e
...
...
@@ -1141,4 +1141,64 @@ TEST(CpuMatrix, copyFrom) {
TensorCheckEqual
(
cpu
,
copy
);
}
void
testBatch2seqPadding
(
int
batchSize
,
int
inputDim
)
{
MatrixPtr
cpuInput
=
std
::
make_shared
<
CpuMatrix
>
(
batchSize
,
inputDim
);
MatrixPtr
gpuInput
=
std
::
make_shared
<
GpuMatrix
>
(
batchSize
,
inputDim
);
cpuInput
->
randomizeUniform
();
gpuInput
->
copyFrom
(
*
cpuInput
);
IVectorPtr
cpuSequence
;
generateSequenceStartPositions
(
batchSize
,
cpuSequence
);
IVectorPtr
gpuSequence
=
IVector
::
create
(
cpuSequence
->
getSize
(),
true
);
gpuSequence
->
copyFrom
(
*
cpuSequence
);
size_t
numSeq
=
cpuSequence
->
getSize
()
-
1
;
size_t
maxSeqLen
=
*
std
::
max_element
(
cpuSequence
->
getData
(),
cpuSequence
->
getData
()
+
numSeq
);
MatrixPtr
cBatch
=
std
::
make_shared
<
CpuMatrix
>
(
numSeq
*
maxSeqLen
,
inputDim
);
MatrixPtr
gBatch
=
std
::
make_shared
<
GpuMatrix
>
(
numSeq
*
maxSeqLen
,
inputDim
);
MatrixPtr
cCheck
=
std
::
make_shared
<
CpuMatrix
>
(
numSeq
*
maxSeqLen
,
inputDim
);
hl_sequence2batch_copy_padding
(
gBatch
->
getData
(),
gpuInput
->
getData
(),
cpuSequence
->
getData
(),
inputDim
,
maxSeqLen
,
numSeq
,
false
,
true
);
cCheck
->
copyFrom
(
*
gBatch
);
int
*
seqStart
=
cpuSequence
->
getData
();
float
*
batchData
=
cBatch
->
getData
();
float
*
seqData
=
cpuInput
->
getData
();
for
(
size_t
i
=
0
;
i
<
maxSeqLen
;
i
++
)
{
for
(
size_t
j
=
0
;
j
<
numSeq
;
j
++
)
{
size_t
sequenceStart
=
seqStart
[
j
];
size_t
sequenceLength
=
seqStart
[
j
+
1
]
-
seqStart
[
j
];
if
(
i
<
sequenceLength
)
{
memcpy
(
batchData
+
(
i
*
numSeq
+
j
)
*
inputDim
,
seqData
+
(
sequenceStart
+
i
)
*
inputDim
,
inputDim
*
sizeof
(
real
));
}
else
{
memset
(
batchData
+
(
i
*
numSeq
+
j
)
*
inputDim
,
0
,
inputDim
*
sizeof
(
real
));
}
}
}
TensorCheckErr
(
*
cBatch
,
*
cCheck
);
}
TEST
(
Matrix
,
warpCTC
)
{
for
(
auto
batchSize
:
{
51
,
526
,
2884
})
{
for
(
auto
inputDim
:
{
32
,
512
,
2026
})
{
VLOG
(
3
)
<<
" batchSize="
<<
batchSize
<<
" inputDim="
<<
inputDim
;
testBatch2seqPadding
(
batchSize
,
inputDim
);
}
}
}
#endif
paddle/operators/add_op.cc
浏览文件 @
65880f7e
...
...
@@ -19,16 +19,16 @@ namespace operators {
class
AddOp
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{
PADDLE_ENFORCE
(
inputs
.
size
()
==
2
,
"Input size of AddOp must be two
"
);
PADDLE_ENFORCE
(
outputs
.
size
()
==
1
,
"Output size of AddOp must be one"
);
PADDLE_ENFORCE
(
inputs
[
0
]
!=
nullptr
&&
inputs
[
1
]
!=
nullptr
&&
outputs
[
0
]
!=
nullptr
,
"Inputs/
Outputs of AddOp must all be set"
);
PADDLE_ENFORCE
(
inputs
[
0
]
->
dims
()
==
inputs
[
1
]
->
dims
(),
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
2
,
"Input size of AddOp must be two"
);
PADDLE_ENFORCE
(
ctx
.
OutputSize
()
==
1
,
"Output size of AddOp must be one
"
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
0
)
!=
nullptr
&&
ctx
.
InputVar
(
1
)
!=
nullptr
,
"Inputs of AddOp must all be set"
);
PADDLE_ENFORCE
(
ctx
.
OutputVar
(
0
)
!=
nullptr
,
"
Outputs of AddOp must all be set"
);
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
()
==
ctx
.
Input
<
Tensor
>
(
1
)
->
dims
(),
"Two input of Add Op's dimension must be same."
);
outputs
[
0
]
->
Resize
(
inputs
[
0
]
->
dims
());
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
());
}
};
...
...
@@ -49,8 +49,7 @@ The equation is: Out = X + Y
class
AddOpGrad
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{}
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{}
std
::
string
DebugString
()
const
override
{
LOG
(
INFO
)
<<
"AddOpGrad"
;
return
""
;
...
...
paddle/operators/add_op.h
浏览文件 @
65880f7e
...
...
@@ -21,16 +21,17 @@ namespace operators {
template
<
typename
Place
,
typename
T
>
class
AddKernel
:
public
OpKernel
{
public:
void
Compute
(
const
Kernel
Context
&
context
)
const
override
{
auto
input0
=
context
.
Input
(
0
)
->
Get
<
Tensor
>
(
);
auto
input1
=
context
.
Input
(
1
)
->
Get
<
Tensor
>
(
);
auto
output
=
context
.
Output
(
0
)
->
GetMutable
<
Tensor
>
(
);
void
Compute
(
const
Execution
Context
&
context
)
const
override
{
auto
input0
=
context
.
Input
<
Tensor
>
(
0
);
auto
input1
=
context
.
Input
<
Tensor
>
(
1
);
auto
output
=
context
.
Output
<
Tensor
>
(
0
);
output
->
mutable_data
<
T
>
(
context
.
GetPlace
());
EigenVector
<
T
>::
Flatten
(
*
output
).
device
(
*
(
context
.
GetEigenDevice
<
Place
>
()))
=
EigenVector
<
T
>::
Flatten
(
input0
)
+
EigenVector
<
T
>::
Flatten
(
input1
);
framework
::
EigenVector
<
T
>::
Flatten
(
*
input0
)
+
framework
::
EigenVector
<
T
>::
Flatten
(
*
input1
);
}
};
...
...
paddle/operators/cross_entropy_op.cc
浏览文件 @
65880f7e
...
...
@@ -19,20 +19,20 @@ namespace operators {
class
OnehotCrossEntropyOp
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{
PADDLE_ENFORCE
(
inputs
.
size
()
==
2
,
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
2
,
"Input size of OnehotCrossEntropyOp must be two"
);
PADDLE_ENFORCE
(
outputs
.
s
ize
()
==
1
,
PADDLE_ENFORCE
(
ctx
.
OutputS
ize
()
==
1
,
"Output size of OnehotCrossEntropyOp must be one"
);
PADDLE_ENFORCE
(
inputs
[
0
]
!=
nullptr
&&
inputs
[
1
]
!=
nullptr
,
PADDLE_ENFORCE
(
ctx
.
InputVar
(
0
)
!=
nullptr
&&
ctx
.
InputVar
(
1
)
!=
nullptr
,
"Inputs of OnehotCrossEntropyOp must all be set"
);
PADDLE_ENFORCE
(
outputs
[
0
]
!=
nullptr
,
PADDLE_ENFORCE
(
ctx
.
OutputVar
(
0
)
!=
nullptr
,
"Outputs of OnehotCrossEntropyOp must all be set"
);
PADDLE_ENFORCE
(
inputs
[
0
]
->
dims
().
size
()
==
2
,
"X's dimension must be 2."
);
PADDLE_ENFORCE
(
outputs
[
0
]
->
dims
().
size
()
==
1
,
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
().
size
()
==
2
,
"X's dimension must be 2."
);
PADDLE_ENFORCE
(
ctx
.
Output
<
Tensor
>
(
0
)
->
dims
().
size
()
==
1
,
"label's dimension must be 1."
);
outputs
[
0
]
->
Resize
({
inputs
[
0
]
->
dims
()[
0
]});
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
({
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
()[
0
]});
}
};
...
...
paddle/operators/cross_entropy_op.h
浏览文件 @
65880f7e
...
...
@@ -23,18 +23,18 @@ class OnehotCrossEntropyOpKernel : public OpKernel {
public:
constexpr
T
LOG_THRESHOLD
()
const
{
return
static_cast
<
T
>
(
1e-20
);
}
void
Compute
(
const
KernelContext
&
context
)
const
override
{
auto
X
=
c
ontext
.
Input
(
0
)
->
Get
<
Tensor
>
(
);
const
T
*
X_data
=
X
.
data
<
T
>
();
const
int
*
label_data
=
c
ontext
.
Input
(
1
)
->
Get
<
Tensor
>
().
data
<
int
>
();
auto
*
Y
=
context
.
Output
(
0
)
->
GetMutable
<
Tensor
>
(
);
void
Compute
(
const
ExecutionContext
&
ctx
)
const
override
{
auto
X
=
c
tx
.
Input
<
Tensor
>
(
0
);
const
T
*
X_data
=
X
->
data
<
T
>
();
const
int
*
label_data
=
c
tx
.
Input
<
Tensor
>
(
1
)
->
data
<
int
>
();
auto
Y
=
ctx
.
Output
<
Tensor
>
(
0
);
Y
->
mutable_data
<
T
>
(
c
ontext
.
GetPlace
());
Y
->
mutable_data
<
T
>
(
c
tx
.
GetPlace
());
T
*
Y_data
=
Y
->
data
<
T
>
();
int
batch_size
=
X
.
dims
()[
0
];
int
class_num
=
X
.
dims
()[
1
];
int
batch_size
=
X
->
dims
()[
0
];
int
class_num
=
X
->
dims
()[
1
];
// Y[i] = -log(X[i][j])
for
(
int
i
=
0
;
i
<
batch_size
;
++
i
)
{
...
...
paddle/operators/mul_op.cc
浏览文件 @
65880f7e
...
...
@@ -19,18 +19,17 @@ namespace operators {
class
MulOp
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{
PADDLE_ENFORCE
(
inputs
.
size
()
==
2
,
"The mul op must take two inputs"
);
auto
dim0
=
inputs
[
0
]
->
dims
();
auto
dim1
=
inputs
[
1
]
->
dims
();
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
2
,
"The mul op must take two inputs"
);
auto
dim0
=
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
();
auto
dim1
=
ctx
.
Input
<
Tensor
>
(
1
)
->
dims
();
PADDLE_ENFORCE
(
dim0
.
size
()
==
2
&&
dim1
.
size
()
==
2
,
"The input of mul op must be matrix"
);
PADDLE_ENFORCE
(
dim0
[
1
]
==
dim1
[
0
],
"First matrix's width must be equal with second matrix's height."
);
PADDLE_ENFORCE
(
outputs
.
s
ize
()
==
1
,
"The mul op must take one output"
);
outputs
[
0
]
->
Resize
({
dim0
[
0
],
dim1
[
1
]});
PADDLE_ENFORCE
(
ctx
.
OutputS
ize
()
==
1
,
"The mul op must take one output"
);
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
({
dim0
[
0
],
dim1
[
1
]});
}
};
...
...
@@ -51,8 +50,7 @@ The equation is: Out = X * Y
class
MulOpGrad
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{}
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{}
std
::
string
DebugString
()
const
override
{
LOG
(
INFO
)
<<
"MulGrad"
;
return
""
;
...
...
paddle/operators/mul_op.h
浏览文件 @
65880f7e
...
...
@@ -22,19 +22,17 @@ namespace operators {
template
<
typename
Place
,
typename
T
>
class
MulKernel
:
public
OpKernel
{
public:
void
Compute
(
const
Kernel
Context
&
context
)
const
override
{
void
Compute
(
const
Execution
Context
&
context
)
const
override
{
Eigen
::
array
<
Eigen
::
IndexPair
<
Eigen
::
DenseIndex
>
,
1
>
dim_pair
=
{
{
Eigen
::
IndexPair
<
Eigen
::
DenseIndex
>
(
1
,
0
)}};
auto
input0
=
context
.
Input
(
0
)
->
Get
<
Tensor
>
();
auto
input1
=
context
.
Input
(
1
)
->
Get
<
Tensor
>
();
auto
*
output
=
context
.
Output
(
0
)
->
GetMutable
<
Tensor
>
();
auto
output
=
context
.
Output
<
Tensor
>
(
0
);
output
->
mutable_data
<
T
>
(
context
.
GetPlace
());
EigenMatrix
<
T
>::
From
(
*
output
).
device
(
*
(
context
.
GetEigenDevice
<
Place
>
()))
=
EigenMatrix
<
T
>::
From
(
input0
).
contract
(
EigenMatrix
<
T
>::
From
(
input1
),
dim_pair
);
EigenMatrix
<
T
>::
From
(
*
context
.
Input
<
Tensor
>
(
"X"
))
.
contract
(
EigenMatrix
<
T
>::
From
(
*
context
.
Input
<
Tensor
>
(
"Y"
)),
dim_pair
);
}
};
}
// namespace operators
...
...
paddle/operators/rowwise_add_op.cc
浏览文件 @
65880f7e
...
...
@@ -18,17 +18,17 @@ namespace operators {
class
RowWiseAddOp
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{
PADDLE_ENFORCE
(
inputs
.
size
()
==
2UL
,
"Two inputs is needed by rowwise add"
);
auto
dim0
=
inputs
[
0
]
->
dims
();
auto
dim1
=
inputs
[
1
]
->
dims
();
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
2UL
,
"Two inputs is needed by rowwise add"
);
auto
dim0
=
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
();
auto
dim1
=
ctx
.
Input
<
Tensor
>
(
1
)
->
dims
();
PADDLE_ENFORCE
(
dim0
.
size
()
==
2
,
"Input 0 must be matrix"
);
PADDLE_ENFORCE
(
dim1
.
size
()
==
1
,
"The second input must be vector"
);
PADDLE_ENFORCE
(
dim0
[
1
]
==
dim1
[
0
],
"The width of two input must be same"
);
PADDLE_ENFORCE
(
outputs
.
s
ize
()
==
1
,
"The output size must be 1"
);
outputs
[
0
]
->
Resize
(
inputs
[
0
]
->
dims
());
PADDLE_ENFORCE
(
ctx
.
OutputS
ize
()
==
1
,
"The output size must be 1"
);
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
());
}
};
...
...
paddle/operators/rowwise_add_op.h
浏览文件 @
65880f7e
...
...
@@ -21,14 +21,12 @@ namespace operators {
template
<
typename
Place
,
typename
T
>
class
RowWiseAddKernel
:
public
OpKernel
{
public:
void
Compute
(
const
KernelContext
&
context
)
const
override
{
auto
in0
=
context
.
Input
(
0
)
->
Get
<
Tensor
>
();
auto
in1
=
context
.
Input
(
1
)
->
Get
<
Tensor
>
();
auto
*
out
=
context
.
Output
(
0
)
->
GetMutable
<
Tensor
>
();
void
Compute
(
const
ExecutionContext
&
context
)
const
override
{
auto
out
=
context
.
Output
<
Tensor
>
(
0
);
out
->
mutable_data
<
T
>
(
context
.
GetPlace
());
auto
input
=
EigenMatrix
<
T
>::
From
(
in0
);
auto
bias
=
EigenVector
<
T
>::
From
(
in1
);
auto
input
=
EigenMatrix
<
T
>::
From
(
*
context
.
Input
<
Tensor
>
(
0
)
);
auto
bias
=
EigenVector
<
T
>::
From
(
*
context
.
Input
<
Tensor
>
(
1
)
);
auto
output
=
EigenMatrix
<
T
>::
From
(
*
out
);
const
int
bias_size
=
bias
.
dimension
(
0
);
...
...
paddle/operators/sgd_op.cc
浏览文件 @
65880f7e
...
...
@@ -19,16 +19,15 @@ namespace operators {
class
SGDOp
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{
PADDLE_ENFORCE
(
inputs
.
size
()
==
2
,
"Input size of SGDOp must be two"
);
PADDLE_ENFORCE
(
outputs
.
size
()
==
1
,
"Output size of SGDOp must be one"
);
PADDLE_ENFORCE
(
inputs
[
0
]
!=
nullptr
,
"inputs[0] mast be set"
);
PADDLE_ENFORCE
(
inputs
[
1
]
!=
nullptr
,
"inputs[1] mast be set"
);
PADDLE_ENFORCE
(
outputs
[
0
]
!=
nullptr
,
"outputs[0] mast be set"
);
PADDLE_ENFORCE
(
inputs
[
0
]
->
dims
()
==
inputs
[
1
]
->
dims
(),
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
2
,
"Input size of SGDOp must be two"
);
PADDLE_ENFORCE
(
ctx
.
OutputSize
()
==
1
,
"Output size of SGDOp must be one"
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
0
)
!=
nullptr
,
"inputs[0] mast be set"
);
PADDLE_ENFORCE
(
ctx
.
InputVar
(
1
)
!=
nullptr
,
"inputs[1] mast be set"
);
PADDLE_ENFORCE
(
ctx
.
OutputVar
(
0
)
!=
nullptr
,
"outputs[0] mast be set"
);
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
()
==
ctx
.
Input
<
Tensor
>
(
1
)
->
dims
(),
"Two input of SGD Op's dimension must be same."
);
outputs
[
0
]
->
Resize
(
inputs
[
0
]
->
dims
());
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
());
}
};
...
...
paddle/operators/sgd_op.h
浏览文件 @
65880f7e
...
...
@@ -21,16 +21,16 @@ namespace operators {
template
<
typename
Place
,
typename
T
>
class
SGDOpKernel
:
public
OpKernel
{
public:
void
Compute
(
const
Kernel
Context
&
ctx
)
const
override
{
auto
param
=
ctx
.
Input
(
"param"
)
->
Get
<
Tensor
>
(
);
auto
grad
=
ctx
.
Input
(
"grad"
)
->
Get
<
Tensor
>
(
);
auto
*
param_out
=
ctx
.
Output
(
0
)
->
GetMutable
<
Tensor
>
(
);
void
Compute
(
const
Execution
Context
&
ctx
)
const
override
{
auto
param
=
ctx
.
Input
<
Tensor
>
(
"param"
);
auto
grad
=
ctx
.
Input
<
Tensor
>
(
"grad"
);
auto
param_out
=
ctx
.
Output
<
Tensor
>
(
0
);
float
lr
=
ctx
.
op_
.
GetAttr
<
float
>
(
"learning_rate"
);
param_out
->
mutable_data
<
T
>
(
ctx
.
GetPlace
());
EigenVector
<
T
>::
Flatten
(
*
param_out
).
device
(
*
(
ctx
.
GetEigenDevice
<
Place
>
()))
=
EigenVector
<
T
>::
Flatten
(
param
)
-
lr
*
EigenVector
<
T
>::
Flatten
(
grad
);
EigenVector
<
T
>::
Flatten
(
*
param
)
-
lr
*
EigenVector
<
T
>::
Flatten
(
*
grad
);
}
};
...
...
paddle/operators/sigmoid_op.cc
浏览文件 @
65880f7e
...
...
@@ -18,11 +18,10 @@ namespace operators {
class
SigmoidOp
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{
PADDLE_ENFORCE
(
inputs
.
size
()
==
1
,
"Sigmoid Op only have one input"
);
PADDLE_ENFORCE
(
outputs
.
size
()
==
1
,
"Sigmoid Op only have one output"
);
outputs
[
0
]
->
Resize
(
inputs
[
0
]
->
dims
());
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
1
,
"Sigmoid Op only have one input"
);
PADDLE_ENFORCE
(
ctx
.
OutputSize
()
==
1
,
"Sigmoid Op only have one output"
);
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
());
}
};
...
...
@@ -38,8 +37,7 @@ public:
class
SigmoidOpGrad
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{}
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{}
std
::
string
DebugString
()
const
override
{
LOG
(
INFO
)
<<
"SigmoidGrad"
;
return
""
;
...
...
paddle/operators/sigmoid_op.h
浏览文件 @
65880f7e
...
...
@@ -22,15 +22,14 @@ namespace operators {
template
<
typename
Place
,
typename
T
>
class
SigmoidKernel
:
public
OpKernel
{
public:
void
Compute
(
const
KernelContext
&
context
)
const
override
{
auto
input
=
context
.
Input
(
0
)
->
Get
<
Tensor
>
();
auto
*
output
=
context
.
Output
(
0
)
->
GetMutable
<
Tensor
>
();
void
Compute
(
const
ExecutionContext
&
context
)
const
override
{
auto
input
=
context
.
Input
<
Tensor
>
(
0
);
auto
output
=
context
.
Output
<
Tensor
>
(
0
);
output
->
mutable_data
<
T
>
(
context
.
GetPlace
());
EigenVector
<
T
>::
Flatten
(
*
output
).
device
(
*
(
context
.
GetEigenDevice
<
Place
>
()))
=
1.0
/
(
1.0
+
(
-
1.0
*
EigenVector
<
T
>::
Flatten
(
input
)).
exp
());
1.0
/
(
1.0
+
(
-
1.0
*
EigenVector
<
T
>::
Flatten
(
*
input
)).
exp
());
}
};
}
// namespace operators
...
...
paddle/operators/softmax_op.cc
浏览文件 @
65880f7e
...
...
@@ -18,14 +18,13 @@ namespace operators {
class
SoftmaxOp
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{
PADDLE_ENFORCE
(
inputs
.
size
()
==
1
,
"Only one input is need for softmax"
);
PADDLE_ENFORCE
(
inputs
[
0
]
->
dims
().
size
()
==
2
,
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{
PADDLE_ENFORCE
(
ctx
.
InputSize
()
==
1
,
"Only one input is need for softmax"
);
PADDLE_ENFORCE
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
().
size
()
==
2
,
"The input of softmax op must be matrix"
);
PADDLE_ENFORCE
(
outputs
.
size
()
==
1
,
"Only one output is need for softmax"
);
outputs
[
0
]
->
Resize
(
inputs
[
0
]
->
dims
());
PADDLE_ENFORCE
(
ctx
.
OutputSize
()
==
1
,
"Only one output is need for softmax"
);
ctx
.
Output
<
Tensor
>
(
0
)
->
Resize
(
ctx
.
Input
<
Tensor
>
(
0
)
->
dims
());
}
};
...
...
@@ -41,8 +40,7 @@ public:
class
SoftmaxOpGrad
:
public
OperatorWithKernel
{
protected:
void
InferShape
(
const
std
::
vector
<
const
Tensor
*>
&
inputs
,
const
std
::
vector
<
Tensor
*>
&
outputs
)
const
override
{}
void
InferShape
(
const
InferShapeContext
&
ctx
)
const
override
{}
std
::
string
DebugString
()
const
override
{
LOG
(
INFO
)
<<
"SoftmaxOpGrad"
;
return
""
;
...
...
paddle/operators/softmax_op.h
浏览文件 @
65880f7e
...
...
@@ -22,12 +22,12 @@ namespace operators {
template
<
typename
Place
,
typename
T
>
class
SoftmaxKernel
:
public
OpKernel
{
public:
void
Compute
(
const
Kernel
Context
&
context
)
const
override
{
auto
input
=
context
.
Input
(
0
)
->
Get
<
Tensor
>
(
);
auto
*
output
=
context
.
Output
(
0
)
->
GetMutable
<
Tensor
>
(
);
void
Compute
(
const
Execution
Context
&
context
)
const
override
{
auto
input
=
context
.
Input
<
Tensor
>
(
0
);
auto
output
=
context
.
Output
<
Tensor
>
(
0
);
output
->
mutable_data
<
T
>
(
context
.
GetPlace
());
auto
logits
=
EigenMatrix
<
T
>::
From
(
input
);
auto
logits
=
EigenMatrix
<
T
>::
From
(
*
input
);
auto
softmax
=
EigenMatrix
<
T
>::
From
(
*
output
);
const
int
kBatchDim
=
0
;
...
...
paddle/operators/type_alias.h
浏览文件 @
65880f7e
...
...
@@ -22,7 +22,9 @@ namespace paddle {
namespace
operators
{
using
OpKernel
=
framework
::
OpKernel
;
using
KernelContext
=
framework
::
KernelContext
;
using
InferShapeContext
=
framework
::
InferShapeContext
;
using
ExecutionContext
=
framework
::
ExecutionContext
;
using
Variable
=
framework
::
Variable
;
template
<
typename
T
,
int
MajorType
=
Eigen
::
RowMajor
,
typename
IndexType
=
Eigen
::
DenseIndex
>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录