Enable users to create custom cpp op outside framework. (#19256)
* How to write custom op needs to follow framework OP spec. * Package fluid_framework.so and headers into whl. * Add paddle.sysconfig.get_include() and paddle.sysconfig.get_lib() to get include dir and lib dir. * Export some C-APIs to merge OpInfo between core.so and custom_op.so. * Add unit testing. * Update API.spec.fix-python-transpose
parent
f1eebf75aa
commit
1a3eef026c
@ -0,0 +1,58 @@
|
||||
/* Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
||||
|
||||
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 <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/fluid/framework/block_desc.h"
|
||||
#include "paddle/fluid/framework/c/c_api.h"
|
||||
#include "paddle/fluid/framework/op_registry.h"
|
||||
#include "paddle/fluid/framework/program_desc.h"
|
||||
#include "paddle/fluid/framework/scope.h"
|
||||
#include "paddle/fluid/platform/init.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
paddle::framework::OpInfoMap &PD_GetOpInfoMap() {
|
||||
return paddle::framework::OpInfoMap::Instance();
|
||||
}
|
||||
|
||||
void PD_InitDevicesPool(paddle::platform::DeviceContextPool *pool) {
|
||||
paddle::platform::DeviceContextPool::SetPool(pool);
|
||||
}
|
||||
|
||||
std::vector<std::string> PD_GetGradOpDescStrs(
|
||||
const paddle::framework::OpDesc &op_desc,
|
||||
const std::unordered_set<std::string> &no_grad_set,
|
||||
std::unordered_map<std::string, std::string> *grad_to_var,
|
||||
const std::vector<paddle::framework::BlockDesc *> &grad_block) {
|
||||
auto &op_info = PD_GetOpInfoMap().Get(op_desc.Type());
|
||||
std::vector<std::string> ret;
|
||||
if (op_info.grad_op_maker_) {
|
||||
auto grad_op_descs =
|
||||
op_info.grad_op_maker_(op_desc, no_grad_set, grad_to_var, grad_block);
|
||||
size_t op_num = grad_op_descs.size();
|
||||
ret.resize(op_num);
|
||||
for (size_t i = 0; i < op_num; ++i) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
grad_op_descs[i]->Proto()->SerializePartialToString(&ret[i]), true,
|
||||
"Cannot serialize message.");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // end extern "C"
|
@ -0,0 +1,46 @@
|
||||
/* copyright (c) 2019 paddlepaddle authors. all rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/fluid/framework/block_desc.h"
|
||||
#include "paddle/fluid/framework/op_desc.h"
|
||||
#include "paddle/fluid/framework/op_info.h"
|
||||
#include "paddle/fluid/platform/device_context.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// C-API to get global OpInfo map.
|
||||
paddle::framework::OpInfoMap &PD_GetOpInfoMap();
|
||||
|
||||
// C-API to init global DeviceContextPool from outside.
|
||||
void PD_InitDevicesPool(paddle::platform::DeviceContextPool *pool);
|
||||
|
||||
// C-API to serialize the grad op protocol message to a binary string.
|
||||
std::vector<std::string> PD_GetGradOpDescStrs(
|
||||
const paddle::framework::OpDesc &op_desc,
|
||||
const std::unordered_set<std::string> &no_grad_set,
|
||||
std::unordered_map<std::string, std::string> *grad_to_var,
|
||||
const std::vector<paddle::framework::BlockDesc *> &grad_block);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,115 @@
|
||||
/* Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/fluid/framework/op_desc.h"
|
||||
#include "paddle/fluid/platform/dynload/dynamic_loader.h"
|
||||
#include "paddle/fluid/platform/port.h"
|
||||
|
||||
namespace paddle {
|
||||
namespace framework {
|
||||
|
||||
template <typename T>
|
||||
T *DynLoad(void *handle, std::string name) {
|
||||
T *func = reinterpret_cast<T *>(dlsym(handle, name.c_str()));
|
||||
#if !defined(_WIN32)
|
||||
auto errorno = dlerror();
|
||||
#else
|
||||
auto errorno = GetLastError();
|
||||
#endif // !_WIN32
|
||||
PADDLE_ENFORCE_NOT_NULL(func, errorno);
|
||||
return func;
|
||||
}
|
||||
|
||||
void LoadOpLib(const std::string &dso_name) {
|
||||
void *handle = paddle::platform::dynload::GetOpDsoHandle(dso_name);
|
||||
|
||||
typedef OpInfoMap &get_op_info_t();
|
||||
get_op_info_t *get_op_info =
|
||||
DynLoad<get_op_info_t>(handle, "PD_GetOpInfoMap");
|
||||
auto &op_info = get_op_info();
|
||||
auto *dyn_info_map = op_info.mutable_map();
|
||||
|
||||
typedef std::vector<std::string> grad_op_desc_maker_t(
|
||||
const OpDesc &, const std::unordered_set<std::string> &,
|
||||
std::unordered_map<std::string, std::string> *,
|
||||
const std::vector<BlockDesc *> &);
|
||||
|
||||
grad_op_desc_maker_t *grad_op_desc_maker =
|
||||
DynLoad<grad_op_desc_maker_t>(handle, "PD_GetGradOpDescStrs");
|
||||
|
||||
auto &info_map = OpInfoMap::Instance();
|
||||
for (const auto &n : *(dyn_info_map)) {
|
||||
auto type = n.first;
|
||||
if (type == "recurrent" || type == "recurrent_grad" ||
|
||||
type == "conditional_block" || type == "conditional_block_grad") {
|
||||
continue;
|
||||
}
|
||||
if (info_map.Has(n.first)) {
|
||||
PADDLE_THROW("Op %s has been registered.");
|
||||
}
|
||||
OpInfo info;
|
||||
info.creator_ = n.second.creator_;
|
||||
|
||||
// If get the protocol buffer from dynamic library directly, there
|
||||
// will be deconstruction error
|
||||
// ** Error in `python`: free(): invalid pointer:
|
||||
// ... paddle::framework::proto::OpDesc::SharedDtor()
|
||||
// It seems a bug in protobuf, see
|
||||
// https://github.com/protocolbuffers/protobuf/issues/435
|
||||
// So, get the serialized binary string from dynamic library,
|
||||
// then deserialize to protocol buffer.
|
||||
info.grad_op_maker_ = [grad_op_desc_maker](
|
||||
const OpDesc &op_desc,
|
||||
const std::unordered_set<std::string> &no_grad_set,
|
||||
std::unordered_map<std::string, std::string> *grad_to_var,
|
||||
const std::vector<BlockDesc *> &grad_block) {
|
||||
std::vector<std::string> strs =
|
||||
grad_op_desc_maker(op_desc, no_grad_set, grad_to_var, grad_block);
|
||||
std::vector<std::unique_ptr<OpDesc>> ret;
|
||||
for (auto &str : strs) {
|
||||
proto::OpDesc proto_desc;
|
||||
PADDLE_ENFORCE_EQ(proto_desc.ParseFromString(str), true,
|
||||
"Failed to parse OpDesc from string");
|
||||
ret.emplace_back(new OpDesc(proto_desc, nullptr));
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
info.proto_ = n.second.proto_;
|
||||
info.checker_ = n.second.checker_;
|
||||
info.infer_var_type_ = n.second.infer_var_type_;
|
||||
info.infer_shape_ = n.second.infer_shape_;
|
||||
info.infer_inplace_ = n.second.infer_inplace_;
|
||||
info.infer_no_need_buffer_vars_ = n.second.infer_no_need_buffer_vars_;
|
||||
info.use_default_grad_op_desc_maker_ =
|
||||
n.second.use_default_grad_op_desc_maker_;
|
||||
|
||||
info_map.Insert(type, info);
|
||||
}
|
||||
|
||||
typedef void init_device_t(platform::DeviceContextPool *);
|
||||
init_device_t *init_dev =
|
||||
DynLoad<init_device_t>(handle, "PD_InitDevicesPool");
|
||||
init_dev(&(platform::DeviceContextPool::Instance()));
|
||||
}
|
||||
|
||||
} // namespace framework
|
||||
} // namespace paddle
|
@ -0,0 +1,25 @@
|
||||
if (WITH_GPU)
|
||||
nv_library(relu_op_shared SHARED SRCS relu_op.cc relu_op.cu DEPS paddle_framework_shared)
|
||||
else()
|
||||
cc_library(relu_op_shared SHARED SRCS relu_op.cc DEPS paddle_framework_shared)
|
||||
endif()
|
||||
set_target_properties(relu_op_shared PROPERTIES OUTPUT_NAME relu2_op)
|
||||
target_link_libraries(relu_op_shared ${FLUID_FRAMEWORK_SHARED_LIB})
|
||||
|
||||
# remove the linked glog and gflags when compling relu_op_shared
|
||||
# otherwise, there is running error:
|
||||
# ERROR: something wrong with flag 'logtostderr' in file
|
||||
# 'third_party/glog/src/extern_glog/src/logging.cc'.
|
||||
# One possibility: file 'third_party/glog/src/extern_glog/src/logging.cc'
|
||||
# is being linked both statically and dynamically into this executable.
|
||||
get_target_property(TARGET_LIBRARIES relu_op_shared LINK_LIBRARIES)
|
||||
LIST(REMOVE_ITEM TARGET_LIBRARIES glog)
|
||||
LIST(REMOVE_ITEM TARGET_LIBRARIES gflags)
|
||||
set_property(TARGET relu_op_shared PROPERTY LINK_LIBRARIES ${TARGET_LIBRARIES} )
|
||||
|
||||
file(GLOB TEST_OPS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "test_*.py")
|
||||
string(REPLACE ".py" "" TEST_OPS "${TEST_OPS}")
|
||||
|
||||
foreach(src ${TEST_OPS})
|
||||
py_test(${src} SRCS ${src}.py)
|
||||
endforeach()
|
@ -0,0 +1,112 @@
|
||||
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "paddle/fluid/framework/op_registry.h"
|
||||
|
||||
namespace paddle {
|
||||
namespace operators {
|
||||
|
||||
class Relu2Op : public framework::OperatorWithKernel {
|
||||
public:
|
||||
using framework::OperatorWithKernel::OperatorWithKernel;
|
||||
|
||||
void InferShape(framework::InferShapeContext* ctx) const override {
|
||||
auto in_dims = ctx->GetInputDim("X");
|
||||
ctx->SetOutputDim("Y", in_dims);
|
||||
}
|
||||
};
|
||||
|
||||
class Relu2OpMaker : public framework::OpProtoAndCheckerMaker {
|
||||
public:
|
||||
void Make() override {
|
||||
AddInput("X", "The input tensor.");
|
||||
AddOutput("Y", "Output of relu_op");
|
||||
AddComment(R"DOC(
|
||||
Relu2 Operator.
|
||||
)DOC");
|
||||
}
|
||||
};
|
||||
|
||||
class Relu2GradOp : public framework::OperatorWithKernel {
|
||||
public:
|
||||
using framework::OperatorWithKernel::OperatorWithKernel;
|
||||
|
||||
void InferShape(framework::InferShapeContext* ctx) const override {
|
||||
auto in_dims = ctx->GetInputDim(framework::GradVarName("Y"));
|
||||
ctx->SetOutputDim(framework::GradVarName("X"), in_dims);
|
||||
}
|
||||
};
|
||||
|
||||
class Relu2GradMaker : public framework::SingleGradOpDescMaker {
|
||||
public:
|
||||
using framework::SingleGradOpDescMaker::SingleGradOpDescMaker;
|
||||
|
||||
std::unique_ptr<framework::OpDesc> Apply() const override {
|
||||
auto* op = new framework::OpDesc();
|
||||
op->SetType("relu2_grad");
|
||||
op->SetInput("Y", Output("Y"));
|
||||
op->SetInput(framework::GradVarName("Y"), OutputGrad("Y"));
|
||||
op->SetAttrMap(Attrs());
|
||||
op->SetOutput(framework::GradVarName("X"), InputGrad("X"));
|
||||
return std::unique_ptr<framework::OpDesc>(op);
|
||||
}
|
||||
};
|
||||
|
||||
using Tensor = framework::Tensor;
|
||||
|
||||
template <typename DeviceContext, typename T>
|
||||
class Relu2Kernel : public framework::OpKernel<T> {
|
||||
public:
|
||||
void Compute(const framework::ExecutionContext& ctx) const override {
|
||||
auto* in_t = ctx.Input<Tensor>("X");
|
||||
auto* out_t = ctx.Output<Tensor>("Y");
|
||||
auto x = in_t->data<T>();
|
||||
auto y = out_t->mutable_data<T>(ctx.GetPlace());
|
||||
for (int i = 0; i < in_t->numel(); ++i) {
|
||||
y[i] = std::max(static_cast<T>(0.), x[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DeviceContext, typename T>
|
||||
class Relu2GradKernel : public framework::OpKernel<T> {
|
||||
public:
|
||||
void Compute(const framework::ExecutionContext& ctx) const override {
|
||||
auto* dy_t = ctx.Input<Tensor>(framework::GradVarName("Y"));
|
||||
auto* y_t = ctx.Input<Tensor>("Y");
|
||||
auto* dx_t = ctx.Output<Tensor>(framework::GradVarName("X"));
|
||||
|
||||
auto dy = dy_t->data<T>();
|
||||
auto y = y_t->data<T>();
|
||||
auto dx = dx_t->mutable_data<T>(ctx.GetPlace());
|
||||
|
||||
for (int i = 0; i < y_t->numel(); ++i) {
|
||||
dx[i] = dy[i] * (y[i] > static_cast<T>(0) ? 1. : 0.);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace operators
|
||||
} // namespace paddle
|
||||
|
||||
namespace ops = paddle::operators;
|
||||
using CPU = paddle::platform::CPUDeviceContext;
|
||||
REGISTER_OPERATOR(relu2, ops::Relu2Op, ops::Relu2OpMaker, ops::Relu2GradMaker);
|
||||
REGISTER_OPERATOR(relu2_grad, ops::Relu2GradOp);
|
||||
REGISTER_OP_CPU_KERNEL(relu2,
|
||||
ops::Relu2Kernel<CPU, float>,
|
||||
ops::Relu2Kernel<CPU, double>);
|
||||
REGISTER_OP_CPU_KERNEL(relu2_grad,
|
||||
ops::Relu2GradKernel<CPU, float>,
|
||||
ops::Relu2GradKernel<CPU, double>);
|
@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "paddle/fluid/framework/op_registry.h"
|
||||
|
||||
namespace paddle {
|
||||
namespace operators {
|
||||
|
||||
using Tensor = framework::Tensor;
|
||||
|
||||
template <typename T>
|
||||
__global__ void KeRelu2(const T* x, const int num, T* y) {
|
||||
int gid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
for (int i = gid; i < num; i += blockDim.x * gridDim.x) {
|
||||
y[i] = max(x[i], static_cast<T>(0.));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DeviceContext, typename T>
|
||||
class Relu2CUDAKernel : public framework::OpKernel<T> {
|
||||
public:
|
||||
void Compute(const framework::ExecutionContext& ctx) const override {
|
||||
auto* in_t = ctx.Input<Tensor>("X");
|
||||
auto* out_t = ctx.Output<Tensor>("Y");
|
||||
auto x = in_t->data<T>();
|
||||
auto y = out_t->mutable_data<T>(ctx.GetPlace());
|
||||
|
||||
auto& dev_ctx = ctx.template device_context<DeviceContext>();
|
||||
|
||||
int num = in_t->numel();
|
||||
int block = 512;
|
||||
int grid = (num + block - 1) / block;
|
||||
KeRelu2<T><<<grid, block, 0, dev_ctx.stream()>>>(x, num, y);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
__global__ void KeRelu2Grad(const T* y, const T* dy, const int num, T* dx) {
|
||||
int gid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
for (int i = gid; i < num; i += blockDim.x * gridDim.x) {
|
||||
dx[i] = dy[i] * (y[i] > 0 ? 1. : 0.);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DeviceContext, typename T>
|
||||
class Relu2GradCUDAKernel : public framework::OpKernel<T> {
|
||||
public:
|
||||
void Compute(const framework::ExecutionContext& ctx) const override {
|
||||
auto* dy_t = ctx.Input<Tensor>(framework::GradVarName("Y"));
|
||||
auto* y_t = ctx.Input<Tensor>("Y");
|
||||
auto* dx_t = ctx.Output<Tensor>(framework::GradVarName("X"));
|
||||
|
||||
auto dy = dy_t->data<T>();
|
||||
auto y = y_t->data<T>();
|
||||
auto dx = dx_t->mutable_data<T>(ctx.GetPlace());
|
||||
|
||||
auto& dev_ctx = ctx.template device_context<DeviceContext>();
|
||||
|
||||
int num = dy_t->numel();
|
||||
int block = 512;
|
||||
int grid = (num + block - 1) / block;
|
||||
KeRelu2Grad<T><<<grid, block, 0, dev_ctx.stream()>>>(y, dy, num, dx);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace operators
|
||||
} // namespace paddle
|
||||
|
||||
using CUDA = paddle::platform::CUDADeviceContext;
|
||||
REGISTER_OP_CUDA_KERNEL(relu2,
|
||||
paddle::operators::Relu2CUDAKernel<CUDA, float>,
|
||||
paddle::operators::Relu2CUDAKernel<CUDA, double>);
|
||||
|
||||
REGISTER_OP_CUDA_KERNEL(relu2_grad,
|
||||
paddle::operators::Relu2GradCUDAKernel<CUDA, float>,
|
||||
paddle::operators::Relu2GradCUDAKernel<CUDA, double>);
|
@ -0,0 +1,112 @@
|
||||
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import numpy as np
|
||||
import unittest
|
||||
import contextlib
|
||||
|
||||
import paddle
|
||||
import paddle.fluid as fluid
|
||||
|
||||
file_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
fluid.load_op_library(os.path.join(file_dir, 'librelu2_op.so'))
|
||||
|
||||
from paddle.fluid.layer_helper import LayerHelper
|
||||
|
||||
|
||||
def relu2(x, name=None):
|
||||
helper = LayerHelper("relu2", **locals())
|
||||
out = helper.create_variable(
|
||||
type=x.type, name=name, dtype=x.dtype, persistable=False)
|
||||
helper.append_op(type="relu2", inputs={"X": x}, outputs={"Y": out})
|
||||
return out
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def scope_prog_guard():
|
||||
prog = fluid.Program()
|
||||
startup_prog = fluid.Program()
|
||||
scope = fluid.core.Scope()
|
||||
with fluid.scope_guard(scope):
|
||||
with fluid.program_guard(prog, startup_prog):
|
||||
yield
|
||||
|
||||
|
||||
def linear_fc(data, label, use_custom_relu):
|
||||
hidden = fluid.layers.fc(data, size=128)
|
||||
hidden = relu2(hidden) if use_custom_relu else fluid.layers.relu(hidden)
|
||||
hidden = fluid.layers.fc(hidden, size=128)
|
||||
hidden = fluid.layers.fc(hidden, size=10, act='softmax')
|
||||
loss = fluid.layers.cross_entropy(input=hidden, label=label)
|
||||
loss = fluid.layers.mean(loss)
|
||||
return loss
|
||||
|
||||
|
||||
def custom_op_test(use_gpu=True, use_custom_relu=True):
|
||||
with scope_prog_guard():
|
||||
np.random.seed(0)
|
||||
fluid.default_startup_program().random_seed = 10
|
||||
fluid.default_main_program().random_seed = 10
|
||||
|
||||
data = fluid.layers.data(
|
||||
name='data', shape=[1, 28, 28], dtype='float32')
|
||||
label = fluid.layers.data(name='label', shape=[1], dtype='int64')
|
||||
loss = linear_fc(data, label, use_custom_relu)
|
||||
|
||||
optimizer = fluid.optimizer.Momentum(learning_rate=0.1, momentum=0.9)
|
||||
optimizer.minimize(loss)
|
||||
|
||||
place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
|
||||
exe = fluid.Executor(place)
|
||||
exe.run(fluid.default_startup_program())
|
||||
|
||||
compile_program = fluid.compiler.CompiledProgram(
|
||||
fluid.default_main_program()).with_data_parallel(
|
||||
loss_name=loss.name)
|
||||
|
||||
reader = paddle.batch(paddle.dataset.mnist.train(), batch_size=32)
|
||||
feeder = fluid.DataFeeder(feed_list=[data, label], place=place)
|
||||
|
||||
num = 4
|
||||
for i, data in enumerate(reader()):
|
||||
outs, = exe.run(compile_program,
|
||||
feed=feeder.feed(data),
|
||||
fetch_list=[loss])
|
||||
if i == num:
|
||||
break
|
||||
return outs
|
||||
|
||||
|
||||
class CustomOpTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
os.environ['CPU_NUM'] = str(2)
|
||||
|
||||
def test_cpu(self):
|
||||
actual = custom_op_test(False, True)
|
||||
expect = custom_op_test(False, False)
|
||||
self.assertEqual(actual.all(), expect.all())
|
||||
|
||||
def test_gpu(self):
|
||||
if not fluid.core.is_compiled_with_cuda():
|
||||
return
|
||||
actual = custom_op_test(True, True)
|
||||
expect = custom_op_test(True, False)
|
||||
self.assertEqual(actual.all(), expect.all())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -0,0 +1,35 @@
|
||||
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import unittest
|
||||
import paddle
|
||||
|
||||
|
||||
class SysConfigTest(unittest.TestCase):
|
||||
def test_include(self):
|
||||
inc_dir = paddle.sysconfig.get_include()
|
||||
inc_dirs = inc_dir.split(os.sep)
|
||||
self.assertEqual(inc_dirs[-1], 'include')
|
||||
self.assertEqual(inc_dirs[-2], 'paddle')
|
||||
|
||||
def test_libs(self):
|
||||
lib_dir = paddle.sysconfig.get_lib()
|
||||
lib_dirs = lib_dir.split(os.sep)
|
||||
self.assertEqual(lib_dirs[-1], 'libs')
|
||||
self.assertEqual(lib_dirs[-2], 'paddle')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -0,0 +1,51 @@
|
||||
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved
|
||||
#
|
||||
# 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.
|
||||
|
||||
import os
|
||||
|
||||
__all__ = ['get_include', 'get_lib']
|
||||
|
||||
|
||||
def get_include():
|
||||
"""
|
||||
Get the directory containing the PaddlePaddle C++ header files.
|
||||
Returns:
|
||||
The directory as string.
|
||||
|
||||
Examples:
|
||||
.. code-block:: python
|
||||
|
||||
import paddle
|
||||
include_dir = paddle.sysconfig.get_include()
|
||||
|
||||
"""
|
||||
import paddle
|
||||
return os.path.join(os.path.dirname(paddle.__file__), 'include')
|
||||
|
||||
|
||||
def get_lib():
|
||||
"""
|
||||
Get the directory containing the libpaddle_framework.
|
||||
Returns:
|
||||
The directory as string.
|
||||
|
||||
Examples:
|
||||
.. code-block:: python
|
||||
|
||||
import paddle
|
||||
include_dir = paddle.sysconfig.get_lib()
|
||||
|
||||
"""
|
||||
import paddle
|
||||
return os.path.join(os.path.dirname(paddle.__file__), 'libs')
|
Loading…
Reference in new issue