package edu.uci.jforests.eval.ranking;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import org.junit.Test;
import edu.uci.jforests.eval.EvaluationMetric;
import edu.uci.jforests.sample.RankingSample;
import edu.uci.jforests.sample.Sample;
import edu.uci.jforests.util.MathUtil;
import edu.uci.jforests.util.concurrency.BlockingThreadPoolExecutor;
/** Implements the URisk risk-sensitive evaluation metric, as defined by Wang et al.
* See Lidan Wang, Paul Bennet and Kevyn Collin-Thompson, SIGIR 2012.
* Implementation Details
* It is assumed that the natural ordering of documents for each query defines
* the baseline.
* @author Craig Macdonald, University of Glasgow
*/
public class URiskAwareEval extends RankingEvaluationMetric {
EvaluationMetric parent;
final double ALPHA;
static class URiskSwapScorer extends SwapScorer
{
/** alpha value in the Utility measure */
double alpha;
/** swapscorer of the parent metric */
SwapScorer parentSwap;
/** M_m: the performance over all queries of the current model
* BEFORE any documents are swapped
*/
double[] modelEval;
/** M_b: the mean performance of the baseline ranking over all queries. */
double[] baselineEval;
public URiskSwapScorer(
double[] targets,
int[] boundaries,
int trunc,
int[][] labelCounts,
double _alpha,
SwapScorer _parent
)
{
super(targets, boundaries, trunc, labelCounts);
this.alpha = _alpha;
this.parentSwap = _parent;
}
@Override
public void setCurrentIterationEvaluation(int iteration, double[] nDCG) {
final double meanNDCG = MathUtil.getAvg(nDCG);
if (iteration == 0)
{
System.err.println("Baseline in iteration 0 has peformance " + meanNDCG);
baselineEval = nDCG;
modelEval = new double[baselineEval.length];
}
else
{
System.err.println("Model in iteration "+iteration+" has peformance " + meanNDCG);
modelEval = nDCG;
}
System.err.println("Iteration " + iteration + " NDCG=" + Arrays.toString(nDCG));
}
@Override
public double getDelta(int queryIndex, int betterIdx, int rank_i,
int worseIdx, int rank_j) {
final double M_m = modelEval[queryIndex];
final double M_b = baselineEval[queryIndex];
//This follows the notation of Section 4.3.2 in Wang et al. to the fullest extent possible.
//Assertions and if conditions are used to ensure that the conditions described in the proof
//are arrived at.
//change in effectiveness
final double delta_M = parentSwap.getDelta(queryIndex, betterIdx, rank_i, worseIdx, rank_j);
final double rel_i = targets[betterIdx];
final double rel_j = targets[worseIdx];
//Our LambaMART implementation only calls getDelta when i is of higher quality better than j.
assert rel_i >= rel_j;
//the upshot is that not all scenarios in the proof can ever occur. we now check that conditions
//are as expected
//hence, delta_M should always be zero or positive, if it is later ranked than j. This would be a "good" swap
if (rank_i > rank_j)
{
assert delta_M >= 0 : "rank_i=" + rank_i + " rank_j="+rank_j + " delta_M="+delta_M;
}
//hence, delta_M should always be zero or negative, if it is earlier ranked than j. This would be a "bad" swap
if (rank_i < rank_j)
{
assert delta_M <= 0 : "rank_i=" + rank_i + " rank_j="+rank_j + " delta_M="+delta_M;
}
//change in tradeoff
final double delta_T;
//Scenario A
if (M_m <= M_b)
{
//case A1
if (rel_i > rel_j && rank_i< rank_j)
{
assert delta_M < 0;
delta_T = (1.0d + alpha) * delta_M;
assert delta_T < 0 : "M_b="+M_b+" M_m="+M_m + " delta_M=" + delta_M + " => delta_T=" + delta_T ;
}
//case A2
else
{
assert rel_i > rel_j && rank_i > rank_j : "M_b="+M_b+" M_m="+M_m + " delta_M=" + delta_M ;
assert delta_M >= 0 : "M_b="+M_b+" M_m="+M_m + " delta_M=" + delta_M
+ " rel_i="+rel_i+" rel_j="+rel_j+" rank_i="+rank_i+" rank_j="+rank_j;
if (M_b > M_m + delta_M)
{
delta_T = (1.0d + alpha) * delta_M;
}
else
{
assert M_b <= M_m + delta_M : "M_b="+M_b+" M_m="+M_m + " delta_M=" + delta_M ;
delta_T = alpha * (M_b - M_m) + delta_M;
}
assert delta_T > 0 : "M_b="+M_b+" M_m="+M_m + " delta_M=" + delta_M
+ " rel_i="+rel_i+" rel_j="+rel_j+" rank_i="+rank_i+" rank_j="+rank_j+" => delta_T=" + delta_T ;
}
}
else //Scenario B
{
assert M_m > M_b : "M_b="+M_b+" M_m="+M_m + " delta_M=" + delta_M;
if( rel_i > rel_j && rank_i < rank_j )
{
assert delta_M <= 0 : "rank_i=" + rank_i + " rank_j="+rank_j + " delta_M="+delta_M;
//case B1
if (M_b > M_m - Math.abs(delta_M))
{
delta_T = alpha * (M_m - M_b) - (1 + alpha) * Math.abs(delta_M);
}
else
{
assert M_b <= M_m - Math.abs(delta_M) : "M_b="+M_b+" M_m="+M_m + " delta_M=" + delta_M ;
delta_T = delta_M;
}
assert delta_T < 0 : "M_b="+M_b+" M_m="+M_m + " delta_M=" + delta_M
+ " rel_i="+rel_i+" rel_j="+rel_j+" rank_i="+rank_i+" rank_j="+rank_j + " => delta_T=" + delta_T ;
}
else
{
assert rel_i > rel_j && rank_i > rank_j;
delta_T = delta_M;
assert delta_T > 0;
}
}
return delta_T;
}
}
public URiskAwareEval(EvaluationMetric _parent, double alpha)
{
super(_parent.largerIsBetter());
assert _parent.largerIsBetter();
parent = _parent;
ALPHA = alpha;
}
@Override @SuppressWarnings("unused")
public double measure(double[] predictions, Sample sample) throws Exception {
final RankingSample rankingSample = (RankingSample)sample;
assert rankingSample.queryBoundaries.length -1 == rankingSample.numQueries;
final double[] naturalOrder = computeNaturalOrderScores(predictions.length, rankingSample.queryBoundaries);
double[] baselinePerQuery = ((RankingEvaluationMetric) parent).measureByQuery(naturalOrder, sample);
double[] perQuery = ((RankingEvaluationMetric) parent).measureByQuery(predictions, sample);
double T1 = 0, T2 = 0;
final int queryLength = perQuery.length;
double F_reward = 0.0d;
double F_risk = 0.0d;
for(int i=0;i
Ads help maintain this website.