Upload consensus_fusion.py with huggingface_hub
Browse files- consensus_fusion.py +84 -16
consensus_fusion.py
CHANGED
|
@@ -7,26 +7,89 @@ try:
|
|
| 7 |
except ImportError:
|
| 8 |
MATH_VERIFY_AVAILABLE = False
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
def _normalize_answer(ans: str) -> Any:
|
| 11 |
-
"""Uses
|
|
|
|
| 12 |
if MATH_VERIFY_AVAILABLE:
|
| 13 |
try:
|
| 14 |
-
return parse(
|
| 15 |
except:
|
| 16 |
-
return
|
| 17 |
-
|
| 18 |
-
# Legacy fallback
|
| 19 |
-
s = str(ans).strip()
|
| 20 |
-
s = re.sub(r'\$', '', s)
|
| 21 |
-
s = re.sub(r'\\(?:approx|approx|cdot|,|;|\s)', ' ', s)
|
| 22 |
-
s = s.replace("\\", "").replace("{", "").replace("}", "")
|
| 23 |
-
s = s.replace(" ", "").lower()
|
| 24 |
-
try:
|
| 25 |
-
f = float(s)
|
| 26 |
-
s = str(int(f)) if f == int(f) else str(round(f, 6))
|
| 27 |
-
except:
|
| 28 |
-
pass
|
| 29 |
-
return s
|
| 30 |
|
| 31 |
def normalize_answers(answers: List[str]) -> Dict[str, List[int]]:
|
| 32 |
"""Group answers that are numerically/symbolically equivalent."""
|
|
@@ -129,6 +192,10 @@ def evaluate_consensus(
|
|
| 129 |
conf_exp = res.get("Confidence Explanation", "")
|
| 130 |
raw_ans = res.get("Answer", "N/A")
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
# Check if the agent itself marked this as divergent/hallucinating
|
| 133 |
is_self_flagged = any(t in conf_exp.lower() for t in ["divergent", "wrong", "hallucin", "low confidence", "divergence"])
|
| 134 |
|
|
@@ -173,6 +240,7 @@ def evaluate_consensus(
|
|
| 173 |
scores.append({
|
| 174 |
"agent": agent_data["agent"],
|
| 175 |
"raw_answer": raw_ans,
|
|
|
|
| 176 |
"V_sym": round(v_sym, 3),
|
| 177 |
"L_logic": round(l_logic, 3),
|
| 178 |
"C_clf": round(c_clf, 3),
|
|
|
|
| 7 |
except ImportError:
|
| 8 |
MATH_VERIFY_AVAILABLE = False
|
| 9 |
|
| 10 |
+
def _fix_sqrt(string: str) -> str:
|
| 11 |
+
if "\\sqrt" not in string: return string
|
| 12 |
+
splits = string.split("\\sqrt")
|
| 13 |
+
new_string = splits[0]
|
| 14 |
+
for split in splits[1:]:
|
| 15 |
+
if len(split) > 0 and split[0] != "{":
|
| 16 |
+
new_string += "\\sqrt{" + split[0] + "}" + split[1:]
|
| 17 |
+
else:
|
| 18 |
+
new_string += "\\sqrt" + split
|
| 19 |
+
return new_string
|
| 20 |
+
|
| 21 |
+
def _fix_fracs(string: str) -> str:
|
| 22 |
+
substrs = string.split("\\frac")
|
| 23 |
+
new_str = substrs[0]
|
| 24 |
+
if len(substrs) > 1:
|
| 25 |
+
for substr in substrs[1:]:
|
| 26 |
+
new_str += "\\frac"
|
| 27 |
+
if len(substr) > 0 and substr[0] == "{":
|
| 28 |
+
new_str += substr
|
| 29 |
+
elif len(substr) >= 2:
|
| 30 |
+
new_str += "{" + substr[0] + "}{" + substr[1] + "}" + substr[2:]
|
| 31 |
+
else:
|
| 32 |
+
new_str += substr
|
| 33 |
+
return new_str
|
| 34 |
+
|
| 35 |
+
def _fix_a_slash_b(string: str) -> str:
|
| 36 |
+
if "/" not in string or len(string.split("/")) != 2: return string
|
| 37 |
+
a, b = string.split("/")
|
| 38 |
+
try:
|
| 39 |
+
a_int = int(re.sub(r'[^0-9-]', '', a))
|
| 40 |
+
b_int = int(re.sub(r'[^0-9-]', '', b))
|
| 41 |
+
return f"\\frac{{{a_int}}}{{{b_int}}}"
|
| 42 |
+
except: return string
|
| 43 |
+
|
| 44 |
+
def _strip_string(string: str) -> str:
|
| 45 |
+
string = string.replace("\n", "").replace("\\!", "").replace("\\\\", "\\")
|
| 46 |
+
string = string.replace("tfrac", "frac").replace("dfrac", "frac")
|
| 47 |
+
string = string.replace("\\left", "").replace("\\right", "")
|
| 48 |
+
string = string.replace("^{\\circ}", "").replace("^\\circ", "")
|
| 49 |
+
string = string.replace("\\$", "").replace("$", "")
|
| 50 |
+
string = string.replace("\\%", "").replace("\%", "")
|
| 51 |
+
if "sqrt" in string: string = _fix_sqrt(string)
|
| 52 |
+
string = string.replace(" ", "")
|
| 53 |
+
if "frac" in string: string = _fix_fracs(string)
|
| 54 |
+
if string == "0.5": string = "\\frac{1}{2}"
|
| 55 |
+
string = _fix_a_slash_b(string)
|
| 56 |
+
return string
|
| 57 |
+
|
| 58 |
+
def find_math_answer(s: str) -> str:
|
| 59 |
+
s = s.lower()
|
| 60 |
+
if 'oxed{' in s:
|
| 61 |
+
try:
|
| 62 |
+
ans = re.findall(r'oxed{(.*)}', s, flags=re.S)[-1]
|
| 63 |
+
if '}' in ans and ('{' not in ans or ans.find('}') < ans.find('{')):
|
| 64 |
+
ans = ans.split('}')[0]
|
| 65 |
+
s = ans
|
| 66 |
+
except: pass
|
| 67 |
+
s = s.split('=')[-1].split('\\approx')[-1]
|
| 68 |
+
return _strip_string(s)
|
| 69 |
+
|
| 70 |
+
def extract_choice(text: str) -> str:
|
| 71 |
+
"""Extracts alphabet choice (A, B, C, D) from model response."""
|
| 72 |
+
patterns = [
|
| 73 |
+
r'the answer is \(([a-e])\)',
|
| 74 |
+
r'the answer is ([a-e])\.',
|
| 75 |
+
r'final answer: ([a-e])',
|
| 76 |
+
r'^\(([a-e])\)',
|
| 77 |
+
r'^([a-e])\n'
|
| 78 |
+
]
|
| 79 |
+
for p in patterns:
|
| 80 |
+
match = re.search(p, text.lower())
|
| 81 |
+
if match: return match.group(1).upper()
|
| 82 |
+
return ""
|
| 83 |
+
|
| 84 |
def _normalize_answer(ans: str) -> Any:
|
| 85 |
+
"""Uses advanced heuristics + math_verify to normalize answer."""
|
| 86 |
+
cleaned = find_math_answer(str(ans))
|
| 87 |
if MATH_VERIFY_AVAILABLE:
|
| 88 |
try:
|
| 89 |
+
return parse(cleaned)
|
| 90 |
except:
|
| 91 |
+
return cleaned
|
| 92 |
+
return cleaned
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
def normalize_answers(answers: List[str]) -> Dict[str, List[int]]:
|
| 95 |
"""Group answers that are numerically/symbolically equivalent."""
|
|
|
|
| 192 |
conf_exp = res.get("Confidence Explanation", "")
|
| 193 |
raw_ans = res.get("Answer", "N/A")
|
| 194 |
|
| 195 |
+
# Heuristic Bonus: Capture choices (A/B/C/D)
|
| 196 |
+
choice = extract_choice(str(raw_ans))
|
| 197 |
+
normalized_ans = choice if choice else _normalize_answer(raw_ans)
|
| 198 |
+
|
| 199 |
# Check if the agent itself marked this as divergent/hallucinating
|
| 200 |
is_self_flagged = any(t in conf_exp.lower() for t in ["divergent", "wrong", "hallucin", "low confidence", "divergence"])
|
| 201 |
|
|
|
|
| 240 |
scores.append({
|
| 241 |
"agent": agent_data["agent"],
|
| 242 |
"raw_answer": raw_ans,
|
| 243 |
+
"normalized_answer": str(normalized_ans),
|
| 244 |
"V_sym": round(v_sym, 3),
|
| 245 |
"L_logic": round(l_logic, 3),
|
| 246 |
"C_clf": round(c_clf, 3),
|