| import gradio as gr |
| import plotly |
|
|
| |
| import torch |
| import time |
| from codecarbon import OfflineEmissionsTracker |
| import numpy as np |
| import os |
| from thop import profile, clever_format |
| from tqdm.notebook import tqdm |
| from torchprofile import profile_macs |
|
|
| |
| def get_model_size(model, temp_path="temp_model.pth"): |
| torch.save(model.state_dict(), temp_path) |
| model_size = os.path.getsize(temp_path) |
| os.remove(temp_path) |
| |
| return model_size |
|
|
| |
| def get_num_parameters(model): |
| return sum(p.numel() for p in model.parameters() if p.requires_grad) |
|
|
|
|
| |
| @torch.inference_mode() |
| def evaluate_cpu_speed(model, dummy_input, warmup_rounds=5, test_rounds=20): |
| device = torch.device("cpu") |
| model.eval() |
| model.to(device) |
| dummy_input = dummy_input.to(device) |
| |
| |
| for _ in range(warmup_rounds): |
| _ = model(dummy_input) |
| |
| |
| latencies = [] |
| for _ in range(test_rounds): |
| start_time = time.perf_counter() |
| _ = model(dummy_input) |
| end_time = time.perf_counter() |
| latencies.append(end_time - start_time) |
| |
| latencies = np.array(latencies) * 1000 |
| mean_latency = np.mean(latencies) |
| std_latency = np.std(latencies) |
|
|
| |
| throughput = dummy_input.size(0) * 1000 / mean_latency |
|
|
| return mean_latency, std_latency, throughput |
|
|
| |
| @torch.inference_mode() |
| def get_model_macs(model, inputs) -> int: |
| return profile_macs(model, inputs) |
|
|
|
|
| |
| @torch.inference_mode() |
| def evaluate_emissions(model, dummy_input, warmup_rounds=5, test_rounds=20): |
| device = torch.device("cpu") |
| model.eval() |
| model.to(device) |
| dummy_input = dummy_input.to(device) |
|
|
| |
| for _ in range(warmup_rounds): |
| _ = model(dummy_input) |
| |
| |
| tracker = OfflineEmissionsTracker(country_iso_code="USA") |
| tracker.start() |
| for _ in range(test_rounds): |
| _ = model(dummy_input) |
| tracker.stop() |
| total_emissions = tracker.final_emissions |
| total_energy_consumed = tracker.final_emissions_data.energy_consumed |
| |
| |
| average_emissions_per_inference = total_emissions / test_rounds |
| average_energy_per_inference = total_energy_consumed / test_rounds |
| |
| return average_emissions_per_inference, average_energy_per_inference |
|
|
| |
| @torch.inference_mode() |
| def benchmark(model, dummy_input): |
| |
| print('disk size') |
| disk_size = get_model_size(model) |
| |
| |
| |
| print('cpu speed') |
| cpu_latency, cpu_std_latency, cpu_throughput = evaluate_cpu_speed(model, dummy_input) |
| |
| |
| |
| print('macs') |
| macs, params = profile(model, inputs=(dummy_input, )) |
| macs, num_parameters = clever_format([macs, params], "%.3f") |
|
|
| print('emissions') |
| |
| avg_emissions, avg_energy = evaluate_emissions(model, dummy_input) |
| |
| |
| print(f"Model Size: {disk_size / 1e6:.2f} MB (disk), {num_parameters} parameters") |
| print(f"CPU Latency: {cpu_latency:.3f} ms (± {cpu_std_latency:.3f} ms)") |
| print(f"CPU Throughput: {cpu_throughput:.2f} inferences/sec") |
| print(f"Model MACs: {macs}") |
| print(f"Average Carbon Emissions per Inference: {avg_emissions*1e3:.6f} gCO2e") |
| print(f"Average Energy Consumption per Inference: {avg_energy*1e3:.6f} Wh") |
|
|
| return { |
|
|
| 'disk_size': disk_size, |
| 'num_parameters': num_parameters, |
| 'cpu_latency': cpu_latency, |
| 'cpu_throughput': cpu_throughput, |
| 'macs': macs, |
| 'avg_emissions': avg_emissions, |
| 'avg_energy': avg_energy |
| |
| } |
| def parse_metric_value(value_str): |
| """Convert string values with units (M, G) to float""" |
| if isinstance(value_str, (int, float)): |
| return float(value_str) |
| |
| value_str = str(value_str) |
| if 'G' in value_str: |
| return float(value_str.replace('G', '')) * 1000 |
| elif 'M' in value_str: |
| return float(value_str.replace('M', '')) |
| elif 'K' in value_str: |
| return float(value_str.replace('K', '')) / 1000 |
| else: |
| return float(value_str) |
|
|
| def create_radar_plot(benchmark_results): |
| import plotly.graph_objects as go |
| |
| |
| metrics = { |
| '💾': { |
| 'value': benchmark_results['disk_size'] / 1e6, |
| 'hover_format': 'Model Size: {:.2f} MB', |
| 'unit': 'MB' |
| }, |
| '🧮': { |
| 'value': parse_metric_value(benchmark_results['num_parameters']), |
| 'hover_format': 'Parameters: {:.2f}M', |
| 'unit': 'M' |
| }, |
| '⏱️': { |
| 'value': benchmark_results['cpu_latency'], |
| 'hover_format': 'Latency: {:.2f} ms', |
| 'unit': 'ms' |
| }, |
| '⚡': { |
| 'value': parse_metric_value(benchmark_results['macs']), |
| 'hover_format': 'MACs: {:.2f}G', |
| 'unit': 'G' |
| }, |
| '🔋': { |
| 'value': benchmark_results['avg_energy'] * 1e6, |
| 'hover_format': 'Energy: {:.3f} mWh', |
| 'unit': 'mWh' |
| } |
| } |
| |
| |
| reference_values = { |
| '💾': {'min': 0, 'max': max(metrics['💾']['value'], 1000)}, |
| '🧮': {'min': 0, 'max': max(metrics['🧮']['value'], 50)}, |
| '⏱️': {'min': 0, 'max': max(metrics['⏱️']['value'], 200)}, |
| '⚡': {'min': 0, 'max': max(metrics['⚡']['value'], 5000)}, |
| '🔋': {'min': 0, 'max': max(metrics['🔋']['value'], 10)} |
| } |
| |
| |
| normalized_values = [] |
| hover_texts = [] |
| labels = [] |
| |
| for icon, metric in metrics.items(): |
| |
| normalized_value = (metric['value'] - reference_values[icon]['min']) / \ |
| (reference_values[icon]['max'] - reference_values[icon]['min']) |
| normalized_values.append(normalized_value) |
| |
| |
| hover_texts.append(metric['hover_format'].format(metric['value'])) |
| labels.append(icon) |
| |
| |
| normalized_values.append(normalized_values[0]) |
| hover_texts.append(hover_texts[0]) |
| labels.append(labels[0]) |
| |
| fig = go.Figure() |
| |
| fig.add_trace(go.Scatterpolar( |
| r=normalized_values, |
| theta=labels, |
| fill='toself', |
| name='Model Metrics', |
| hovertext=hover_texts, |
| hoverinfo='text', |
| line=dict(color='#FF8C00'), |
| fillcolor='rgba(255, 140, 0, 0.3)' |
| )) |
| |
| fig.update_layout( |
| polar=dict( |
| radialaxis=dict( |
| visible=True, |
| range=[0, 1], |
| showticklabels=False, |
| gridcolor='rgba(128, 128, 128, 0.5)', |
| linecolor='rgba(128, 128, 128, 0.5)' |
| ), |
| angularaxis=dict( |
| tickfont=dict(size=24), |
| gridcolor='rgba(128, 128, 128, 0.5)' |
| ), |
| bgcolor='rgba(0,0,0,0)' |
| ), |
| showlegend=False, |
|
|
| margin=dict(t=100, b=100, l=100, r=100), |
| paper_bgcolor='rgba(0,0,0,0)', |
| plot_bgcolor='rgba(0,0,0,0)' |
| ) |
| |
| return fig |
|
|
| |
|
|
| def benchmark_interface(model_name): |
| import torchvision.models as models |
| |
| model_mapping = { |
| 'ResNet18': models.resnet18(pretrained=False), |
| 'ResNet50': models.resnet50(pretrained=False), |
| 'MobileNetV2': models.mobilenet_v2(pretrained=False), |
| 'EfficientNet-B0': models.efficientnet_b0(pretrained=False), |
| 'VGG16': models.vgg16(pretrained=False), |
| 'DenseNet121': models.densenet121(pretrained=False) |
| } |
| |
| model = model_mapping[model_name] |
| dummy_input = torch.randn(1, 3, 224, 224) |
| |
| |
| results = benchmark(model, dummy_input) |
| |
| |
| plot = create_radar_plot(results) |
| |
| return plot |
|
|
| available_models = ['ResNet18', 'ResNet50', 'MobileNetV2', 'EfficientNet-B0', 'VGG16', 'DenseNet121'] |
|
|
| iface = gr.Interface( |
| fn=benchmark_interface, |
| inputs=[ |
| gr.Dropdown(choices=available_models, label="Select Model", value='ResNet18') |
| ], |
| outputs=[ |
| gr.Plot(label="Model Benchmark Results") |
| ], |
| ) |
|
|
| iface.launch() |