Skip to content

Commit 56a4912

Browse files
authored
Make NCE_OP more efficient and support SelectedRows (#14469)
* Fix truncated normal. * Fix. * Make nce support more distribution. * Fix API.spec. * Fix python API. * Fix. test=develop * Fix API.spec test=develop * Fix sampler. * Fix order of arguments in python API. test=develop * NCE add selectedrows support * NCE update weighted sampling * fix bugs in nce_op, and assign_value_op optimized * fix bugs in nce_op, revert assign_value_op * nce_op optimize * nce_op optimize * nce_op optimize * add selectedRows test later test=develop * add selectedRows supported * add selectedRows supported test=develop * add selectedRows supported * add nce selectedRows supported, test=develop * add nce selectedRows supported * add nce selectedRows supported, test=develop * fix height in nce, test=develop * add ut * add ut, test=develop * make AutoGrownIndex inline test=develop * fix tinny error, test=develop
1 parent 1c48d61 commit 56a4912

File tree

7 files changed

+398
-143
lines changed

7 files changed

+398
-143
lines changed

paddle/fluid/API.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ paddle.fluid.layers.warpctc ArgSpec(args=['input', 'label', 'blank', 'norm_by_ti
9797
paddle.fluid.layers.sequence_reshape ArgSpec(args=['input', 'new_dim'], varargs=None, keywords=None, defaults=None)
9898
paddle.fluid.layers.transpose ArgSpec(args=['x', 'perm', 'name'], varargs=None, keywords=None, defaults=(None,))
9999
paddle.fluid.layers.im2sequence ArgSpec(args=['input', 'filter_size', 'stride', 'padding', 'input_image_size', 'out_stride', 'name'], varargs=None, keywords=None, defaults=(1, 1, 0, None, 1, None))
100-
paddle.fluid.layers.nce ArgSpec(args=['input', 'label', 'num_total_classes', 'sample_weight', 'param_attr', 'bias_attr', 'num_neg_samples', 'name', 'sampler', 'custom_dist', 'seed'], varargs=None, keywords=None, defaults=(None, None, None, None, None, 'uniform', None, 0))
100+
paddle.fluid.layers.nce ArgSpec(args=['input', 'label', 'num_total_classes', 'sample_weight', 'param_attr', 'bias_attr', 'num_neg_samples', 'name', 'sampler', 'custom_dist', 'seed', 'is_sparse'], varargs=None, keywords=None, defaults=(None, None, None, None, None, 'uniform', None, 0, False))
101101
paddle.fluid.layers.hsigmoid ArgSpec(args=['input', 'label', 'num_classes', 'param_attr', 'bias_attr', 'name'], varargs=None, keywords=None, defaults=(None, None, None))
102102
paddle.fluid.layers.beam_search ArgSpec(args=['pre_ids', 'pre_scores', 'ids', 'scores', 'beam_size', 'end_id', 'level', 'name'], varargs=None, keywords=None, defaults=(0, None))
103103
paddle.fluid.layers.row_conv ArgSpec(args=['input', 'future_context_size', 'param_attr', 'act'], varargs=None, keywords=None, defaults=(None, None))

paddle/fluid/operators/math/sampler.cc

Lines changed: 9 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -60,75 +60,30 @@ float LogUniformSampler::Probability(int64_t value) const {
6060
return (log((value + 2.0) / (value + 1.0))) / log_range_;
6161
}
6262

63-
CustomSampler::CustomSampler(int64_t range, const float* probabilities,
63+
CustomSampler::CustomSampler(int64_t range, const float *probabilities,
64+
const int *alias, const float *alias_probabilities,
6465
unsigned int seed)
6566
: Sampler(range, seed) {
66-
random_engine_ = std::make_shared<std::mt19937_64>(seed_);
67+
random_engine_ = std::make_shared<std::mt19937>(seed_);
6768
real_dist_ = std::make_shared<std::uniform_real_distribution<>>(0, 1);
6869
int_dist_ = std::make_shared<std::uniform_int_distribution<>>(0, range);
69-
alias_probs_ = std::make_shared<std::vector<float>>(range + 1);
70-
alias_ = std::make_shared<std::vector<int64_t>>(range + 1);
71-
probs_ = std::make_shared<std::vector<float>>(range + 1);
72-
73-
std::queue<std::pair<int64_t, float>> bigs;
74-
std::queue<std::pair<int64_t, float>> littles;
75-
for (int64_t i = 0; i <= range; ++i) {
76-
(*probs_)[i] = probabilities[i];
77-
float normal_prob = probabilities[i] * (range + 1);
78-
if (normal_prob - 1.0 > 1e-4) {
79-
bigs.emplace(i, normal_prob);
80-
} else if (1.0 - normal_prob > 1e-4) {
81-
littles.emplace(i, normal_prob);
82-
} else {
83-
(*alias_probs_)[i] = normal_prob;
84-
(*alias_)[i] = -1;
85-
}
86-
}
87-
88-
while ((!littles.empty()) && (!bigs.empty())) {
89-
auto big = bigs.front();
90-
auto little = littles.front();
91-
bigs.pop();
92-
littles.pop();
93-
(*alias_probs_)[little.first] = little.second;
94-
(*alias_)[little.first] = big.first;
95-
auto big_left = big.second - (1 - little.second);
96-
if (big_left - 1.0 > 1e-4) {
97-
bigs.emplace(big.first, big_left);
98-
} else if (1.0 - big_left > 1e-4) {
99-
littles.emplace(big.first, big_left);
100-
} else {
101-
(*alias_probs_)[big.first] = big_left;
102-
(*alias_)[big.first] = -1;
103-
}
104-
}
10570

106-
if (!littles.empty()) { // littles.second is close to 1.0
107-
auto little = littles.front();
108-
(*alias_probs_)[little.first] = 1.0;
109-
(*alias_)[little.first] = -1;
110-
}
111-
112-
if (!bigs.empty()) { // bigs.second is close to 1.0
113-
auto big = bigs.front();
114-
(*alias_probs_)[big.first] = 1.0;
115-
(*alias_)[big.first] = -1;
116-
}
71+
alias_probs_ = alias_probabilities;
72+
probs_ = probabilities;
73+
alias_ = alias;
11774
}
11875

11976
int64_t CustomSampler::Sample() const {
12077
auto index = (*int_dist_)(*random_engine_);
12178
auto p = (*real_dist_)(*random_engine_);
122-
if (p > (*alias_probs_)[index]) {
123-
return (*alias_)[index];
79+
if (p > alias_probs_[index]) {
80+
return alias_[index];
12481
} else {
12582
return index;
12683
}
12784
}
12885

129-
float CustomSampler::Probability(int64_t value) const {
130-
return (*probs_)[value];
131-
}
86+
float CustomSampler::Probability(int64_t value) const { return probs_[value]; }
13287

13388
} // namespace math
13489
} // namespace operators

paddle/fluid/operators/math/sampler.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ See the License for the specific language governing permissions and
1313
limitations under the License. */
1414

1515
#pragma once
16+
1617
#include <cstdint>
1718
#include <memory>
1819
#include <random>
@@ -38,9 +39,12 @@ class Sampler {
3839
seed_ = seed;
3940
}
4041
}
42+
4143
virtual ~Sampler();
44+
4245
// Sample a single value
4346
virtual int64_t Sample() const = 0;
47+
4448
// The probability that a single call to Sample() returns the given value.
4549
virtual float Probability(int64_t value) const = 0;
4650

@@ -99,6 +103,7 @@ class LogUniformSampler : public Sampler {
99103
class CustomSampler : public Sampler {
100104
public:
101105
explicit CustomSampler(int64_t range, const float* probabilities,
106+
const int* alias, const float* alias_probabilities,
102107
unsigned int seed = 0UL);
103108

104109
~CustomSampler() override {}
@@ -108,10 +113,10 @@ class CustomSampler : public Sampler {
108113
float Probability(int64_t value) const override;
109114

110115
private:
111-
std::shared_ptr<std::vector<float>> alias_probs_;
112-
std::shared_ptr<std::vector<int64_t>> alias_;
113-
std::shared_ptr<std::vector<float>> probs_;
114-
std::shared_ptr<std::mt19937_64> random_engine_;
116+
const float* alias_probs_;
117+
const int* alias_;
118+
const float* probs_;
119+
std::shared_ptr<std::mt19937> random_engine_;
115120
std::shared_ptr<std::uniform_real_distribution<>> real_dist_;
116121
std::shared_ptr<std::uniform_int_distribution<>> int_dist_;
117122
};

paddle/fluid/operators/nce_op.cc

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ limitations under the License. */
1414

1515
#include "paddle/fluid/operators/nce_op.h"
1616

17+
#include <string>
1718
#include <vector>
1819

1920
namespace paddle {
@@ -25,7 +26,7 @@ class NCEOp : public framework::OperatorWithKernel {
2526
public:
2627
using framework::OperatorWithKernel::OperatorWithKernel;
2728

28-
void InferShape(framework::InferShapeContext* ctx) const override {
29+
void InferShape(framework::InferShapeContext *ctx) const override {
2930
PADDLE_ENFORCE(ctx->HasInput("Input"));
3031
PADDLE_ENFORCE(ctx->HasInput("Label"));
3132
PADDLE_ENFORCE(ctx->HasInput("Weight"));
@@ -67,7 +68,7 @@ class NCEOp : public framework::OperatorWithKernel {
6768

6869
protected:
6970
framework::OpKernelType GetExpectedKernelType(
70-
const framework::ExecutionContext& ctx) const override {
71+
const framework::ExecutionContext &ctx) const override {
7172
return framework::OpKernelType(
7273
framework::ToDataType(ctx.Input<Tensor>("Input")->type()),
7374
platform::CPUPlace());
@@ -101,11 +102,24 @@ class NCEOpMaker : public framework::OpProtoAndCheckerMaker {
101102
.AsDispensable();
102103

103104
AddInput(
104-
"CustomDistribution",
105+
"CustomDistProbs",
105106
"(Tensor) It is used in 'CostumDist' sampler. "
106107
"It is a tensor with shape [num_total_classes]."
107108
"The i-th element is the probsbility of the i-th class being sampled.")
108109
.AsDispensable();
110+
AddInput(
111+
"CustomDistAlias",
112+
"(Tensor) It is used in 'CostumDist' sampler. "
113+
"It is a tensor with shape [num_total_classes]."
114+
"The i-th element is the probsbility of the i-th class being sampled.")
115+
.AsDispensable();
116+
AddInput(
117+
"CustomDistAliasProbs",
118+
"(Tensor) It is used in 'CostumDist' sampler. "
119+
"It is a tensor with shape [num_total_classes]."
120+
"The i-th element is the probsbility of the i-th class being sampled.")
121+
.AsDispensable();
122+
109123
AddOutput("Cost",
110124
"(Tensor) A tensor of shape [batch_size, 1]. Cost of samples.");
111125
AddOutput("SampleLogits",
@@ -124,21 +138,22 @@ class NCEOpMaker : public framework::OpProtoAndCheckerMaker {
124138
"kernel to compute grads."
125139
"")
126140
.AsIntermediate();
141+
127142
AddAttr<int>("num_total_classes",
128143
"Total number of classes in all samples.");
129144
AddAttr<int>("num_neg_samples",
130145
"The number of negative classes. The default value is 10.")
131146
.SetDefault(10);
132-
133147
AddAttr<int>("sampler",
134148
"(int) Which sampler to be used to sample negative class."
135149
"0: Uniform; 1: LogUniform; 2: CostumDist.")
136150
.SetDefault(0);
137-
138151
AddAttr<int>("seed",
139152
"(int) The seed used in sampler. If it is 0, "
140153
"the sampler will generate a seed randomly.")
141154
.SetDefault(0);
155+
AddAttr<bool>("is_sparse", "(boolean, default false) Sparse update.")
156+
.SetDefault(false);
142157

143158
AddAttr<std::vector<int>>("custom_neg_classes",
144159
"This attribute only be used in unitest. Classes "
@@ -156,11 +171,19 @@ By default this operator uses a uniform distribution for sampling.
156171
}
157172
};
158173

174+
class NCEOpGradDescMaker : public framework::DefaultGradOpDescMaker<true> {
175+
using ::paddle::framework::DefaultGradOpDescMaker<
176+
true>::DefaultGradOpDescMaker;
177+
178+
protected:
179+
virtual std::string GradOpType() const { return "nce_grad"; }
180+
};
181+
159182
class NCEOpGrad : public framework::OperatorWithKernel {
160183
public:
161184
using framework::OperatorWithKernel::OperatorWithKernel;
162185

163-
void InferShape(framework::InferShapeContext* ctx) const override {
186+
void InferShape(framework::InferShapeContext *ctx) const override {
164187
PADDLE_ENFORCE(ctx->HasInput("Input"));
165188
PADDLE_ENFORCE(ctx->HasInput("Weight"));
166189
PADDLE_ENFORCE(ctx->HasInput("Cost"));
@@ -190,20 +213,45 @@ class NCEOpGrad : public framework::OperatorWithKernel {
190213

191214
protected:
192215
framework::OpKernelType GetExpectedKernelType(
193-
const framework::ExecutionContext& ctx) const override {
216+
const framework::ExecutionContext &ctx) const override {
194217
return framework::OpKernelType(
195218
framework::ToDataType(ctx.Input<Tensor>("Input")->type()),
196219
platform::CPUPlace());
197220
}
198221
};
199222

223+
class NCEOpGradVarTypeInference : public framework::VarTypeInference {
224+
public:
225+
void operator()(const framework::OpDesc &op_desc,
226+
framework::BlockDesc *block) const override {
227+
auto weight_grad = op_desc.Output(framework::GradVarName("Weight")).front();
228+
auto bias_grad = op_desc.Output(framework::GradVarName("Bias")).front();
229+
230+
auto attr = op_desc.GetAttr("is_sparse");
231+
bool is_sparse = boost::get<bool>(attr);
232+
if (is_sparse) {
233+
VLOG(30) << "nce_op_grad op " << weight_grad << " and " << bias_grad
234+
<< " is set to SelectedRows";
235+
block->Var(weight_grad)
236+
->SetType(framework::proto::VarType::SELECTED_ROWS);
237+
block->Var(bias_grad)->SetType(framework::proto::VarType::SELECTED_ROWS);
238+
} else {
239+
VLOG(30) << "nce_op_grad op " << weight_grad << " and " << bias_grad
240+
<< " is set to LoDTensor";
241+
block->Var(weight_grad)->SetType(framework::proto::VarType::LOD_TENSOR);
242+
block->Var(bias_grad)->SetType(framework::proto::VarType::LOD_TENSOR);
243+
}
244+
block->Var(weight_grad)->SetDataType(block->Var("Input")->GetDataType());
245+
block->Var(bias_grad)->SetDataType(block->Var("Input")->GetDataType());
246+
}
247+
};
248+
200249
} // namespace operators
201250
} // namespace paddle
202251

203252
namespace ops = paddle::operators;
204-
REGISTER_OPERATOR(nce, ops::NCEOp, ops::NCEOpMaker,
205-
paddle::framework::DefaultGradOpDescMaker<true>);
206-
REGISTER_OPERATOR(nce_grad, ops::NCEOpGrad);
253+
REGISTER_OPERATOR(nce, ops::NCEOp, ops::NCEOpGradDescMaker, ops::NCEOpMaker);
254+
REGISTER_OPERATOR(nce_grad, ops::NCEOpGrad, ops::NCEOpGradVarTypeInference);
207255
REGISTER_OP_CPU_KERNEL(nce, ops::NCEKernel<paddle::platform::CPUPlace, float>,
208256
ops::NCEKernel<paddle::platform::CPUPlace, double>);
209257
REGISTER_OP_CPU_KERNEL(nce_grad,

0 commit comments

Comments
 (0)