Supastrikas-004 manayporwal07 commited on
Commit
f826db5
·
verified ·
1 Parent(s): 2f5f109

Update evaluator_module.py (#35)

Browse files

- Update evaluator_module.py (ad320ec347545b90f7eba3ccdb98ae61a8b6dde6)


Co-authored-by: Manay Porwal <manayporwal07@users.noreply.huggingface.co>

Files changed (1) hide show
  1. evaluator_module.py +28 -133
evaluator_module.py CHANGED
@@ -11,11 +11,6 @@ import hashlib
11
  from datetime import datetime
12
  import concurrent.futures
13
  import random
14
- from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
15
- import evaluate
16
- import warnings
17
- warnings.filterwarnings("ignore")
18
-
19
 
20
  class AetherScoreEvaluator:
21
  def __init__(self):
@@ -28,160 +23,64 @@ class AetherScoreEvaluator:
28
  self.nlp = spacy.load("en_core_web_sm")
29
 
30
  # LLM Judge Model
31
- # self.judge_model = pipeline(
32
- # "text2text-generation",
33
- # model="google/flan-t5-base",
34
- # device=-1 # 0 for GPU
35
- # )
36
- # Load evaluation metrics
37
- self.rouge = evaluate.load('rouge')
38
- self.sacrebleu = evaluate.load('sacrebleu')
39
- self.bertscore = evaluate.load('bertscore')
40
-
41
- # Load a model to generate factual reference answers
42
- model_name = "google/flan-t5-base" # small & fast
43
- self.judge_tokenizer = AutoTokenizer.from_pretrained(model_name)
44
- self.judge_model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
45
-
46
 
47
  # Sentence Transformer for Sentence ----> Embedding
48
  self.sentence_model = SentenceTransformer('all-MiniLM-L6-v2')
49
 
50
  # Scoring weights # Domain Specific weights can be added for better results
51
- self.weights = {
52
- 'instruction_following': 0.20,
53
- 'hallucination_score': 0.15,
54
- 'assumption_control': 0.15,
55
- 'coherence': 0.15,
56
- 'accuracy': 0.15,
57
- 'llm_judge_score': 0.20
58
- }
59
  # In-memory cache
60
  self.cache = {}
61
 
62
  # LLM Judge
63
  def _evaluate_with_llm_judge(self, prompt: str, response: str) -> Dict:
 
64
  print("Using HF model LLM Judge...")
65
 
66
  query = (
67
  f"Prompt: {prompt}\n"
68
  f"Response: {response}\n\n"
69
- "Return only a JSON object:\n"
70
  "{\n"
71
- ' "score": <float between 0 and 1>,\n'
72
- ' "explanation": "<short reasoning>"\n'
73
- "}\n"
 
74
  )
75
 
76
  try:
77
  result = self.judge_model(query, max_new_tokens=128, truncation=True)
78
  output = result[0]['generated_text']
79
  except Exception as e:
80
- return {"llm_judge_score": (0.0, f"HF model failed: {e}")}
 
 
 
81
 
82
- # Defaults
83
- score = 0.5
84
- explanation = output.strip()
85
 
86
  try:
87
  parsed = json.loads(output.strip())
88
- score = float(parsed.get("score", score))
 
89
  explanation = parsed.get("explanation", explanation)
90
  except Exception:
91
  pass # fallback to defaults
92
 
93
- # Hardcoded interpretation
94
- if score < 0.3:
95
- auto_expl = "Poor alignment with the prompt."
96
- elif score < 0.7:
97
- auto_expl = "Partially aligned with the prompt."
98
- else:
99
- auto_expl = "Well aligned with the prompt."
100
-
101
- explanation = f"{explanation} | {auto_expl}"
102
-
103
- return {"llm_judge_score": (score, explanation)}
104
-
105
-
106
-
107
-
108
- # LLM Judge
109
- def _generate_reference(self, prompt: str) -> str:
110
- """Generate a factual reference answer from the prompt."""
111
- input_text = f"Provide a factual answer: {prompt}"
112
- input_ids = self.judge_tokenizer(input_text, return_tensors="pt").input_ids
113
- outputs = self.judge_model.generate(
114
- input_ids,
115
- max_length=256,
116
- num_beams=4,
117
- early_stopping=True
118
- )
119
- return self.judge_tokenizer.decode(outputs[0], skip_special_tokens=True)
120
-
121
- def detect(self, prompt: str, output: str) -> dict:
122
- """Compute hallucination-related scores."""
123
- reference = self._generate_reference(prompt)
124
-
125
- # Metric 1: ROUGE-L
126
- rouge_l = self.rouge.compute(predictions=[output], references=[reference])['rougeL']
127
-
128
- # Metric 2: SacreBLEU (normalize 0–1)
129
- sacrebleu = self.sacrebleu.compute(predictions=[output], references=[[reference]])['score'] / 100
130
-
131
- # Metric 3: BERTScore F1
132
- bert_f1 = np.mean(
133
- self.bertscore.compute(predictions=[output], references=[reference], lang='en')['f1']
134
- )
135
-
136
- # Combine (weights adjusted to sum = 1)
137
- weights = {
138
- "rouge_l": 0.33,
139
- "sacrebleu": 0.33,
140
- "bertscore_f1": 0.34,
141
- }
142
- halluc_score = (
143
- weights["rouge_l"] * (1 - rouge_l)
144
- + weights["sacrebleu"] * (1 - sacrebleu)
145
- + weights["bertscore_f1"] * (1 - bert_f1)
146
- )
147
-
148
- return {
149
- "prompt": prompt,
150
- "output": output,
151
- "reference": reference,
152
- "scores": {
153
- "rougeL": rouge_l,
154
- "sacrebleu": sacrebleu,
155
- "bertscore": bert_f1,
156
- },
157
- "hallucination_score": float(halluc_score),
158
- }
159
-
160
- def hallucination_score(self,prompt: str, output: str) -> dict:
161
- """
162
- Keeps the SAME return format as _evaluate_with_llm_judge:
163
- {
164
- "hallucination_score": (float, explanation),
165
- "assumption_control": (float, explanation)
166
- }
167
- """
168
- result=self.detect(prompt,output)
169
-
170
- explanation = (
171
- f"Compared output with generated reference.\n"
172
- f"ROUGE={result['scores']['rougeL']:.2f}, "
173
- f"BLEU={result['scores']['sacrebleu']:.2f}, "
174
- f"BERTScore={result['scores']['bertscore']:.2f}."
175
- )
176
-
177
- halluc_score = result["hallucination_score"]
178
- assumption_score = 1 - halluc_score # simple proxy
179
-
180
  return {
181
  "hallucination_score": (halluc_score, explanation),
182
- "assumption_control": (assumption_score, "Inverse of hallucination score (simplified)."),
183
  }
184
 
 
185
  # Single Evaluation # Inputs-->> Prompt, Agent Response, Expected Answer(Optional), Agent Name and Task type( General, QA, Summarizaton)etc
186
  def evaluate_single(self, prompt: str, response: str, expected_answer: Optional[str] = None, task_type: str = "general") -> Dict:
187
 
@@ -194,15 +93,11 @@ class AetherScoreEvaluator:
194
 
195
  scores, reasons = {}, {}
196
 
197
- # hallucination and assumption controle
198
- halluc_results = self.hallucination_score(prompt, response)
199
- scores['hallucination_score'], reasons['hallucination_score'] = halluc_results['hallucination_score']
200
- scores['assumption_control'], reasons['assumption_control'] = halluc_results['assumption_control']
201
-
202
- # llm judge score
203
  llm_judge_results = self._evaluate_with_llm_judge(prompt, response)
204
- scores['llm_judge_score'], reasons['llm_judge_score'] = llm_judge_results['llm_judge_score']
205
-
 
206
  # Evaluating Instruction Following, Coherence and Accuracy
207
  scores['instruction_following'], reasons['instruction_following'] = self._evaluate_instruction_following(prompt, response)
208
  scores['coherence'], reasons['coherence'] = self._evaluate_coherence(response)
 
11
  from datetime import datetime
12
  import concurrent.futures
13
  import random
 
 
 
 
 
14
 
15
  class AetherScoreEvaluator:
16
  def __init__(self):
 
23
  self.nlp = spacy.load("en_core_web_sm")
24
 
25
  # LLM Judge Model
26
+ self.judge_model = pipeline(
27
+ "text2text-generation",
28
+ model="google/flan-t5-base",
29
+ device=-1 # 0 for GPU
30
+ )
 
 
 
 
 
 
 
 
 
 
31
 
32
  # Sentence Transformer for Sentence ----> Embedding
33
  self.sentence_model = SentenceTransformer('all-MiniLM-L6-v2')
34
 
35
  # Scoring weights # Domain Specific weights can be added for better results
36
+ self.weights = {'instruction_following': 0.25, 'hallucination_score': 0.20,
37
+ 'assumption_control': 0.20, 'coherence': 0.20, 'accuracy': 0.15}
38
+
 
 
 
 
 
39
  # In-memory cache
40
  self.cache = {}
41
 
42
  # LLM Judge
43
  def _evaluate_with_llm_judge(self, prompt: str, response: str) -> Dict:
44
+
45
  print("Using HF model LLM Judge...")
46
 
47
  query = (
48
  f"Prompt: {prompt}\n"
49
  f"Response: {response}\n\n"
50
+ "Return ONLY a valid JSON object in this exact format:\n"
51
  "{\n"
52
+ " \"hallucination_score\": <float between 0 and 1>,\n"
53
+ " \"assumption_control\": <float between 0 and 1>,\n"
54
+ " \"explanation\": \"<one or two sentences>\"\n"
55
+ "}"
56
  )
57
 
58
  try:
59
  result = self.judge_model(query, max_new_tokens=128, truncation=True)
60
  output = result[0]['generated_text']
61
  except Exception as e:
62
+ return {
63
+ "hallucination_score": (0.1, f"HF model failed: {e}"),
64
+ "assumption_control": (0.1, f"HF model failed: {e}")
65
+ }
66
 
67
+ # Default values
68
+ halluc_score, assumption_score, explanation = 0.5, 0.5, output
 
69
 
70
  try:
71
  parsed = json.loads(output.strip())
72
+ halluc_score = float(parsed.get("hallucination_score", halluc_score))
73
+ assumption_score = float(parsed.get("assumption_control", assumption_score))
74
  explanation = parsed.get("explanation", explanation)
75
  except Exception:
76
  pass # fallback to defaults
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  return {
79
  "hallucination_score": (halluc_score, explanation),
80
+ "assumption_control": (assumption_score, explanation)
81
  }
82
 
83
+
84
  # Single Evaluation # Inputs-->> Prompt, Agent Response, Expected Answer(Optional), Agent Name and Task type( General, QA, Summarizaton)etc
85
  def evaluate_single(self, prompt: str, response: str, expected_answer: Optional[str] = None, task_type: str = "general") -> Dict:
86
 
 
93
 
94
  scores, reasons = {}, {}
95
 
96
+ # Taking Back scores and reasons of hallucination and assumption control from LLM Judge
 
 
 
 
 
97
  llm_judge_results = self._evaluate_with_llm_judge(prompt, response)
98
+ scores['hallucination_score'], reasons['hallucination_score'] = llm_judge_results['hallucination_score']
99
+ scores['assumption_control'], reasons['assumption_control'] = llm_judge_results['assumption_control']
100
+
101
  # Evaluating Instruction Following, Coherence and Accuracy
102
  scores['instruction_following'], reasons['instruction_following'] = self._evaluate_instruction_following(prompt, response)
103
  scores['coherence'], reasons['coherence'] = self._evaluate_coherence(response)