Spaces:
Sleeping
Sleeping
from src.search.ga import GeneticSearch | |
from src.hw_nats_fast_interface import HW_NATS_FastInterface | |
from src.utils import DEVICES, DATASETS, union_of_dicts | |
import streamlit as st | |
import numpy as np | |
import pandas as pd | |
import matplotlib.pyplot as plt | |
import plotly.graph_objects as go | |
from collections import OrderedDict | |
import json | |
st.set_page_config(layout="wide") | |
TIME_TO_SCORE_EACH_ARCHITECTURE=0.15 | |
DAYS_7 = 604800 | |
NEBULOS_COLOR = '#FF6961' | |
TF_COLOR = '#A7C7E7' | |
def load_lookup_table(): | |
"""Load recap table of NebulOS metrics and cache it. | |
""" | |
df_nebuloss = pd.read_csv('data/df_nebuloss.csv').rename(columns = {'test_accuracy' : 'validation_accuracy'}) | |
return df_nebuloss | |
def subset_dataframe(df_nebuloss, dataset): | |
"""Subset df_nebuloss based on the right dataset. | |
""" | |
return df_nebuloss[df_nebuloss['dataset'] == dataset] | |
def compute_quantiles(df_nebuloss_dataset): | |
"""Turn the values of df_nebuloss (of a certain dataset) into the corresponding quantiles, computed along the columns | |
""" | |
# compute quantiles | |
quantiles = df_nebuloss_dataset.drop(columns = ['idx']).rank(pct = True) | |
# re-attach the original indices | |
quantiles['idx'] = df_nebuloss_dataset['idx'] | |
return quantiles | |
# Streamlit app | |
def main(): | |
# mapping the devices pseudo-symbols to actual names | |
device_mapping_dict = { | |
"edgegpu": "NVIDIA Jetson nano", | |
"eyeriss": "Eyeriss", | |
"fpga": "FPGA", | |
} | |
inverse_device_mapping_dict = { | |
"NVIDIA Jetson nano": "edgegpu", | |
"Eyeriss": "eyeriss", | |
"FPGA": "fpga" | |
} | |
# load the lookup table of NebulOS metrics | |
df_nebuloss = load_lookup_table() | |
# add a title | |
st.sidebar.title("🚀 NebulOS: Fair Green AI🌿") | |
st.sidebar.write( | |
""" | |
Welcome to the live demo of NebulOS! This Streamlit app serves the scope of presenting the results obtained with our | |
Hardware-Aware Training-Free Automated Architecture Design procedure. | |
You can check out the source code for the search process at https://www.github.com/fracapuano/NebulOS. | |
You can find an extended abstract of our solution at https://sites.google.com/view/nebulos. | |
Drop us a line if you want to know more about the project (and forget to ⭐ our GitHub repo). | |
Contact person: Francesco Capuano ({first}.{last}@asp-poli.it) | |
""" | |
) | |
# dropdown menu for dataset selection | |
dataset = st.sidebar.selectbox("Select Dataset", DATASETS) | |
# dropdown menu for device selection | |
device = st.sidebar.selectbox("Select Device", list(inverse_device_mapping_dict.keys())) | |
# mapping selected device to usable one | |
device = inverse_device_mapping_dict[device] | |
# slider for performance weight selection | |
performance_weight = st.sidebar.slider( | |
"Select trade-off between PERFORMANCE WEIGHT and HARDWARE WEIGHT.\nHigher values will give larger weight to validation accuracy, with less and less importance to the hardware performance.", | |
min_value=0.0, | |
max_value=1.0, | |
value=0.5, | |
step=0.05 | |
) | |
# hardware weight (complementary to performance weight) | |
hardware_weight = 1.0 - performance_weight | |
# subset the dataframe for the current daset and device | |
df_nebuloss_dataset = subset_dataframe(df_nebuloss, dataset) | |
# best architecture index | |
best_arch_idx = 9930 | |
nebulos_chunks = [] | |
for i in range(4): # the number of chunks is 4 in this case | |
with open(f"data/nebuloss_{i+1}.json", "r") as f: | |
nebulos_chunks.append(json.load(f)) | |
searchspace_dict = union_of_dicts(nebulos_chunks) | |
# Trigger the search and plot NebulOS Architecture | |
searchspace_interface = HW_NATS_FastInterface(datapath=searchspace_dict, device=device, dataset=dataset) | |
search = GeneticSearch( | |
searchspace=searchspace_interface, | |
fitness_weights=np.array([performance_weight, hardware_weight]) | |
) | |
results = search.solve(return_trajectory=True) | |
arch_idx = searchspace_interface.architecture_to_index["/".join(results[0].genotype)] | |
# Create scatter plot | |
scatter_trace1 = go.Scatter( | |
x=df_nebuloss_dataset.loc[df_nebuloss['dataset'] == dataset, f'{device}_energy'], | |
y=df_nebuloss_dataset.loc[df_nebuloss['dataset'] == dataset, 'validation_accuracy'], | |
mode='markers', | |
marker=dict(color='#D3D3D3', size=5), | |
name='Architectures in the search space' | |
) | |
# Scatter plot for best architecture | |
scatter_trace2 = go.Scatter( | |
x=df_nebuloss_dataset.loc[df_nebuloss_dataset['idx'] == best_arch_idx, f'{device}_energy'], | |
y=df_nebuloss_dataset.loc[df_nebuloss_dataset['idx'] == best_arch_idx, 'validation_accuracy'], | |
mode='markers', | |
marker=dict(color=TF_COLOR, symbol='circle-dot', size=12), | |
name='Best TF-Architecture' | |
) | |
scatter_trace3 = go.Scatter( | |
x=df_nebuloss_dataset.loc[df_nebuloss_dataset['idx'] == arch_idx, f'{device}_energy'], | |
y=df_nebuloss_dataset.loc[df_nebuloss_dataset['idx'] == arch_idx, 'validation_accuracy'], | |
mode='markers', | |
marker=dict(color=NEBULOS_COLOR, symbol='circle-dot', size=12), | |
name='NebulOS Architecture' | |
) | |
scatter_layout = go.Layout( | |
title=f'Validation Accuracy vs. {device_mapping_dict[device]} Energy Consumption', | |
xaxis=dict(title=f'{device.upper()} Energy'), | |
yaxis=dict(title='Validation Accuracy'), | |
showlegend=True | |
) | |
scatter_fig = go.Figure(data=[scatter_trace1, scatter_trace2, scatter_trace3], layout=scatter_layout) | |
# Extracting quantile values | |
metrics_considered = OrderedDict() | |
# these are the metrics that we want to plot | |
metrics_considered["flops"] = "FLOPS", | |
metrics_considered["params"] = "Num. Params", | |
metrics_considered["validation_accuracy"] = "Accuracy", | |
metrics_considered[f"{device}_energy"] = f"{device_mapping_dict[device]} - Energy Consumption", | |
metrics_considered[f"{device}_latency"] = f"{device_mapping_dict[device]} - Latency" | |
# this retrieves the optimal row | |
best_row_to_plot = df_nebuloss_dataset.loc[ | |
df_nebuloss_dataset['idx'] == best_arch_idx, | |
list(metrics_considered.keys()) | |
].values | |
# this retrieves the row that has been found by the NAS search | |
row_to_plot = df_nebuloss_dataset.loc[ | |
df_nebuloss_dataset['idx'] == arch_idx, | |
list(metrics_considered.keys()) | |
].values | |
row_to_plot = row_to_plot/best_row_to_plot | |
best_row_to_plot = best_row_to_plot/best_row_to_plot | |
best_row_to_plot = best_row_to_plot.flatten().tolist() | |
row_to_plot = row_to_plot.flatten().tolist() | |
# Bar chart for NebulOS Architecture | |
bar_trace1 = go.Bar( | |
x=list(metrics_considered.keys()), | |
y=row_to_plot, | |
name='NebulOS Architecture', | |
marker=dict(color=NEBULOS_COLOR) | |
) | |
# Bar chart for Best TF-Architecture | |
bar_trace2 = go.Bar( | |
x=list(metrics_considered.keys()), | |
y=best_row_to_plot, | |
name='Best TF-Architecture Found', | |
marker=dict(color=TF_COLOR) | |
) | |
# Layout configuration | |
bar_layout = go.Layout( | |
title=f'Hardware-Agnostic Architecture (blue) vs. NebulOS (red)', | |
yaxis=dict(title="(%)Hardware-Agnostic Architecture Value"), | |
barmode='group' | |
) | |
# Combining traces with the layout | |
bar_fig = go.Figure(data=[bar_trace2, bar_trace1], layout=bar_layout) | |
# Create two columns in Streamlit to show data near each other | |
col1, col2 = st.columns(2) | |
# Display scatter plot in the first column | |
with col1: | |
st.plotly_chart(scatter_fig) | |
# Display bar chart in the second column | |
with col2: | |
st.plotly_chart(bar_fig) | |
best_architecture = df_nebuloss_dataset.loc[ | |
df_nebuloss_dataset['idx'] == best_arch_idx, | |
list(metrics_considered.keys()) | |
] | |
best_architecture_string = searchspace_interface[best_arch_idx]["architecture_string"] | |
found_architecture = df_nebuloss_dataset.loc[ | |
df_nebuloss_dataset['idx'] == arch_idx, | |
list(metrics_considered.keys()) | |
] | |
message = \ | |
f""" | |
<h4>NebulOS Search Process: Outcome</h4> | |
<p> | |
This search took ~{round(results[-1]*TIME_TO_SCORE_EACH_ARCHITECTURE, 2)} seconds (scoring {results[-1]} architectures using ~{TIME_TO_SCORE_EACH_ARCHITECTURE} seconds each) | |
</p> | |
The architecture found for <b>{device_mapping_dict[device]}</b> is: <b>{searchspace_interface[arch_idx]["architecture_string"]}</b><br> | |
The optimal (hardware-agnostic) architecture in the searchspace is <b>{best_architecture_string}</b> | |
</p> | |
<p> | |
You can find the recap, in terms of the percentage of the Training-Free metric found in the table to your right 👉 | |
</p> | |
""" | |
# Sample data - replace these with your actual ratio values | |
data = { | |
"Metric": ["FLOPS", "Number of Parameters", "Validation Accuracy", "Energy Consumption", "Latency"], | |
"NebulOS vs. Hardware Agnostic Network": ["{:.4g}%".format(100*val) for val in row_to_plot] | |
} | |
col1, _, col2 = st.columns([2,1,2]) | |
recap_df = pd.DataFrame(data).sort_values(by="Metric").set_index("Metric") | |
with col1: | |
st.write(message, unsafe_allow_html=True) | |
with col2: | |
st.dataframe(recap_df) | |
if __name__ == "__main__": | |
main() |