Spaces:
Running
Running
Upload server/reward.py with huggingface_hub
Browse files- server/reward.py +63 -0
server/reward.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
import numpy as np
|
| 3 |
+
from sentence_transformers import SentenceTransformer
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def _cosine(a: np.ndarray, b: np.ndarray) -> float:
|
| 7 |
+
na = np.linalg.norm(a)
|
| 8 |
+
nb = np.linalg.norm(b)
|
| 9 |
+
if na == 0 or nb == 0:
|
| 10 |
+
return 0.0
|
| 11 |
+
return float(np.dot(a, b) / (na * nb))
|
| 12 |
+
|
| 13 |
+
_embedder: SentenceTransformer | None = None
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def get_embedder() -> SentenceTransformer:
|
| 17 |
+
global _embedder
|
| 18 |
+
if _embedder is None:
|
| 19 |
+
_embedder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
|
| 20 |
+
return _embedder
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def compute_semantic_similarity(hypothesis: str, true_secret: str) -> float:
|
| 24 |
+
embedder = get_embedder()
|
| 25 |
+
emb_h = embedder.encode([hypothesis])[0]
|
| 26 |
+
emb_t = embedder.encode([true_secret])[0]
|
| 27 |
+
raw = _cosine(emb_h, emb_t)
|
| 28 |
+
# random sentence pairs score ~0.2, normalize to [0, 1]
|
| 29 |
+
return max(0.0, (raw - 0.2) / 0.8)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def compute_reward(
|
| 33 |
+
hypothesis: str,
|
| 34 |
+
true_secret: str,
|
| 35 |
+
n_questions_used: int,
|
| 36 |
+
max_questions: int,
|
| 37 |
+
category_predicted: str | None,
|
| 38 |
+
category_true: str,
|
| 39 |
+
hint_keywords: list[str],
|
| 40 |
+
) -> dict:
|
| 41 |
+
semantic = compute_semantic_similarity(hypothesis, true_secret)
|
| 42 |
+
|
| 43 |
+
# fewer questions = higher bonus (range: 0.6 – 1.0)
|
| 44 |
+
efficiency = 1.0 - (n_questions_used / max_questions) * 0.4
|
| 45 |
+
|
| 46 |
+
category_bonus = 0.1 if category_predicted == category_true else 0.0
|
| 47 |
+
|
| 48 |
+
hypothesis_lower = hypothesis.lower()
|
| 49 |
+
kw_hits = sum(1 for kw in hint_keywords if kw.lower() in hypothesis_lower)
|
| 50 |
+
keyword_bonus = min(0.1, 0.033 * kw_hits)
|
| 51 |
+
|
| 52 |
+
base = semantic * efficiency
|
| 53 |
+
total = min(1.0, base + category_bonus + keyword_bonus)
|
| 54 |
+
|
| 55 |
+
return {
|
| 56 |
+
"reward": round(total, 4),
|
| 57 |
+
"components": {
|
| 58 |
+
"semantic": round(semantic, 4),
|
| 59 |
+
"efficiency": round(efficiency, 4),
|
| 60 |
+
"category_bonus": category_bonus,
|
| 61 |
+
"keyword_bonus": round(keyword_bonus, 4),
|
| 62 |
+
},
|
| 63 |
+
}
|