Download reference/code/TAD/AUP_leaderboard/plot_lines.py from Ouzhang/hku_diffusion_dllm: direct link, hf CLI and curl.
- Browser
- Download file 13.5 kB
-
https://huggingface.co/Ouzhang/hku_diffusion_dllm/resolve/main/reference/code/TAD/AUP_leaderboard/plot_lines.py
- Command line
-
hf download hf://Ouzhang/hku_diffusion_dllm/reference/code/TAD/AUP_leaderboard/plot_lines.py
-
curl -L -o plot_lines.py https://huggingface.co/Ouzhang/hku_diffusion_dllm/resolve/main/reference/code/TAD/AUP_leaderboard/plot_lines.py
13.5 kB
| import matplotlib.pyplot as plt | |
| import numpy as np | |
| from matplotlib.collections import LineCollection | |
| from matplotlib.colors import LinearSegmentedColormap | |
| from aup_utils import get_aup | |
| # Pre-defined Tech Colors (Gradients) | |
| # Format: 'name': [start_color, end_color] | |
| TECH_STYLE = { | |
| 'red': ['#8B0000', '#FF0011'], # Dark Red -> Neon Red | |
| 'blue': ['#00008B', '#00CCFF'], # Dark Blue -> Neon Cyan | |
| 'green': ['#004d00', '#14b881'], # Dark Green -> Neon Green | |
| 'purple': ['#4B0082', '#D766FF'], # Indigo -> Neon Purple | |
| 'yellow': ['#8B8000', '#FFD700'], # Dark Yellow -> Gold | |
| 'orange': ['#8B4500', '#FF5500'], # Saddle Brown -> Neon Orange | |
| 'grey': ['#404040', '#AAAAAA'], # Dim Grey -> Light Grey | |
| 'cyan': ['#008B8B', '#00FFFF'], # Dark Cyan -> Neon Cyan | |
| 'magenta': ['#8B008B', '#FF00FF'], # Dark Magenta -> Neon Magenta | |
| 'pink': ['#C71585', '#FF1493'], # Deep Pink -> Neon Pink | |
| 'lime': ['#32CD32', '#7FFF00'], # Lime Green -> Chartreuse | |
| 'teal': ['#008080', '#00CED1'], # Teal -> Dark Turquoise | |
| } | |
| def plot_aup_curve(methods: dict, y_max: float, assigned_colors: dict = None, save_path: str = None, is_dark_mode: bool = True, outlier_threshold: float = None, font_size_axis: int = 12, font_size_legend: int = 12, font_size_tick: int = 12, dataset_name: str = None): | |
| """ | |
| Plot accuracy-parallelism curves with a high-tech aesthetic. | |
| Args: | |
| methods: dict of {method_name: [(rho, y), ...]} | |
| y_max: maximum accuracy across all methods (for AUP calculation) | |
| assigned_colors: dict of {method_name: color_name} (optional) | |
| save_path: path to save the figure | |
| is_dark_mode: whether to use dark mode (True) or light mode (False) | |
| outlier_threshold: y-value threshold to detect outliers (auto if None) | |
| font_size_axis: font size for axis labels | |
| font_size_legend: font size for legend (method names) | |
| font_size_tick: font size for tick labels (axis numbers) | |
| dataset_name: name of the dataset to display at the bottom (optional) | |
| """ | |
| # Default color cycle if not assigned | |
| default_colors = ['purple', 'blue', 'green', 'orange', 'red', 'yellow', 'grey', 'cyan', 'magenta', 'pink'] | |
| # Determine style settings | |
| if is_dark_mode: | |
| style_context = 'dark_background' | |
| bg_color = 'black' # Or default dark | |
| fg_color = 'white' | |
| grid_color = 'white' | |
| grid_alpha = 0.15 | |
| spine_color = 'white' | |
| else: | |
| style_context = 'default' | |
| bg_color = '#F5F5F7' # Apple light gray | |
| fg_color = 'black' | |
| grid_color = '#86868b' # Apple gray | |
| grid_alpha = 0.3 | |
| spine_color = '#86868b' | |
| with plt.style.context(style_context): | |
| all_rho = [] | |
| all_y = [] | |
| aup_results = [] # Store (aup, method_name, text_color) | |
| method_data = [] # Store processed data for plotting | |
| # First Pass: Process Data & AUP | |
| for i, (method_name, pairs) in enumerate(methods.items()): | |
| if not pairs: | |
| continue | |
| # Determine color | |
| if assigned_colors: | |
| c_name = assigned_colors[i] if isinstance(assigned_colors, list) else assigned_colors.get(method_name, default_colors[i % len(default_colors)]) | |
| else: | |
| c_name = default_colors[i % len(default_colors)] | |
| grad_colors = TECH_STYLE.get(c_name, TECH_STYLE['grey']) | |
| main_color = grad_colors[1] | |
| if is_dark_mode: | |
| text_color = grad_colors[1] | |
| else: | |
| text_color = grad_colors[0] | |
| cmap = LinearSegmentedColormap.from_list(f"tech_{c_name}", grad_colors) | |
| rho, y = zip(*sorted(pairs, key=lambda x: x[0])) | |
| rho = np.array(rho) | |
| y = np.array(y) | |
| if np.max(y) <= 1.0: | |
| y = y * 100 | |
| # Calculate AUP | |
| aup_val = get_aup(list(rho), list(y), y_max) | |
| aup_results.append((aup_val, method_name, text_color)) | |
| # Store for processing | |
| method_data.append({ | |
| 'name': method_name, | |
| 'rho': rho, | |
| 'y': y, | |
| 'colors': (main_color, text_color), | |
| 'cmap': cmap | |
| }) | |
| # Collect all data for initial stats | |
| all_rho.extend(rho) | |
| all_y.extend(y) | |
| # Detect Outliers | |
| outlier_names = set() | |
| if all_y: | |
| if outlier_threshold is None: | |
| sorted_y = sorted(all_y) | |
| q1, q3 = np.percentile(sorted_y, [25, 75]) | |
| iqr = q3 - q1 | |
| outlier_threshold = q3 + 1.5 * iqr | |
| for m in method_data: | |
| if np.max(m['y']) > outlier_threshold: | |
| outlier_names.add(m['name']) | |
| # Setup Figure (Broken Axis if outliers exist) | |
| if outlier_names: | |
| fig, (ax_top, ax_bottom) = plt.subplots(2, 1, sharex=True, figsize=(9, 6), | |
| gridspec_kw={'height_ratios': [1, 4], 'hspace': 0.1}) | |
| axes_list = [ax_top, ax_bottom] | |
| ax = ax_bottom # Main reference | |
| else: | |
| fig, ax = plt.subplots(figsize=(9, 6)) | |
| axes_list = [ax] | |
| ax_bottom = ax | |
| if not is_dark_mode: | |
| fig.patch.set_facecolor(bg_color) | |
| for a in axes_list: | |
| a.set_facecolor(bg_color) | |
| # Second Pass: Plotting (draw curves/markers, collect label info) | |
| non_outlier_rho = [] | |
| non_outlier_y_vals = [] | |
| outlier_y_vals = [] | |
| pending_labels = [] # Collect labels for anti-overlap processing | |
| for m in method_data: | |
| method_name = m['name'] | |
| is_outlier = method_name in outlier_names | |
| # Select target axis | |
| if outlier_names: | |
| target_ax = ax_top if is_outlier else ax_bottom | |
| else: | |
| target_ax = ax | |
| rho = m['rho'] | |
| y = m['y'] | |
| main_color, text_color = m['colors'] | |
| cmap = m['cmap'] | |
| if is_outlier: | |
| outlier_y_vals.extend(y) | |
| else: | |
| non_outlier_rho.extend(rho) | |
| non_outlier_y_vals.extend(y) | |
| # Generate smooth curve points (fitting) | |
| if len(rho) >= 3: | |
| z = np.polyfit(rho, y, 2) | |
| p = np.poly1d(z) | |
| x_smooth = np.linspace(rho.min(), rho.max(), 300) | |
| y_smooth = p(x_smooth) | |
| elif len(rho) == 2: | |
| # Quadratic with vertex at (rho[0], y[0]) | |
| x_smooth = np.linspace(rho.min(), rho.max(), 300) | |
| if rho[1] != rho[0]: | |
| a = (y[1] - y[0]) / ((rho[1] - rho[0]) ** 2) | |
| y_smooth = a * (x_smooth - rho[0]) ** 2 + y[0] | |
| else: | |
| y_smooth = np.linspace(y[0], y[1], 300) | |
| else: | |
| x_smooth = rho | |
| y_smooth = y | |
| # Plotting curves and markers | |
| x_span = max(all_rho) if all_rho else 1.0 | |
| if len(rho) > 1: | |
| points = np.array([x_smooth, y_smooth]).T.reshape(-1, 1, 2) | |
| segments = np.concatenate([points[:-1], points[1:]], axis=1) | |
| norm = plt.Normalize(x_smooth.min(), x_smooth.max()) | |
| lc = LineCollection(segments, cmap=cmap, norm=norm) | |
| lc.set_array(x_smooth) | |
| lc.set_linewidth(3) | |
| lc.set_alpha(0.9) | |
| target_ax.add_collection(lc) | |
| # Markers | |
| marker_edge = main_color | |
| if not is_dark_mode: marker_edge = 'white' | |
| target_ax.scatter(rho, y, color='white', edgecolors=main_color, s=60, zorder=10, marker='o', linewidth=1.5) | |
| # Collect label position (defer drawing) | |
| label_x = rho[-1] + x_span * 0.03 | |
| label_y = y[-1] | |
| pending_labels.append((label_x, label_y, method_name, text_color, target_ax)) | |
| else: | |
| target_ax.scatter(rho, y, color=main_color, s=120, marker='o', zorder=10, label=method_name) | |
| label_x = rho[0] + x_span * 0.03 | |
| label_y = y[0] | |
| pending_labels.append((label_x, label_y, method_name, text_color, target_ax)) | |
| # Anti-overlap: adjust label y positions | |
| # Group labels by target axis | |
| from collections import defaultdict | |
| labels_by_ax = defaultdict(list) | |
| for lx, ly, name, color, tax in pending_labels: | |
| labels_by_ax[id(tax)].append([lx, ly, name, color, tax]) | |
| for ax_id, labels in labels_by_ax.items(): | |
| # Sort by y position (ascending) | |
| labels.sort(key=lambda item: item[1]) | |
| # Estimate minimum spacing in data units | |
| if all_y: | |
| y_range = max(all_y) - min(all_y) | |
| else: | |
| y_range = 10.0 | |
| min_spacing = y_range * 0.06 # ~6% of y-range as minimum gap | |
| # Push overlapping labels apart (greedy bottom-up) | |
| for i in range(1, len(labels)): | |
| prev_y = labels[i-1][1] | |
| curr_y = labels[i][1] | |
| if curr_y - prev_y < min_spacing: | |
| labels[i][1] = prev_y + min_spacing | |
| # Draw all labels with adjusted positions (from labels_by_ax, not pending_labels) | |
| for ax_id, labels in labels_by_ax.items(): | |
| for lx, ly, name, color, tax in labels: | |
| tax.text(lx, ly, name, | |
| color=color, | |
| fontsize=font_size_legend, | |
| fontweight='bold', | |
| ha='left', va='center') | |
| # Styling for all axes | |
| for a in axes_list: | |
| a.grid(True, linestyle='--', alpha=grid_alpha, color=grid_color) | |
| a.spines['top'].set_visible(False) | |
| a.spines['right'].set_visible(False) | |
| a.spines['bottom'].set_color(spine_color) | |
| a.spines['left'].set_color(spine_color) | |
| a.tick_params(colors=fg_color, labelsize=font_size_tick) | |
| # Labeling | |
| ax_bottom.set_xlabel(r'Parallelism $\rho$ (TPF, tokens/forward)', fontsize=font_size_axis, color=fg_color) | |
| # Y label centered? We'll just put it on the bottom ax or create a shared one. | |
| # Simple approach: label bottom axis | |
| ax_bottom.set_ylabel(r'Accuracy (%)', fontsize=font_size_axis, color=fg_color) | |
| # Adjust Limits | |
| if non_outlier_rho: | |
| max_rho = max(non_outlier_rho) | |
| for a in axes_list: | |
| a.set_xlim(left=0 if max_rho > 5 else 0.8, right=max_rho * 1.25) | |
| # Y-Limits Bottom | |
| if non_outlier_y_vals: | |
| min_y = min(non_outlier_y_vals) | |
| max_y = max(non_outlier_y_vals) | |
| ax_bottom.set_ylim(bottom=min_y - 1.0, top=max_y + 0.2 if outlier_names else max_y + 1.0) | |
| # Broken Axis Logic | |
| if outlier_names: | |
| # Y-Limits Top | |
| if outlier_y_vals: | |
| tmin, tmax = min(outlier_y_vals), max(outlier_y_vals) | |
| # Add some margin | |
| margin = 1.0 | |
| ax_top.set_ylim(tmin - margin, tmax + margin) | |
| # Hide spines | |
| ax_top.spines['bottom'].set_visible(False) | |
| ax_bottom.spines['top'].set_visible(False) | |
| # Remove ticks from top ax (prevent white dots) | |
| ax_top.tick_params(axis='x', which='both', bottom=False, top=False, labeltop=False) | |
| ax_bottom.xaxis.tick_bottom() | |
| # Diagonal break lines (Left side only) | |
| d = .015 | |
| kwargs = dict(transform=ax_top.transAxes, color=fg_color, clip_on=False) | |
| ax_top.plot((-d, +d), (-d, +d), **kwargs) | |
| kwargs.update(transform=ax_bottom.transAxes) | |
| ax_bottom.plot((-d, +d), (1 - d, 1 + d), **kwargs) | |
| # Add AUP Score List in Top Right | |
| # Sort by AUP descending | |
| aup_results.sort(key=lambda x: x[0], reverse=True) | |
| text_x = 0.98 | |
| text_y = 0.95 | |
| line_height = 0.06 | |
| # for aup_val, m_name, m_color in aup_results: | |
| # label_str = f"{m_name}: {aup_val:.2f}" | |
| # ax.text(text_x, text_y, label_str, | |
| # transform=ax.transAxes, | |
| # color=m_color, | |
| # fontsize=12, | |
| # fontweight='bold', | |
| # ha='right', | |
| # va='top') | |
| # text_y -= line_height | |
| # Add dataset name at the top | |
| if dataset_name: | |
| fig.suptitle(dataset_name, fontsize=font_size_legend+6, color=fg_color, fontweight='bold') | |
| plt.tight_layout() | |
| if save_path: | |
| # If light mode, save with correct facecolor | |
| fc = bg_color if not is_dark_mode else 'black' | |
| plt.savefig(save_path, dpi=300, bbox_inches='tight', facecolor=fc) | |
| print(f"Figure saved to {save_path}") | |