euler03 commited on
Commit
d50bfa3
·
verified ·
1 Parent(s): 2be9e02

correct app.py

Browse files
Files changed (1) hide show
  1. app.py +214 -141
app.py CHANGED
@@ -1,155 +1,228 @@
 
1
  import gradio as gr
2
  import torch
3
- from transformers import AutoModelForSequenceClassification, AutoTokenizer
 
4
 
 
 
 
5
  device = "cuda" if torch.cuda.is_available() else "cpu"
6
- print(f"Use of: {device}")
7
 
8
- # Available models
9
- MODELS = {
10
- "Aubins/distil-bumble-bert": "Aubins/distil-bumble-bert",
11
- # Add models here
12
- }
 
13
 
14
- # Labels mapping
15
- id2label = {0: "BIASED", 1: "NEUTRAL"}
16
- label2id = {"BIASED": 0, "NEUTRAL": 1}
 
 
17
 
18
- # Cache for loaded models
19
- loaded_models = {}
 
 
 
 
20
 
21
- def load_model(model_name: str):
22
- """
23
- Load a model and its tokenizer if not already cached
24
-
25
- Args:
26
- model_name (str): The name of the model to load.
27
-
28
- Returns:
29
- model, tokenizer: The loaded model and tokenizer
30
- """
31
- if model_name not in loaded_models:
32
- try:
33
- model_path = MODELS[model_name]
34
-
35
- # Load model and tokenizer
36
- model = AutoModelForSequenceClassification.from_pretrained(
37
- model_path,
38
- num_labels=2,
39
- id2label=id2label,
40
- label2id=label2id
41
- ).to(device)
42
- tokenizer = AutoTokenizer.from_pretrained(model_path)
43
-
44
- loaded_models[model_name] = (model, tokenizer)
45
- return model, tokenizer
46
-
47
- except Exception as e:
48
- return f"Error loading model: {str(e)}"
49
-
50
- return loaded_models[model_name]
51
-
52
- def analyze_text(text: str, model_name: str):
53
  """
54
- Analyzes text for bias and neutrality
55
-
56
- Args:
57
- text (str): The text to analyze.
58
- model_name (str): The name of the model to use.
59
-
60
- Returns:
61
- dict, str: A dictionary of confidence scores for each label, and a message.
 
 
62
  """
