m7mdal7aj commited on
Commit
fb754b1
1 Parent(s): 7016023

Update my_model/tabs/results.py

Browse files
Files changed (1) hide show
  1. my_model/tabs/results.py +282 -93
my_model/tabs/results.py CHANGED
@@ -1,103 +1,292 @@
1
- import pandas as pd
2
- from fuzzywuzzy import fuzz
3
- from collections import Counter
4
- from nltk.stem import PorterStemmer
5
- from ast import literal_eval
6
- from typing import Union, List
7
  import streamlit as st
8
- from my_model.results.evaluation import KBVQAEvaluator
9
- from my_model.config import evaluation_config as config
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- class ResultDemonstrator(KBVQAEvaluator):
 
12
 
 
 
 
 
 
 
 
 
 
 
 
13
 
 
 
14
 
15
- def run(self):
16
-
17
- import pandas as pd
18
- import altair as alt
19
-
20
- # Sample data
21
- data = pd.DataFrame({
22
- 'x': range(10),
23
- 'y': [2, 1, 4, 3, 5, 6, 9, 7, 10, 8]
24
  })
25
-
26
- # Create a scatter plot
27
- chart = alt.Chart(data).mark_point().encode(
28
- x='x',
29
- y='y'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  )
31
-
32
- # Display the chart in Streamlit
33
- st.altair_chart(chart, use_container_width=True)
34
- # Display the chart in Streamlit
35
  st.altair_chart(chart, use_container_width=True)
36
 
37
- import matplotlib.pyplot as plt
38
- import numpy as np
39
-
40
- # Data
41
- x = np.random.randn(100)
42
- y = np.random.randn(100)
43
-
44
- # Create a scatter plot
45
- fig, ax = plt.subplots()
46
- ax.scatter(x, y)
47
-
48
- # Display the plot in Streamlit
49
- st.pyplot(fig)
50
-
51
- ############
52
-
53
- # Load data from Excel
54
- d = pd.read_excel('my_model/results/evaluation_results.xlsx', sheet_name="Main Data")
55
-
56
- token_counts = d['trimmed_tokens_count_caption_detic']
57
-
58
- # Assume 'accuracies' and 'token_counts' need to be computed or are columns in the DataFrame
59
- # Compute colors and labels for the plot (assuming these columns are already in the DataFrame)
60
- d['color'] = d['vqa_score_13b_caption+detic'].apply(lambda x: 'green' if x == 1 else 'orange' if round(x, 2) == 0.67 else 'red')
61
- d['label'] = d['vqa_score_13b_caption+detic'].apply(lambda x: 'Correct' if x == 1 else 'Partially Correct' if x == 0.67 else 'Incorrect')
62
-
63
- # Define colors and labels for the legend
64
-
65
- plt.figure(figsize=(10, 6))
66
- # Create a scatter plot with smaller dots using the 's' parameter
67
- scatter = plt.scatter(range(len(token_counts)), token_counts, c=d['color'], s=20, label=d['label'])
68
-
69
- # Create a custom legend
70
- from matplotlib.lines import Line2D
71
- legend_elements = [Line2D([0], [0], marker='o', color='w', label='Full VQA Score', markerfacecolor='green', markersize=10),
72
- Line2D([0], [0], marker='o', color='w', label='Partial VQA Score', markerfacecolor='orange', markersize=10),
73
- Line2D([0], [0], marker='o', color='w', label='Zero VQA Score', markerfacecolor='red', markersize=10)]
74
- #plt.legend(handles=legend_elements, loc='upper right')
75
- plt.legend(handles=legend_elements, loc='best', bbox_to_anchor=(1, 1))
76
- # Set the title and labels
77
- plt.title('Token Counts VS VQA Score')
78
- plt.xlabel('Index')
79
- plt.ylabel('Number of Tokens')
80
-
81
- # Display the plot
82
- plt.show()
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
- # Creating the scatter plot
91
- scatter_chart = alt.Chart(d).mark_circle(size=20).encode(
92
- x=alt.X('index:Q', title='Index'), # Assuming 'index' is a column or can be created
93
- y=alt.Y('token_counts:Q', title='Number of Tokens'),
94
- color=alt.Color('color:N', legend=alt.Legend(title="VQA Score")),
95
- tooltip=['index', 'token_counts', 'label']
96
- ).interactive()
97
-
98
- # Display the chart
99
- st.altair_chart(scatter_chart, use_container_width=True)
100
-
101
-
102
- ####################
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import altair as alt
3
+ import evaluation_config as config
 
 
 
