Skip to content

Commit c15ac20

Browse files
authored
Merge pull request #4199 from tensor-tang/mkldnn_act
add MKLDNN relu and tanh
2 parents 5b42d2b + 9d692e3 commit c15ac20

10 files changed

+361
-23
lines changed

paddle/gserver/activations/ActivationFunction.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ limitations under the License. */
2222
#include <type_traits>
2323
#include "paddle/parameter/Argument.h"
2424
#include "paddle/utils/ClassRegistrar.h"
25-
2625
#include "paddle/utils/Logging.h"
2726

27+
#ifdef PADDLE_USE_MKLDNN
28+
#include "MKLDNNActivation.h"
29+
#endif
30+
2831
namespace paddle {
2932

3033
static ClassRegistrar<ActivationFunction> gActivationRegistrar;
@@ -456,6 +459,12 @@ Error __must_check backward(Argument& act) {
456459
END_DEFINE_ACTIVATION(log)
457460

458461
ActivationFunction* ActivationFunction::create(const std::string& type) {
462+
#ifdef PADDLE_USE_MKLDNN
463+
if (!type.empty() && type.compare(0, 7, "mkldnn_") == 0) {
464+
return MKLDNNActivation::create(type);
465+
}
466+
#endif
467+
459468
return gActivationRegistrar.createByType(type);
460469
}
461470

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/* Copyright (c) 2017 PaddlePaddle Authors. All Rights Reserve.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#include "MKLDNNActivation.h"
16+
#include "mkldnn.hpp"
17+
#include "paddle/utils/ClassRegistrar.h"
18+
19+
namespace paddle {
20+
21+
static ClassRegistrar<ActivationFunction> gMKLDNNActivationRegistrar;
22+
/**
23+
* @def MKLDNN_ACTIVATION_CLASS_NAME
24+
* @note MKLDNN_ACTIVATION_CLASS_NAME(relu) relu_;
25+
* means mkldnn_reluActivation relu_;
26+
*/
27+
#define MKLDNN_ACTIVATION_CLASS_NAME(ACT_TYPE) mkldnn_##ACT_TYPE##Activation
28+
29+
/**
30+
* @def DEFINE_MKLDNN_ELTWISE_ACTIVATION
31+
*/
32+
#define DEFINE_MKLDNN_ELTWISE_ACTIVATION(ACT_TYPE, ALPHA, BWD_ALPHA) \
33+
class MKLDNN_ACTIVATION_CLASS_NAME(ACT_TYPE) \
34+
: public MKLDNNEltwiseActivation { \
35+
private: \
36+
static const std::string name; \
37+
static const float alpha; \
38+
static const float bwdAlpha; \
39+
\
40+
public: \
41+
const std::string& getName() const { return name; } \
42+
float getAlpha() const { return alpha; } \
43+
float getBwdAlpha() const { return bwdAlpha; } \
44+
}; \
45+
const std::string MKLDNN_ACTIVATION_CLASS_NAME(ACT_TYPE)::name = \
46+
"mkldnn_" #ACT_TYPE; \
47+
const float MKLDNN_ACTIVATION_CLASS_NAME(ACT_TYPE)::alpha = ALPHA; \
48+
const float MKLDNN_ACTIVATION_CLASS_NAME(ACT_TYPE)::bwdAlpha = BWD_ALPHA; \
49+
static InitFunction __reg_activation__mkldnn_##ACT_TYPE([] { \
50+
gMKLDNNActivationRegistrar \
51+
.registerClass<MKLDNN_ACTIVATION_CLASS_NAME(ACT_TYPE)>( \
52+
"mkldnn_" #ACT_TYPE); \
53+
});
54+
55+
/**
56+
* @brief MKLDNN Relu Activation.
57+
* Actually mkldnn_relu is Leaky Relu.
58+
* f(x) = x (x >= 0)
59+
* f(x) = negative_slope * x (x < 0)
60+
* @note the negative_slope should be -0.f in forward
61+
*/
62+
DEFINE_MKLDNN_ELTWISE_ACTIVATION(relu, -0.f, 0.f)
63+
64+
/**
65+
* @brief MKLDNN Tanh Activation.
66+
*/
67+
DEFINE_MKLDNN_ELTWISE_ACTIVATION(tanh, 0.f, 0.f)
68+
69+
/**
70+
* @brief MKLDNN ELU(Exponential Linear Unit) Activation.
71+
* f(x) = x (x >= 0)
72+
* f(x) = negative_slope * (exp(x) - 1) (x < 0)
73+
*/
74+
DEFINE_MKLDNN_ELTWISE_ACTIVATION(elu, 0.f, 0.f)
75+
76+
ActivationFunction* MKLDNNActivation::create(const std::string& type) {
77+
return gMKLDNNActivationRegistrar.createByType(type);
78+
}
79+
80+
std::vector<std::string> MKLDNNActivation::getAllRegisteredTypes() {
81+
std::vector<std::string> types;
82+
gMKLDNNActivationRegistrar.forEachType(
83+
[&](const std::string& type) { types.push_back(type); });
84+
return types;
85+
}
86+
87+
} // namespace paddle
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/* Copyright (c) 2017 PaddlePaddle Authors. All Rights Reserve.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#pragma once
16+
#include "ActivationFunction.h"
17+
#include "mkldnn.hpp"
18+
#include "paddle/gserver/layers/MKLDNNBase.h"
19+
#include "paddle/math/MKLDNNMatrix.h"
20+
#include "paddle/parameter/Argument.h"
21+
22+
namespace paddle {
23+
24+
/**
25+
* @brief Base class of MKLDNN Activation.
26+
* Common activation function are provieded,
27+
* including mkldnn_relu, mkldnn_elu, mkldnn_tanh, mkldnn_softmax
28+
*/
29+
class MKLDNNActivation : public ActivationFunction {
30+
protected:
31+
// input value element count
32+
size_t cnt_;
33+
// should not merge the resetBwd into resetFwd,
34+
// because the grad data would be changing before backward.
35+
bool needResetBwd_;
36+
// mkldnn matrix, primitive, stream and pipeline
37+
MKLDNNMatrixPtr val_;
38+
MKLDNNMatrixPtr grad_;
39+
std::shared_ptr<MKLDNNStream> stream_;
40+
std::shared_ptr<mkldnn::primitive> fwd_;
41+
std::shared_ptr<mkldnn::primitive> bwd_;
42+
std::vector<mkldnn::primitive> pipelineFwd_;
43+
std::vector<mkldnn::primitive> pipelineBwd_;
44+
45+
public:
46+
MKLDNNActivation() : cnt_(0), needResetBwd_(true) {}
47+
~MKLDNNActivation() {}
48+
static ActivationFunction* create(const std::string& type);
49+
static std::vector<std::string> getAllRegisteredTypes();
50+
virtual const std::string& getName() const = 0;
51+
virtual Error __must_check forward(Argument& act) = 0;
52+
virtual Error __must_check backward(Argument& act) = 0;
53+
};
54+
55+
/**
56+
* @brief Base class of MKLDNN Eltwise Activation,
57+
* includes mkldnn_relu, mkldnn_elu and mkldnn_tanh.
58+
*/
59+
class MKLDNNEltwiseActivation : public MKLDNNActivation {
60+
typedef mkldnn::eltwise_forward eltwise_fwd;
61+
typedef mkldnn::eltwise_backward eltwise_bwd;
62+
63+
protected:
64+
// save the forward primitive desc, which can be used backward
65+
std::shared_ptr<eltwise_fwd::primitive_desc> fwdPD_;
66+
// eltwise_bwd need src input value
67+
MKLDNNMatrixPtr inVal_;
68+
// use for copy data
69+
std::shared_ptr<mkldnn::reorder> copyInVal_;
70+
71+
public:
72+
MKLDNNEltwiseActivation() {}
73+
74+
~MKLDNNEltwiseActivation() {}
75+
76+
virtual const std::string& getName() const = 0;
77+
78+
// in common, the alpha of forward and backward should be equal.
79+
// but for relu, to avoid negative value, they should be opposite
80+
virtual float getAlpha() const = 0;
81+
virtual float getBwdAlpha() const = 0;
82+
virtual float getBeta() const { return 0.f; }
83+
virtual mkldnn::algorithm getAlgo(const std::string& type) const {
84+
if (type == "mkldnn_relu") {
85+
return mkldnn::algorithm::eltwise_relu;
86+
} else if (type == "mkldnn_tanh") {
87+
return mkldnn::algorithm::eltwise_tanh;
88+
} else if (type == "mkldnn_elu") {
89+
return mkldnn::algorithm::eltwise_elu;
90+
} else {
91+
LOG(FATAL) << "Unkown eltwise activation type: " << type;
92+
}
93+
return (mkldnn::algorithm)0;
94+
}
95+
96+
/**
97+
* reshape and reset the forward primitives
98+
*/
99+
void resetFwd(Argument& act) {
100+
if (cnt_ == act.value->getElementCnt()) {
101+
return;
102+
}
103+
cnt_ = act.value->getElementCnt();
104+
stream_.reset(new MKLDNNStream());
105+
auto eng = CPUEngine::Instance().getEngine();
106+
107+
// get algo setting
108+
mkldnn::algorithm algo = getAlgo(this->getName());
109+
// note: alpha represents the NegativeSlope when used in relu.
110+
float alpha = getAlpha();
111+
float beta = getBeta();
112+
113+
/// forward
114+
pipelineFwd_.clear();
115+
val_ = std::dynamic_pointer_cast<MKLDNNMatrix>(act.value);
116+
if (val_ == nullptr) {
117+
int bs = act.getBatchSize();
118+
int ih = act.getFrameHeight() > 0 ? act.getFrameHeight() : 1;
119+
int iw = act.getFrameWidth() > 0 ? act.getFrameWidth() : 1;
120+
int ic = cnt_ / bs / ih / iw;
121+
CHECK_EQ(cnt_, (size_t)bs * ic * ih * iw);
122+
val_ = MKLDNNMatrix::create(
123+
act.value, {bs, ic, ih, iw}, mkldnn::memory::format::nchw, eng);
124+
CHECK(val_);
125+
}
126+
auto fwdDesc = eltwise_fwd::desc(mkldnn::prop_kind::forward_training,
127+
algo,
128+
val_->getMemoryDesc(),
129+
alpha,
130+
beta);
131+
fwdPD_.reset(new eltwise_fwd::primitive_desc(fwdDesc, eng));
132+
// use inplace for forward but save input value before submit
133+
inVal_ = val_;
134+
if (act.grad) {
135+
// only copy when need do backward
136+
inVal_ = MKLDNNMatrix::create(nullptr, val_->getPrimitiveDesc());
137+
copyInVal_ = std::make_shared<mkldnn::reorder>(*val_, *inVal_);
138+
CHECK(copyInVal_) << "should not be emptry";
139+
pipelineFwd_.push_back(*copyInVal_);
140+
}
141+
fwd_.reset(new eltwise_fwd(*fwdPD_, *val_, *val_));
142+
pipelineFwd_.push_back(*fwd_);
143+
needResetBwd_ = true;
144+
}
145+
146+
/**
147+
* reset the backward primitives, can not merge into resetFwd as the grad data
148+
* would be changing before backward.
149+
*/
150+
void resetBwd(Argument& act) {
151+
if (!needResetBwd_) {
152+
return;
153+
}
154+
needResetBwd_ = false;
155+
mkldnn::algorithm algo = getAlgo(this->getName());
156+
float alpha = getBwdAlpha();
157+
float beta = getBeta();
158+
grad_ = MKLDNNMatrix::create(act.grad, val_->getPrimitiveDesc());
159+
auto eng = CPUEngine::Instance().getEngine();
160+
auto bwdDesc = eltwise_bwd::desc(
161+
algo, grad_->getMemoryDesc(), val_->getMemoryDesc(), alpha, beta);
162+
auto bwdPD = eltwise_bwd::primitive_desc(bwdDesc, eng, *fwdPD_);
163+
CHECK(inVal_);
164+
bwd_.reset(new eltwise_bwd(bwdPD, *inVal_, *grad_, *grad_));
165+
pipelineBwd_.clear();
166+
pipelineBwd_.push_back(*bwd_);
167+
}
168+
169+
Error __must_check forward(Argument& act) {
170+
resetFwd(act);
171+
stream_->submit(pipelineFwd_);
172+
return Error();
173+
}
174+
175+
Error __must_check backward(Argument& act) {
176+
resetBwd(act);
177+
stream_->submit(pipelineBwd_);
178+
return Error();
179+
}
180+
};
181+
182+
} // namespace paddle

