public PairRankedItems previous; // automatically construct linked list #endregion Fields #region Constructors public PairRankedItems(RankedItem r1, RankedItem r2) { item1 = r1; item2 = r2; previous = mostRecent; mostRecent = this; }
/// <summary> /// Find every pair, compute alpha, and store the pair in pairRankedItems. Items are assumed already ranked. /// In fact the convex version (limited to [0,1]) is equivalent to the non-convex version (allowing infinite steps) /// (just rescale by 1/(1-alpha)). However the latter is useful because (1) we can easily control step size and (2) /// the old weights are left unchanged. /// </summary> /// <param name="rankedItems"></param> /// <param name="convex"></param> /// <param name="maxStep">max step to use for non-convex version.</param> /// <param name="ctr"></param> /// <returns></returns> public static PairRankedItems FillPairRankedItems(RankedItem[] rankedItems, bool convex, bool alphaPos, double maxStep, ref int ctr) { PairRankedItems pri = null; for(int i = 0; i < rankedItems.Length - 1; ++i) { RankedItem x = rankedItems[i]; for(int j = i + 1; j < rankedItems.Length; ++j) { RankedItem y = rankedItems[j]; // See boostingNotes.docx. if(convex) { // The convex combination version: (1-alpha)*score1 + alpha*score2. Disadvantage: previously computed weights // keep changing. if(x.score2 - x.score1 + y.score1 - y.score2 != 0) { // Note ranks go inversely with score double alpha = ( y.score1 - x.score1 ) / ( x.score2 - x.score1 + y.score1 - y.score2 ); if(alpha >= 0.0 && alpha <= 1.0) { pri = new PairRankedItems(x, y); pri.alpha = alpha; } } } else { // The score1 + alpha*score2 version. Advantage: leaves previously computed weights the same. if(x.score2 != y.score2) { double alpha = ( x.score1 - y.score1 ) / ( y.score2 - x.score2 ); // alpha=0 corresponds to original rank order for both convex and non-convex combinations if( (alphaPos && alpha >= 0.0 && alpha <= maxStep) || (!alphaPos && alpha <= 0.0 && alpha >= -maxStep) ) { pri = new PairRankedItems(x, y); pri.alpha = alpha; } } } } } return pri; }
/// <summary> /// Loop through every alpha. If both labels are the same, just swap ranks (and no change to NDCG). /// If not, still swap ranks, and compute cumulative delta NDCG. Keep track of that alpha that gave the /// best NDCG. Also treat as a special case alpha=0 (which may give the best result, and which may not /// be one of the listed alphas, since those always correspond to swapping points). /// </summary> /// <param name="pairRankedItems">Assumed sorted by alpha, with the value closest to zero first. WARNING: SIDE EFFECTS on RankedItems.</param> /// <param name="dcg"></param> /// <param name="bestAlpha"></param> /// <param name="bestNDCGGain"></param> void FindBestAlpha(PairRankedItems[] pairRankedItems, DCGScorer scorer, int nQueries, out double bestAlpha, out double bestMeanNDCGGain) { bestAlpha = 0.0; double bestNDCGGain = 0.0; int bestIndex = 0; double NDCGGain = 0.0; // This really is a gain in a gain (again) double[] markups = DCGScorer.discounts; // Position dependent part of NDCG // Rely on jittering to take care of degeneracy. int loopLength = pairRankedItems.Length; //int degCtr = 0; //while (loopLength != 0) //{ for (int i = 0; i < loopLength; ++i) { PairRankedItems pairRankedItem = pairRankedItems[i]; RankedItem x = pairRankedItem.item1; RankedItem y = pairRankedItem.item2; int rankx = x.rank; int ranky = y.rank; if (rankx != ranky + 1 && rankx != ranky - 1) { throw new Exception("FindBestAlpha: degenerate scores encountered."); //pairRankedItems[degCtr++] = pairRankedItem; //Console.WriteLine("Warning: we've hit a degenerate pair: QueryID {0}", x.QueryID); //Console.WriteLine("QueryID: {0} s1_1 {1} s1_2 {2} s2_1 {3} s2_2 {4} rank1 {5} rank2 {6} crossing error...", x.QueryID, x.score1, y.score1, x.score2, y.score2, rankx, ranky); } else { if (x.label != y.label) { double ndcgx = x.ndcgWt; double ndcgy = y.ndcgWt; double markupx = markups[rankx]; double markupy = markups[ranky]; NDCGGain += (ndcgx - ndcgy) * (markupy - markupx); if (NDCGGain > bestNDCGGain) { bestNDCGGain = NDCGGain; bestIndex = i; } } // Positions swap only if in the open interval (not at the edges), otherwise could get a spurious gain if ((convex && pairRankedItem.alpha != 0.0 && pairRankedItem.alpha != 1.0) || (!convex && pairRankedItem.alpha != 0.0 && pairRankedItem.alpha < maxStep && pairRankedItem.alpha > -maxStep)) { x.rank = ranky; y.rank = rankx; } } } // if(degCtr > 0) // Console.WriteLine("Num degenerates = {0}", degCtr); // loopLength = degCtr; // degCtr = 0; // } // Put the best alpha half way between that found (which is on the border) and the next, unless it's the last. if(bestIndex < pairRankedItems.Length - 1) bestAlpha = 0.5 * ( pairRankedItems[bestIndex].alpha + pairRankedItems[bestIndex + 1].alpha ); else if(bestIndex == pairRankedItems.Length - 1) bestAlpha = pairRankedItems[bestIndex].alpha; else // The passed pairRankedItems array could be empty. bestAlpha = 0.0; bestMeanNDCGGain = bestNDCGGain / (double)nQueries; }
public static PairRankedItems[] PRI_ListToArray() { int ctr = 0; PairRankedItems priPtr = PairRankedItems.mostRecent; while(priPtr != null) { ++ctr; priPtr = priPtr.previous; } PairRankedItems[] pairRankedItems = new PairRankedItems[ctr]; ctr = 0; priPtr = PairRankedItems.mostRecent; while(priPtr != null) { pairRankedItems[ctr++] = priPtr; priPtr = priPtr.previous; } return pairRankedItems; }
public int CompareTo(PairRankedItems other) { return alpha > other.alpha ? 1 : ( alpha < other.alpha ? -1 : 0 ); }
public static void Reset() { mostRecent = null; }