4
  import streamlit as st
5
+ from PIL import Image
6
+ import pandas as pd
7
+ import random
8
+
9
+
10
+ class ResultDemonstrator:
11
+ """
12
+ A class to demonstrate the results of the Knowledge-Based Visual Question Answering (KB-VQA) model.
13
+
14
+ Attributes:
15
+ main_data (pd.DataFrame): Data loaded from an Excel file containing evaluation results.
16
+ sample_img_pool (list[str]): List of image file names available for demonstration.
17
+ model_names (list[str]): List of model names as defined in the configuration.
18
+ model_configs (list[str]): List of model configurations as defined in the configuration.
19
+ """
20
+
21
+ def __init__(self) -> None:
22
+ """
23
+ Initializes the ResultDemonstrator class by loading the data from an Excel file.
24
+ """
25
+ # Load data
26
+ self.main_data = pd.read_excel('evaluation_results.xlsx', sheet_name="Main Data")
27
+ self.sample_img_pool = list(os.listdir("demo"))
28
+ self.model_names = config.MODEL_NAMES
29
+ self.model_configs = config.MODEL_CONFIGURATIONS
30
+
31
+ @staticmethod
32
+ def display_table(data: pd.DataFrame) -> None:
33
+ """
34
+ Displays a DataFrame using Streamlit's dataframe display function.
35
+
36
+ Args:
37
+ data (pd.DataFrame): The data to display.
38
+ """
39
+ st.dataframe(data)
40
+
41
+ def calculate_and_append_data(self, data_list: list, score_column: str, model_config: str) -> None:
42
+ """
43
+ Calculates mean scores by category and appends them to the data list.
44
+
45
+ Args:
46
+ data_list (list): List to append new data rows.
47
+ score_column (str): Name of the column to calculate mean scores for.
48
+ model_config (str): Configuration of the model.
49
+ """
50
+ if score_column in self.main_data.columns:
51
+ category_means = self.main_data.groupby('question_category')[score_column].mean()
52
+ for category, mean_value in category_means.items():
53
+ data_list.append({
54
+ "Category": category,
55
+ "Configuration": model_config,
56
+ "Mean Value": round(mean_value * 100, 2)
57
+ })
58
+
59
+ def display_ablation_results_per_question_category(self) -> None:
60
+ """Displays ablation results per question category for each model configuration."""
61
+
62
+ score_types = ['vqa', 'vqa_gpt4', 'em', 'em_gpt4']
63
+ data_lists = {key: [] for key in score_types}
64
+ column_names = {
65
+ 'vqa': 'vqa_score_{config}',
66
+ 'vqa_gpt4': 'gpt4_vqa_score_{config}',
67
+ 'em': 'exact_match_score_{config}',
68
+ 'em_gpt4': 'gpt4_em_score_{config}'
69
+ }
70
+
71
+ for model_name in config.MODEL_NAMES:
72
+ for conf in config.MODEL_CONFIGURATIONS:
73
+ model_config = f"{model_name}_{conf}"
74
+ for score_type, col_template in column_names.items():
75
+ self.calculate_and_append_data(data_lists[score_type],
76
+ col_template.format(config=model_config),
77
+ model_config)
78
+
79
+ # Process and display results for each score type
80
+ for score_type, data_list in data_lists.items():
81
+ df = pd.DataFrame(data_list)
82
+ results_df = df.pivot(index='Category', columns='Configuration', values='Mean Value').applymap(
83
+ lambda x: f"{x:.2f}%")
84
+
85
+ with st.expander(f"{score_type.upper()} Scores per Question Category and Model Configuration"):
86
+ self.display_table(results_df)
87
+
88
+ def display_main_results(self) -> None:
89
+ """Displays the main model results from the Scores sheet, these are displayed from the file directly."""
90
+ main_scores = pd.read_excel('evaluation_results.xlsx', sheet_name="Scores", index_col=0)
91
+ st.markdown("### Main Model Results (Inclusive of Ablation Experiments)")
92
+ main_scores.reset_index()
93
+ self.display_table(main_scores)
94
+
95
+ def plot_token_count_vs_scores(self, conf: str, model_name: str, score_name: str = 'VQA Score') -> None:
96
+ """
97
+ Plots an interactive scatter plot comparing token counts to VQA or EM scores using Altair.
98
+
99
+ Args:
100
+ conf (str): The configuration name.
101
+ model_name (str): The name of the model.
102
+ score_name (str): The type of score to plot.
103
+ """
104
+
105
+ # Construct the full model configuration name
106
+ model_configuration = f"{model_name}_{conf}"
107
 
