Spaces:
Paused
Paused
File size: 6,822 Bytes
3abf967 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | """
predict_relevance.py - Real-Time Inference Module for Model A (Evidence Relevance).
Input: claim + candidate evidence
Output: RELEVANT (1) or NOT_RELEVANT (0) with calibrated probabilities and decision threshold.
Hardware: Strictly CUDA accelerated.
"""
import os
import sys
import json
import argparse
from typing import Dict, Any, Optional
import torch
import torch.nn.functional as F
from transformers import AutoTokenizer, AutoModelForSequenceClassification
DEFAULT_MODEL_DIR = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"models",
"evidence_relevance",
"roberta-base"
)
_CACHED_MODEL = None
_CACHED_TOKENIZER = None
_CACHED_DEVICE = None
_CACHED_THRESHOLD = 0.35
def load_relevance_model(model_dir: str = DEFAULT_MODEL_DIR):
global _CACHED_MODEL, _CACHED_TOKENIZER, _CACHED_DEVICE, _CACHED_THRESHOLD
if _CACHED_MODEL is not None and _CACHED_TOKENIZER is not None:
return _CACHED_MODEL, _CACHED_TOKENIZER, _CACHED_DEVICE, _CACHED_THRESHOLD
if not torch.cuda.is_available():
error_msg = (
"[CRITICAL ERROR] CUDA is NOT available for Model A inference!\n"
"Per user configuration, inference requires an active NVIDIA GPU with CUDA.\n"
"Silent fallback to CPU is disabled."
)
print(error_msg, file=sys.stderr)
raise RuntimeError("CUDA is required for TruthLens AI Model A inference.")
device = torch.device("cuda")
if not os.path.exists(model_dir):
raise FileNotFoundError(f"Model A directory not found at: {model_dir}")
# Load selected threshold from training_config.json
config_path = os.path.join(model_dir, "training_config.json")
threshold = 0.35
if os.path.exists(config_path):
try:
with open(config_path, "r", encoding="utf-8") as f:
cfg = json.load(f)
threshold = cfg.get("selected_threshold", 0.35)
except Exception:
pass
tokenizer = AutoTokenizer.from_pretrained(model_dir)
model = AutoModelForSequenceClassification.from_pretrained(model_dir)
model.to(device)
model.eval()
_CACHED_MODEL = model
_CACHED_TOKENIZER = tokenizer
_CACHED_DEVICE = device
_CACHED_THRESHOLD = threshold
return model, tokenizer, device, threshold
def predict_relevance(
claim: str,
evidence: str,
model_dir: str = DEFAULT_MODEL_DIR,
threshold: Optional[float] = None
) -> Dict[str, Any]:
"""
Classifies candidate evidence as RELEVANT or NOT_RELEVANT for a given claim.
"""
model, tokenizer, device, default_threshold = load_relevance_model(model_dir)
decision_threshold = threshold if threshold is not None else default_threshold
claim_clean = str(claim).strip()
evidence_clean = str(evidence).strip() if evidence else "None"
input_text = f"[CLAIM] {claim_clean} [SEP] {evidence_clean}"
inputs = tokenizer(
input_text,
max_length=256,
padding=True,
truncation=True,
return_tensors="pt"
)
inputs = {k: v.to(device) for k, v in inputs.items()}
with torch.no_grad():
with torch.amp.autocast("cuda", dtype=torch.float16):
outputs = model(**inputs)
probs = F.softmax(outputs.logits, dim=-1).squeeze(0).cpu().numpy()
prob_not_rel = float(round(float(probs[0]), 4))
prob_rel = float(round(float(probs[1]), 4))
is_relevant = prob_rel >= decision_threshold
predicted_label = "RELEVANT" if is_relevant else "NOT_RELEVANT"
confidence = prob_rel if is_relevant else prob_not_rel
return {
"label": predicted_label,
"confidence": round(float(confidence), 4),
"probability_relevant": prob_rel,
"probability_not_relevant": prob_not_rel,
"threshold": decision_threshold,
}
def run_demo_tests():
print("=" * 70)
print(" TRUTHLENS AI — MODEL A (EVIDENCE RELEVANCE) INFERENCE TEST")
print("=" * 70)
test_cases = [
{
"description": "Example 1: Clearly Relevant Evidence",
"claim": "40mg/day dosage of folic acid and 2mg/day dosage of vitamin B12 does not affect chronic kidney disease (CKD) progression.",
"evidence": "Treatment with high doses of folic acid and B vitamins did not improve survival or reduce the incidence of vascular disease in patients with advanced chronic kidney disease or end-stage renal disease.",
"expected": "RELEVANT"
},
{
"description": "Example 2: Clearly Irrelevant Evidence",
"claim": "40mg/day dosage of folic acid and 2mg/day dosage of vitamin B12 does not affect chronic kidney disease (CKD) progression.",
"evidence": "Jupiter is the fifth planet from the Sun and the largest in the Solar System, primarily composed of hydrogen.",
"expected": "NOT_RELEVANT"
},
{
"description": "Example 3: Difficult Hard-Negative Evidence (Same document topic/keywords, but not answering the claim)",
"claim": "40mg/day dosage of folic acid and 2mg/day dosage of vitamin B12 does not affect chronic kidney disease (CKD) progression.",
"evidence": "Plasma homocysteine levels are elevated in more than 85% of patients with chronic kidney disease, but the cardiovascular benefits of lowering homocysteine remain an active area of nephrology research.",
"expected": "NOT_RELEVANT"
}
]
for i, tc in enumerate(test_cases, 1):
print(f"\n--- {tc['description']} ---")
print(f"Claim : {tc['claim']}")
print(f"Evidence : {tc['evidence']}")
res = predict_relevance(tc["claim"], tc["evidence"])
print("Prediction Result:")
print(json.dumps(res, indent=2))
print(f"Verdict Match: {'CORRECT' if res['label'] == tc['expected'] else 'INCORRECT'} (Expected: {tc['expected']})")
print("\n" + "=" * 70)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="TruthLens AI — Model A Evidence Relevance Predictor")
parser.add_argument("--claim", type=str, default=None, help="Claim statement")
parser.add_argument("--evidence", type=str, default=None, help="Candidate evidence passage")
parser.add_argument("--model_dir", type=str, default=DEFAULT_MODEL_DIR, help="Model directory")
parser.add_argument("--threshold", type=float, default=None, help="Decision threshold")
parser.add_argument("--demo", action="store_true", help="Run 3 standard test cases")
args = parser.parse_args()
if args.claim and args.evidence:
result = predict_relevance(args.claim, args.evidence, model_dir=args.model_dir, threshold=args.threshold)
print(json.dumps(result, indent=2))
else:
run_demo_tests()
|