Study G — Joint Impact of \(\alpha\), \(p_e\), and Batch Size on Convergence¶
Explores how the number of steps to convergence depends jointly on problem size \(N\), learning rate \(\alpha\), dataset sparsity \(p_e\), and batch size. Provides a comprehensive view of the hyperparameter interaction space.
Parameter |
Value |
|---|---|
Problem Size \(N\) |
[1, 100] (10 values, log spaced) |
Num. Parallel Nodes \(P\) |
\(\lfloor 1000/N \rfloor\) |
Learning Rate \(\alpha\) |
{1, N/10} |
Batch Size \(M\) |
{10, 100, 1000,10000} |
Bernouilli Prob. \(p_e\) |
{0.001, 1/N} |
Max. Steps \(S\) |
25000 |
Oracle Proportion \(p_w\) |
0.5 |
[1]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import matplotlib.colors as mcolors
import sys
import os
import glob
# Add parent directory to path
sys.path.append('..')
from plotting.plot_utility import setup_plot_style
%matplotlib inline
setup_plot_style()
[2]:
def get_latest_study_folder(study_prefix):
pattern = f'../studies/results/{study_prefix}_*/'
folders = glob.glob(pattern)
if folders:
return max(folders) # Latest by name (timestamp-based)
return None
mode = 'auto' # Change to 'manual' to specify your own path
if mode == 'auto':
results_dir = get_latest_study_folder('G_alpha_p_e_batch')
if results_dir is None:
raise FileNotFoundError("No Study G results found. Run run_study_G.py first.")
print(f"Auto-detected folder: {results_dir}")
else:
results_dir = '../studies/results/G_alpha_p_e_batch_20260119_120906/'
print(f"Using manual path: {results_dir}")
results_list = np.load(f'{results_dir}results_list.npy', allow_pickle=True).tolist()
alpha_types = np.load(f'{results_dir}alpha_types.npy', allow_pickle=True).item()
p_e_types = np.load(f'{results_dir}p_e_types.npy', allow_pickle=True).item()
n_values = np.load(f'{results_dir}n_values.npy')
batch_sizes = np.load(f'{results_dir}batch_sizes.npy')
print(f"\nN values: {n_values}")
print(f"Batch sizes: {batch_sizes}")
print(f"Alpha types: {alpha_types}")
print(f"p_e types: {p_e_types}")
print(f"\nData structure: flat list of {len(results_list)} snapshots")
print(f"Snapshot keys: {list(results_list[0].keys()) if results_list else 'N/A'}")
Auto-detected folder: ../studies/results/G_alpha_p_e_batch_20260420_233418/
N values: [ 10 16 27 46 77 129 215 359 599 1000]
Batch sizes: [ 10 100 1000 10000]
Alpha types: {0: '1', 1: 'N/10'}
p_e types: {0: '1/1000', 1: '1/N'}
Data structure: flat list of 160 snapshots
Snapshot keys: ['N', 'batch_size', 'alpha', 'p_e', 'alpha_type', 'p_e_type', 'steps', 'p_diff_history']
Steps to Convergence vs \(N\)¶
Comprehensive visualisation of convergence time across all hyperparameter combinations, encoded as:
Colour: batch size (viridis colormap)
Line style: \(\alpha\) value (solid / dashed / dotted)
Marker: \(p_e\) value (circle / square / triangle)
See Paper Appendix G.5 Figure 26.
[3]:
# Build plot_data directly from the flat results_list using stored type indices
# plot_data[alpha_type][p_e_type][batch_size] = [(N, steps), ...]
plot_data = {}
for snapshot in results_list:
steps = snapshot['steps']
if steps <= 0:
continue
alpha_cat = snapshot['alpha_type']
p_e_cat = snapshot['p_e_type']
batch_size = snapshot['batch_size']
N = snapshot['N']
if alpha_cat not in plot_data:
plot_data[alpha_cat] = {}
if p_e_cat not in plot_data[alpha_cat]:
plot_data[alpha_cat][p_e_cat] = {}
if batch_size not in plot_data[alpha_cat][p_e_cat]:
plot_data[alpha_cat][p_e_cat][batch_size] = []
plot_data[alpha_cat][p_e_cat][batch_size].append((N, steps))
# Sort each series by N
for alpha_cat in plot_data:
for p_e_cat in plot_data[alpha_cat]:
for batch_size in plot_data[alpha_cat][p_e_cat]:
plot_data[alpha_cat][p_e_cat][batch_size].sort(key=lambda x: x[0])
print("Data organization complete!")
print(f"Alpha type keys: {sorted(plot_data.keys())} -> {alpha_types}")
if plot_data:
first_alpha_cat = sorted(plot_data.keys())[0]
print(f"p_e type keys for alpha_type={first_alpha_cat}: {sorted(plot_data[first_alpha_cat].keys())} -> {p_e_types}")
Data organization complete!
Alpha type keys: [0, 1] -> {0: '1', 1: 'N/10'}
p_e type keys for alpha_type=0: [0, 1] -> {0: '1/1000', 1: '1/N'}
[4]:
# Default style pools — extend as needed
_linestyle_pool = ['-', '--', ':', '-.']
_marker_pool = ['.', 's', '^', 'D', 'v', 'o', 'x']
# Auto-assign a linestyle to each alpha_type key (sorted), with optional manual overrides
_alpha_linestyles_manual = {0: '-', 1: '--', 2: ':'} # override or leave empty {}
alpha_linestyles = {
k: _alpha_linestyles_manual.get(k, _linestyle_pool[i % len(_linestyle_pool)])
for i, k in enumerate(sorted(alpha_types.keys()))
}
# Auto-assign a marker to each p_e_type key (sorted), with optional manual overrides
_p_e_markers_manual = {0: '.', 1: 's', 2: '^'} # override or leave empty {}
p_e_markers = {
k: _p_e_markers_manual.get(k, _marker_pool[i % len(_marker_pool)])
for i, k in enumerate(sorted(p_e_types.keys()))
}
cmap = cm.get_cmap('viridis')
norm = mcolors.LogNorm(vmin=batch_sizes.min(), vmax=batch_sizes.max())
fig, ax = plt.subplots()
for alpha_cat in sorted(plot_data.keys()):
for p_e_cat in sorted(plot_data[alpha_cat].keys()):
for batch_size in batch_sizes:
if batch_size not in plot_data[alpha_cat][p_e_cat]:
continue
data_points = plot_data[alpha_cat][p_e_cat][batch_size]
N_list = [point[0] for point in data_points]
steps_list = [point[1] for point in data_points]
if len(N_list) == 0:
continue
color = cmap(norm(batch_size))
linestyle = alpha_linestyles.get(alpha_cat, '-')
marker = p_e_markers.get(p_e_cat, 'o')
ax.plot(N_list, steps_list,
color=color,
linestyle=linestyle,
marker=marker,
alpha=0.7)
sm = cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, ax=ax, label='Batch Size')
from matplotlib.lines import Line2D
# Build legend entries from the type mapping dicts
alpha_legend = [
Line2D([0], [0], color='black', linestyle=alpha_linestyles.get(k, '-'),
label=rf'$\alpha={v}$')
for k, v in sorted(alpha_types.items())
]
p_e_legend = [
Line2D([0], [0], color='black', marker=p_e_markers.get(k, 'o'), linestyle='',
label=rf'$p_e={v}$')
for k, v in sorted(p_e_types.items())
]
legend_elements = alpha_legend + p_e_legend
ax.legend(handles=legend_elements, loc='upper left', bbox_to_anchor=(0.01, 0.925), ncol=1)
ax.set_xlabel('N (Model Size)')
ax.set_ylabel('Steps to Convergence')
ax.set_xscale('log')
ax.set_yscale('log')
plt.tight_layout()
plt.show()
/var/folders/1v/3smrwxkj7_x0r1cz_0ryqhhm0000gn/T/ipykernel_32184/771859517.py:19: MatplotlibDeprecationWarning: The get_cmap function was deprecated in Matplotlib 3.7 and will be removed in 3.11. Use ``matplotlib.colormaps[name]`` or ``matplotlib.colormaps.get_cmap()`` or ``pyplot.get_cmap()`` instead.
cmap = cm.get_cmap('viridis')