package edu.uci.jforests.eval.ranking;

//import static org.junit.Assert.assertEquals;
//import org.junit.Test;
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.CDF_Normal;
import edu.uci.jforests.util.MathUtil;
import edu.uci.jforests.util.concurrency.BlockingThreadPoolExecutor;

/** Implements the TRisk risk-sensitive optimisation metric called SARO, as defined by Dincer et al.
 * See Taner Dincer, Craig Macdonald and Iadh Ounis, Hypothesis Testing for Risk-Sensitive Evaluation 
 * of Retrieval Systems, SIGIR 2014.
 * 

Implementation Details

* It is assumed that the natural ordering of documents for each query defines * the baseline. * @author Craig Macdonald, University of Glasgow; Taner Dincer, Mugla University. */ public class TRiskAwareSAROEval extends URiskAwareEval { public TRiskAwareSAROEval(EvaluationMetric _parent, double alpha) { super(_parent, alpha); } class SAROSwapScorer extends URiskSwapScorer { double currPairedSTD = 0.0d; double c; double baselineMean; public SAROSwapScorer(double[] targets, int[] boundaries, int trunc, int[][] labelCounts, double _alpha, SwapScorer _parent) { super(targets, boundaries, trunc, labelCounts, _alpha, _parent); c = ((double) boundaries.length -1); } @Override /* Basic Version */ public double getDelta(int queryIndex, int betterIdx, int rank_i, int worseIdx, int rank_j) { //get the change in NDCG final double delta_M = parentSwap.getDelta(queryIndex, betterIdx, rank_i, worseIdx, rank_j); final double M_m = modelEval[queryIndex]; final double M_b = baselineEval[queryIndex]; final double rel_i = targets[betterIdx]; final double rel_j = targets[worseIdx]; // Score difference double d_i = M_m - M_b; final double TRisk = d_i / currPairedSTD; // beta asymptotically ranges in between a value as small as 0 and a value as large as alpha, // proportionally to the level of risk commited by the current topic. // This version follows the way how URisk weighs a given delta. //NB: in Dincer et al, beta is called \alpha' final double beta = (1 - CDF_Normal.normp(TRisk)) * alpha; final double delta_T; //Scenarios are as defined by Wang et al, SIGIR 2012. //Scenario A if (M_m <= M_b) { //case A1 if (rel_i > rel_j && rank_i < rank_j) { delta_T = (1.0d + beta) * delta_M; } //case A2 else { if (M_b > M_m + delta_M) { delta_T = (1.0d + beta) * delta_M; } else { delta_T = beta * (M_b - M_m) + delta_M; } } } else //Scenario B { if( rel_i > rel_j && rank_i < rank_j ) { //case B1 if (M_b > M_m - Math.abs(delta_M)) { delta_T = beta * (M_m - M_b) - (1 + beta) * Math.abs(delta_M); } else { delta_T = delta_M; } } else { delta_T = delta_M; } } return delta_T; } @Override public void setCurrentIterationEvaluation(int iteration, double[] nDCG) { super.setCurrentIterationEvaluation(iteration, nDCG); if (iteration == 0) { double[] params = getEstimates(baselineEval, modelEval, this.alpha); currPairedSTD = Math.sqrt(params[1]); System.err.println("Iteration 0 Paired STD="+ currPairedSTD); baselineMean = MathUtil.getAvg(baselineEval); System.err.println("Iteration 0 NDCG=" + Arrays.toString(nDCG)); } else { final double modelMean = MathUtil.getAvg(nDCG); // if (modelMean < baselineMean) // { // /* This does not allign with the paper. std is re-evaluated when modelMean < baselineMean. Why? */ // double[] params = getEstimates(baselineEval, modelEval, this.alpha); // currPairedSTD = Math.sqrt(params[1]); // System.err.println("Iteration " + iteration + " Paired STD=" + currPairedSTD); // } // else // { // System.err.println("Iteration " + iteration + " Paired STD unchanged " + currPairedSTD); // } System.err.println("Iteration " + iteration + " NDCG=" + Arrays.toString(nDCG)); } } } /** returns double[] params where params[0] = URisk; params[1] = PairedVar. */ public static double[] getEstimates(final double[] baselinePerQuery, final double[] perQuery, final double ALPHA) { final double c = baselinePerQuery.length; double sum = 0D; double SSQR = 0D; double d_i = 0D; for(int i=0; i < c; i++) { if (perQuery[i] > baselinePerQuery[i]) d_i = perQuery[i] - baselinePerQuery[i]; else d_i = (1 + ALPHA) * (perQuery[i] - baselinePerQuery[i]); sum += d_i; SSQR += d_i * d_i; } final double URisk = sum /c; final double SQRS = sum * sum; final double pairedVar = SSQR == SQRS ? 0 : ( SSQR - (SQRS / c) ) / (c-1); return new double[] {URisk, pairedVar}; } /* Returns TRisk = Math.sqrt(c / PairedVar) * URisk */ public static double T_measure(final double[] baselinePerQuery, final double[] perQuery, final double ALPHA) { final double c = baselinePerQuery.length; double[] params = getEstimates(baselinePerQuery, perQuery, ALPHA); return params[1] == 0D ? 0 : Math.sqrt(c / params[1]) * params[0]; } @Override 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); return T_measure(baselinePerQuery, perQuery, this.ALPHA); } @Override public SwapScorer getSwapScorer(double[] targets, int[] boundaries, int trunc, int[][] labelCounts) throws Exception { final SwapScorer parentMeasure = ((RankingEvaluationMetric) parent).getSwapScorer(targets, boundaries, trunc, labelCounts); return new SAROSwapScorer(targets, boundaries, trunc, labelCounts, ALPHA, parentMeasure); } }

Ads help maintain this website.