GroundFlow / QWENVL-PRIVATE /analysis /analysis_auto.py
TerryPei's picture
sync QWENVL-PRIVATE from /opt/tiger/thothvl_pretrain (HF tokens redacted)
070f264 verified
Raw History Blame Contribute Delete
22.6 kB
import os
import glob
import pickle
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# ==========================================
# 1. DATA LOADING & PROCESSING
# ==========================================
def load_data_for_task(base_dir, task_name, model_pattern):
"""
Loads pickle files matching the model_pattern for a specific task.
Returns:
- samples_data: dict of sample-level data (scores, visual tokens)
- metadata: dict containing 'e2e_latency', 'total_tokens', etc.
"""
# Construct search pattern. logic handles wildcards if passed in model_pattern
search_pattern = os.path.join(base_dir, f"{task_name}_analysis_rank*_{model_pattern}*.pkl")
files = glob.glob(search_pattern)
if not files:
print(f" [Warning] No files found for task '{task_name}' with pattern '*{model_pattern}*'")
return {}, {}
merged_samples = {}
aggregated_metadata = {"e2e_latency": 0, "total_tokens": 0}
found_metadata = False
for file_path in files:
try:
with open(file_path, "rb") as f:
data_chunk = pickle.load(f)
# Separate metric_dict from sample data
if 'metric_dict' in data_chunk:
m_dict = data_chunk.pop('metric_dict')
# Sum latencies/tokens if split across multiple files (rare but safe)
aggregated_metadata['e2e_latency'] += m_dict.get('e2e_latency', 0)
aggregated_metadata['total_tokens'] += m_dict.get('total_tokens', 0)
found_metadata = True
merged_samples.update(data_chunk)
except Exception as e:
print(f" [Error] Failed to load {file_path}: {e}")
if not found_metadata:
print(f" [Info] No 'metric_dict' found in files for {task_name}.")
return merged_samples, aggregated_metadata
def extract_metrics(task_data_map, task_meta_map):
"""
Calculates average metrics (Throughput, Avg Visual Tokens) from raw data.
"""
visual_tokens = []
# 1. Extract Visual Token Counts
for doc_id, content in task_data_map.items():
if "visual_token_num" in content:
val = content["visual_token_num"]
# Handle if it's a tensor/array or scalar
if hasattr(val, 'item'):
val = val.item()
visual_tokens.append(float(val))
avg_vis_tokens = np.mean(visual_tokens) if visual_tokens else 0
# 2. Calculate Throughput (Samples / Second)
# Total samples in this dataset
n_samples = len(task_data_map)
# Total time (e2e_latency is usually in seconds, ensure your logger units match)
total_time = task_meta_map.get('e2e_latency', 0)
if total_time > 0:
throughput = n_samples / total_time
else:
throughput = 0
return {
"avg_visual_tokens": avg_vis_tokens,
"throughput": throughput,
"n_samples": n_samples
}
# ==========================================
# 2. PLOTTING FUNCTIONS
# ==========================================
def plot_tradeoff_curves(results, save_dir):
"""
Plots Accuracy vs. Efficiency (Throughput) and Accuracy vs. Visual Tokens.
Handles Baseline, Ours, and RoI curves with distinct styles.
"""
labels = list(results.keys())
# Calculate Macro Averages (across all tasks for each config)
avg_accs = []
avg_throughputs = []
avg_tokens = []
for label in labels:
metrics = results[label]
# Calculate mean of values; ensure we handle potential empty lists safely
acc_values = [m['acc'] for m in metrics.values()]
th_values = [m['throughput'] for m in metrics.values()]
tok_values = [m['avg_visual_tokens'] for m in metrics.values()]
avg_accs.append(np.mean(acc_values) if acc_values else 0)
avg_throughputs.append(np.mean(th_values) if th_values else 0)
avg_tokens.append(np.mean(tok_values) if tok_values else 0)
# --- STYLE DEFINITIONS ---
# Customize colors and markers here
styles = {
'Baseline': {'marker': 'o', 'linestyle': '--', 'color': '#7f8c8d'}, # Grey, Dashed, Circle
'Ours': {'marker': '*', 'linestyle': '-', 'color': '#e74c3c'}, # Red, Solid, Star
'RoI': {'marker': 's', 'linestyle': '-', 'color': '#3498db'} # Blue, Solid, Square
}
# Helper function to generate both plots without code duplication
def create_plot(x_data, x_label, file_name, title):
plt.figure(figsize=(10, 7))
sns.set_theme(style="whitegrid") # Optional: makes grid look nice
# Identify unique prefixes (Baseline, Ours, RoI) from the config keys
prefixes = sorted(list(set([l.split('\n')[0] for l in labels])))
# Sort prefixes to ensure Legend order (e.g. Baseline -> Ours -> RoI) if desired
# You can manually force order: prefixes = ['Baseline', 'Ours', 'RoI']
for prefix in prefixes:
# Get style, default to black if unknown prefix
style = styles.get(prefix, {'marker': 'D', 'linestyle': '-.', 'color': 'black'})
# Filter data for this group
group_indices = [i for i, l in enumerate(labels) if l.startswith(prefix)]
if not group_indices: continue
g_x = [x_data[i] for i in group_indices]
g_y = [avg_accs[i] for i in group_indices]
g_labels = [labels[i] for i in group_indices]
# Sort points by X-axis (efficiency or tokens) to draw correct line
sorted_pairs = sorted(zip(g_x, g_y, g_labels), key=lambda x: x[0])
s_x, s_y, s_l = zip(*sorted_pairs)
# Plot Line
plt.plot(s_x, s_y,
marker=style['marker'],
linestyle=style['linestyle'],
color=style['color'],
label=prefix,
linewidth=2,
markersize=10 if style['marker'] != '*' else 14) # Stars need to be larger
# Annotate Points (Labels like "Max-256")
for x, y, lbl in zip(s_x, s_y, s_l):
clean_lbl = lbl.split('\n')[-1]
# Dynamic offsets to prevent overlapping labels
offset = (0, 10)
if prefix == 'RoI':
offset = (0, -15)
elif prefix == 'Ours':
offset = (-10, 10)
plt.annotate(clean_lbl, (x, y), xytext=offset, textcoords='offset points',
ha='center', fontsize=8, fontweight='bold',
bbox=dict(boxstyle="round,pad=0.2", fc="white", alpha=0.6, ec="none"))
plt.title(title, fontsize=14)
plt.xlabel(x_label, fontweight='bold')
plt.ylabel('Average Accuracy (%)', fontweight='bold')
plt.grid(True, linestyle=':', alpha=0.6)
plt.legend(fontsize=10)
plt.tight_layout()
save_path = os.path.join(save_dir, file_name)
plt.savefig(save_path, dpi=300)
plt.close()
print(f"Saved plot: {save_path}")
# 1. Plot Efficiency
create_plot(avg_throughputs,
'Average Throughput (Samples / Second) \n← Slower | Faster →',
"acc_vs_efficiency.png",
"Accuracy vs. Efficiency Trade-off")
# 2. Plot Visual Tokens
create_plot(avg_tokens,
'Average Visual Tokens per Image \n← Cheaper | More Detailed →',
"acc_vs_visual_tokens.png",
"Accuracy vs. Visual Cost")
# ==========================================
# 3. EXISTING UTILS (Preserved)
# ==========================================
def print_threshold_ratios(task_data_map, threshold):
print(f"\n{'=' * 20} Threshold Analysis (Score > {threshold}) {'=' * 20}")
print(f"{'Dataset Name':<20} | {'Total Samples':<15} | {'Above Thr':<10} | {'Ratio':<10}")
print("-" * 65)
for task_name, content in task_data_map.items():
scores = content['scores']
if len(scores) == 0: continue
above_mask = scores > threshold
count_above = np.sum(above_mask)
ratio = count_above / len(scores)
print(f"{task_name:<20} | {len(scores):<15} | {count_above:<10} | {ratio:.2%}")
print("-" * 65 + "\n")
def extract_scores_list(data_dict):
scores = []
for doc_id, content in data_dict.items():
if "high_res_pred_score" in content:
val = content["high_res_pred_score"]
if isinstance(val, np.ndarray): val = val.flatten()[0]
scores.append(float(val))
return np.array(scores)
def plot_cost_benefit_combo(results, task_name, save_dir):
"""
Better Idea: Combines Relative Efficiency (Bars) with Accuracy Gain (Points/Lines)
on a dual-axis chart.
"""
# 1. Parse Data (Similar to above, but also gets Accuracy)
data_map = {}
for label, metrics in results.items():
method, resolution = label.split('\n')
if resolution not in data_map: data_map[resolution] = {}
task_data = metrics.get(task_name, {'throughput': 0, 'acc': 0})
data_map[resolution][method] = {
'speed': task_data['throughput'],
'acc': task_data['acc']
}
resolutions = sorted(data_map.keys(), key=lambda x: int(x.split('-')[-1]))
methods = ['Baseline', 'Ours', 'RoI']
colors = {'Baseline': '#95a5a6', 'Ours': '#e74c3c', 'RoI': '#3498db'}
x = np.arange(len(resolutions))
width = 0.25
fig, ax1 = plt.subplots(figsize=(11, 7))
ax2 = ax1.twinx() # Create secondary y-axis for Accuracy Gain
# Loop for Bars (Efficiency) AND Points (Accuracy)
for i, method in enumerate(methods):
eff_ratios = []
acc_gains = []
for res in resolutions:
base_speed = data_map[res].get('Baseline', {}).get('speed', 1.0)
base_acc = data_map[res].get('Baseline', {}).get('acc', 0.0)
curr = data_map[res].get(method, {'speed': 0, 'acc': 0})
# Efficiency Ratio
ratio = (curr['speed'] / base_speed) * 100 if base_speed > 0 else 0
eff_ratios.append(ratio)
# Accuracy Delta (Gain over Baseline of SAME resolution)
delta = curr['acc'] - base_acc
acc_gains.append(delta)
offset = (i - 1) * width
# PLOT 1: Efficiency Bars (Left Axis)
ax1.bar(x + offset, eff_ratios, width, color=colors[method], alpha=0.3, label=f"{method} (Speed)")
# PLOT 2: Accuracy Gain Markers (Right Axis)
# We plot these slightly offset to align with bars, or connected with lines
if method == 'Baseline':
# Plot baseline as flat line at 0
ax2.plot(x + offset, acc_gains, color=colors[method], marker='_', markersize=20, linestyle='None')
else:
# Plot gains as distinct markers
ax2.plot(x + offset, acc_gains, color=colors[method], marker='D', markersize=8,
linestyle='-', linewidth=2, label=f"{method} (Acc Gain)")
# Annotate specific gains
for j, gain in enumerate(acc_gains):
ax2.annotate(f"+{gain:.1f}", (x[j] + offset, gain), xytext=(0, 8),
textcoords='offset points', ha='center', color=colors[method], fontweight='bold')
# Formatting
ax1.set_ylabel("Relative Efficiency (%)", fontsize=12, fontweight='bold')
ax1.set_ylim(0, 120)
ax1.set_xlabel("Resolution", fontsize=12)
ax1.set_xticks(x)
ax1.set_xticklabels(resolutions)
ax2.set_ylabel("Accuracy Gain over Baseline (Points)", fontsize=12, fontweight='bold')
# Set symmetric limits or auto limits for acc gain
ax2.axhline(0, color='gray', linestyle=':', linewidth=1)
plt.title(f"Cost-Benefit Analysis: {task_name}\n(Bars = Speed %, Points = Acc Gain)", fontsize=14)
# Custom Legend
lines, labels = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines + lines2, labels + labels2, loc='upper left')
plt.tight_layout()
save_path = os.path.join(save_dir, f"{task_name}_cost_benefit.png")
plt.savefig(save_path, dpi=300)
plt.close()
print(f"Saved Cost-Benefit plot: {save_path}")
# ==========================================
# 4. MAIN EXECUTION
# ==========================================
if __name__ == "__main__":
BASE_DIR = "../archive_data/"
SAVE_DIR = "./analysis_plots/"
os.makedirs(SAVE_DIR, exist_ok=True)
# --- CONFIGURATION: Define Models and Manual Accuracies ---
# Keys: Display Label (First line is Group, second is variant)
# Values: 'pattern': filename part to match, 'accs': dict of {task: accuracy}
TARGET_TASKS = [
"docvqa_val",
"infovqa_val",
"textvqa_val",
"chartqa", # Added from table
"ocrbench" # Added from table
# "vstar_bench",
# "mmerealworld_lite",
]
# --- CONFIGURATION: Define Models and Manual Accuracies ---
# Patterns are derived from: Visual_Tokens * 28 * 28
# 128 tokens = 100352 pixels
# 256 tokens = 200704 pixels
# 576 tokens = 451584 pixels
# 1024 tokens = 802816 pixels
# 2048 tokens = 1605632 pixels
MODEL_CONFIGS = {
# --- BASELINE CONFIGURATIONS ---
# 'Baseline\nMax-256': {
# 'pattern': 'Qwen2.5-VL-7B-Instruct_min100352_max200704',
# 'accs': {
# 'docvqa_val': 81.2,
# 'chartqa': 76.8,
# 'ocrbench': 77.8,
# 'infovqa_val': 54.0,
# 'textvqa_val': 75.5,
# 'vstar_bench': 53.4,
# 'mmerealworld_lite': 27.6
# }
# },
'Baseline\nMax-576': {
'pattern': 'Qwen2.5-VL-7B-Instruct_min200704_max451584',
'accs': {
'docvqa_val': 92.0,
'chartqa': 83.0,
'ocrbench': 82.8,
'infovqa_val': 70.1,
'textvqa_val': 81.1,
'vstar_bench': 64.4,
'mmerealworld_lite': 35.4
}
},
'Baseline\nMax-1024': {
'pattern': 'Qwen2.5-VL-7B-Instruct_min200704_max802816',
'accs': {
'docvqa_val': 93.9,
'chartqa': 84.6,
'ocrbench': 84.4,
'infovqa_val': 76.5,
'textvqa_val': 82.8,
'vstar_bench': 70.2,
'mmerealworld_lite': 37.5
}
},
'Baseline\nMax-2048': {
'pattern': 'Qwen2.5-VL-7B-Instruct_min200704_max1605632',
'accs': {
'docvqa_val': 94.7,
'chartqa': 84.5,
'ocrbench': 84.8,
'infovqa_val': 80.4,
'textvqa_val': 82.9,
'vstar_bench': 72.3,
'mmerealworld_lite': 39.3
}
},
#
# # # # --- YOUR "OURS" CONFIGURATIONS (Update these patterns as needed) ---
# 'Ours\nMax-256': {
# 'pattern': 'qwen2_5vl-7b-roi-K18T3-134k-gatedv2PB-wGTwRoI-twiginit-filled_min100352_max200704',
# 'accs': {
# 'docvqa_val': 89.1,
# 'chartqa': 82.5,
# 'ocrbench': 83.8,
# 'infovqa_val': 69.6,
# 'textvqa_val': 81.8,
# 'vstar_bench': 67.5,
# 'mmerealworld_lite': 37.3
# }
# },
# 'Ours\nMax-576': {
# 'pattern': 'qwen2_5vl-7b-roi-K18T3-134k-gatedv2PB-wGTwRoI-twiginit-filled_min200704_max451584',
# 'accs': {
# 'docvqa_val': 93.9,
# 'chartqa': 84.2,
# 'ocrbench': 84.8,
# 'infovqa_val': 78.5,
# 'textvqa_val': 82.8,
# 'vstar_bench': 80.1,
# 'mmerealworld_lite': 43.4
# }
# },
# 'Ours\nMax-1024': {
# 'pattern': 'qwen2_5vl-7b-roi-K18T3-134k-gatedv2PB-wGTwRoI-twiginit-filled_min200704_max802816',
# 'accs': {
# 'docvqa_val': 94.6,
# 'chartqa': 85.4,
# 'ocrbench': 84.8,
# 'infovqa_val': 81.5,
# 'textvqa_val': 83.7,
# 'vstar_bench': 83.3,
# 'mmerealworld_lite': 46.5
# }
# },
# 'Ours\nMax-1536': {
# 'pattern': 'qwen2_5vl-7b-roi-K18T3-134k-gatedv2PB-wGTwRoI-twiginit-filled_min200704_max1204224',
# 'accs': {
# 'docvqa_val': 94.8,
# 'chartqa': 85.4,
# 'ocrbench': 85.4,
# 'infovqa_val': 82.4,
# 'textvqa_val': 83.6,
# 'vstar_bench': 88.0,
# 'mmerealworld_lite': 47.2
# }
# },
###--- "OURS-RoI" CONFIGURATIONS ---
'RoI\nMax-256': {
'pattern': 'qwen2_5vl-7b-roi-K18T3-185k-v1bf16-denoisedv2-twiginit-filled_min100352_max200704',
'accs': {
'docvqa_val': 89.2, 'chartqa': 83.0, 'ocrbench': 83.6, 'infovqa_val': 69.9,
'textvqa_val': 81.4, 'vstar_bench': 67.5, 'mmerealworld_lite': 37.6
}
},
'RoI\nMax-576': {
'pattern': 'qwen2_5vl-7b-roi-K18T3-185k-v1bf16-denoisedv2-twiginit-filled_min200704_max451584',
'accs': {
'docvqa_val': 94.1, 'chartqa': 85.8, 'ocrbench': 84.9, 'infovqa_val': 79.6,
'textvqa_val': 82.9, 'vstar_bench': 80.6, 'mmerealworld_lite': 44.6
}
},
'RoI\nMax-1024': {
'pattern': 'qwen2_5vl-7b-roi-K18T3-185k-v1bf16-denoisedv2-twiginit-filled_min200704_max802816',
'accs': {
'docvqa_val': 94.7, 'chartqa': 86.3, 'ocrbench': 84.8, 'infovqa_val': 82.0,
'textvqa_val': 83.9, 'vstar_bench': 82.2, 'mmerealworld_lite': 46.7
}
},
# 'RoI\nMax-1536': {
# 'pattern': 'qwen2_5vl-7b-roi-K18T3-185k-v1bf16-denoisedv2-twiginit-filled_min200704_max1204224',
# 'accs': {
# 'docvqa_val': 94.9, 'chartqa': 85.8, 'ocrbench': 84.8, 'infovqa_val': 83.2,
# 'textvqa_val': 83.9, 'vstar_bench': 88.0, 'mmerealworld_lite': 47.0
# }
# },
# # # --- YOUR "OURS" CONFIGURATIONS (Update these patterns as needed) ---
'Ours\nMax-1024': {
'pattern': 'qwen2_5vl-7b-roi-K18T3-134k-gatedv2PB-stage3_min200704_max802816',
'accs': {
'docvqa_val': 94.8,
'chartqa': 86.4,
'ocrbench': 85.4,
'infovqa_val': 81.2,
'textvqa_val': 84.1,
'vstar_bench': 83.8,
'mmerealworld_lite': 47.3
}
},
'Ours\nMax-576': {
'pattern': 'qwen2_5vl-7b-roi-K18T3-134k-gatedv2PB-stage3_min200704_max451584',
'accs': {
'docvqa_val': 94.0,
'chartqa': 85.4,
'ocrbench': 85.4,
'infovqa_val': 79.4,
'textvqa_val': 83.5,
'vstar_bench': 79.6,
'mmerealworld_lite': 45.7
}
},
'Ours\nMax-384': {
'pattern': 'qwen2_5vl-7b-roi-K18T3-134k-gatedv2PB-stage3_min100352_max301056',
'accs': {
'docvqa_val': 93.4,
'chartqa': 84.2,
'ocrbench': 85.3,
'infovqa_val': 75.7,
'textvqa_val': 82.7,
'vstar_bench': 73.8,
'mmerealworld_lite': 41.2
}
},
'Ours\nMax-256': {
'pattern': 'qwen2_5vl-7b-roi-K18T3-134k-gatedv2PB-stage3_min100352_max200704',
'accs': {
'docvqa_val': 90.4,
'chartqa': 82.7,
'ocrbench': 83.5,
'infovqa_val': 71.7,
'textvqa_val': 81.8,
'vstar_bench': 68.6,
'mmerealworld_lite': 37.9
}
},
# 'Ours\nMax-1536': {
# 'pattern': 'qwen2_5vl-7b-roi-K18T3-post7k-repos-full2_min200704_max1204224',
# 'accs': {
# 'docvqa_val': 95.0,
# 'chartqa': 86.4,
# 'ocrbench': 84.8,
# 'infovqa_val': 82.6,
# 'textvqa_val': 84.3,
# 'vstar_bench': 86.9,
# 'mmerealworld_lite': 48.2
# }
# },
}
print("--- Loading Data for Comparison ---")
# Store all results here: { "Baseline\nMax-256": { "textvqa": {throughput, tokens, acc}, ... } }
overall_results = {}
for label, config in MODEL_CONFIGS.items():
print(f"Processing config: {label.replace(chr(10), ' ')}") # chr(10) is newline
overall_results[label] = {}
# Load data for each task for this specific config
for task in TARGET_TASKS:
# 1. Load Raw Data
raw_data, metadata = load_data_for_task(BASE_DIR, task, config['pattern'])
if not raw_data:
# Fill dummy if missing to avoid crash, or skip
overall_results[label][task] = {'throughput': 0, 'avg_visual_tokens': 0, 'acc': 0}
continue
# 2. Extract Metrics (Speed, Tokens)
metrics = extract_metrics(raw_data, metadata)
# 3. Add Manual Accuracy
acc = config['accs'].get(task, 0.0) # Default 0 if task missing in config
# 4. Store Scores for Histograms (Optional, if you want detailed score plots per config)
# scores = extract_scores_list(raw_data)
# Save combined
overall_results[label][task] = {
'throughput': metrics['throughput'],
'avg_visual_tokens': metrics['avg_visual_tokens'],
'acc': acc,
# 'scores': scores # Uncomment if you want to pass this to the histogram plotter
}
print(
f" > {task}: {metrics['throughput']:.1f} samp/s, {metrics['avg_visual_tokens']:.0f} tokens, Acc: {acc}")
# --- PLOT COMPARISONS ---
if overall_results:
print("\n--- Generating Comparison Plots ---")
plot_tradeoff_curves(overall_results, SAVE_DIR)
print("\nDone! Plots saved to:", SAVE_DIR)
for i in range(len(TARGET_TASKS)):
plot_cost_benefit_combo(overall_results, TARGET_TASKS[i], SAVE_DIR)