108
+ # Determine the score column name and legend mapping based on the score type
109
+ if score_name == 'VQA Score':
110
 
111
+ score_column_name = f"vqa_score_{model_configuration}"
112
+ scores = self.main_data[score_column_name]
113
+ # Map scores to categories for the legend
114
+ legend_map = ['Correct' if score == 1 else 'Partially Correct' if round(score, 2) == 0.67 else 'Incorrect' for score in scores]
115
+ color_scale = alt.Scale(domain=['Correct', 'Partially Correct', 'Incorrect'], range=['green', 'orange', 'red'])
116
+ else:
117
+ score_column_name = f"exact_match_score_{model_configuration}"
118
+ scores = self.main_data[score_column_name]
119
+ # Map scores to categories for the legend
120
+ legend_map = ['Correct' if score == 1 else 'Incorrect' for score in scores]
121
+ color_scale = alt.Scale(domain=['Correct', 'Incorrect'], range=['green', 'red'])
122
 
123
+ # Retrieve token counts from the data
124
+ token_counts = self.main_data[f'tokens_count_{conf}']
125
 
126
+ # Create a DataFrame for the scatter plot
127
+ scatter_data = pd.DataFrame({
128
+ 'Index': range(len(token_counts)),
129
+ 'Token Counts': token_counts,
130
+ score_name: legend_map
 
 
 
 
131
  })
132
+
133
+ # Create an interactive scatter plot using Altair
134
+ chart = alt.Chart(scatter_data).mark_circle(
135
+ size=60,
136
+ fillOpacity=1, # Sets the fill opacity to maximum
137
+ strokeWidth=1, # Adjusts the border width making the circles bolder
138
+ stroke='black' # Sets the border color to black
139
+ ).encode(
140
+ x=alt.X('Index', scale=alt.Scale(domain=[0, 1020])),
141
+ y=alt.Y('Token Counts', scale=alt.Scale(domain=[token_counts.min()-200, token_counts.max()+200])),
142
+ color=alt.Color(score_name, scale=color_scale, legend=alt.Legend(title=score_name)),
143
+ tooltip=['Index', 'Token Counts', score_name]
144
+ ).interactive() # Enables zoom & pan
145
+
146
+ chart = chart.properties(
147
+ title={
148
+ "text": f"Token Counts vs {score_name} + Score + ({model_configuration})",
149
+ "color": "black", # Optional color
150
+ "fontSize": 20, # Optional font size
151
+ "anchor": "middle", # Optional anchor position
152
+ "offset": 0 # Optional offset
153
+ },
154
+ width=700,
155
+ height=500
156
  )
157
+
158
+ # Display the interactive plot in Streamlit
 
 
159
  st.altair_chart(chart, use_container_width=True)
160
 
