Spaces:
Sleeping
Sleeping
Ryan
commited on
Commit
·
08f222a
1
Parent(s):
39cf944
update
Browse files- .idea/workspace.xml +1 -6
- _archive/app copy.py +355 -0
- app.py +139 -66
- processors/ngram_analysis.py +89 -70
- ui/analysis_screen.py +26 -16
- visualization/ngram_visualizer.py +69 -76
.idea/workspace.xml
CHANGED
@@ -5,12 +5,7 @@
|
|
5 |
</component>
|
6 |
<component name="ChangeListManager">
|
7 |
<list default="true" id="8e67814c-7f04-433c-ab7a-2b65a1106d4c" name="Changes" comment="">
|
8 |
-
<change afterPath="$PROJECT_DIR$/processors/ngram_analysis.py" afterDir="false" />
|
9 |
-
<change afterPath="$PROJECT_DIR$/visualization/ngram_visualizer.py" afterDir="false" />
|
10 |
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
11 |
-
<change beforePath="$PROJECT_DIR$/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/app.py" afterDir="false" />
|
12 |
-
<change beforePath="$PROJECT_DIR$/ui/analysis_screen.py" beforeDir="false" afterPath="$PROJECT_DIR$/ui/analysis_screen.py" afterDir="false" />
|
13 |
-
<change beforePath="$PROJECT_DIR$/visualization/bow_visualizer.py" beforeDir="false" afterPath="$PROJECT_DIR$/visualization/bow_visualizer.py" afterDir="false" />
|
14 |
</list>
|
15 |
<option name="SHOW_DIALOG" value="false" />
|
16 |
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
@@ -60,7 +55,7 @@
|
|
60 |
<option name="presentableId" value="Default" />
|
61 |
<updated>1745170754325</updated>
|
62 |
<workItem from="1745170755404" duration="245000" />
|
63 |
-
<workItem from="1745172030020" duration="
|
64 |
</task>
|
65 |
<servers />
|
66 |
</component>
|
|
|
5 |
</component>
|
6 |
<component name="ChangeListManager">
|
7 |
<list default="true" id="8e67814c-7f04-433c-ab7a-2b65a1106d4c" name="Changes" comment="">
|
|
|
|
|
8 |
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
|
|
|
|
|
|
9 |
</list>
|
10 |
<option name="SHOW_DIALOG" value="false" />
|
11 |
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
|
55 |
<option name="presentableId" value="Default" />
|
56 |
<updated>1745170754325</updated>
|
57 |
<workItem from="1745170755404" duration="245000" />
|
58 |
+
<workItem from="1745172030020" duration="9539000" />
|
59 |
</task>
|
60 |
<servers />
|
61 |
</component>
|
_archive/app copy.py
ADDED
@@ -0,0 +1,355 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from ui.dataset_input import create_dataset_input, load_example_dataset
|
3 |
+
from ui.analysis_screen import create_analysis_screen, process_analysis_request
|
4 |
+
from visualization.bow_visualizer import process_and_visualize_analysis
|
5 |
+
import nltk
|
6 |
+
import os
|
7 |
+
import json
|
8 |
+
|
9 |
+
# Download necessary NLTK resources function remains unchanged
|
10 |
+
def download_nltk_resources():
|
11 |
+
"""Download required NLTK resources if not already downloaded"""
|
12 |
+
try:
|
13 |
+
# Create nltk_data directory in the user's home directory if it doesn't exist
|
14 |
+
nltk_data_path = os.path.expanduser("~/nltk_data")
|
15 |
+
os.makedirs(nltk_data_path, exist_ok=True)
|
16 |
+
|
17 |
+
# Add this path to NLTK's data path
|
18 |
+
nltk.data.path.append(nltk_data_path)
|
19 |
+
|
20 |
+
# Download required resources
|
21 |
+
resources = ['punkt', 'wordnet', 'stopwords', 'punkt_tab']
|
22 |
+
for resource in resources:
|
23 |
+
try:
|
24 |
+
# Different resources can be in different directories in NLTK
|
25 |
+
locations = [
|
26 |
+
f'tokenizers/{resource}',
|
27 |
+
f'corpora/{resource}',
|
28 |
+
f'taggers/{resource}',
|
29 |
+
f'{resource}'
|
30 |
+
]
|
31 |
+
|
32 |
+
found = False
|
33 |
+
for location in locations:
|
34 |
+
try:
|
35 |
+
nltk.data.find(location)
|
36 |
+
print(f"Resource {resource} already downloaded")
|
37 |
+
found = True
|
38 |
+
break
|
39 |
+
except LookupError:
|
40 |
+
continue
|
41 |
+
|
42 |
+
if not found:
|
43 |
+
print(f"Downloading {resource}...")
|
44 |
+
nltk.download(resource, quiet=True)
|
45 |
+
except Exception as e:
|
46 |
+
print(f"Error with resource {resource}: {e}")
|
47 |
+
|
48 |
+
print("NLTK resources check completed")
|
49 |
+
except Exception as e:
|
50 |
+
print(f"Error downloading NLTK resources: {e}")
|
51 |
+
|
52 |
+
def create_app():
|
53 |
+
"""
|
54 |
+
Create a streamlined Gradio app for dataset input and Bag of Words analysis.
|
55 |
+
|
56 |
+
Returns:
|
57 |
+
gr.Blocks: The Gradio application
|
58 |
+
"""
|
59 |
+
with gr.Blocks(title="LLM Response Comparator") as app:
|
60 |
+
# Application state to share data between tabs
|
61 |
+
dataset_state = gr.State({})
|
62 |
+
analysis_results_state = gr.State({})
|
63 |
+
|
64 |
+
# Dataset Input Tab
|
65 |
+
with gr.Tab("Dataset Input"):
|
66 |
+
dataset_inputs, example_dropdown, load_example_btn, create_btn, prompt, response1, model1, response2, model2 = create_dataset_input()
|
67 |
+
|
68 |
+
# Add status indicator to show when dataset is created
|
69 |
+
dataset_status = gr.Markdown("*No dataset loaded*")
|
70 |
+
|
71 |
+
# Load example dataset
|
72 |
+
load_example_btn.click(
|
73 |
+
fn=load_example_dataset,
|
74 |
+
inputs=[example_dropdown],
|
75 |
+
outputs=[prompt, response1, model1, response2, model2] # Update all field values
|
76 |
+
)
|
77 |
+
|
78 |
+
# Save dataset to state and update status
|
79 |
+
def create_dataset(p, r1, m1, r2, m2):
|
80 |
+
if not p or not r1 or not r2:
|
81 |
+
return {}, "❌ **Error:** Please fill in at least the prompt and both responses"
|
82 |
+
|
83 |
+
dataset = {
|
84 |
+
"entries": [
|
85 |
+
{"prompt": p, "response": r1, "model": m1 or "Model 1"},
|
86 |
+
{"prompt": p, "response": r2, "model": m2 or "Model 2"}
|
87 |
+
]
|
88 |
+
}
|
89 |
+
return dataset, "✅ **Dataset created successfully!** You can now go to the Analysis tab"
|
90 |
+
|
91 |
+
create_btn.click(
|
92 |
+
fn=create_dataset,
|
93 |
+
inputs=[prompt, response1, model1, response2, model2],
|
94 |
+
outputs=[dataset_state, dataset_status]
|
95 |
+
)
|
96 |
+
|
97 |
+
# Analysis Tab
|
98 |
+
with gr.Tab("Analysis"):
|
99 |
+
# Use create_analysis_screen to get UI components including visualization container
|
100 |
+
analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider = create_analysis_screen()
|
101 |
+
|
102 |
+
# Pre-create visualization components (initially hidden)
|
103 |
+
visualization_area_visible = gr.Checkbox(value=False, visible=False, label="Visualization Visible")
|
104 |
+
analysis_title = gr.Markdown("## Analysis Results", visible=False)
|
105 |
+
prompt_title = gr.Markdown(visible=False)
|
106 |
+
models_compared = gr.Markdown(visible=False)
|
107 |
+
|
108 |
+
# Container for model 1 words
|
109 |
+
model1_title = gr.Markdown(visible=False)
|
110 |
+
model1_words = gr.Markdown(visible=False)
|
111 |
+
|
112 |
+
# Container for model 2 words
|
113 |
+
model2_title = gr.Markdown(visible=False)
|
114 |
+
model2_words = gr.Markdown(visible=False)
|
115 |
+
|
116 |
+
# Similarity metrics
|
117 |
+
similarity_metrics_title = gr.Markdown("### Similarity Metrics", visible=False)
|
118 |
+
similarity_metrics = gr.Markdown(visible=False)
|
119 |
+
|
120 |
+
# Status or error message area
|
121 |
+
status_message_visible = gr.Checkbox(value=False, visible=False, label="Status Message Visible")
|
122 |
+
status_message = gr.Markdown(visible=False)
|
123 |
+
|
124 |
+
# Define a helper function to extract parameter values and run the analysis
|
125 |
+
def run_analysis(dataset, selected_analyses, bow_top, ngram_n, ngram_top):
|
126 |
+
try:
|
127 |
+
if not dataset or "entries" not in dataset or not dataset["entries"]:
|
128 |
+
return (
|
129 |
+
{}, # analysis_results_state
|
130 |
+
False, # analysis_output visibility
|
131 |
+
False, # visualization_area_visible
|
132 |
+
gr.update(visible=False), # analysis_title
|
133 |
+
gr.update(visible=False), # prompt_title
|
134 |
+
gr.update(visible=False), # models_compared
|
135 |
+
gr.update(visible=False), # model1_title
|
136 |
+
gr.update(visible=False), # model1_words
|
137 |
+
gr.update(visible=False), # model2_title
|
138 |
+
gr.update(visible=False), # model2_words
|
139 |
+
gr.update(visible=False), # similarity_metrics_title
|
140 |
+
gr.update(visible=False), # similarity_metrics
|
141 |
+
True, # status_message_visible
|
142 |
+
gr.update(visible=True, value="❌ **Error:** No dataset loaded. Please create or load a dataset first.") # status_message
|
143 |
+
)
|
144 |
+
|
145 |
+
parameters = {
|
146 |
+
"bow_top": bow_top,
|
147 |
+
"ngram_n": ngram_n,
|
148 |
+
"ngram_top": ngram_top
|
149 |
+
}
|
150 |
+
print("Running analysis with parameters:", parameters)
|
151 |
+
|
152 |
+
# Process the analysis request
|
153 |
+
analysis_results, _ = process_analysis_request(dataset, selected_analyses, parameters)
|
154 |
+
|
155 |
+
# If there's an error or no results
|
156 |
+
if not analysis_results or "analyses" not in analysis_results or not analysis_results["analyses"]:
|
157 |
+
return (
|
158 |
+
analysis_results,
|
159 |
+
False,
|
160 |
+
False,
|
161 |
+
gr.update(visible=False),
|
162 |
+
gr.update(visible=False),
|
163 |
+
gr.update(visible=False),
|
164 |
+
gr.update(visible=False),
|
165 |
+
gr.update(visible=False),
|
166 |
+
gr.update(visible=False),
|
167 |
+
gr.update(visible=False),
|
168 |
+
gr.update(visible=False),
|
169 |
+
gr.update(visible=False),
|
170 |
+
True,
|
171 |
+
gr.update(visible=True, value="❌ **No results found.** Try different analysis options.")
|
172 |
+
)
|
173 |
+
|
174 |
+
# Extract information to display in components
|
175 |
+
prompt = list(analysis_results["analyses"].keys())[0]
|
176 |
+
analyses = analysis_results["analyses"][prompt]
|
177 |
+
|
178 |
+
if "bag_of_words" not in analyses:
|
179 |
+
return (
|
180 |
+
analysis_results,
|
181 |
+
False,
|
182 |
+
False,
|
183 |
+
gr.update(visible=False),
|
184 |
+
gr.update(visible=False),
|
185 |
+
gr.update(visible=False),
|
186 |
+
gr.update(visible=False),
|
187 |
+
gr.update(visible=False),
|
188 |
+
gr.update(visible=False),
|
189 |
+
gr.update(visible=False),
|
190 |
+
gr.update(visible=False),
|
191 |
+
gr.update(visible=False),
|
192 |
+
True,
|
193 |
+
gr.update(visible=True, value="❌ **No Bag of Words analysis found.** Make sure to select it in the options.")
|
194 |
+
)
|
195 |
+
|
196 |
+
bow_results = analyses["bag_of_words"]
|
197 |
+
models = bow_results.get("models", [])
|
198 |
+
|
199 |
+
if len(models) < 2:
|
200 |
+
return (
|
201 |
+
analysis_results,
|
202 |
+
False,
|
203 |
+
False,
|
204 |
+
gr.update(visible=False),
|
205 |
+
gr.update(visible=False),
|
206 |
+
gr.update(visible=False),
|
207 |
+
gr.update(visible=False),
|
208 |
+
gr.update(visible=False),
|
209 |
+
gr.update(visible=False),
|
210 |
+
gr.update(visible=False),
|
211 |
+
gr.update(visible=False),
|
212 |
+
gr.update(visible=False),
|
213 |
+
True,
|
214 |
+
gr.update(visible=True, value="❌ **Not enough models to compare.** Please ensure you have two model responses.")
|
215 |
+
)
|
216 |
+
|
217 |
+
# Extract and format information for display
|
218 |
+
model1_name = models[0]
|
219 |
+
model2_name = models[1]
|
220 |
+
|
221 |
+
# Format important words for each model
|
222 |
+
important_words = bow_results.get("important_words", {})
|
223 |
+
model1_words_text = "No important words found"
|
224 |
+
model2_words_text = "No important words found"
|
225 |
+
|
226 |
+
if model1_name in important_words:
|
227 |
+
word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model1_name][:10]]
|
228 |
+
model1_words_text = ", ".join(word_list)
|
229 |
+
|
230 |
+
if model2_name in important_words:
|
231 |
+
word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model2_name][:10]]
|
232 |
+
model2_words_text = ", ".join(word_list)
|
233 |
+
|
234 |
+
# Format similarity metrics
|
235 |
+
similarity_text = "No similarity metrics found"
|
236 |
+
comparisons = bow_results.get("comparisons", {})
|
237 |
+
comparison_key = f"{model1_name} vs {model2_name}"
|
238 |
+
|
239 |
+
if comparison_key in comparisons:
|
240 |
+
metrics = comparisons[comparison_key]
|
241 |
+
cosine = metrics.get("cosine_similarity", 0)
|
242 |
+
jaccard = metrics.get("jaccard_similarity", 0)
|
243 |
+
semantic = metrics.get("semantic_similarity", 0) # Add semantic similarity
|
244 |
+
common_words = metrics.get("common_word_count", 0)
|
245 |
+
|
246 |
+
similarity_text = f"""
|
247 |
+
- **Cosine Similarity**: {cosine:.2f} (higher means more similar word frequency patterns)
|
248 |
+
- **Jaccard Similarity**: {jaccard:.2f} (higher means more word overlap)
|
249 |
+
- **Semantic Similarity**: {semantic:.2f} (higher means more similar meaning)
|
250 |
+
- **Common Words**: {common_words} words appear in both responses
|
251 |
+
"""
|
252 |
+
|
253 |
+
# Return all updated component values
|
254 |
+
return (
|
255 |
+
analysis_results, # analysis_results_state
|
256 |
+
False, # analysis_output visibility
|
257 |
+
True, # visualization_area_visible
|
258 |
+
gr.update(visible=True), # analysis_title
|
259 |
+
gr.update(visible=True, value=f"## Analysis of Prompt: \"{prompt[:100]}...\""), # prompt_title
|
260 |
+
gr.update(visible=True, value=f"### Comparing responses from {model1_name} and {model2_name}"), # models_compared
|
261 |
+
gr.update(visible=True, value=f"#### Top Words Used by {model1_name}"), # model1_title
|
262 |
+
gr.update(visible=True, value=model1_words_text), # model1_words
|
263 |
+
gr.update(visible=True, value=f"#### Top Words Used by {model2_name}"), # model2_title
|
264 |
+
gr.update(visible=True, value=model2_words_text), # model2_words
|
265 |
+
gr.update(visible=True), # similarity_metrics_title
|
266 |
+
gr.update(visible=True, value=similarity_text), # similarity_metrics
|
267 |
+
False, # status_message_visible
|
268 |
+
gr.update(visible=False) # status_message
|
269 |
+
)
|
270 |
+
|
271 |
+
except Exception as e:
|
272 |
+
import traceback
|
273 |
+
error_msg = f"Error in analysis: {str(e)}\n{traceback.format_exc()}"
|
274 |
+
print(error_msg)
|
275 |
+
|
276 |
+
return (
|
277 |
+
{"error": error_msg}, # analysis_results_state
|
278 |
+
True, # analysis_output visibility (show raw JSON for debugging)
|
279 |
+
False, # visualization_area_visible
|
280 |
+
gr.update(visible=False),
|
281 |
+
gr.update(visible=False),
|
282 |
+
gr.update(visible=False),
|
283 |
+
gr.update(visible=False),
|
284 |
+
gr.update(visible=False),
|
285 |
+
gr.update(visible=False),
|
286 |
+
gr.update(visible=False),
|
287 |
+
gr.update(visible=False),
|
288 |
+
gr.update(visible=False),
|
289 |
+
True, # status_message_visible
|
290 |
+
gr.update(visible=True, value=f"❌ **Error during analysis:**\n\n```\n{str(e)}\n```") # status_message
|
291 |
+
)
|
292 |
+
|
293 |
+
# Function to update visibility based on checkbox state
|
294 |
+
def update_visibility(viz_visible, status_visible):
|
295 |
+
return [
|
296 |
+
gr.update(visible=viz_visible), # analysis_title
|
297 |
+
gr.update(visible=viz_visible), # prompt_title
|
298 |
+
gr.update(visible=viz_visible), # models_compared
|
299 |
+
gr.update(visible=viz_visible), # model1_title
|
300 |
+
gr.update(visible=viz_visible), # model1_words
|
301 |
+
gr.update(visible=viz_visible), # model2_title
|
302 |
+
gr.update(visible=viz_visible), # model2_words
|
303 |
+
gr.update(visible=viz_visible), # similarity_metrics_title
|
304 |
+
gr.update(visible=viz_visible), # similarity_metrics
|
305 |
+
gr.update(visible=status_visible) # status_message
|
306 |
+
]
|
307 |
+
|
308 |
+
# Connect visibility checkboxes to update function
|
309 |
+
visualization_area_visible.change(
|
310 |
+
fn=update_visibility,
|
311 |
+
inputs=[visualization_area_visible, status_message_visible],
|
312 |
+
outputs=[
|
313 |
+
analysis_title,
|
314 |
+
prompt_title,
|
315 |
+
models_compared,
|
316 |
+
model1_title,
|
317 |
+
model1_words,
|
318 |
+
model2_title,
|
319 |
+
model2_words,
|
320 |
+
similarity_metrics_title,
|
321 |
+
similarity_metrics,
|
322 |
+
status_message
|
323 |
+
]
|
324 |
+
)
|
325 |
+
|
326 |
+
# Run analysis with proper parameters
|
327 |
+
run_analysis_btn.click(
|
328 |
+
fn=run_analysis,
|
329 |
+
inputs=[dataset_state, analysis_options, bow_top_slider, ngram_n, ngram_top],
|
330 |
+
outputs=[
|
331 |
+
analysis_results_state,
|
332 |
+
analysis_output,
|
333 |
+
visualization_area_visible,
|
334 |
+
analysis_title,
|
335 |
+
prompt_title,
|
336 |
+
models_compared,
|
337 |
+
model1_title,
|
338 |
+
model1_words,
|
339 |
+
model2_title,
|
340 |
+
model2_words,
|
341 |
+
similarity_metrics_title,
|
342 |
+
similarity_metrics,
|
343 |
+
status_message_visible,
|
344 |
+
status_message
|
345 |
+
]
|
346 |
+
)
|
347 |
+
|
348 |
+
return app
|
349 |
+
|
350 |
+
if __name__ == "__main__":
|
351 |
+
# Download required NLTK resources before launching the app
|
352 |
+
download_nltk_resources()
|
353 |
+
|
354 |
+
app = create_app()
|
355 |
+
app.launch()
|
app.py
CHANGED
@@ -175,28 +175,137 @@ def create_app():
|
|
175 |
prompt = list(analysis_results["analyses"].keys())[0]
|
176 |
analyses = analysis_results["analyses"][prompt]
|
177 |
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
|
|
|
|
|
|
195 |
|
196 |
-
|
197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
|
199 |
-
|
|
|
200 |
return (
|
201 |
analysis_results,
|
202 |
False,
|
@@ -211,59 +320,23 @@ def create_app():
|
|
211 |
gr.update(visible=False),
|
212 |
gr.update(visible=False),
|
213 |
True,
|
214 |
-
gr.update(visible=True, value="❌ **
|
215 |
)
|
216 |
-
|
217 |
-
# Extract and format information for display
|
218 |
-
model1_name = models[0]
|
219 |
-
model2_name = models[1]
|
220 |
-
|
221 |
-
# Format important words for each model
|
222 |
-
important_words = bow_results.get("important_words", {})
|
223 |
-
model1_words_text = "No important words found"
|
224 |
-
model2_words_text = "No important words found"
|
225 |
-
|
226 |
-
if model1_name in important_words:
|
227 |
-
word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model1_name][:10]]
|
228 |
-
model1_words_text = ", ".join(word_list)
|
229 |
-
|
230 |
-
if model2_name in important_words:
|
231 |
-
word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model2_name][:10]]
|
232 |
-
model2_words_text = ", ".join(word_list)
|
233 |
-
|
234 |
-
# Format similarity metrics
|
235 |
-
similarity_text = "No similarity metrics found"
|
236 |
-
comparisons = bow_results.get("comparisons", {})
|
237 |
-
comparison_key = f"{model1_name} vs {model2_name}"
|
238 |
|
239 |
-
if comparison_key in comparisons:
|
240 |
-
metrics = comparisons[comparison_key]
|
241 |
-
cosine = metrics.get("cosine_similarity", 0)
|
242 |
-
jaccard = metrics.get("jaccard_similarity", 0)
|
243 |
-
semantic = metrics.get("semantic_similarity", 0) # Add semantic similarity
|
244 |
-
common_words = metrics.get("common_word_count", 0)
|
245 |
-
|
246 |
-
similarity_text = f"""
|
247 |
-
- **Cosine Similarity**: {cosine:.2f} (higher means more similar word frequency patterns)
|
248 |
-
- **Jaccard Similarity**: {jaccard:.2f} (higher means more word overlap)
|
249 |
-
- **Semantic Similarity**: {semantic:.2f} (higher means more similar meaning)
|
250 |
-
- **Common Words**: {common_words} words appear in both responses
|
251 |
-
"""
|
252 |
-
|
253 |
# Return all updated component values
|
254 |
return (
|
255 |
analysis_results, # analysis_results_state
|
256 |
False, # analysis_output visibility
|
257 |
True, # visualization_area_visible
|
258 |
gr.update(visible=True), # analysis_title
|
259 |
-
gr.update(visible=
|
260 |
-
gr.update(visible=
|
261 |
-
gr.update(visible=
|
262 |
-
gr.update(visible=
|
263 |
-
gr.update(visible=
|
264 |
-
gr.update(visible=
|
265 |
-
gr.update(visible=
|
266 |
-
gr.update(visible=
|
267 |
False, # status_message_visible
|
268 |
gr.update(visible=False) # status_message
|
269 |
)
|
@@ -326,7 +399,7 @@ def create_app():
|
|
326 |
# Run analysis with proper parameters
|
327 |
run_analysis_btn.click(
|
328 |
fn=run_analysis,
|
329 |
-
inputs=[dataset_state, analysis_options, bow_top_slider
|
330 |
outputs=[
|
331 |
analysis_results_state,
|
332 |
analysis_output,
|
|
|
175 |
prompt = list(analysis_results["analyses"].keys())[0]
|
176 |
analyses = analysis_results["analyses"][prompt]
|
177 |
|
178 |
+
# Initialize visualization components visibilities and contents
|
179 |
+
visualization_area_visible = False
|
180 |
+
prompt_title_visible = False
|
181 |
+
prompt_title_value = ""
|
182 |
+
models_compared_visible = False
|
183 |
+
models_compared_value = ""
|
184 |
+
|
185 |
+
model1_title_visible = False
|
186 |
+
model1_title_value = ""
|
187 |
+
model1_words_visible = False
|
188 |
+
model1_words_value = ""
|
189 |
+
|
190 |
+
model2_title_visible = False
|
191 |
+
model2_title_value = ""
|
192 |
+
model2_words_visible = False
|
193 |
+
model2_words_value = ""
|
194 |
+
|
195 |
+
similarity_title_visible = False
|
196 |
+
similarity_metrics_visible = False
|
197 |
+
similarity_metrics_value = ""
|
198 |
|
199 |
+
# Check for Bag of Words analysis
|
200 |
+
if "bag_of_words" in analyses:
|
201 |
+
visualization_area_visible = True
|
202 |
+
bow_results = analyses["bag_of_words"]
|
203 |
+
models = bow_results.get("models", [])
|
204 |
+
|
205 |
+
if len(models) >= 2:
|
206 |
+
prompt_title_visible = True
|
207 |
+
prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
|
208 |
+
|
209 |
+
models_compared_visible = True
|
210 |
+
models_compared_value = f"### Comparing responses from {models[0]} and {models[1]}"
|
211 |
+
|
212 |
+
# Extract and format information for display
|
213 |
+
model1_name = models[0]
|
214 |
+
model2_name = models[1]
|
215 |
+
|
216 |
+
# Format important words for each model
|
217 |
+
important_words = bow_results.get("important_words", {})
|
218 |
+
|
219 |
+
if model1_name in important_words:
|
220 |
+
model1_title_visible = True
|
221 |
+
model1_title_value = f"#### Top Words Used by {model1_name}"
|
222 |
+
|
223 |
+
word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model1_name][:10]]
|
224 |
+
model1_words_visible = True
|
225 |
+
model1_words_value = ", ".join(word_list)
|
226 |
+
|
227 |
+
if model2_name in important_words:
|
228 |
+
model2_title_visible = True
|
229 |
+
model2_title_value = f"#### Top Words Used by {model2_name}"
|
230 |
+
|
231 |
+
word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model2_name][:10]]
|
232 |
+
model2_words_visible = True
|
233 |
+
model2_words_value = ", ".join(word_list)
|
234 |
+
|
235 |
+
# Format similarity metrics
|
236 |
+
comparisons = bow_results.get("comparisons", {})
|
237 |
+
comparison_key = f"{model1_name} vs {model2_name}"
|
238 |
+
|
239 |
+
if comparison_key in comparisons:
|
240 |
+
metrics = comparisons[comparison_key]
|
241 |
+
cosine = metrics.get("cosine_similarity", 0)
|
242 |
+
jaccard = metrics.get("jaccard_similarity", 0)
|
243 |
+
semantic = metrics.get("semantic_similarity", 0)
|
244 |
+
common_words = metrics.get("common_word_count", 0)
|
245 |
+
|
246 |
+
similarity_title_visible = True
|
247 |
+
similarity_metrics_visible = True
|
248 |
+
similarity_metrics_value = f"""
|
249 |
+
- **Cosine Similarity**: {cosine:.2f} (higher means more similar word frequency patterns)
|
250 |
+
- **Jaccard Similarity**: {jaccard:.2f} (higher means more word overlap)
|
251 |
+
- **Semantic Similarity**: {semantic:.2f} (higher means more similar meaning)
|
252 |
+
- **Common Words**: {common_words} words appear in both responses
|
253 |
+
"""
|
254 |
+
|
255 |
+
# Check for N-gram analysis (if not found, we'll fallback to BOW)
|
256 |
+
if "ngram_analysis" in analyses and not visualization_area_visible:
|
257 |
+
visualization_area_visible = True
|
258 |
+
ngram_results = analyses["ngram_analysis"]
|
259 |
+
models = ngram_results.get("models", [])
|
260 |
+
ngram_size = ngram_results.get("ngram_size", 2)
|
261 |
+
size_name = "Unigrams" if ngram_size == 1 else f"{ngram_size}-grams"
|
262 |
+
|
263 |
+
if len(models) >= 2:
|
264 |
+
prompt_title_visible = True
|
265 |
+
prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
|
266 |
+
|
267 |
+
models_compared_visible = True
|
268 |
+
models_compared_value = f"### {size_name} Analysis: Comparing responses from {models[0]} and {models[1]}"
|
269 |
+
|
270 |
+
# Extract and format information for display
|
271 |
+
model1_name = models[0]
|
272 |
+
model2_name = models[1]
|
273 |
+
|
274 |
+
# Format important n-grams for each model
|
275 |
+
important_ngrams = ngram_results.get("important_ngrams", {})
|
276 |
+
|
277 |
+
if model1_name in important_ngrams:
|
278 |
+
model1_title_visible = True
|
279 |
+
model1_title_value = f"#### Top {size_name} Used by {model1_name}"
|
280 |
+
|
281 |
+
ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model1_name][:10]]
|
282 |
+
model1_words_visible = True
|
283 |
+
model1_words_value = ", ".join(ngram_list)
|
284 |
+
|
285 |
+
if model2_name in important_ngrams:
|
286 |
+
model2_title_visible = True
|
287 |
+
model2_title_value = f"#### Top {size_name} Used by {model2_name}"
|
288 |
+
|
289 |
+
ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model2_name][:10]]
|
290 |
+
model2_words_visible = True
|
291 |
+
model2_words_value = ", ".join(ngram_list)
|
292 |
+
|
293 |
+
# Format similarity metrics if available
|
294 |
+
if "comparisons" in ngram_results:
|
295 |
+
comparison_key = f"{model1_name} vs {model2_name}"
|
296 |
+
|
297 |
+
if comparison_key in ngram_results["comparisons"]:
|
298 |
+
metrics = ngram_results["comparisons"][comparison_key]
|
299 |
+
common_count = metrics.get("common_ngram_count", 0)
|
300 |
+
|
301 |
+
similarity_title_visible = True
|
302 |
+
similarity_metrics_visible = True
|
303 |
+
similarity_metrics_value = f"""
|
304 |
+
- **Common {size_name}**: {common_count} {size_name.lower()} appear in both responses
|
305 |
+
"""
|
306 |
|
307 |
+
# If we don't have visualization data from any analysis
|
308 |
+
if not visualization_area_visible:
|
309 |
return (
|
310 |
analysis_results,
|
311 |
False,
|
|
|
320 |
gr.update(visible=False),
|
321 |
gr.update(visible=False),
|
322 |
True,
|
323 |
+
gr.update(visible=True, value="❌ **No visualization data found.** Make sure to select at least one analysis type.")
|
324 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
325 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
326 |
# Return all updated component values
|
327 |
return (
|
328 |
analysis_results, # analysis_results_state
|
329 |
False, # analysis_output visibility
|
330 |
True, # visualization_area_visible
|
331 |
gr.update(visible=True), # analysis_title
|
332 |
+
gr.update(visible=prompt_title_visible, value=prompt_title_value), # prompt_title
|
333 |
+
gr.update(visible=models_compared_visible, value=models_compared_value), # models_compared
|
334 |
+
gr.update(visible=model1_title_visible, value=model1_title_value), # model1_title
|
335 |
+
gr.update(visible=model1_words_visible, value=model1_words_value), # model1_words
|
336 |
+
gr.update(visible=model2_title_visible, value=model2_title_value), # model2_title
|
337 |
+
gr.update(visible=model2_words_visible, value=model2_words_value), # model2_words
|
338 |
+
gr.update(visible=similarity_title_visible), # similarity_metrics_title
|
339 |
+
gr.update(visible=similarity_metrics_visible, value=similarity_metrics_value), # similarity_metrics
|
340 |
False, # status_message_visible
|
341 |
gr.update(visible=False) # status_message
|
342 |
)
|
|
|
399 |
# Run analysis with proper parameters
|
400 |
run_analysis_btn.click(
|
401 |
fn=run_analysis,
|
402 |
+
inputs=[dataset_state, analysis_options, bow_top_slider],
|
403 |
outputs=[
|
404 |
analysis_results_state,
|
405 |
analysis_output,
|
processors/ngram_analysis.py
CHANGED
@@ -36,74 +36,93 @@ def compare_ngrams(texts, model_names, n=2, top_n=25):
|
|
36 |
if not texts or len(texts) < 1:
|
37 |
return result
|
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 |
-
result["
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
|
109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
if not texts or len(texts) < 1:
|
37 |
return result
|
38 |
|
39 |
+
# Convert n to integer if it's a string
|
40 |
+
if isinstance(n, str):
|
41 |
+
n = int(n)
|
42 |
+
|
43 |
+
# Convert top_n to integer if necessary
|
44 |
+
if isinstance(top_n, str):
|
45 |
+
top_n = int(top_n)
|
46 |
+
|
47 |
+
try:
|
48 |
+
# Create n-gram representations using CountVectorizer
|
49 |
+
vectorizer = CountVectorizer(
|
50 |
+
ngram_range=(n, n), # Use the specified n-gram size
|
51 |
+
max_features=1000,
|
52 |
+
stop_words='english'
|
53 |
+
)
|
54 |
+
|
55 |
+
X = vectorizer.fit_transform(texts)
|
56 |
+
|
57 |
+
# Get feature names (n-grams)
|
58 |
+
feature_names = vectorizer.get_feature_names_out()
|
59 |
+
|
60 |
+
# Create n-gram count matrix
|
61 |
+
ngram_counts = {}
|
62 |
+
for i, model in enumerate(model_names):
|
63 |
+
counts = X[i].toarray()[0]
|
64 |
+
ngram_counts[model] = {}
|
65 |
+
|
66 |
+
# Store n-gram frequencies for this model
|
67 |
+
for j, ngram in enumerate(feature_names):
|
68 |
+
if counts[j] > 0: # Only store n-grams that appear
|
69 |
+
ngram_counts[model][ngram] = int(counts[j])
|
70 |
+
|
71 |
+
# Add to n-gram count matrix
|
72 |
+
if ngram not in result["ngram_count_matrix"]:
|
73 |
+
result["ngram_count_matrix"][ngram] = {}
|
74 |
+
result["ngram_count_matrix"][ngram][model] = int(counts[j])
|
75 |
+
|
76 |
+
# Find important n-grams for each model
|
77 |
+
for model, ngram_freq in ngram_counts.items():
|
78 |
+
# Sort by frequency
|
79 |
+
sorted_ngrams = sorted(ngram_freq.items(), key=lambda x: x[1], reverse=True)
|
80 |
+
|
81 |
+
# Store top N n-grams
|
82 |
+
result["important_ngrams"][model] = [
|
83 |
+
{"ngram": ngram, "count": count}
|
84 |
+
for ngram, count in sorted_ngrams[:top_n]
|
85 |
+
]
|
86 |
+
|
87 |
+
# Calculate differential n-grams (n-grams with biggest frequency difference between models)
|
88 |
+
if len(model_names) >= 2:
|
89 |
+
model1, model2 = model_names[0], model_names[1]
|
90 |
+
|
91 |
+
# Calculate differences
|
92 |
+
diff_scores = {}
|
93 |
+
for ngram in result["ngram_count_matrix"]:
|
94 |
+
count1 = result["ngram_count_matrix"][ngram].get(model1, 0)
|
95 |
+
count2 = result["ngram_count_matrix"][ngram].get(model2, 0)
|
96 |
+
|
97 |
+
# Absolute difference
|
98 |
+
diff_scores[ngram] = abs(count1 - count2)
|
99 |
+
|
100 |
+
# Sort by difference
|
101 |
+
sorted_diffs = sorted(diff_scores.items(), key=lambda x: x[1], reverse=True)
|
102 |
+
result["differential_ngrams"] = [ngram for ngram, _ in sorted_diffs[:top_n]]
|
103 |
+
|
104 |
+
# Calculate overlap statistics
|
105 |
+
model1_ngrams = set(ngram_counts.get(model1, {}).keys())
|
106 |
+
model2_ngrams = set(ngram_counts.get(model2, {}).keys())
|
107 |
+
common_ngrams = model1_ngrams.intersection(model2_ngrams)
|
108 |
+
|
109 |
+
# Initialize comparisons if needed
|
110 |
+
if "comparisons" not in result:
|
111 |
+
result["comparisons"] = {}
|
112 |
+
|
113 |
+
comparison_key = f"{model1} vs {model2}"
|
114 |
+
result["comparisons"][comparison_key] = {
|
115 |
+
"common_ngram_count": len(common_ngrams)
|
116 |
+
}
|
117 |
|
118 |
+
return result
|
119 |
+
except Exception as e:
|
120 |
+
import traceback
|
121 |
+
error_msg = f"N-gram analysis error: {str(e)}\n{traceback.format_exc()}"
|
122 |
+
print(error_msg)
|
123 |
+
# Return basic structure with error
|
124 |
+
return {
|
125 |
+
"models": model_names,
|
126 |
+
"ngram_size": n,
|
127 |
+
"error": str(e)
|
128 |
+
}
|
ui/analysis_screen.py
CHANGED
@@ -5,19 +5,18 @@ from visualization.bow_visualizer import process_and_visualize_analysis
|
|
5 |
# Import analysis modules
|
6 |
# Uncomment these when implemented
|
7 |
# from processors.topic_modeling import extract_topics, compare_topics
|
8 |
-
|
9 |
# from processors.bias_detection import compare_bias
|
10 |
from processors.bow_analysis import compare_bow
|
11 |
# from processors.metrics import calculate_similarity
|
12 |
# from processors.diff_highlighter import highlight_differences
|
13 |
-
from processors.ngram_analysis import compare_ngrams
|
14 |
|
15 |
def create_analysis_screen():
|
16 |
"""
|
17 |
Create the analysis options screen
|
18 |
|
19 |
Returns:
|
20 |
-
tuple: (analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider)
|
21 |
"""
|
22 |
with gr.Column() as analysis_screen:
|
23 |
gr.Markdown("## Analysis Options")
|
@@ -48,7 +47,19 @@ def create_analysis_screen():
|
|
48 |
elem_id="bow_top_slider"
|
49 |
)
|
50 |
|
51 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
with gr.Group() as analysis_params:
|
53 |
# Topic modeling parameters
|
54 |
with gr.Group(visible=False) as topic_params:
|
@@ -56,14 +67,11 @@ def create_analysis_screen():
|
|
56 |
topic_count = gr.Slider(minimum=2, maximum=10, value=3, step=1,
|
57 |
label="Number of Topics")
|
58 |
|
59 |
-
# N-gram parameters
|
60 |
with gr.Group(visible=False) as ngram_params:
|
61 |
gr.Markdown("### N-gram Parameters")
|
62 |
-
|
63 |
-
|
64 |
-
ngram_top = gr.Slider(minimum=5, maximum=30, value=10, step=1,
|
65 |
-
label="Top N-grams to Display")
|
66 |
-
|
67 |
# Bias detection parameters
|
68 |
with gr.Group(visible=False) as bias_params:
|
69 |
gr.Markdown("### Bias Detection Parameters")
|
@@ -84,18 +92,21 @@ def create_analysis_screen():
|
|
84 |
|
85 |
# Function to update parameter visibility based on selected analyses
|
86 |
def update_params_visibility(selected):
|
|
|
87 |
return {
|
88 |
topic_params: gr.update(visible="Topic Modeling" in selected),
|
89 |
-
ngram_params: gr.update(visible=
|
90 |
bias_params: gr.update(visible="Bias Detection" in selected),
|
91 |
-
similarity_params: gr.update(visible="Similarity Metrics" in selected)
|
|
|
|
|
92 |
}
|
93 |
|
94 |
# Set up event handler for analysis selection
|
95 |
analysis_options.change(
|
96 |
fn=update_params_visibility,
|
97 |
inputs=[analysis_options],
|
98 |
-
outputs=[topic_params, ngram_params, bias_params, similarity_params]
|
99 |
)
|
100 |
|
101 |
# Run analysis button
|
@@ -104,11 +115,10 @@ def create_analysis_screen():
|
|
104 |
# Analysis output area - hidden JSON component to store raw results
|
105 |
analysis_output = gr.JSON(label="Analysis Results", visible=False)
|
106 |
|
107 |
-
# Return the
|
108 |
-
# Note: Removed the visualization_container from return values since we'll pre-create it
|
109 |
return analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider, ngram_n, ngram_top
|
110 |
|
111 |
-
# function
|
112 |
def process_analysis_request(dataset, selected_analyses, parameters):
|
113 |
"""
|
114 |
Process the analysis request and run selected analyses
|
|
|
5 |
# Import analysis modules
|
6 |
# Uncomment these when implemented
|
7 |
# from processors.topic_modeling import extract_topics, compare_topics
|
8 |
+
from processors.ngram_analysis import compare_ngrams
|
9 |
# from processors.bias_detection import compare_bias
|
10 |
from processors.bow_analysis import compare_bow
|
11 |
# from processors.metrics import calculate_similarity
|
12 |
# from processors.diff_highlighter import highlight_differences
|
|
|
13 |
|
14 |
def create_analysis_screen():
|
15 |
"""
|
16 |
Create the analysis options screen
|
17 |
|
18 |
Returns:
|
19 |
+
tuple: (analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider, ngram_n, ngram_top)
|
20 |
"""
|
21 |
with gr.Column() as analysis_screen:
|
22 |
gr.Markdown("## Analysis Options")
|
|
|
47 |
elem_id="bow_top_slider"
|
48 |
)
|
49 |
|
50 |
+
# Create N-gram parameters accessible at top level
|
51 |
+
ngram_n = gr.Radio(
|
52 |
+
choices=["1", "2", "3"], value="2",
|
53 |
+
label="N-gram Size",
|
54 |
+
visible=False
|
55 |
+
)
|
56 |
+
ngram_top = gr.Slider(
|
57 |
+
minimum=5, maximum=30, value=10, step=1,
|
58 |
+
label="Top N-grams to Display",
|
59 |
+
visible=False
|
60 |
+
)
|
61 |
+
|
62 |
+
# Parameters for each analysis type
|
63 |
with gr.Group() as analysis_params:
|
64 |
# Topic modeling parameters
|
65 |
with gr.Group(visible=False) as topic_params:
|
|
|
67 |
topic_count = gr.Slider(minimum=2, maximum=10, value=3, step=1,
|
68 |
label="Number of Topics")
|
69 |
|
70 |
+
# N-gram parameters group (using external ngram_n and ngram_top)
|
71 |
with gr.Group(visible=False) as ngram_params:
|
72 |
gr.Markdown("### N-gram Parameters")
|
73 |
+
# We're already using ngram_n and ngram_top defined above
|
74 |
+
|
|
|
|
|
|
|
75 |
# Bias detection parameters
|
76 |
with gr.Group(visible=False) as bias_params:
|
77 |
gr.Markdown("### Bias Detection Parameters")
|
|
|
92 |
|
93 |
# Function to update parameter visibility based on selected analyses
|
94 |
def update_params_visibility(selected):
|
95 |
+
ngram_visible = "N-gram Analysis" in selected
|
96 |
return {
|
97 |
topic_params: gr.update(visible="Topic Modeling" in selected),
|
98 |
+
ngram_params: gr.update(visible=ngram_visible),
|
99 |
bias_params: gr.update(visible="Bias Detection" in selected),
|
100 |
+
similarity_params: gr.update(visible="Similarity Metrics" in selected),
|
101 |
+
ngram_n: gr.update(visible=ngram_visible),
|
102 |
+
ngram_top: gr.update(visible=ngram_visible)
|
103 |
}
|
104 |
|
105 |
# Set up event handler for analysis selection
|
106 |
analysis_options.change(
|
107 |
fn=update_params_visibility,
|
108 |
inputs=[analysis_options],
|
109 |
+
outputs=[topic_params, ngram_params, bias_params, similarity_params, ngram_n, ngram_top]
|
110 |
)
|
111 |
|
112 |
# Run analysis button
|
|
|
115 |
# Analysis output area - hidden JSON component to store raw results
|
116 |
analysis_output = gr.JSON(label="Analysis Results", visible=False)
|
117 |
|
118 |
+
# Return the components needed by app.py
|
|
|
119 |
return analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider, ngram_n, ngram_top
|
120 |
|
121 |
+
# Process analysis request function
|
122 |
def process_analysis_request(dataset, selected_analyses, parameters):
|
123 |
"""
|
124 |
Process the analysis request and run selected analyses
|
visualization/ngram_visualizer.py
CHANGED
@@ -7,7 +7,31 @@ from plotly.subplots import make_subplots
|
|
7 |
|
8 |
|
9 |
def create_ngram_visualization(analysis_results):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
Create visualizations for n-gram analysis results
|
12 |
|
13 |
Args:
|
@@ -27,6 +51,11 @@ def create_ngram_visualization(analysis_results):
|
|
27 |
# Process N-gram analysis if available
|
28 |
if "ngram_analysis" in analyses:
|
29 |
ngram_results = analyses["ngram_analysis"]
|
|
|
|
|
|
|
|
|
|
|
30 |
|
31 |
# Show models being compared
|
32 |
models = ngram_results.get("models", [])
|
@@ -40,25 +69,38 @@ def create_ngram_visualization(analysis_results):
|
|
40 |
# Get important n-grams for each model
|
41 |
important_ngrams = ngram_results.get("important_ngrams", {})
|
42 |
|
43 |
-
#
|
44 |
if important_ngrams:
|
45 |
for model_name, ngrams in important_ngrams.items():
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
|
63 |
# Visualize differential n-grams (n-grams with biggest frequency difference)
|
64 |
diff_ngrams = ngram_results.get("differential_ngrams", [])
|
@@ -71,42 +113,17 @@ def create_ngram_visualization(analysis_results):
|
|
71 |
model1, model2 = models[0], models[1]
|
72 |
diff_data = []
|
73 |
|
74 |
-
for ngram in diff_ngrams[:
|
75 |
if ngram in ngram_matrix:
|
76 |
counts = ngram_matrix[ngram]
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
# Create grouped bar chart
|
87 |
-
fig = go.Figure()
|
88 |
-
fig.add_trace(go.Bar(
|
89 |
-
x=diff_df['ngram'],
|
90 |
-
y=diff_df[model1],
|
91 |
-
name=model1,
|
92 |
-
marker_color='indianred'
|
93 |
-
))
|
94 |
-
fig.add_trace(go.Bar(
|
95 |
-
x=diff_df['ngram'],
|
96 |
-
y=diff_df[model2],
|
97 |
-
name=model2,
|
98 |
-
marker_color='lightsalmon'
|
99 |
-
))
|
100 |
-
|
101 |
-
fig.update_layout(
|
102 |
-
title=f"{size_name} Frequency Comparison",
|
103 |
-
xaxis_title="N-gram",
|
104 |
-
yaxis_title="Frequency",
|
105 |
-
barmode='group',
|
106 |
-
height=500
|
107 |
-
)
|
108 |
-
|
109 |
-
output_components.append(gr.Plot(value=fig))
|
110 |
|
111 |
# Add similarity comparison if available
|
112 |
if "comparisons" in ngram_results:
|
@@ -123,28 +140,4 @@ def create_ngram_visualization(analysis_results):
|
|
123 |
|
124 |
output_components.append(gr.Markdown(metrics_text))
|
125 |
|
126 |
-
|
127 |
-
if len(output_components) <= 1:
|
128 |
-
output_components.append(gr.Markdown(f"No detailed N-gram analysis found in results."))
|
129 |
-
|
130 |
-
return output_components
|
131 |
-
|
132 |
-
|
133 |
-
def process_and_visualize_ngram_analysis(analysis_results):
|
134 |
-
"""
|
135 |
-
Process the n-gram analysis results and create visualization components
|
136 |
-
|
137 |
-
Args:
|
138 |
-
analysis_results (dict): The analysis results
|
139 |
-
|
140 |
-
Returns:
|
141 |
-
list: List of gradio components for visualization
|
142 |
-
"""
|
143 |
-
try:
|
144 |
-
print(f"Starting visualization of n-gram analysis results")
|
145 |
-
return create_ngram_visualization(analysis_results)
|
146 |
-
except Exception as e:
|
147 |
-
import traceback
|
148 |
-
error_msg = f"N-gram visualization error: {str(e)}\n{traceback.format_exc()}"
|
149 |
-
print(error_msg)
|
150 |
-
return [gr.Markdown(f"**Error during n-gram visualization:**\n\n```\n{error_msg}\n```")]
|
|
|
7 |
|
8 |
|
9 |
def create_ngram_visualization(analysis_results):
|
10 |
+
# If no components were added other than header, show a message
|
11 |
+
if len(output_components) <= 1:
|
12 |
+
output_components.append(gr.Markdown(f"No detailed N-gram analysis found in results."))
|
13 |
+
|
14 |
+
return output_components
|
15 |
+
|
16 |
+
|
17 |
+
def process_and_visualize_ngram_analysis(analysis_results):
|
18 |
"""
|
19 |
+
Process the n-gram analysis results and create visualization components
|
20 |
+
|
21 |
+
Args:
|
22 |
+
analysis_results (dict): The analysis results
|
23 |
+
|
24 |
+
Returns:
|
25 |
+
list: List of gradio components for visualization
|
26 |
+
"""
|
27 |
+
try:
|
28 |
+
print(f"Starting visualization of n-gram analysis results")
|
29 |
+
return create_ngram_visualization(analysis_results)
|
30 |
+
except Exception as e:
|
31 |
+
import traceback
|
32 |
+
error_msg = f"N-gram visualization error: {str(e)}\n{traceback.format_exc()}"
|
33 |
+
print(error_msg)
|
34 |
+
return [gr.Markdown(f"**Error during n-gram visualization:**\n\n```\n{error_msg}\n```")]"""
|
35 |
Create visualizations for n-gram analysis results
|
36 |
|
37 |
Args:
|
|
|
51 |
# Process N-gram analysis if available
|
52 |
if "ngram_analysis" in analyses:
|
53 |
ngram_results = analyses["ngram_analysis"]
|
54 |
+
|
55 |
+
# Check if there's an error in the analysis
|
56 |
+
if "error" in ngram_results:
|
57 |
+
output_components.append(gr.Markdown(f"**Error in N-gram analysis:** {ngram_results['error']}"))
|
58 |
+
continue
|
59 |
|
60 |
# Show models being compared
|
61 |
models = ngram_results.get("models", [])
|
|
|
69 |
# Get important n-grams for each model
|
70 |
important_ngrams = ngram_results.get("important_ngrams", {})
|
71 |
|
72 |
+
# Display important n-grams for each model
|
73 |
if important_ngrams:
|
74 |
for model_name, ngrams in important_ngrams.items():
|
75 |
+
output_components.append(gr.Markdown(f"#### Top {size_name} Used by {model_name}"))
|
76 |
+
|
77 |
+
if ngrams:
|
78 |
+
# Create a formatted list of n-grams for display
|
79 |
+
ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in ngrams[:10]]
|
80 |
+
output_components.append(gr.Markdown(", ".join(ngram_list)))
|
81 |
+
else:
|
82 |
+
output_components.append(gr.Markdown("No significant n-grams found."))
|
83 |
+
|
84 |
+
# Only if we have enough data, create a bar chart
|
85 |
+
if len(ngrams) >= 3:
|
86 |
+
try:
|
87 |
+
df = pd.DataFrame(ngrams)
|
88 |
+
# Create bar chart for top n-grams
|
89 |
+
fig = px.bar(df[:10], x='ngram', y='count',
|
90 |
+
title=f"Top {size_name} Used by {model_name}",
|
91 |
+
labels={'ngram': 'N-gram', 'count': 'Frequency'},
|
92 |
+
height=400)
|
93 |
+
|
94 |
+
# Improve layout
|
95 |
+
fig.update_layout(
|
96 |
+
xaxis_title="N-gram",
|
97 |
+
yaxis_title="Frequency",
|
98 |
+
xaxis={'categoryorder': 'total descending'}
|
99 |
+
)
|
100 |
+
|
101 |
+
output_components.append(gr.Plot(value=fig))
|
102 |
+
except Exception as e:
|
103 |
+
output_components.append(gr.Markdown(f"Visualization error: {str(e)}"))
|
104 |
|
105 |
# Visualize differential n-grams (n-grams with biggest frequency difference)
|
106 |
diff_ngrams = ngram_results.get("differential_ngrams", [])
|
|
|
113 |
model1, model2 = models[0], models[1]
|
114 |
diff_data = []
|
115 |
|
116 |
+
for ngram in diff_ngrams[:10]: # Limit to top 10 for readability
|
117 |
if ngram in ngram_matrix:
|
118 |
counts = ngram_matrix[ngram]
|
119 |
+
model1_count = counts.get(model1, 0)
|
120 |
+
model2_count = counts.get(model2, 0)
|
121 |
+
|
122 |
+
# Only include if there's a meaningful difference
|
123 |
+
if abs(model1_count - model2_count) > 0:
|
124 |
+
output_components.append(gr.Markdown(
|
125 |
+
f"- **{ngram}**: {model1}: {model1_count}, {model2}: {model2_count}"
|
126 |
+
))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
|
128 |
# Add similarity comparison if available
|
129 |
if "comparisons" in ngram_results:
|
|
|
140 |
|
141 |
output_components.append(gr.Markdown(metrics_text))
|
142 |
|
143 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|