paddle/gserver/layers/MKLDNNConvLayer.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,9 @@ void MKLDNNConvLayer::resetOutValue(
294294
std::shared_ptr<conv_fwd::primitive_desc>& pd, MKLDNNMatrixPtr& out) {
295295
out = MKLDNNMatrix::create(output_.value, pd->dst_primitive_desc());
296296

297-
// change original output value from cpu matrix to mkldnn matrix
298-
output_.value = std::dynamic_pointer_cast<Matrix>(out);
299-
300297
// create reorder if output value has cpu device and pd do not match
301298
cpuOutVal_ = nullptr;
302-
cpuOutVal_ = nullptr;
299+
cvtOutVal_ = nullptr;
303300
if (!outputIsOnlyMKLDNN()) {
304301
const MatrixPtr& cpuOut = getOutput(CPU_DEVICE).value;
305302
memory::dims outDims = memory::dims{bs_, oc_, oh_, ow_};

paddle/gserver/layers/MKLDNNFcLayer.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,10 @@ void MKLDNNFcLayer::resetWgtBiasValue(MKLDNNMatrixPtr& wgt,
172172

173173
void MKLDNNFcLayer::resetOutValue(MKLDNNMatrixPtr& out) {
174174
out = MKLDNNMatrix::create(output_.value, {bs_, oc_}, format::nc, engine_);
175-
// change original output value to mkldnn output value
176-
output_.value = std::dynamic_pointer_cast<Matrix>(out);
177175
if (!outputIsOnlyMKLDNN()) {
178176
// fc cpu output value do not need create convert
179177
// just share point
180-
getOutput(CPU_DEVICE).value->setData(output_.value->getData());
178+
getOutput(CPU_DEVICE).value->setData(out->getData());
181179
}
182180
}
183181

paddle/gserver/layers/MKLDNNLayer.h

+4
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ class MKLDNNLayer : public Layer {
119119
inputElemenCnt_ = elemenCnt;
120120
reshape(bs_, ic_, ih_, iw_, oc_, oh_, ow_);
121121
resetFwd(pipelineFwd_, inVal_, wgtVal_, biasVal_, outVal_);
122+
if (outVal_) {
123+
// change original output value to mkldnn output value
124+
output_.value = std::dynamic_pointer_cast<Matrix>(outVal_);
125+
}
122126
convertWeightsFromPaddle();
123127
needResetBwd_ = true;
124128
}

paddle/gserver/layers/MKLDNNPoolLayer.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ void MKLDNNPoolLayer::resetOutValue(MKLDNNMatrixPtr& out) {
134134
memory::dims outDims = memory::dims{bs_, oc_, oh_, ow_};
135135
out = MKLDNNMatrix::create(
136136
output_.value, outDims, inVal_->getFormat(), engine_);
137-
output_.value = std::dynamic_pointer_cast<Matrix>(out);
138137

139138
// create reorder if output value has cpu device and pd do not match
140139
cpuOutVal_ = nullptr;

0 commit comments

Comments
 (0)