|
|
import os |
|
|
import json |
|
|
import pandas as pd |
|
|
from pathlib import Path |
|
|
from collections import defaultdict |
|
|
|
|
|
def parse_error_analysis(vis_dir): |
|
|
"""Parse the error_analysis.txt file to get accuracy and misclassification details""" |
|
|
metrics = {} |
|
|
class_accuracies = {} |
|
|
misclassified_files = [] |
|
|
|
|
|
with open(os.path.join(vis_dir, 'error_analysis.txt'), 'r') as f: |
|
|
lines = f.readlines() |
|
|
parsing_errors = False |
|
|
header_found = False |
|
|
|
|
|
for line in lines: |
|
|
|
|
|
if line.startswith("Overall Accuracy:"): |
|
|
metrics['overall_accuracy'] = float(line.split(":")[1].strip().rstrip('%')) / 100 |
|
|
|
|
|
|
|
|
if "samples)" in line and ":" in line: |
|
|
class_name = line.split(":")[0].strip() |
|
|
accuracy = float(line.split(":")[1].split("%")[0].strip()) / 100 |
|
|
samples = int(line.split("(")[1].split(" ")[0]) |
|
|
class_accuracies[class_name] = { |
|
|
'accuracy': accuracy, |
|
|
'samples': samples |
|
|
} |
|
|
|
|
|
|
|
|
if "Misclassified Videos:" in line: |
|
|
parsing_errors = True |
|
|
continue |
|
|
if "Filename" in line and "True Class" in line: |
|
|
header_found = True |
|
|
continue |
|
|
if parsing_errors and header_found and line.strip() and not line.startswith("-"): |
|
|
try: |
|
|
|
|
|
parts = line.strip().split() |
|
|
|
|
|
confidence_idx = next(i for i, part in enumerate(parts) if part.endswith('%')) |
|
|
|
|
|
filename = ' '.join(parts[:confidence_idx-2]) |
|
|
true_class = parts[confidence_idx-2] |
|
|
pred_class = parts[confidence_idx-1] |
|
|
confidence = float(parts[confidence_idx].rstrip('%')) / 100 |
|
|
|
|
|
misclassified_files.append({ |
|
|
'filename': filename, |
|
|
'true_class': true_class, |
|
|
'predicted_class': pred_class, |
|
|
'confidence': confidence |
|
|
}) |
|
|
except Exception as e: |
|
|
print(f"Warning: Could not parse line: {line.strip()}") |
|
|
continue |
|
|
|
|
|
metrics['class_accuracies'] = class_accuracies |
|
|
metrics['misclassified_files'] = misclassified_files |
|
|
return metrics |
|
|
|
|
|
def analyze_trial(trial_dir): |
|
|
"""Analyze all visualization directories in a trial and aggregate results""" |
|
|
trial_metrics = { |
|
|
'overall_accuracy': 0, |
|
|
'total_samples': 0, |
|
|
'class_accuracies': defaultdict(lambda: {'correct': 0, 'total': 0}), |
|
|
'misclassified_files': [] |
|
|
} |
|
|
|
|
|
|
|
|
vis_dirs = [d for d in trial_dir.iterdir() if d.is_dir() and d.name.startswith('visualization_')] |
|
|
if not vis_dirs: |
|
|
return None |
|
|
|
|
|
for vis_dir in vis_dirs: |
|
|
try: |
|
|
metrics = parse_error_analysis(vis_dir) |
|
|
|
|
|
|
|
|
samples = sum(m['samples'] for m in metrics['class_accuracies'].values()) |
|
|
trial_metrics['total_samples'] += samples |
|
|
trial_metrics['overall_accuracy'] += metrics['overall_accuracy'] * samples |
|
|
|
|
|
|
|
|
for class_name, class_metrics in metrics['class_accuracies'].items(): |
|
|
trial_metrics['class_accuracies'][class_name]['correct'] += ( |
|
|
class_metrics['accuracy'] * class_metrics['samples'] |
|
|
) |
|
|
trial_metrics['class_accuracies'][class_name]['total'] += class_metrics['samples'] |
|
|
|
|
|
|
|
|
for error in metrics['misclassified_files']: |
|
|
error['vis_dir'] = vis_dir.name |
|
|
trial_metrics['misclassified_files'].append(error) |
|
|
|
|
|
except Exception as e: |
|
|
print(f"Error processing visualization directory {vis_dir}: {e}") |
|
|
|
|
|
|
|
|
if trial_metrics['total_samples'] > 0: |
|
|
trial_metrics['overall_accuracy'] /= trial_metrics['total_samples'] |
|
|
|
|
|
for class_metrics in trial_metrics['class_accuracies'].values(): |
|
|
if class_metrics['total'] > 0: |
|
|
class_metrics['accuracy'] = class_metrics['correct'] / class_metrics['total'] |
|
|
|
|
|
return trial_metrics |
|
|
|
|
|
def analyze_trials(hyperparam_dir): |
|
|
results = { |
|
|
'search_dirs': defaultdict(lambda: { |
|
|
'best_overall': {'accuracy': 0, 'trial': None}, |
|
|
'best_per_class': defaultdict(lambda: {'accuracy': 0, 'trial': None}), |
|
|
'misclassified_files': [] |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
for search_dir in Path(hyperparam_dir).iterdir(): |
|
|
if not search_dir.is_dir() or not search_dir.name.startswith('search_'): |
|
|
continue |
|
|
|
|
|
|
|
|
for trial_dir in search_dir.iterdir(): |
|
|
if not trial_dir.is_dir() or not trial_dir.name.startswith('trial_'): |
|
|
continue |
|
|
|
|
|
trial_metrics = analyze_trial(trial_dir) |
|
|
if trial_metrics is None: |
|
|
continue |
|
|
|
|
|
search_results = results['search_dirs'][search_dir.name] |
|
|
|
|
|
|
|
|
if trial_metrics['overall_accuracy'] > search_results['best_overall']['accuracy']: |
|
|
search_results['best_overall']['accuracy'] = trial_metrics['overall_accuracy'] |
|
|
search_results['best_overall']['trial'] = trial_dir.name |
|
|
|
|
|
|
|
|
for class_name, class_metrics in trial_metrics['class_accuracies'].items(): |
|
|
if class_metrics['accuracy'] > search_results['best_per_class'][class_name]['accuracy']: |
|
|
search_results['best_per_class'][class_name]['accuracy'] = class_metrics['accuracy'] |
|
|
search_results['best_per_class'][class_name]['trial'] = trial_dir.name |
|
|
|
|
|
|
|
|
search_results['misclassified_files'].extend(trial_metrics['misclassified_files']) |
|
|
|
|
|
return results |
|
|
|
|
|
def save_analysis_report(results, hyperparam_dir): |
|
|
output_file = os.path.join(hyperparam_dir, 'trial_analysis_report.txt') |
|
|
|
|
|
with open(output_file, 'w') as f: |
|
|
for search_dir, search_results in results['search_dirs'].items(): |
|
|
f.write(f"\n=== Results for {search_dir} ===\n") |
|
|
f.write("-" * 80 + "\n") |
|
|
|
|
|
|
|
|
f.write("\nBest Overall Model:\n") |
|
|
f.write(f"Trial: {search_results['best_overall']['trial']}\n") |
|
|
f.write(f"Accuracy: {search_results['best_overall']['accuracy']:.2%}\n") |
|
|
|
|
|
|
|
|
f.write("\nBest Model Per Class:\n") |
|
|
f.write(f"{'Class':<20} {'Accuracy':<10} {'Trial'}\n") |
|
|
f.write("-" * 60 + "\n") |
|
|
for class_name, data in search_results['best_per_class'].items(): |
|
|
f.write(f"{class_name:<20} {data['accuracy']:.2%} {data['trial']}\n") |
|
|
|
|
|
|
|
|
f.write("\nMost Frequently Misclassified Files:\n") |
|
|
f.write(f"{'Filename':<40} {'True Class':<15} {'Predicted':<15} {'Confidence':<10} {'Dataset'}\n") |
|
|
f.write("-" * 100 + "\n") |
|
|
|
|
|
|
|
|
misclassified = sorted(search_results['misclassified_files'], |
|
|
key=lambda x: x['confidence']) |
|
|
for error in misclassified[:10]: |
|
|
f.write(f"{error['filename']:<40} {error['true_class']:<15} " |
|
|
f"{error['predicted_class']:<15} {error['confidence']:<10.2%} {error['vis_dir']}\n") |
|
|
|
|
|
f.write("\n" + "=" * 80 + "\n") |
|
|
|
|
|
def print_results(results): |
|
|
"""Print a summary of the analysis results""" |
|
|
for search_dir, search_results in results['search_dirs'].items(): |
|
|
print(f"\n=== Results for {search_dir} ===") |
|
|
print("-" * 80) |
|
|
|
|
|
|
|
|
print(f"\nBest Overall Model:") |
|
|
print(f"Trial: {search_results['best_overall']['trial']}") |
|
|
print(f"Accuracy: {search_results['best_overall']['accuracy']:.2%}") |
|
|
|
|
|
|
|
|
print(f"\nBest Model Per Class:") |
|
|
print(f"{'Class':<20} {'Accuracy':<10} {'Trial'}") |
|
|
print("-" * 60) |
|
|
for class_name, data in search_results['best_per_class'].items(): |
|
|
print(f"{class_name:<20} {data['accuracy']:.2%} {data['trial']}") |
|
|
|
|
|
|
|
|
print(f"\nTop 5 Most Problematic Files:") |
|
|
print(f"{'Filename':<40} {'True Class':<15} {'Predicted':<15} {'Confidence'}") |
|
|
print("-" * 80) |
|
|
misclassified = sorted(search_results['misclassified_files'], |
|
|
key=lambda x: x['confidence'])[:5] |
|
|
for error in misclassified: |
|
|
print(f"{error['filename']:<40} {error['true_class']:<15} " |
|
|
f"{error['predicted_class']:<15} {error['confidence']:.2%}") |
|
|
|
|
|
if __name__ == "__main__": |
|
|
hyperparam_dir = "runs_hyperparam/hyperparam_20241106_124214" |
|
|
results = analyze_trials(hyperparam_dir) |
|
|
|
|
|
|
|
|
print_results(results) |
|
|
|
|
|
|
|
|
save_analysis_report(results, hyperparam_dir) |