pytorch/caffe2/operators/reduction_ops.h
Yangxin Zhong 514f20ea51 Histogram Binning Calibration
Summary:
Adding a calibration module called histogram binning:

Divide the prediction range (e.g., [0, 1]) into B bins. In each bin, use two parameters to store the number of positive examples and the number of examples that fall into this bucket. So we basically have a histogram for the model prediction.

As a result, for each bin, we have a statistical value for the real CTR (num_pos / num_example). We use this statistical value as the final calibrated prediction if the pre-cali prediction falls into the corresponding bin.

In this way, the predictions within each bin should be well-calibrated if we have sufficient examples. That is, we have a fine-grained calibrated model by this calibration module.

Theoretically, this calibration layer can fix any uncalibrated model or prediction if we have sufficient bins and examples. It provides the potential to use any kind of training weight allocation to our training data, without worrying about the calibration issue.

Test Plan:
buck test dper3/dper3/modules/calibration/tests:calibration_test -- test_histogram_binning_calibration

buck test dper3/dper3_models/ads_ranking/tests:model_paradigm_e2e_tests -- test_sparse_nn_histogram_binning_calibration

All tests passed.

Example workflows:
f215431958

{F326445092}

f215445048

{F326445223}

Reviewed By: chenshouyuan

Differential Revision: D23356450

fbshipit-source-id: c691b66c51ef33908c17575ce12e5bee5fb325ff
2020-09-06 17:11:16 -07:00

192 lines
5.8 KiB
C++

