Spaces:
Sleeping
Sleeping
mokshad
Fix model accuracy: correct BERT label, reduce temperature, add perplexity signal, fix TTR for short texts
e518cdb | # core/aggregator.py | |
| # Combines BERT score + stylometric score + perplexity into a final probability. | |
| from core.stylometrics import compute_stylometric_score | |
| from core.explainer import find_ai_phrases, generate_explanation | |
| from core.perplexity_scorer import perplexity_scorer | |
| from config.settings import settings | |
| def aggregate_scores( | |
| bert_result: dict, | |
| text: str, | |
| sentence_scores: list[dict], | |
| ) -> dict: | |
| """ | |
| Three-signal hybrid: | |
| - BERT (60%): neural classifier fine-tuned on AI vs human text | |
| - Perplexity (20%): GPT-2 perplexity — model-agnostic, works for any LLM | |
| - Stylometrics (20%): structural writing features | |
| Perplexity replaces pure BERT weight because it catches AI text that | |
| slop-detector-bert wasn't trained on (modern GPT-4/Claude output). | |
| """ | |
| stylo_result = compute_stylometric_score(text) | |
| stylo_score = stylo_result["stylometric_ai_score"] | |
| bert_prob = bert_result["ai_probability"] | |
| # Perplexity signal — lower perplexity = more predictable = more AI-like | |
| raw_perplexity = perplexity_scorer.get_perplexity(text) | |
| perplexity_score = perplexity_scorer.perplexity_to_ai_score(raw_perplexity) | |
| bert_weight = 0.60 | |
| perplexity_weight = 0.20 | |
| stylo_weight = 0.20 | |
| final_probability = ( | |
| bert_prob * bert_weight + | |
| perplexity_score * perplexity_weight + | |
| stylo_score * stylo_weight | |
| ) | |
| final_probability = round(max(0.0, min(1.0, final_probability)), 4) | |
| ai_phrases = find_ai_phrases(text) | |
| explanation = generate_explanation( | |
| ai_probability=final_probability, | |
| stylometric_data=stylo_result, | |
| sentence_scores=sentence_scores, | |
| ai_phrases=ai_phrases, | |
| perplexity=raw_perplexity, | |
| ) | |
| is_ai = final_probability >= settings.CONFIDENCE_THRESHOLD | |
| return { | |
| "ai_probability": final_probability, | |
| "is_ai": is_ai, | |
| "verdict": explanation["verdict"], | |
| "confidence_threshold": settings.CONFIDENCE_THRESHOLD, | |
| "scores": { | |
| "bert_score": bert_prob, | |
| "stylometric_score": round(stylo_score, 4), | |
| "bert_weight": bert_weight, | |
| "stylometric_weight": stylo_weight, | |
| }, | |
| "stylometric_features": stylo_result["features"], | |
| "sentence_scores": sentence_scores, | |
| "explanation": explanation, | |
| "chunks_analyzed": bert_result.get("chunks_analyzed", 1), | |
| "perplexity": round(raw_perplexity, 2) if raw_perplexity is not None else None, | |
| "perplexity_score": round(perplexity_score, 4), | |
| } |