161
+ @staticmethod
162
+ def color_scores(value: float) -> str:
163
+ """
164
+ Applies color coding based on the score value.
165
+
166
+ Args:
167
+ value (float): The score value.
168
+
169
+ Returns:
170
+ str: CSS color style based on score value.
171
+ """
172
+
173
+ try:
174
+ value = float(value) # Convert to float to handle numerical comparisons
175
+ except ValueError:
176
+ return 'color: black;' # Return black if value is not a number
177
+
178
+ if value == 1.0:
179
+ return 'color: green;'
180
+ elif value == 0.0:
181
+ return 'color: red;'
182
+ elif value == 0.67:
183
+ return 'color: orange;'
184
+ return 'color: black;'
185
+
186
+ def show_samples(self, num_samples: int = 3) -> None:
187
+ """
188
+ Displays random sample images and their associated models answers and evaluations.
189
+
190
+ Args:
191
+ num_samples (int): Number of sample images to display.
192
+ """
193
+
194
+ # Sample images from the pool
195
+ target_imgs = random.sample(self.sample_img_pool, num_samples)
196
+ # Generate model configurations
197
+ model_configs = [f"{model_name}_{conf}" for model_name in self.model_names for conf in self.model_configs]
198
+ # Define column names for scores dynamically
199
+ column_names = {
200
+ 'vqa': 'vqa_score_{config}',
201
+ 'vqa_gpt4': 'gpt4_vqa_score_{config}',
202
+ 'em': 'exact_match_score_{config}',
203
+ 'em_gpt4': 'gpt4_em_score_{config}'
204
+ }
205
+
206
+ for img_filename in target_imgs:
207
+ image_data = self.main_data[self.main_data['image_filename'] == img_filename]
208
+ im = Image.open(f"demo/{img_filename}")
209
+ col1, col2 = st.columns([1, 2]) # to display images side by side with their data.
210
+ # Create a container for each image
211
+ with st.container():
212
+ st.write("-------------------------------")
213
+ with col1:
214
+ st.image(im, use_column_width=True)
215
+ with st.expander('Show Caption'):
216
+ st.text(image_data.iloc[0]['caption'])
217
+ with st.expander('Show DETIC Objects'):
218
+ st.text(image_data.iloc[0]['objects_detic_trimmed'])
219
+ with st.expander('Show YOLOv5 Objects'):
220
+ st.text(image_data.iloc[0]['objects_yolov5'])
221
+ with col2:
222
+ if not image_data.empty:
223
+ st.write(f"**Question: {image_data.iloc[0]['question']}**")
224
+ st.write(f"**Ground Truth Answers:** {image_data.iloc[0]['raw_answers']}")
225
+
226
+ # Initialize an empty DataFrame for summary data
227
+ summary_data = pd.DataFrame(
228
+ columns=['Model Configuration', 'Answer', 'VQA Score', 'VQA Score (GPT-4)', 'EM Score',
229
+ 'EM Score (GPT-4)'])
230
+
231
+ for config in model_configs:
232
+ # Collect data for each model configuration
233
+ row_data = {
234
+ 'Model Configuration': config,
235
+ 'Answer': image_data.iloc[0].get(f'{config}', '-')
236
+ }
237
+ for score_type, score_template in column_names.items():
238
+ score_col = score_template.format(config=config)
239
+ score_value = image_data.iloc[0].get(score_col, '-')
240
+ if pd.notna(score_value) and not isinstance(score_value, str):
241
+ # Format score to two decimals if it's a valid number
242
+ score_value = f"{float(score_value):.2f}"
243
+ row_data[score_type.replace('_', ' ').title()] = score_value
244
+
245
+ # Convert row data to a DataFrame and concatenate it
246
+ rd = pd.DataFrame([row_data])
247
+ rd.columns = summary_data.columns
248
+ summary_data = pd.concat([summary_data, rd], axis=0, ignore_index=True)
249
+
250
+ # Apply styling to DataFrame for score coloring
251
+ styled_summary = summary_data.style.applymap(self.color_scores,
252
+ subset=['VQA Score', 'VQA Score (GPT-4)',
253
+ 'EM Score',
254
+ 'EM Score (GPT-4)'])
255
+ st.markdown(styled_summary.to_html(escape=False, index=False), unsafe_allow_html=True)
256
+ else:
257
+ st.write("No data available for this image.")
258
+
259
+ def run_demo(self):
260
+ """
261
+ Run the interactive Streamlit demo for visualizing model evaluation results and analysis.
262
+ """
263
+
264
+ col1, col2 = st.columns([1, 4])
265
+ with col1:
266
+ # User selects the evaluation analysis aspect
267
+ section_type = st.radio("Select Evaluation Aspect", ["Evaluation Results & Analysis", 'Evaluation Samples'])
268
+
269
+ # Only show analysis type if the section type is "Evaluation Results & Analysis"
270
+ if section_type == "Evaluation Results & Analysis":
271
+ analysis_type = st.radio("Select Type", ["Main & Ablation Results", "Results per Question Category",
272
+ "Prompt Length (token count) Impact on Performance"], index=2)
273
+ if analysis_type == "Prompt Length (token count) Impact on Performance":
274
+ # Based on the selection, other options appear
275
+ model_name = st.radio("Select Model Size", self.model_names)
276
+ score_name = st.radio("Select Score Type", ["VQA Score", "Exact Match"])
277
 
278
+ elif section_type == 'Evaluation Samples':
279
+ samples_button = st.button("Generate Random Samples")
280
+ with col2:
281
+ if section_type == "Evaluation Results & Analysis":
282
+ if analysis_type == "Prompt Length (token count) Impact on Performance":
283
+ for conf in self.model_configs:
284
+ with st.expander(conf):
285
+ self.plot_token_count_vs_scores(conf, model_name, score_name)
286
+ elif analysis_type == "Main & Ablation Results":
287
+ self.display_main_results()
288
+ elif analysis_type == "Results per Question Category":
289
+ self.display_ablation_results_per_question_category()
290
+ elif section_type == 'Evaluation Samples':
291
+ if samples_button:
292
+ self.show_samples(3)