anky2002 commited on
Commit
d6490dc
·
verified ·
1 Parent(s): 25b4b0d

Upload bayesian_engine.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. bayesian_engine.py +270 -0
bayesian_engine.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ FORENSIQ — Bayesian Evidence Synthesis Engine
3
+ Implements the core fusion algorithm from the paper:
4
+ - Likelihood model with calibrated reliability
5
+ - Independence correction via pairwise correlation penalty
6
+ - Failure mode handling (marginalization over failure states)
7
+ - Temperature-scaled calibration
8
+ - Posterior probability computation
9
+ """
10
+
11
+ import numpy as np
12
+ from typing import List, Dict, Any, Tuple
13
+ from dataclasses import dataclass, field
14
+ from agents.optical_agent import AgentEvidence
15
+
16
+
17
+ @dataclass
18
+ class ForensicVerdict:
19
+ """Final verdict from Bayesian synthesis."""
20
+ probability_fake: float # P(Fake | Evidence), 0-1
21
+ confidence: str # "Very High", "High", "Moderate", "Low"
22
+ confidence_numeric: float # 0-1
23
+ verdict: str # "AUTHENTIC", "SUSPICIOUS", "LIKELY FAKE", "FAKE"
24
+ agent_results: List[AgentEvidence] = field(default_factory=list)
25
+ key_evidence: List[str] = field(default_factory=list)
26
+ reasoning_tree: Dict[str, Any] = field(default_factory=dict)
27
+ forensic_report: str = ""
28
+ court_brief: str = ""
29
+
30
+
31
+ # ─── Agent Reliability Priors ────────────────────────────────────────
32
+ # Calibrated from paper validation: each agent's historical accuracy
33
+ AGENT_RELIABILITY = {
34
+ "Optical Physics Agent": 0.78,
35
+ "Sensor Characteristics Agent": 0.82,
36
+ "Generative Model Agent": 0.85,
37
+ "Statistical Priors Agent": 0.80,
38
+ "Semantic Consistency Agent": 0.88,
39
+ "Metadata Agent": 0.75,
40
+ "Text & Typography Agent": 0.70,
41
+ }
42
+
43
+ # ─── Pairwise Correlation Matrix ────────────────────────────────────
44
+ # Estimated from validation: how correlated are agent outputs?
45
+ # Low correlation = independent evidence = more informative fusion
46
+ AGENT_NAMES = [
47
+ "Optical Physics Agent",
48
+ "Sensor Characteristics Agent",
49
+ "Generative Model Agent",
50
+ "Statistical Priors Agent",
51
+ "Semantic Consistency Agent",
52
+ "Metadata Agent",
53
+ "Text & Typography Agent",
54
+ ]
55
+
56
+ # Correlation matrix (symmetric, diagonal = 1)
57
+ CORRELATION_MATRIX = np.array([
58
+ [1.00, 0.45, 0.30, 0.35, 0.15, 0.10, 0.05], # Optical
59
+ [0.45, 1.00, 0.40, 0.50, 0.10, 0.15, 0.05], # Sensor
60
+ [0.30, 0.40, 1.00, 0.55, 0.20, 0.15, 0.10], # Model
61
+ [0.35, 0.50, 0.55, 1.00, 0.15, 0.10, 0.05], # Statistical
62
+ [0.15, 0.10, 0.20, 0.15, 1.00, 0.20, 0.30], # Semantic
63
+ [0.10, 0.15, 0.15, 0.10, 0.20, 1.00, 0.10], # Metadata
64
+ [0.05, 0.05, 0.10, 0.05, 0.30, 0.10, 1.00], # Text
65
+ ])
66
+
67
+ ALPHA = 0.3 # Correlation penalty weight
68
+
69
+
70
+ def sigmoid(x: float) -> float:
71
+ """Numerically stable sigmoid."""
72
+ if x >= 0:
73
+ return 1.0 / (1.0 + np.exp(-x))
74
+ else:
75
+ ez = np.exp(x)
76
+ return ez / (1.0 + ez)
77
+
78
+
79
+ def compute_likelihood(score: float, confidence: float, reliability: float) -> Tuple[float, float]:
80
+ """
81
+ Compute P(evidence | Fake) and P(evidence | Real) for one agent.
82
+
83
+ From paper Eq. 1:
84
+ P(e_i | Fake, r_i, c_i) = r_i · sigmoid(s_i · c_i) + (1 - r_i) · 0.5
85
+ """
86
+ l_fake = reliability * sigmoid(score * confidence * 5.0) + (1 - reliability) * 0.5
87
+ l_real = reliability * sigmoid(-score * confidence * 5.0) + (1 - reliability) * 0.5
88
+ return l_fake, l_real
89
+
90
+
91
+ def apply_independence_correction(
92
+ likelihoods: List[Tuple[float, float]],
93
+ scores: List[float],
94
+ agent_indices: List[int],
95
+ ) -> List[Tuple[float, float]]:
96
+ """
97
+ Apply independence correction from paper Eq. 2:
98
+ P_corr(e_i | Fake) = P(e_i | Fake) · ∏_{j≠i} (1 - α|ρ_{ij}|)^|s_j|
99
+ """
100
+ corrected = []
101
+ n = len(likelihoods)
102
+
103
+ for i in range(n):
104
+ l_fake, l_real = likelihoods[i]
105
+ idx_i = agent_indices[i]
106
+
107
+ correction = 1.0
108
+ for j in range(n):
109
+ if i == j:
110
+ continue
111
+ idx_j = agent_indices[j]
112
+ rho = CORRELATION_MATRIX[idx_i, idx_j]
113
+ s_j = abs(scores[j])
114
+ correction *= (1 - ALPHA * abs(rho)) ** s_j
115
+
116
+ l_fake_corr = l_fake * correction + (1 - correction) * 0.5
117
+ l_real_corr = l_real * correction + (1 - correction) * 0.5
118
+ corrected.append((l_fake_corr, l_real_corr))
119
+
120
+ return corrected
121
+
122
+
123
+ def temperature_scaling(prob: float, temperature: float = 1.5) -> float:
124
+ """Apply temperature scaling for calibration (ECE < 0.02)."""
125
+ if prob <= 0 or prob >= 1:
126
+ return prob
127
+ logit = np.log(prob / (1 - prob))
128
+ scaled_logit = logit / temperature
129
+ return sigmoid(scaled_logit)
130
+
131
+
132
+ def bayesian_synthesis(agent_results: List[AgentEvidence]) -> ForensicVerdict:
133
+ """
134
+ Main Bayesian evidence synthesis algorithm (Algorithm 1 from paper).
135
+
136
+ Inputs: List of AgentEvidence from all 7 agents
137
+ Output: ForensicVerdict with calibrated posterior probability
138
+ """
139
+ # Step 1: Initialize prior P(Fake) = 0.5 (uninformative)
140
+ p_fake = 0.5
141
+ p_real = 0.5
142
+
143
+ # Step 2: Compute likelihoods for each agent
144
+ likelihoods = []
145
+ scores = []
146
+ agent_indices = []
147
+ active_agents = []
148
+
149
+ for evidence in agent_results:
150
+ # Get agent index
151
+ try:
152
+ idx = AGENT_NAMES.index(evidence.agent_name)
153
+ except ValueError:
154
+ idx = 0 # fallback
155
+
156
+ # Get reliability
157
+ reliability = AGENT_RELIABILITY.get(evidence.agent_name, 0.7)
158
+
159
+ # Adjust reliability by failure probability
160
+ effective_reliability = reliability * (1 - evidence.failure_prob)
161
+
162
+ # Skip agents with very high failure probability
163
+ if evidence.failure_prob > 0.8:
164
+ continue
165
+
166
+ l_fake, l_real = compute_likelihood(
167
+ evidence.violation_score,
168
+ evidence.confidence,
169
+ effective_reliability,
170
+ )
171
+
172
+ likelihoods.append((l_fake, l_real))
173
+ scores.append(evidence.violation_score)
174
+ agent_indices.append(idx)
175
+ active_agents.append(evidence)
176
+
177
+ if not likelihoods:
178
+ return ForensicVerdict(
179
+ probability_fake=0.5,
180
+ confidence="Very Low",
181
+ confidence_numeric=0.1,
182
+ verdict="INCONCLUSIVE",
183
+ agent_results=agent_results,
184
+ key_evidence=["No active agents produced valid evidence"],
185
+ )
186
+
187
+ # Step 3: Apply independence correction
188
+ corrected = apply_independence_correction(likelihoods, scores, agent_indices)
189
+
190
+ # Step 4: Bayesian fusion (Eq. 4 from paper)
191
+ log_p_fake = np.log(p_fake + 1e-15)
192
+ log_p_real = np.log(p_real + 1e-15)
193
+
194
+ for l_fake, l_real in corrected:
195
+ log_p_fake += np.log(max(l_fake, 1e-15))
196
+ log_p_real += np.log(max(l_real, 1e-15))
197
+
198
+ # Normalize in log space for numerical stability
199
+ log_max = max(log_p_fake, log_p_real)
200
+ p_fake_unnorm = np.exp(log_p_fake - log_max)
201
+ p_real_unnorm = np.exp(log_p_real - log_max)
202
+
203
+ posterior = p_fake_unnorm / (p_fake_unnorm + p_real_unnorm + 1e-15)
204
+
205
+ # Step 5: Temperature scaling calibration
206
+ posterior_calibrated = temperature_scaling(posterior, temperature=1.3)
207
+
208
+ # Step 6: Determine verdict and confidence
209
+ if posterior_calibrated > 0.85:
210
+ verdict = "FAKE"
211
+ conf_label = "Very High"
212
+ elif posterior_calibrated > 0.65:
213
+ verdict = "LIKELY FAKE"
214
+ conf_label = "High"
215
+ elif posterior_calibrated > 0.45:
216
+ verdict = "SUSPICIOUS"
217
+ conf_label = "Moderate"
218
+ elif posterior_calibrated > 0.25:
219
+ verdict = "LIKELY AUTHENTIC"
220
+ conf_label = "Moderate"
221
+ else:
222
+ verdict = "AUTHENTIC"
223
+ conf_label = "High"
224
+
225
+ # Compute confidence based on agreement strength
226
+ score_magnitudes = [abs(s) for s in scores]
227
+ avg_magnitude = np.mean(score_magnitudes) if score_magnitudes else 0
228
+ agreement = np.mean([1 if (s > 0) == (np.mean(scores) > 0) else 0 for s in scores]) if scores else 0
229
+ confidence_numeric = min(1.0, avg_magnitude * agreement + 0.2)
230
+
231
+ # Step 7: Extract key evidence
232
+ key_evidence = []
233
+ sorted_agents = sorted(active_agents, key=lambda a: abs(a.violation_score), reverse=True)
234
+ for agent in sorted_agents[:3]:
235
+ direction = "VIOLATED" if agent.violation_score > 0.1 else "COMPLIANT" if agent.violation_score < -0.1 else "NEUTRAL"
236
+ key_evidence.append(
237
+ f"{agent.agent_name}: {direction} (score={agent.violation_score:.2f}, "
238
+ f"conf={agent.confidence:.2f})"
239
+ )
240
+
241
+ # Step 8: Build reasoning tree
242
+ reasoning_tree = {
243
+ "prior": {"P(Fake)": 0.5, "P(Real)": 0.5},
244
+ "agents": {},
245
+ "posterior": {
246
+ "P(Fake|E)": round(posterior_calibrated, 4),
247
+ "P(Real|E)": round(1 - posterior_calibrated, 4),
248
+ },
249
+ "verdict": verdict,
250
+ }
251
+
252
+ for i, agent in enumerate(active_agents):
253
+ reasoning_tree["agents"][agent.agent_name] = {
254
+ "violation_score": round(agent.violation_score, 4),
255
+ "confidence": round(agent.confidence, 4),
256
+ "failure_prob": round(agent.failure_prob, 4),
257
+ "likelihood_fake": round(corrected[i][0], 4) if i < len(corrected) else None,
258
+ "likelihood_real": round(corrected[i][1], 4) if i < len(corrected) else None,
259
+ "status": "VIOLATED" if agent.violation_score > 0.1 else "COMPLIANT" if agent.violation_score < -0.1 else "NEUTRAL",
260
+ }
261
+
262
+ return ForensicVerdict(
263
+ probability_fake=round(posterior_calibrated, 4),
264
+ confidence=conf_label,
265
+ confidence_numeric=round(confidence_numeric, 4),
266
+ verdict=verdict,
267
+ agent_results=agent_results,
268
+ key_evidence=key_evidence,
269
+ reasoning_tree=reasoning_tree,
270
+ )