Files
2024-07-19 13:30:31 +03:00

159 lines
6.5 KiB
Python

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from brokenaxes import brokenaxes
def find_pareto_optimal_points(latencies, mpq_accuracies, mpq_confs):
"""
Finds the Pareto optimal points given latencies and accuracies.
Args:
latencies (numpy.ndarray): An array of latency values.
accuracies (numpy.ndarray): An array of accuracy values.
mpq_confs (numpy.ndarray): An array with the sorted weight_bit_width per layer list based on
estimated latency
Returns:
tuple: A tuple containing the following:
- numpy.ndarray: Array of Pareto optimal points (Latency, Accuracy).
- numpy.ndarray: Indices of Pareto optimal accuracies in the original array.
"""
data = {
'Configuration': [mpq_confs[idx].astype(int) for idx in range(len(mpq_confs))],
'Accuracy': mpq_accuracies,
'MACC Instrs': latencies
}
# Combine latencies, accuracies, and indices
combined = np.column_stack((latencies, mpq_accuracies, np.arange(len(mpq_accuracies))))
# Initialize the Pareto front with the first point
pareto_front = [combined[0]]
for pair in combined[1:]:
# Check if the accuracy of the current pair is better than the last point in the Pareto front
if pair[1] > pareto_front[-1][1]:
pareto_front.append(pair)
# Extract Pareto points and indices
pareto_points_with_indices = np.array(pareto_front)
pareto_points = pareto_points_with_indices[:, :2]
pareto_indices = pareto_points_with_indices[:, 2].astype(int)
# Set print options for clarity
np.set_printoptions(precision = 2, suppress = True)
# Create DataFrame
data = {
'Index': pareto_indices,
'Weights Resolutions': [mpq_confs[idx].astype(int) for idx in pareto_indices],
'Accuracy': [point[1] for point in pareto_points],
'MACC Instrs': [point[0] for point in pareto_points]
}
df = pd.DataFrame(data)
return df, pareto_points, pareto_indices
def identify_best_solution(pareto_solutions_df, fp_accuracy, maximum_acc_loss = 1):
threshold = fp_accuracy - maximum_acc_loss
filtered_df = pareto_solutions_df[pareto_solutions_df['Accuracy'] >= threshold]
filtered_df = filtered_df.sort_values(by = 'MACC Instrs', ascending=True)
if not filtered_df.empty:
best_config = filtered_df.iloc[0] # Get the first row
print(f"\nBest Configuration Details with Maximum Accuracy loss < {maximum_acc_loss}%:")
print(f"Index: {best_config['Index']}")
print(f"Weights Resolutions: {best_config['Weights Resolutions']}")
print(f"Accuracy: {best_config['Accuracy']:.2f}%")
print(f"MACC Instrs: {best_config['MACC Instrs']}")
else:
print("No configurations meet the specified accuracy threshold.")
return best_config
def plot_pareto_solutions(mpq_mac, fp_mac_instr, accuracy,
fp_accuracy, pareto_points, name):
"""
Plots Pareto solutions based on MAC instructions, accuracy, and other parameters.
Args:
mpq_mac (numpy.ndarray): Array of MAC instructions for mixed precision layer configurations.
fp_mac_instr (float): Original (non-optimized) model's MAC instructions.
accuracy (numpy.ndarray): Array of accuracy values for mixed precision layer configurations.
fp_accuracy (float): Original (non-optimized) model's accuracy.
pareto_points (numpy.ndarray): Array of Pareto optimal points (Latency, Accuracy).
Returns:
None: Displays the plot and saves it as a PNG file.
"""
# Define the x-axis and y-axis ranges
ranges = [(0, max(mpq_mac) + 0.5 * max(mpq_mac)),
(fp_mac_instr - 0.15 * fp_mac_instr, fp_mac_instr + 0.15 * fp_mac_instr)]
# Calculate the exponent for notation
exponent = int(np.log10(fp_mac_instr))
notation = 10 ** exponent
# Create the figure and broken axes
fig = plt.figure(figsize = (6, 4))
fig.subplots_adjust(left = 0.148, right = 0.88, top = 0.95, bottom = 0.2)
bax = brokenaxes(xlims = ranges, hspace = 0.05)
# Scatter plot for original model, mixed precision, and Pareto points
bax.scatter(fp_mac_instr, fp_accuracy, color = 'black', s = 9, marker = '*',
label = 'original model')
bax.scatter(mpq_mac, accuracy, color = 'gray', marker = 'o', s = 9,
label = 'mixed configs')
bax.scatter(pareto_points[:, 0], pareto_points[:, 1], color = 'green', marker = 's', s = 9,
label = 'pareto points')
# Customize the plot
bax.set_ylabel('Accuracy (%)', fontsize = 10, labelpad = 35)
bax.grid(True)
# Set the y-axis limit if provided
y_lims = [max(50, 0.8 * np.min(accuracy)), min(1.1 * fp_accuracy, 100)]
bax.set_ylim(y_lims[0], y_lims[1])
# Place the legend
bax.legend(loc = 'upper center', bbox_to_anchor = (0.5, 1.2), ncol = 3, fontsize = 10)
# Format x-axis labels
sFormatter1 = ticker.FuncFormatter(lambda x, _: '{:0.2f}'.format(x / notation))
sFormatter2 = ticker.ScalarFormatter(useOffset = False, useMathText = True)
sFormatter2.set_powerlimits((0, 2))
bax.axs[0].xaxis.set_major_formatter(sFormatter1)
bax.axs[1].xaxis.set_major_formatter(sFormatter2)
bax.axs[0].tick_params(axis = 'y', labelsize = 10)
bax.axs[0].tick_params(axis = 'x', labelsize = 10)
bax.axs[1].tick_params(axis = 'x', labelsize = 10)
bax.axs[1].xaxis.get_offset_text().set_position((1, 0))
bax.axs[1].xaxis.get_offset_text().set_fontsize(10)
# Reposition the x-axis label
bax.set_xlabel('MAC Instructions', ha = 'center', fontsize = 10, labelpad = 25)
plt.savefig(name + ".png")
def pareto_space(fp_accuracy, test_accuracy, weights_per_layer, macc_per_layer, total_macc_opt_sorted, name):
df, pareto_points, _ = find_pareto_optimal_points(np.array(total_macc_opt_sorted),
np.array(test_accuracy), np.array(weights_per_layer))
optimal_config = identify_best_solution(df, np.array(fp_accuracy))
plot_pareto_solutions(np.array(total_macc_opt_sorted),
sum(macc_per_layer), np.array(test_accuracy),
float(fp_accuracy), pareto_points, name)
optimal_config = optimal_config.iloc[1]
return optimal_config.tolist()