DylanJHJ/APRIL / src /autollmrerank /input_assembler /pair_maxheap_topk.py
DylanJHJ's picture
download
raw
3.74 kB
import math
import copy
from tqdm import tqdm
import numpy as np
from typing import Optional, Tuple, List, Dict, Union, Any
from ..utils import Result
from .base import RerankStrategy
import pdb
class PairMaxHeapTopK(RerankStrategy):
def run(
self,
init_results: List[Result],
rank_start: int = 0,
rank_end: int = None,
batch_size: Optional[int] = 32,
num_runs: int = 10,
**kwargs
) -> List[Result]:
results = [copy.deepcopy(result) for result in init_results]
for index, result in tqdm(enumerate(init_results), desc="Pairwise HeapSort"):
sorted_hits = []
# 0. Get the last parent (index - 1) // d
i_parent = (len(result.hits) - 2) // self._window_size
# 1. build maxheap (traverse each paraents)
for i_visit in range(i_parent, -1, -1):
result = self.run_pass(result, target=i_visit)
# 2 swap the top1 with the last element
result.hits[0], result.hits[-1] = result.hits[-1], result.hits[0]
# 3 pop the largest (already at the end of the list)
sorted_hits.append(result.hits.pop(-1))
# Iteration until we have enough sorted hits
while len(sorted_hits) < num_runs: # TODO: maybe we should use variable top_k
# iter-1: build maxheap for the remaining hits (only from the root)
result = self.run_pass(result, target=0)
# iter-2: swap the top1 with the last element
result.hits[0], result.hits[-1] = result.hits[-1], result.hits[0]
# iter-3: pop the largest (already at the end of the list)
sorted_hits.append(result.hits.pop(-1))
# print(f"Sorted hits: {len(sorted_hits)}, Remaining hits: {len(result.hits)}")
# 4. Append the sorted hits to the result
results[index].hits = sorted_hits + result.hits
# Assign reciprocal rank
for result in results:
for rank, hit in enumerate(result.hits, start=1):
hit['score'] = float(1 / rank)
hit['rank'] = rank
return results
def run_pass(self, result: Result, target: int) -> List[Result]:
left = target * self._window_size + 1
right = target * self._window_size + 2
swap_right, swap_left = 0, 0
if left >= len(result.hits):
return result
if left < len(result.hits):
prompt = self._prompt_builder.create_prompt(
result=result,
rank_start=0,
rank_end=len(result.hits),
idx_pairs=[(target, left), (left, target)]
)
outputs = self._llm.generate(prompt, binary_probs=True)
swap_left = (outputs[1] - outputs[0]) # Left>Root - Root>Left
if right < len(result.hits):
prompt = self._prompt_builder.create_prompt(
result=result,
rank_start=0,
rank_end=len(result.hits),
idx_pairs=[(target, right), (right, target)]
)
outputs = self._llm.generate(prompt, binary_probs=True)
swap_right = (outputs[1] - outputs[0]) # Right>Root - Left>Left
if (swap_right > 0) and (swap_right > swap_left):
result.hits[target], result.hits[right] = result.hits[right], result.hits[target]
result = self.run_pass(result, target=right)
elif (swap_left > 0) and (swap_left > swap_right):
result.hits[target], result.hits[left] = result.hits[left], result.hits[target]
result = self.run_pass(result, target=left)
return result

Xet Storage Details

Size:
3.74 kB
·
Xet hash:
5bce281f054d0109877b9dea77fa0944a142b441fea9a2f30ab0137d28acf563

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.