#ifndef CAFFE2_OPERATORS_REDUCTION_OPS_H_
#define CAFFE2_OPERATORS_REDUCTION_OPS_H_
#include "caffe2/core/common_omp.h"
#include "caffe2/core/context.h"
#include "caffe2/core/logging.h"
#include "caffe2/core/operator.h"
#include "caffe2/utils/math.h"
namespace caffe2 {
template <typename T, class Context>
class SumElementsOp : public Operator<Context> {
public:
USE_OPERATOR_CONTEXT_FUNCTIONS;
explicit SumElementsOp(const OperatorDef& operator_def, Workspace* ws)
: Operator<Context>(operator_def, ws),
average_(this->template GetSingleArgument<bool>("average", false)) {}
explicit SumElementsOp(const OperatorDef& operator_def, Workspace* ws, bool average)
: Operator<Context>(operator_def, ws), average_(average) {}
#if !defined(CAFFE2_IS_XPLAT_BUILD) && !defined(C10_MOBILE)
explicit SumElementsOp(const c10::FunctionSchema& schema, std::vector<c10::IValue> inputs, std::vector<c10::IValue*> outputs)
: Operator<Context>(schema, std::move(inputs), std::move(outputs)),
average_(this->template GetSingleArgument<bool>("average", false)) {}
explicit SumElementsOp(const c10::FunctionSchema& schema, std::vector<c10::IValue> inputs, std::vector<c10::IValue*> outputs, bool average)
: Operator<Context>(schema, std::move(inputs), std::move(outputs)), average_(average) {}
#endif
~SumElementsOp() {}
bool RunOnDevice() override {
auto& X = Input(0);
auto* sum = Output(0, vector<int64_t>(), at::dtype<T>());
T* data = sum->template mutable_data<T>();
math::Sum<T, Context>(
X.numel(), X.template data<T>(), data, &context_, &scratch_);
if (average_ && X.numel() > 0) {
math::Scale<float, T, Context>(
1,
static_cast<T>(1.) / X.numel(),
sum->template data<T>(),
data,
&context_);
}
return true;
}
private:
bool average_;
Tensor scratch_{Context::GetDeviceType()};
};
template <typename T, class Context>
class SumElementsIntOp : public Operator<Context> {
public:
USE_OPERATOR_CONTEXT_FUNCTIONS;
template <class... Args>
explicit SumElementsIntOp(Args&&... args)
: Operator<Context>(std::forward<Args>(args)...) {}
~SumElementsIntOp() {}
bool RunOnDevice() override {
auto& X = Input(0);
auto* sum = Output(0, vector<int64_t>(), at::dtype<T>());
T* data = sum->template mutable_data<T>();
math::Sum<T, Context>(
X.numel(), X.template data<T>(), data, &context_, &scratch_);
return true;
}
private:
Tensor scratch_{Context::GetDeviceType()};
};
template <typename T, class Context>
class SumElementsGradientOp : public Operator<Context> {
public:
USE_OPERATOR_CONTEXT_FUNCTIONS;
explicit SumElementsGradientOp(const OperatorDef& operator_def, Workspace* ws)
: Operator<Context>(operator_def, ws),
average_(this->template GetSingleArgument<bool>("average", false)) {}
explicit SumElementsGradientOp(const OperatorDef& operator_def, Workspace* ws, bool average)
: Operator<Context>(operator_def, ws), average_(average) {}
#if !defined(CAFFE2_IS_XPLAT_BUILD) && !defined(C10_MOBILE)
explicit SumElementsGradientOp(const c10::FunctionSchema& schema, std::vector<c10::IValue> inputs, std::vector<c10::IValue*> outputs)
: Operator<Context>(schema, std::move(inputs), std::move(outputs)),
average_(this->template GetSingleArgument<bool>("average", false)) {}
explicit SumElementsGradientOp(const c10::FunctionSchema& schema, std::vector<c10::IValue> inputs, std::vector<c10::IValue*> outputs, bool average)
: Operator<Context>(schema, std::move(inputs), std::move(outputs)), average_(average) {}
#endif
~SumElementsGradientOp() {}
bool RunOnDevice() override;
private:
bool average_;
};
template <class Context>
class SumSqrElementsOp : public Operator<Context> {
public:
USE_SIMPLE_CTOR_DTOR(SumSqrElementsOp)
USE_OPERATOR_CONTEXT_FUNCTIONS;
bool RunOnDevice() override {
return DispatchHelper<TensorTypes<float, double>>::call(this, Input(0));
}
template <typename T>
bool DoRunWithType() {
bool average = this->template GetSingleArgument<bool>("average", false);
auto& X = Input(0);
auto* sum = Output(0, vector<int64_t>(), at::dtype<T>());
math::SumSqr<T, Context>(
X.numel(),
X.template data<T>(),
sum->template mutable_data<T>(),
&context_,
&scratch_);
if (average && X.numel() > 0) {
math::Scale<float, T, Context>(
1,
float(1.) / X.numel(),
sum->template data<T>(),
sum->template mutable_data<T>(),
&context_);
}
return true;
}
private:
Tensor scratch_{Context::GetDeviceType()};
};
template <typename T, class Context, bool ROWWISE>
class MaxReductionOp : public Operator<Context> {
public:
USE_SIMPLE_CTOR_DTOR(MaxReductionOp)
USE_OPERATOR_CONTEXT_FUNCTIONS;
bool RunOnDevice() override {
auto& X = Input(0);
CAFFE_ENFORCE_EQ(X.dim(), 3);
const int batch_size = X.dim32(0);
const int M = X.dim32(1);
const int N = X.dim32(2);
auto* Y = Output(0, {batch_size, ROWWISE ? M : N}, at::dtype<T>());
if (ROWWISE) {
math::RowwiseMax<T, Context>(
batch_size * M,
N,
X.template data<T>(),
Y->template mutable_data<T>(),
&context_);
} else {
const int input_size = N * M;
for (int i = 0; i < batch_size; ++i) {
math::ColwiseMax<T, Context>(
M,
N,
X.template data<T>() + i * input_size,
Y->template mutable_data<T>() + i * N,
&context_);
}
}
return true;
}
};
template <typename T, class Context, bool ROWWISE>
class MaxReductionGradientOp : public Operator<Context> {
public:
USE_SIMPLE_CTOR_DTOR(MaxReductionGradientOp)
USE_OPERATOR_CONTEXT_FUNCTIONS;
bool RunOnDevice() override;
};
} // namespace caffe2
#endif