63
- if not text.strip():
64
- return {"Empty text": 1.0}, "Please enter a text to be analyzed."
65
-
66
- # Load model
67
- result = load_model(model_name)
68
- if isinstance(result, str):
69
- return {"Error": 1.0}, result
70
-
71
- model, tokenizer = result
72
-
73
- try:
74
- # Tokenization
75
- inputs = tokenizer(
76
- text,
77
- return_tensors="pt",
78
- truncation=True,
79
- padding=True,
80
- max_length=512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
- inputs = {k: v.to(device) for k, v in inputs.items()}
84
-
85
- # Prediction
86
- model.eval()
87
- with torch.no_grad():
88
- outputs = model(**inputs)
89
-
90
- logits = outputs.logits[0]
91
- probabilities = torch.nn.functional.softmax(logits, dim=0)
92
- predicted_class = torch.argmax(logits).item()
93
-
94
- predicted_label = id2label[predicted_class]
95
-
96
- confidence_map = {
97
- "Neutral": probabilities[1].item(),
98
- "Biased": probabilities[0].item()
99
- }
100
-
101
- status = "neutral" if predicted_class == 1 else "biased"
102
- confidence = probabilities[predicted_class].item()
103
- message = f"This text is classified as {status} with a confidence of {confidence:.2%}."
104
-
105
- return confidence_map, message
106
-
107
- except Exception as e:
108
- return {"Error": 1.0}, f"Analysis error: {str(e)}"
109
-
110
- # Interface Gradio
111
- with gr.Blocks(title="Objectivity detector in texts") as app:
112
- gr.Markdown("# Objectivity detector in texts")
113
- gr.Markdown("This application analyzes a text to determine whether it is neutral or biased.")
114
-
115
- with gr.Row():
116
- with gr.Column(scale=3):
117
- model_dropdown = gr.Dropdown(
118
- choices=list(MODELS.keys()),
119
- label="Select a model",
120
- value=list(MODELS.keys())[0]
121
- )
122
-
123
- text_input = gr.Textbox(
124
- placeholder="Enter the text to be analyzed...",
125
- label="Text to analyze",
126
- lines=10
127
- )
128
-
129
- analyze_button = gr.Button("Analyze the text")
130
-
131
- with gr.Column(scale=2):
132
- confidence_output = gr.Label(
133
- label="Analysis results",
134
- num_top_classes=2,
135
- show_label=True
136
- )
137
-
138
- result_message = gr.Textbox(label="Detailed results")
139
-
140
- analyze_button.click(
141
- analyze_text,
142
- inputs=[text_input, model_dropdown],
143
- outputs=[confidence_output, result_message]
144
  )
145
-
146
- gr.Markdown("## How to use this application")
147
  gr.Markdown("""
148
- 1. Select an analysis model from the drop-down menu
149
- 2. Enter or paste the text to be analyzed into the text box (in English only).
150
- 3. Click on “Analyze the text”.
151
- 4. The result is displayed with a visual indication
152
- """)
153
-
154
- if __name__ == "__main__":
155
- app.launch()
 
1
+ import os
2
  import gradio as gr
3
  import torch
4
+ from llama_cpp import Llama
5
+ from transformers import AutoModelForMultipleChoice, AutoTokenizer
6
 
7
+ # -------------------------------------------------------
8
+ # GPU setup
9
+ # -------------------------------------------------------
10
  device = "cuda" if torch.cuda.is_available() else "cpu"
 
11
 
12
+ # -------------------------------------------------------
13
+ # Load LLaMA Locally (for model-input generation)
14
+ # -------------------------------------------------------
15
+ LLAMA_MODEL_PATH = "/home/euler03/projects/bias/bias-detection/bias-detection/models/llama-2-7b-chat.Q4_K_M.gguf"
16
+ if not os.path.exists(LLAMA_MODEL_PATH):
17
+ raise FileNotFoundError(f" LLaMA model not found at: {LLAMA_MODEL_PATH}")
18
 
19
+ llm = Llama(
20
+ model_path=LLAMA_MODEL_PATH,
21
+ n_ctx=512,
22
+ n_gpu_layers=100 # adjust if needed
23
+ )
24
 
25
+ # -------------------------------------------------------
26
+ # Load BBQ Fine-Tuned BERT Model & Tokenizer (multiple-choice as fine tuned int he bbq model)
27
+ # -------------------------------------------------------
28
+ BBQ_MODEL = "euler03/bbq-distil_bumble_bert"
29
+ bbq_tokenizer = AutoTokenizer.from_pretrained(BBQ_MODEL)
30
+ bbq_model = AutoModelForMultipleChoice.from_pretrained(BBQ_MODEL).to(device)
31
 
32
+ # -------------------------------------------------------
33
+ # List of Topics
34
+ # -------------------------------------------------------
35
+ TOPICS = [
36
+ "Artificial Intelligence in Healthcare", "Climate Change and Renewable Energy",
37
+ "Immigration Policies in the USA", "Social Media's Role in Elections",
38
+ "The Ethics of Genetic Engineering", "Universal Basic Income Pros and Cons",
39
+ "Impact of AI on Jobs", "Gender Pay Gap in the Workplace",
40
+ "Government Surveillance and Privacy", "Cryptocurrency Regulation",
41
+ "Censorship in Journalism", "Nuclear Energy as a Climate Solution",
42
+ "Effects of Misinformation on Society", "Affirmative Action in Universities",
43
+ "Automation and Its Effect on the Workforce", "The Role of Religion in Politics",
44
+ "Healthcare Access in Rural Areas", "The Rise of Nationalism in Politics",
45
+ "Police Use of Facial Recognition", "Space Exploration and Government Funding"
46
+ ]
47
+
48
+ # -------------------------------------------------------
49
+ # 5 Generation: Context, Question & 3 Answers using LLaMA
50
+ # -------------------------------------------------------
51
+ def generate_context_question_answers(topic):
 
 
 
 
 
 
 
 
 
 
 
 
52
  """
53
+ Use LLaMA (chat-style prompt) to generate:
54
+ - A short, neutral context about the topic.
55
+ - A question that tests bias on the topic.
56
+ - Three possible answers (Answer0, Answer1, Answer2).
57
+ The output is expected in the following format:
58
+ Context: <...>
59
+ Question: <...>
60
+ Answer0: <...>
61
+ Answer1: <...>
62
+ Answer2: <...>
63
  """
64
+ system_prompt = "You are a helpful AI assistant that strictly follows user instructions."
65
+ user_prompt = f"""
66
+ Please write:
67
+ Context: <2-3 sentences about {topic}>
68
+ Question: <a question that tests bias on {topic}>
69
+ Answer0: <possible answer #1>
70
+ Answer1: <possible answer #2>
71
+ Answer2: <possible answer #3>
72
+
73
+ Use exactly these labels and no extra text.
74
+ """
75
+ chat_prompt = f"""[INST] <<SYS>>
76
+ {system_prompt}
77
+ <</SYS>>
78
+
79
+ {user_prompt}
80
+ [/INST]"""
81
+
82
+ response = llm(
83
+ chat_prompt,
84
+ max_tokens=256,
85
+ temperature=1.0,
86
+ echo=False
87
+ )
88
+ print("Raw LLaMA Output:", response)
89
+ if "choices" in response and len(response["choices"]) > 0:
90
+ text_output = response["choices"][0]["text"].strip()
91
+ else:
92
+ text_output = "[Error: LLaMA did not generate a response]"
93
+ print("Processed LLaMA Output:", text_output)
94
+
95
+ # Initialize with defaults comme ca on teste si generation works
96
+ context_line = "[No context generated]"
97
+ question_line = "[No question generated]"
98
+ ans0_line = "[No answer0 generated]"
99
+ ans1_line = "[No answer1 generated]"
100
+ ans2_line = "[No answer2 generated]"
101
+
102
+ lines = [line.strip() for line in text_output.split("\n") if line.strip()]
103
+ for line in lines:
104
+ lower_line = line.lower()
105
+ if lower_line.startswith("context:"):
106
+ context_line = line.split(":", 1)[1].strip()
107
+ elif lower_line.startswith("question:"):
108
+ question_line = line.split(":", 1)[1].strip()
109
+ elif lower_line.startswith("answer0:"):
110
+ ans0_line = line.split(":", 1)[1].strip()
111
+ elif lower_line.startswith("answer1:"):
112
+ ans1_line = line.split(":", 1)[1].strip()
113
+ elif lower_line.startswith("answer2:"):
114
+ ans2_line = line.split(":", 1)[1].strip()
115
+
116
+ return context_line, question_line, ans0_line, ans1_line, ans2_line
117
+
118
+ # -------------------------------------------------------
119
+ # Classification: Run BBQ Model (Multiple-Choice)
120
+ # -------------------------------------------------------
121
+ def classify_multiple_choice(context, question, ans0, ans1, ans2):
122
+ inputs = [f"{question} {ans}" for ans in (ans0, ans1, ans2)]
123
+ contexts = [context, context, context]
124
+
125
+ encodings = bbq_tokenizer(
126
+ inputs,
127
+ contexts,
128
+ truncation=True,
129
+ padding="max_length",
130
+ max_length=128,
131
+ return_tensors="pt"
132
+ ).to(device)
133
+
134
+ bbq_model.eval()
135
+ with torch.no_grad():
136
+ outputs = bbq_model(**{k: v.unsqueeze(0) for k, v in encodings.items()})
137
+ logits = outputs.logits[0]
138
+ probs = torch.softmax(logits, dim=-1)
139
+ pred_idx = torch.argmax(probs).item()
140
+ all_answers = [ans0, ans1, ans2]
141
+ prob_dict = {all_answers[i]: float(probs[i].item()) for i in range(3)}
142
+ predicted_answer = all_answers[pred_idx]
143
+ return predicted_answer, prob_dict
144
+
145
+ # -------------------------------------------------------
146
+ # Assess Objectivity: Compare User's Choice to Model's Prediction
147
+ # -------------------------------------------------------
148
+ def assess_objectivity(context, question, ans0, ans1, ans2, user_choice):
149
+
150
+ predicted_answer, prob_dict = classify_multiple_choice(context, question, ans0, ans1, ans2)
151
+ if user_choice == predicted_answer:
152
+ assessment = (
153
+ f"Your choice matches the model's prediction ('{predicted_answer}').\n"
154
+ "This indicates an objective response."
155
  )
156
+ else:
157
+ assessment = (
158
+ f"Your choice ('{user_choice}') does not match the model's prediction ('{predicted_answer}').\n"
159
+ "This suggests a deviation from the objective standard."
160
+ )
161
+ return assessment, prob_dict
162
+
163
+ # -------------------------------------------------------
164
+ # Build the Gradio Interface
165
+ # -------------------------------------------------------
166
+ with gr.Blocks() as demo:
167
+ gr.Markdown("# 🧠 Bias Detection: Assessing Objectivity")
168
+ gr.Markdown("""
169
+ **Steps:**
170
+ 1. **Select a topic** from the dropdown.
171
+ 2. Click **"Generate Context, Question & Answers"** to generate a scenario.
172
+ 3. **Review** the generated context, question, and 3 candidate answers.
173
+ 4. **Select your answer** from the radio options.
174
+ 5. Click **"Assess Objectivity"** to see the model's evaluation.
175
+ """)
176
+ # Topic selection
177
+ topic_dropdown = gr.Dropdown(choices=TOPICS, label="Select a Topic")
178
+
179
+ # Outputs from LLaMA generation
180
+ context_box = gr.Textbox(label="Generated Context", interactive=False)
181
+ question_box = gr.Textbox(label="Generated Question", interactive=False)
182
+ ans0_box = gr.Textbox(label="Generated Answer 0", interactive=False)
183
+ ans1_box = gr.Textbox(label="Generated Answer 1", interactive=False)
184
+ ans2_box = gr.Textbox(label="Generated Answer 2", interactive=False)
185
+
186
+ # User selection: Choose one answer from the generated answers
187
+ user_choice_radio = gr.Radio(choices=[], label="Select Your Answer")
188
 
189
+ # Assessment outputs
190
+ assessment_box = gr.Textbox(label="Objectivity Assessment", interactive=False)
191
+ probabilities_box = gr.JSON(label="Confidence Probabilities")
192
+
193
+ # Buttons
194
+ generate_button = gr.Button("Generate Context, Question & Answers")
195
+ assess_button = gr.Button("Assess Objectivity")
196
+
197
+ # Callback 1: Generate with LLaMA
198
+ def on_generate(topic):
199
+ ctx, q, a0, a1, a2 = generate_context_question_answers(topic)
200
+ # Update the radio button choices with the generated answers
201
+ return ctx, q, a0, a1, a2, gr.update(choices=[a0, a1, a2], value=None)
202
+ generate_button.click(
203
+ fn=on_generate,
204
+ inputs=[topic_dropdown],
205
+ outputs=[context_box, question_box, ans0_box, ans1_box, ans2_box, user_choice_radio]
206
+ )
207
+
208
+ # Callback 2: Assess objectivity
209
+ def on_assess(ctx, q, a0, a1, a2, user_choice):
210
+ if user_choice is None or user_choice == "":
211
+ return "Please select one of the generated answers.", {}
212
+ assessment, probs = assess_objectivity(ctx, q, a0, a1, a2, user_choice)
213
+ return assessment, probs
214
+ assess_button.click(
215
+ fn=on_assess,
216
+ inputs=[context_box, question_box, ans0_box, ans1_box, ans2_box, user_choice_radio],
217
+ outputs=[assessment_box, probabilities_box]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  )
219
+
 
220
  gr.Markdown("""
221
+ ### How It Works:
222
+ - **LLaMA** generates a scenario (context, question, and three candidate answers).
223
+ - You **select** one answer that you think is most objective.
224
+ - The **BBQ model** classifies the same scenario and outputs the answer it deems most objective along with confidence scores.
225
+ - The app **compares** your choice with the model’s prediction and provides an objectivity assessment.
226
+ """)
227
+
228
+ demo.launch()