AloysiusJoy commited on
Commit
3087a05
·
verified ·
1 Parent(s): b35e412

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +196 -0
  2. requirements.txt +4 -0
app.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import joblib
2
+ import json
3
+ import gradio as gr
4
+ import numpy as np
5
+ import os
6
+
7
+ # -----------------------------
8
+ # Load models
9
+ # -----------------------------
10
+ BASE_DIR = "saved_models"
11
+
12
+ human_ai_model = joblib.load(os.path.join(BASE_DIR, "human_ai_model.joblib"))
13
+ attrib_model = joblib.load(os.path.join(BASE_DIR, "attrib_model.joblib"))
14
+
15
+ # Load config (optional but recommended)
16
+ config_path = os.path.join(BASE_DIR, "config.json")
17
+ if os.path.exists(config_path):
18
+ with open(config_path) as f:
19
+ CONFIG = json.load(f)
20
+ else:
21
+ CONFIG = {
22
+ "min_words": 30,
23
+ "human_confidence_threshold": 0.5,
24
+ "attrib_confidence_gap": 0.15
25
+ }
26
+
27
+ # -----------------------------
28
+ # Helper functions
29
+ # -----------------------------
30
+
31
+ def classify_with_confidence(text, model, top_k=3):
32
+ scores = model.decision_function([text])[0]
33
+ classes = model.classes_
34
+
35
+ ranked = sorted(
36
+ zip(classes, scores),
37
+ key=lambda x: x[1],
38
+ reverse=True
39
+ )
40
+ return ranked[:top_k]
41
+
42
+
43
+ def safe_attribution(text):
44
+ ranked = classify_with_confidence(text, attrib_model)
45
+
46
+ best_model, best_score = ranked[0]
47
+ second_model, second_score = ranked[1]
48
+
49
+ confidence_gap = best_score - second_score
50
+
51
+ return {
52
+ "predicted_model": best_model,
53
+ "confidence_gap": round(confidence_gap, 3),
54
+ "top_candidates": [
55
+ (m, round(s, 3)) for m, s in ranked
56
+ ]
57
+ }
58
+
59
+
60
+ def human_ai_decision(text):
61
+ score = human_ai_model.decision_function([text])[0]
62
+ label = human_ai_model.predict([text])[0]
63
+
64
+ return {
65
+ "label": label,
66
+ "confidence": round(abs(score), 3),
67
+ "raw_score": round(score, 3)
68
+ }
69
+
70
+
71
+ def full_classify(text):
72
+ text = text.strip()
73
+ word_count = len(text.split())
74
+
75
+ # -------- Guardrails --------
76
+ if word_count < CONFIG["min_words"]:
77
+ return {
78
+ "final_label": "uncertain",
79
+ "reason": f"Text too short ({word_count} words)"
80
+ }
81
+
82
+ # -------- Stage 1: Human vs AI --------
83
+ hvai = human_ai_decision(text)
84
+
85
+ if hvai["label"] == "human" and hvai["confidence"] >= CONFIG["human_confidence_threshold"]:
86
+ return {
87
+ "final_label": "human",
88
+ "details": hvai
89
+ }
90
+
91
+ # -------- Stage 2: Attribution --------
92
+ attrib = safe_attribution(text)
93
+
94
+ if attrib["predicted_model"] == "human_story":
95
+ return {
96
+ "final_label": "human",
97
+ "details": attrib
98
+ }
99
+
100
+ if attrib["confidence_gap"] < CONFIG["attrib_confidence_gap"]:
101
+ return {
102
+ "final_label": "ai_uncertain",
103
+ "details": attrib
104
+ }
105
+
106
+ return {
107
+ "final_label": "ai",
108
+ "predicted_model": attrib["predicted_model"],
109
+ "details": attrib
110
+ }
111
+
112
+ # -----------------------------
113
+ # Gradio Interface
114
+ # -----------------------------
115
+
116
+ # -----------------------------
117
+ # Gradio UI (Improved)
118
+ # -----------------------------
119
+
120
+ with gr.Blocks() as demo:
121
+ gr.Markdown(
122
+ """
123
+ # AI Text Attribution Demo
124
+
125
+ This tool analyzes a piece of text to determine:
126
+
127
+ **1.** Whether it is likely **human-written or AI-generated**
128
+ **2.** If AI-generated, which **language model style it most closely resembles**
129
+
130
+ Results are **probabilistic**. Some outputs are marked **uncertain** to avoid overclaiming.
131
+ """
132
+ )
133
+
134
+ with gr.Row():
135
+ with gr.Column(scale=2):
136
+ text_input = gr.Textbox(
137
+ lines=12,
138
+ label="Input Text",
139
+ placeholder="Paste text here (minimum ~30 words recommended)..."
140
+ )
141
+ analyze_btn = gr.Button("Analyze Text")
142
+
143
+ with gr.Column(scale=1):
144
+ headline = gr.Markdown("### Result will appear here")
145
+ explanation = gr.Markdown("")
146
+
147
+ gr.Markdown("### 🔍 Detailed Model Output")
148
+ raw_output = gr.JSON(label="Raw Output")
149
+
150
+ def ui_wrapper(text):
151
+ result = full_classify(text)
152
+
153
+ # ---------- Headline & explanation ----------
154
+ if result["final_label"] == "human":
155
+ headline_md = "## ✅ Likely Human-Written"
156
+ explanation_md = (
157
+ "The text shows high stylistic variability and does not strongly match "
158
+ "known AI generation patterns."
159
+ )
160
+
161
+ elif result["final_label"] == "ai":
162
+ model = result["predicted_model"]
163
+ gap = result["details"]["confidence_gap"]
164
+
165
+ headline_md = "## 🤖 Likely AI-Generated"
166
+ explanation_md = (
167
+ f"The text most closely resembles **{model}**.\n\n"
168
+ f"Confidence gap: **{gap}** — higher values indicate stronger attribution confidence."
169
+ )
170
+
171
+ elif result["final_label"] == "ai_uncertain":
172
+ headline_md = "## ⚠️ AI-Generated (Uncertain Attribution)"
173
+ explanation_md = (
174
+ "The text appears AI-generated, but its style is similar to multiple models.\n\n"
175
+ "A confident attribution cannot be made."
176
+ )
177
+
178
+ else:
179
+ headline_md = "## ❓ Uncertain"
180
+ explanation_md = result.get(
181
+ "reason",
182
+ "The system could not make a reliable determination."
183
+ )
184
+
185
+ return headline_md, explanation_md, result
186
+
187
+ analyze_btn.click(
188
+ fn=ui_wrapper,
189
+ inputs=text_input,
190
+ outputs=[headline, explanation, raw_output]
191
+ )
192
+
193
+
194
+ if __name__ == "__main__":
195
+ demo.launch()
196
+
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio
2
+ scikit-learn
3
+ joblib
4
+ numpy