Spaces:
Sleeping
Sleeping
Commit ·
4455cfa
1
Parent(s): c76b5a4
fix: eval dùng 8b-instant + checkpoint resume + cắt context để tiết kiệm token
Browse files- evaluate.py +50 -18
evaluate.py
CHANGED
|
@@ -21,6 +21,22 @@ rag_module.LLM_MODEL = JUDGE_MODEL
|
|
| 21 |
from src.rag import answer as rag_answer
|
| 22 |
print("rag ok")
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
def judge(prompt: str) -> float:
|
| 26 |
"""Gọi LLM judge, trả về score 0.0-1.0."""
|
|
@@ -39,7 +55,7 @@ def judge(prompt: str) -> float:
|
|
| 39 |
|
| 40 |
|
| 41 |
def eval_faithfulness(question: str, answer: str, contexts: list[str]) -> float:
|
| 42 |
-
context_text = "\n".join(contexts)
|
| 43 |
prompt = f"""Đánh giá xem câu trả lời có trung thực với ngữ cảnh không.
|
| 44 |
Chỉ trả về một số từ 0.0 đến 1.0 (1.0 = hoàn toàn trung thực, 0.0 = bịa đặt).
|
| 45 |
|
|
@@ -62,7 +78,7 @@ Score:"""
|
|
| 62 |
|
| 63 |
|
| 64 |
def eval_context_recall(question: str, ground_truth: str, contexts: list[str]) -> float:
|
| 65 |
-
context_text = "\n".join(contexts)
|
| 66 |
prompt = f"""Đánh giá xem ngữ cảnh có chứa đủ thông tin để trả lời câu hỏi theo đáp án chuẩn không.
|
| 67 |
Chỉ trả về một số từ 0.0 đến 1.0 (1.0 = đủ thông tin, 0.0 = thiếu hoàn toàn).
|
| 68 |
|
|
@@ -74,17 +90,36 @@ Score:"""
|
|
| 74 |
return judge(prompt)
|
| 75 |
|
| 76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
def evaluate(golden_set_path: str = "data/golden_set.json", sample: int = 20):
|
| 78 |
with open(golden_set_path, encoding="utf-8") as f:
|
| 79 |
golden = json.load(f)
|
| 80 |
|
| 81 |
-
# Lấy sample câu hỏi có ground_truth thực (bỏ out_of_scope)
|
| 82 |
items = [q for q in golden if q.get("type") != "out_of_scope"][:sample]
|
| 83 |
|
| 84 |
-
|
| 85 |
-
|
|
|
|
|
|
|
| 86 |
|
| 87 |
-
|
|
|
|
|
|
|
| 88 |
question = item["question"]
|
| 89 |
ground_truth = item["answer"]
|
| 90 |
|
|
@@ -108,24 +143,15 @@ def evaluate(golden_set_path: str = "data/golden_set.json", sample: int = 20):
|
|
| 108 |
})
|
| 109 |
|
| 110 |
print(f" Faithfulness: {faith:.2f} | Relevancy: {relevancy:.2f} | Recall: {recall:.2f}")
|
| 111 |
-
|
|
|
|
| 112 |
|
| 113 |
except Exception as e:
|
| 114 |
print(f" Lỗi: {e}")
|
| 115 |
time.sleep(5)
|
| 116 |
|
| 117 |
if results:
|
| 118 |
-
avg_faith
|
| 119 |
-
avg_rel = sum(r["answer_relevancy"] for r in results) / len(results)
|
| 120 |
-
avg_recall = sum(r["context_recall"] for r in results) / len(results)
|
| 121 |
-
|
| 122 |
-
print(f"\n{'='*50}")
|
| 123 |
-
print(f"KẾT QUẢ ĐÁNH GIÁ ({len(results)} câu hỏi)")
|
| 124 |
-
print(f"{'='*50}")
|
| 125 |
-
print(f"Faithfulness: {avg_faith:.3f}")
|
| 126 |
-
print(f"Answer Relevancy: {avg_rel:.3f}")
|
| 127 |
-
print(f"Context Recall: {avg_recall:.3f}")
|
| 128 |
-
print(f"Average: {(avg_faith + avg_rel + avg_recall) / 3:.3f}")
|
| 129 |
|
| 130 |
with open("eval_results.json", "w", encoding="utf-8") as f:
|
| 131 |
json.dump({
|
|
@@ -133,11 +159,17 @@ def evaluate(golden_set_path: str = "data/golden_set.json", sample: int = 20):
|
|
| 133 |
"faithfulness": avg_faith,
|
| 134 |
"answer_relevancy": avg_rel,
|
| 135 |
"context_recall": avg_recall,
|
|
|
|
| 136 |
},
|
| 137 |
"details": results,
|
| 138 |
}, f, ensure_ascii=False, indent=2)
|
| 139 |
print("\nĐã lưu chi tiết vào eval_results.json")
|
| 140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
|
| 142 |
if __name__ == "__main__":
|
| 143 |
import sys
|
|
|
|
| 21 |
from src.rag import answer as rag_answer
|
| 22 |
print("rag ok")
|
| 23 |
|
| 24 |
+
CHECKPOINT_PATH = "eval_checkpoint.json"
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def _save_checkpoint(results: list):
|
| 28 |
+
with open(CHECKPOINT_PATH, "w", encoding="utf-8") as f:
|
| 29 |
+
json.dump(results, f, ensure_ascii=False, indent=2)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _load_checkpoint() -> list:
|
| 33 |
+
if os.path.exists(CHECKPOINT_PATH):
|
| 34 |
+
with open(CHECKPOINT_PATH, encoding="utf-8") as f:
|
| 35 |
+
data = json.load(f)
|
| 36 |
+
print(f"Tiếp tục từ checkpoint: {len(data)} câu đã xong.")
|
| 37 |
+
return data
|
| 38 |
+
return []
|
| 39 |
+
|
| 40 |
|
| 41 |
def judge(prompt: str) -> float:
|
| 42 |
"""Gọi LLM judge, trả về score 0.0-1.0."""
|
|
|
|
| 55 |
|
| 56 |
|
| 57 |
def eval_faithfulness(question: str, answer: str, contexts: list[str]) -> float:
|
| 58 |
+
context_text = "\n".join(c[:600] for c in contexts[:3]) # top-3, max 600 chars mỗi chunk
|
| 59 |
prompt = f"""Đánh giá xem câu trả lời có trung thực với ngữ cảnh không.
|
| 60 |
Chỉ trả về một số từ 0.0 đến 1.0 (1.0 = hoàn toàn trung thực, 0.0 = bịa đặt).
|
| 61 |
|
|
|
|
| 78 |
|
| 79 |
|
| 80 |
def eval_context_recall(question: str, ground_truth: str, contexts: list[str]) -> float:
|
| 81 |
+
context_text = "\n".join(c[:600] for c in contexts[:3])
|
| 82 |
prompt = f"""Đánh giá xem ngữ cảnh có chứa đủ thông tin để trả lời câu hỏi theo đáp án chuẩn không.
|
| 83 |
Chỉ trả về một số từ 0.0 đến 1.0 (1.0 = đủ thông tin, 0.0 = thiếu hoàn toàn).
|
| 84 |
|
|
|
|
| 90 |
return judge(prompt)
|
| 91 |
|
| 92 |
|
| 93 |
+
def _print_summary(results: list):
|
| 94 |
+
avg_faith = sum(r["faithfulness"] for r in results) / len(results)
|
| 95 |
+
avg_rel = sum(r["answer_relevancy"] for r in results) / len(results)
|
| 96 |
+
avg_recall = sum(r["context_recall"] for r in results) / len(results)
|
| 97 |
+
|
| 98 |
+
print(f"\n{'='*50}")
|
| 99 |
+
print(f"KẾT QUẢ ĐÁNH GIÁ ({len(results)} câu hỏi)")
|
| 100 |
+
print(f"Judge model: {JUDGE_MODEL}")
|
| 101 |
+
print(f"{'='*50}")
|
| 102 |
+
print(f"Faithfulness: {avg_faith:.3f}")
|
| 103 |
+
print(f"Answer Relevancy: {avg_rel:.3f}")
|
| 104 |
+
print(f"Context Recall: {avg_recall:.3f}")
|
| 105 |
+
print(f"Average: {(avg_faith + avg_rel + avg_recall) / 3:.3f}")
|
| 106 |
+
return avg_faith, avg_rel, avg_recall
|
| 107 |
+
|
| 108 |
+
|
| 109 |
def evaluate(golden_set_path: str = "data/golden_set.json", sample: int = 20):
|
| 110 |
with open(golden_set_path, encoding="utf-8") as f:
|
| 111 |
golden = json.load(f)
|
| 112 |
|
|
|
|
| 113 |
items = [q for q in golden if q.get("type") != "out_of_scope"][:sample]
|
| 114 |
|
| 115 |
+
# Resume từ checkpoint nếu có
|
| 116 |
+
results = _load_checkpoint()
|
| 117 |
+
done_questions = {r["question"] for r in results}
|
| 118 |
+
remaining = [it for it in items if it["question"] not in done_questions]
|
| 119 |
|
| 120 |
+
print(f"Đánh giá {len(items)} câu hỏi ({len(remaining)} còn lại)...\n")
|
| 121 |
+
|
| 122 |
+
for i, item in enumerate(remaining, len(results) + 1):
|
| 123 |
question = item["question"]
|
| 124 |
ground_truth = item["answer"]
|
| 125 |
|
|
|
|
| 143 |
})
|
| 144 |
|
| 145 |
print(f" Faithfulness: {faith:.2f} | Relevancy: {relevancy:.2f} | Recall: {recall:.2f}")
|
| 146 |
+
_save_checkpoint(results)
|
| 147 |
+
time.sleep(2)
|
| 148 |
|
| 149 |
except Exception as e:
|
| 150 |
print(f" Lỗi: {e}")
|
| 151 |
time.sleep(5)
|
| 152 |
|
| 153 |
if results:
|
| 154 |
+
avg_faith, avg_rel, avg_recall = _print_summary(results)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
with open("eval_results.json", "w", encoding="utf-8") as f:
|
| 157 |
json.dump({
|
|
|
|
| 159 |
"faithfulness": avg_faith,
|
| 160 |
"answer_relevancy": avg_rel,
|
| 161 |
"context_recall": avg_recall,
|
| 162 |
+
"judge_model": JUDGE_MODEL,
|
| 163 |
},
|
| 164 |
"details": results,
|
| 165 |
}, f, ensure_ascii=False, indent=2)
|
| 166 |
print("\nĐã lưu chi tiết vào eval_results.json")
|
| 167 |
|
| 168 |
+
# Xóa checkpoint khi hoàn thành
|
| 169 |
+
if len(results) == len(items) and os.path.exists(CHECKPOINT_PATH):
|
| 170 |
+
os.remove(CHECKPOINT_PATH)
|
| 171 |
+
print("Đã xóa checkpoint.")
|
| 172 |
+
|
| 173 |
|
| 174 |
if __name__ == "__main__":
|
| 175 |
import sys
|