1 #include "caffe2/operators/listwise_l2r_op.h" 11 template <
typename TDATA,
typename TIDX>
12 void arg_sort(
const TDATA* data, TIDX* idx,
const size_t N,
bool reverse) {
13 std::function<bool(size_t, size_t)> cmp_lambda;
15 cmp_lambda = [data](
size_t i,
size_t j) {
return data[i] > data[j]; };
17 cmp_lambda = [data](
size_t i,
size_t j) {
return data[i] < data[j]; };
20 std::generate(idx, idx + N, [&n] {
return n++; });
21 std::sort(idx, idx + N, cmp_lambda);
24 #define PAIRWISE_DIFF(vec, N) \ 25 ((vec.matrix() * Eigen::MatrixXf::Ones(1, N) - \ 26 Eigen::MatrixXf::Ones(N, 1) * vec.matrix().transpose()) \ 29 #define CWISE_SIGM(vec) (1. / (1. + (-(vec)).exp())) 31 #define CWISE_GT(vec1, vec2) ((vec1) > (vec2)) 33 #define CWISE_LT(vec1, vec2) ((vec1) < (vec2)) 35 #define CWISE_SIGN(vec) (CWISE_GT((vec), 0).cast<float>() * 2. - 1.) 37 #define CWISE_LOG_SIGM(vec, huge) \ 38 (CWISE_GT((vec), (huge)) \ 40 0, CWISE_LT((vec), -(huge)).select(vec, CWISE_SIGM((vec)).log()))) 45 void LambdaRankNdcgOp<float, CPUContext>::ResizeInvLogITensor(
int size) {
46 int old_size = inv_log_i_.size();
47 int new_size = std::max(old_size, 1);
48 while (new_size < size) {
51 if (new_size != old_size) {
52 inv_log_i_.Resize(new_size);
53 auto* data = inv_log_i_.template mutable_data<float>();
54 EigenVectorArrayMap<float> vec(data, inv_log_i_.size());
55 const float log2f_ = std::log(2.f);
57 (Eigen::ArrayXf::LinSpaced(new_size, 2, 1 + new_size).log().inverse());
63 void LambdaRankNdcgOp<float, CPUContext>::ComputeDiscounts(
int* idx,
int N) {
65 auto* discount_data = discount_.template mutable_data<float>();
66 auto* inv_log_i_data = inv_log_i_.template mutable_data<float>();
67 for (
int i = 0; i < N; i++) {
68 discount_data[idx[i]] = inv_log_i_data[i];
74 bool LambdaRankNdcgOp<float, CPUContext>::RunOnDevice() {
75 auto& y = Input(PRED);
77 auto* loss = Output(LOSS);
78 auto* dy = Output(DPRED);
80 const auto* y_data = y.template data<float>();
81 const auto* r_data = r.template data<float>();
82 ConstEigenVectorArrayMap<float> y_vec(y_data, y.size());
83 ConstEigenVectorArrayMap<float> r_vec(r_data, r.
size());
84 CAFFE_ENFORCE(y.ndim() == 1);
85 CAFFE_ENFORCE(y.size() == r.
size());
90 auto* rank_idx_data = rank_idx_.template mutable_data<int>();
91 auto* ideal_idx_data = ideal_idx_.template mutable_data<int>();
92 arg_sort(y_data, rank_idx_data, N,
true);
93 arg_sort(r_data, ideal_idx_data, N,
true);
95 const double log2f_ = std::log(2.f);
97 auto* gain_data = gain_.template mutable_data<float>();
98 EigenVectorArrayMap<float> gain_vec(gain_data, gain_.size());
99 gain_vec = (r_vec * log2f_).exp();
101 ResizeInvLogITensor(N);
102 ComputeDiscounts(ideal_idx_data, N);
103 auto* ideal_discount_data = discount_.template mutable_data<float>();
104 EigenVectorArrayMap<float> ideal_discount_vec(
105 ideal_discount_data, discount_.size());
106 double idcg = (gain_vec * ideal_discount_vec).sum();
113 ComputeDiscounts(rank_idx_data, N);
114 auto* discount_data = discount_.template mutable_data<float>();
115 EigenVectorArrayMap<float> discount_vec(discount_data, discount_.size());
117 lambda_.Resize(N * N);
118 auto* lambda_data = lambda_.template mutable_data<float>();
119 EigenArrayMap<float> lambda_mat(lambda_data, N, N);
122 (PAIRWISE_DIFF(discount_vec, N) * PAIRWISE_DIFF(gain_vec, N)).abs();
126 auto* loss_data = loss->template mutable_data<float>();
127 auto* dy_data = dy->template mutable_data<float>();
128 EigenVectorArrayMap<float> dy_vec(dy_data, dy->size());
133 -(lambda_mat * CWISE_SIGN(PAIRWISE_DIFF(r_vec, N)) *
135 -CWISE_SIGN(PAIRWISE_DIFF(r_vec, N)) * PAIRWISE_DIFF(y_vec, N)))
143 CWISE_SIGN(PAIRWISE_DIFF(r_vec, N)) * PAIRWISE_DIFF(y_vec, N), 100))
150 bool LambdaRankNdcgGradientOp<float, CPUContext>::RunOnDevice() {
152 auto& dy_cache = Input(DY_CACHE);
153 auto& dLoss = Input(DLOSS);
154 auto* dy = Output(DY);
155 CAFFE_ENFORCE(y.ndim() == 1);
156 CAFFE_ENFORCE(dy_cache.ndim() == 1);
157 CAFFE_ENFORCE(dy_cache.size() > 0);
158 CAFFE_ENFORCE(y.size() == dy_cache.size());
159 CAFFE_ENFORCE(dLoss.size() == 1);
161 ConstEigenVectorArrayMap<float> dy_cache_vec(
162 dy_cache.template data<float>(), dy_cache.size());
163 dy->Resize(dy_cache.size());
164 EigenVectorArrayMap<float> dy_vec(
165 dy->template mutable_data<float>(), dy->size());
166 float multiplier = dLoss.template data<float>()[0];
167 dy_vec = multiplier * dy_cache_vec;
173 REGISTER_CPU_OPERATOR(LambdaRankNdcg, LambdaRankNdcgOp<float, CPUContext>);
174 REGISTER_CPU_OPERATOR(
175 LambdaRankNdcgGradient,
176 LambdaRankNdcgGradientOp<float, CPUContext>);
178 OPERATOR_SCHEMA(LambdaRankNdcg).NumInputs(2).NumOutputs(2).SetDoc(R
"DOC( 179 It implements the LambdaRank as appeared in Wu, Qiang, et al. "Adapting boosting 180 for information retrieval measures." Information Retrieval 13.3 (2010): 254-270. 182 This method heuristically optimizes the NDCG. 184 OPERATOR_SCHEMA(LambdaRankNdcgGradient).NumInputs(3).NumOutputs(1); 186 class GetLambdaRankNdcgGradient :
public GradientMakerBase {
187 using GradientMakerBase::GradientMakerBase;
188 vector<OperatorDef> GetGradientDefs()
override {
189 return SingleGradientDef(
190 "LambdaRankNdcgGradient",
192 vector<string>{I(0), O(1), GO(0)},
193 vector<string>{GI(0)});
197 REGISTER_GRADIENT(LambdaRankNdcg, GetLambdaRankNdcgGradient);
TIndex size() const
Returns the size (i.e.
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...