Spaces:
Sleeping
Sleeping
- allow user-specified range of regularization strengths
Browse files- backend/src/logic.py +8 -2
- backend/src/manager.py +28 -6
- frontends/gradio/main.py +15 -3
- usage.md +2 -3
backend/src/logic.py
CHANGED
|
@@ -62,6 +62,7 @@ class PlotsData:
|
|
| 62 |
reg_levels: list[float]
|
| 63 |
unreg_solution: np.ndarray
|
| 64 |
path: np.ndarray
|
|
|
|
| 65 |
|
| 66 |
|
| 67 |
def generate_dataset(
|
|
@@ -219,6 +220,8 @@ def compute_regularization_path(
|
|
| 219 |
dataset: Dataset,
|
| 220 |
loss_type: Literal["l1", "l2"],
|
| 221 |
regularizer_type: Literal["l1", "l2"],
|
|
|
|
|
|
|
| 222 |
) -> np.ndarray:
|
| 223 |
X = dataset.X
|
| 224 |
y = np.array(dataset.y)
|
|
@@ -242,8 +245,7 @@ def compute_regularization_path(
|
|
| 242 |
objective = cp.Minimize(loss_expr + lambd * reg_expr)
|
| 243 |
problem = cp.Problem(objective)
|
| 244 |
|
| 245 |
-
|
| 246 |
-
reg_levels = np.linspace(0.001, 50, 100)
|
| 247 |
|
| 248 |
# solve with reg levels in descending order for using warm start
|
| 249 |
w_solutions = []
|
|
@@ -268,6 +270,7 @@ def compute_plot_values(
|
|
| 268 |
w1_range: tuple[float, float],
|
| 269 |
w2_range: tuple[float, float],
|
| 270 |
resolution: int,
|
|
|
|
| 271 |
) -> PlotsData:
|
| 272 |
W1, W2 = build_parameter_grid(w1_range, w2_range, resolution)
|
| 273 |
loss_values = compute_loss(dataset, W1, W2, loss_type)
|
|
@@ -278,7 +281,9 @@ def compute_plot_values(
|
|
| 278 |
dataset,
|
| 279 |
loss_type,
|
| 280 |
regularizer_type,
|
|
|
|
| 281 |
)
|
|
|
|
| 282 |
|
| 283 |
return PlotsData(
|
| 284 |
W1=W1,
|
|
@@ -289,6 +294,7 @@ def compute_plot_values(
|
|
| 289 |
reg_levels=reg_levels,
|
| 290 |
unreg_solution=unreg_solution,
|
| 291 |
path=path,
|
|
|
|
| 292 |
)
|
| 293 |
|
| 294 |
|
|
|
|
| 62 |
reg_levels: list[float]
|
| 63 |
unreg_solution: np.ndarray
|
| 64 |
path: np.ndarray
|
| 65 |
+
path_reg_levels: np.ndarray
|
| 66 |
|
| 67 |
|
| 68 |
def generate_dataset(
|
|
|
|
| 220 |
dataset: Dataset,
|
| 221 |
loss_type: Literal["l1", "l2"],
|
| 222 |
regularizer_type: Literal["l1", "l2"],
|
| 223 |
+
reg_level_range: tuple[float, float],
|
| 224 |
+
num_levels: int = 100,
|
| 225 |
) -> np.ndarray:
|
| 226 |
X = dataset.X
|
| 227 |
y = np.array(dataset.y)
|
|
|
|
| 245 |
objective = cp.Minimize(loss_expr + lambd * reg_expr)
|
| 246 |
problem = cp.Problem(objective)
|
| 247 |
|
| 248 |
+
reg_levels = np.linspace(reg_level_range[0], reg_level_range[1], num_levels)
|
|
|
|
| 249 |
|
| 250 |
# solve with reg levels in descending order for using warm start
|
| 251 |
w_solutions = []
|
|
|
|
| 270 |
w1_range: tuple[float, float],
|
| 271 |
w2_range: tuple[float, float],
|
| 272 |
resolution: int,
|
| 273 |
+
path_reg_level_range: tuple[float, float],
|
| 274 |
) -> PlotsData:
|
| 275 |
W1, W2 = build_parameter_grid(w1_range, w2_range, resolution)
|
| 276 |
loss_values = compute_loss(dataset, W1, W2, loss_type)
|
|
|
|
| 281 |
dataset,
|
| 282 |
loss_type,
|
| 283 |
regularizer_type,
|
| 284 |
+
path_reg_level_range,
|
| 285 |
)
|
| 286 |
+
path_reg_levels = np.linspace(*path_reg_level_range, path.shape[0])
|
| 287 |
|
| 288 |
return PlotsData(
|
| 289 |
W1=W1,
|
|
|
|
| 294 |
reg_levels=reg_levels,
|
| 295 |
unreg_solution=unreg_solution,
|
| 296 |
path=path,
|
| 297 |
+
path_reg_levels=path_reg_levels,
|
| 298 |
)
|
| 299 |
|
| 300 |
|
backend/src/manager.py
CHANGED
|
@@ -158,6 +158,7 @@ class Manager:
|
|
| 158 |
loss_type: str,
|
| 159 |
regularizer_type: str,
|
| 160 |
resolution: int,
|
|
|
|
| 161 |
) -> None:
|
| 162 |
if self.dataset is None:
|
| 163 |
raise ValueError("Dataset is not initialized")
|
|
@@ -177,6 +178,7 @@ class Manager:
|
|
| 177 |
w1_range,
|
| 178 |
w2_range,
|
| 179 |
int(resolution),
|
|
|
|
| 180 |
)
|
| 181 |
|
| 182 |
def handle_generate_plots(
|
|
@@ -196,6 +198,7 @@ class Manager:
|
|
| 196 |
loss_type: str,
|
| 197 |
regularizer_type: str,
|
| 198 |
resolution: int,
|
|
|
|
| 199 |
) -> tuple[Manager, Figure | None, Figure | None, Figure]:
|
| 200 |
self.update_dataset(
|
| 201 |
dataset_type,
|
|
@@ -212,15 +215,28 @@ class Manager:
|
|
| 212 |
y_col,
|
| 213 |
)
|
| 214 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
if self.dataset.X.shape[1] > 2:
|
| 216 |
self.plots_data = None
|
| 217 |
-
path = compute_regularization_path(
|
| 218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
|
| 220 |
self.compute_plots_data(
|
| 221 |
loss_type,
|
| 222 |
regularizer_type,
|
| 223 |
resolution,
|
|
|
|
| 224 |
)
|
| 225 |
|
| 226 |
if self.dataset is None or self.plots_data is None:
|
|
@@ -228,7 +244,11 @@ class Manager:
|
|
| 228 |
|
| 229 |
contour_plot = self._generate_contour_plot(self.plots_data)
|
| 230 |
data_plot = self._generate_data_plot(self.dataset)
|
| 231 |
-
path_plot = self._generate_path_plot(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
return self, contour_plot, data_plot, path_plot
|
| 233 |
|
| 234 |
@staticmethod
|
|
@@ -309,9 +329,11 @@ class Manager:
|
|
| 309 |
return fig
|
| 310 |
|
| 311 |
@staticmethod
|
| 312 |
-
def _generate_path_plot(
|
| 313 |
-
|
| 314 |
-
|
|
|
|
|
|
|
| 315 |
fig, ax = plt.subplots(figsize=(8, 6))
|
| 316 |
ax.set_xlabel("Regularization Strength")
|
| 317 |
ax.set_ylabel("Weight")
|
|
|
|
| 158 |
loss_type: str,
|
| 159 |
regularizer_type: str,
|
| 160 |
resolution: int,
|
| 161 |
+
reg_level_range: tuple[float, float],
|
| 162 |
) -> None:
|
| 163 |
if self.dataset is None:
|
| 164 |
raise ValueError("Dataset is not initialized")
|
|
|
|
| 178 |
w1_range,
|
| 179 |
w2_range,
|
| 180 |
int(resolution),
|
| 181 |
+
reg_level_range,
|
| 182 |
)
|
| 183 |
|
| 184 |
def handle_generate_plots(
|
|
|
|
| 198 |
loss_type: str,
|
| 199 |
regularizer_type: str,
|
| 200 |
resolution: int,
|
| 201 |
+
reg_level_range_input: str,
|
| 202 |
) -> tuple[Manager, Figure | None, Figure | None, Figure]:
|
| 203 |
self.update_dataset(
|
| 204 |
dataset_type,
|
|
|
|
| 215 |
y_col,
|
| 216 |
)
|
| 217 |
|
| 218 |
+
try:
|
| 219 |
+
reg_level_range = self._parse_range(reg_level_range_input)
|
| 220 |
+
except Exception as e:
|
| 221 |
+
raise ValueError(f"Invalid regularization level range: {e}")
|
| 222 |
+
if reg_level_range[0] < 0:
|
| 223 |
+
raise ValueError("Regularization level range cannot include negative values")
|
| 224 |
+
|
| 225 |
if self.dataset.X.shape[1] > 2:
|
| 226 |
self.plots_data = None
|
| 227 |
+
path = compute_regularization_path(
|
| 228 |
+
self.dataset, loss_type, regularizer_type, reg_level_range
|
| 229 |
+
)
|
| 230 |
+
path_reg_levels = np.linspace(*reg_level_range, path.shape[0])
|
| 231 |
+
return self, None, None, self._generate_path_plot(
|
| 232 |
+
path, path_reg_levels, self.dataset.feature_names
|
| 233 |
+
)
|
| 234 |
|
| 235 |
self.compute_plots_data(
|
| 236 |
loss_type,
|
| 237 |
regularizer_type,
|
| 238 |
resolution,
|
| 239 |
+
reg_level_range,
|
| 240 |
)
|
| 241 |
|
| 242 |
if self.dataset is None or self.plots_data is None:
|
|
|
|
| 244 |
|
| 245 |
contour_plot = self._generate_contour_plot(self.plots_data)
|
| 246 |
data_plot = self._generate_data_plot(self.dataset)
|
| 247 |
+
path_plot = self._generate_path_plot(
|
| 248 |
+
self.plots_data.path,
|
| 249 |
+
self.plots_data.path_reg_levels,
|
| 250 |
+
self.dataset.feature_names,
|
| 251 |
+
)
|
| 252 |
return self, contour_plot, data_plot, path_plot
|
| 253 |
|
| 254 |
@staticmethod
|
|
|
|
| 329 |
return fig
|
| 330 |
|
| 331 |
@staticmethod
|
| 332 |
+
def _generate_path_plot(
|
| 333 |
+
path: np.ndarray,
|
| 334 |
+
reg_levels: np.ndarray,
|
| 335 |
+
feature_names: list[str] | None = None,
|
| 336 |
+
) -> Figure:
|
| 337 |
fig, ax = plt.subplots(figsize=(8, 6))
|
| 338 |
ax.set_xlabel("Regularization Strength")
|
| 339 |
ax.set_ylabel("Weight")
|
frontends/gradio/main.py
CHANGED
|
@@ -68,6 +68,7 @@ def handle_generate_plots(
|
|
| 68 |
loss_type: str,
|
| 69 |
regularizer_type: str,
|
| 70 |
resolution: int,
|
|
|
|
| 71 |
):
|
| 72 |
try:
|
| 73 |
result = manager.handle_generate_plots(
|
|
@@ -86,6 +87,7 @@ def handle_generate_plots(
|
|
| 86 |
loss_type,
|
| 87 |
regularizer_type,
|
| 88 |
resolution,
|
|
|
|
| 89 |
)
|
| 90 |
show_2d = manager.dataset.X.shape[1] == 2
|
| 91 |
return (*result, gr.update(visible=show_2d), gr.update(visible=show_2d),
|
|
@@ -97,6 +99,7 @@ def handle_generate_plots(
|
|
| 97 |
|
| 98 |
def launch():
|
| 99 |
default_dataset_type = "Generate"
|
|
|
|
| 100 |
|
| 101 |
default_function = "-50 * x1 + 30 * x2"
|
| 102 |
default_x1_range = "-1, 1"
|
|
@@ -114,10 +117,11 @@ def launch():
|
|
| 114 |
default_loss_type = "l2"
|
| 115 |
default_regularizer_type = "l2"
|
| 116 |
default_resolution = 100
|
|
|
|
| 117 |
|
| 118 |
manager = Manager()
|
| 119 |
manager, default_contour_plot, default_data_plot, default_path_plot = manager.handle_generate_plots(
|
| 120 |
-
|
| 121 |
default_function,
|
| 122 |
default_x1_range,
|
| 123 |
default_x2_range,
|
|
@@ -132,6 +136,7 @@ def launch():
|
|
| 132 |
default_loss_type,
|
| 133 |
default_regularizer_type,
|
| 134 |
default_resolution,
|
|
|
|
| 135 |
)
|
| 136 |
|
| 137 |
with gr.Blocks() as demo:
|
|
@@ -153,10 +158,10 @@ def launch():
|
|
| 153 |
with gr.Tab("Data"):
|
| 154 |
dataset_name = gr.Dropdown(
|
| 155 |
label="Dataset", choices=["Custom", *PREDEFINED_DATASETS],
|
| 156 |
-
value=
|
| 157 |
)
|
| 158 |
gr.Markdown("Predefined datasets use standardized covariates and a centered target.")
|
| 159 |
-
with gr.Group() as custom_controls:
|
| 160 |
with gr.Row():
|
| 161 |
dataset_type = gr.Radio(
|
| 162 |
label="Dataset type",
|
|
@@ -288,6 +293,12 @@ def launch():
|
|
| 288 |
step=1,
|
| 289 |
interactive=True,
|
| 290 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 291 |
regenerate_plots_button2 = gr.Button("Regenerate Plots")
|
| 292 |
|
| 293 |
with gr.Tab("Usage"):
|
|
@@ -319,6 +330,7 @@ def launch():
|
|
| 319 |
loss_type_dropdown,
|
| 320 |
regularizer_type_dropdown,
|
| 321 |
resolution_slider,
|
|
|
|
| 322 |
],
|
| 323 |
outputs=[manager_state, main_plot, data_plot, path_plot,
|
| 324 |
contour_tab, data_tab, plot_tabs, resolution_slider],
|
|
|
|
| 68 |
loss_type: str,
|
| 69 |
regularizer_type: str,
|
| 70 |
resolution: int,
|
| 71 |
+
reg_level_range: str,
|
| 72 |
):
|
| 73 |
try:
|
| 74 |
result = manager.handle_generate_plots(
|
|
|
|
| 87 |
loss_type,
|
| 88 |
regularizer_type,
|
| 89 |
resolution,
|
| 90 |
+
reg_level_range,
|
| 91 |
)
|
| 92 |
show_2d = manager.dataset.X.shape[1] == 2
|
| 93 |
return (*result, gr.update(visible=show_2d), gr.update(visible=show_2d),
|
|
|
|
| 99 |
|
| 100 |
def launch():
|
| 101 |
default_dataset_type = "Generate"
|
| 102 |
+
default_dataset_name = "Synthetic (2 covariates)"
|
| 103 |
|
| 104 |
default_function = "-50 * x1 + 30 * x2"
|
| 105 |
default_x1_range = "-1, 1"
|
|
|
|
| 117 |
default_loss_type = "l2"
|
| 118 |
default_regularizer_type = "l2"
|
| 119 |
default_resolution = 100
|
| 120 |
+
default_reg_level_range = "0.001, 50"
|
| 121 |
|
| 122 |
manager = Manager()
|
| 123 |
manager, default_contour_plot, default_data_plot, default_path_plot = manager.handle_generate_plots(
|
| 124 |
+
default_dataset_name,
|
| 125 |
default_function,
|
| 126 |
default_x1_range,
|
| 127 |
default_x2_range,
|
|
|
|
| 136 |
default_loss_type,
|
| 137 |
default_regularizer_type,
|
| 138 |
default_resolution,
|
| 139 |
+
default_reg_level_range,
|
| 140 |
)
|
| 141 |
|
| 142 |
with gr.Blocks() as demo:
|
|
|
|
| 158 |
with gr.Tab("Data"):
|
| 159 |
dataset_name = gr.Dropdown(
|
| 160 |
label="Dataset", choices=["Custom", *PREDEFINED_DATASETS],
|
| 161 |
+
value=default_dataset_name, interactive=True,
|
| 162 |
)
|
| 163 |
gr.Markdown("Predefined datasets use standardized covariates and a centered target.")
|
| 164 |
+
with gr.Group(visible=False) as custom_controls:
|
| 165 |
with gr.Row():
|
| 166 |
dataset_type = gr.Radio(
|
| 167 |
label="Dataset type",
|
|
|
|
| 293 |
step=1,
|
| 294 |
interactive=True,
|
| 295 |
)
|
| 296 |
+
reg_level_range = gr.Textbox(
|
| 297 |
+
label="Regularization level range",
|
| 298 |
+
value=default_reg_level_range,
|
| 299 |
+
placeholder="minimum, maximum",
|
| 300 |
+
interactive=True,
|
| 301 |
+
)
|
| 302 |
regenerate_plots_button2 = gr.Button("Regenerate Plots")
|
| 303 |
|
| 304 |
with gr.Tab("Usage"):
|
|
|
|
| 330 |
loss_type_dropdown,
|
| 331 |
regularizer_type_dropdown,
|
| 332 |
resolution_slider,
|
| 333 |
+
reg_level_range,
|
| 334 |
],
|
| 335 |
outputs=[manager_state, main_plot, data_plot, path_plot,
|
| 336 |
contour_tab, data_tab, plot_tabs, resolution_slider],
|
usage.md
CHANGED
|
@@ -11,7 +11,7 @@ In the data tab, select a predefined regression dataset, or choose **Custom** to
|
|
| 11 |
|
| 12 |
### Regularization
|
| 13 |
|
| 14 |
-
Select your loss function and regularization type
|
| 15 |
|
| 16 |
### Generating plots
|
| 17 |
|
|
@@ -22,7 +22,7 @@ Click **Regenerate Plots** to update plots with the current settings
|
|
| 22 |
### Contour Plot
|
| 23 |
|
| 24 |
The contour plot shows the loss values and the regularization norms for the linear models on the dataset. The red path shows how the loss and regularization norm values
|
| 25 |
-
change
|
| 26 |
|
| 27 |
|
| 28 |
### Data Plot
|
|
@@ -33,4 +33,3 @@ The data plot shows the data points used to compute the loss values.
|
|
| 33 |
### Regularization path plot
|
| 34 |
|
| 35 |
The regularization path plot shows regularization strength versus coefficient value for every covariate. Datasets with more than two covariates show only this plot. These match the values shown on the red regularization path in the contour plot.
|
| 36 |
-
|
|
|
|
| 11 |
|
| 12 |
### Regularization
|
| 13 |
|
| 14 |
+
Select your loss function and regularization type. Set the regularization level range as two comma-separated values (for example, `0.001, 50`).
|
| 15 |
|
| 16 |
### Generating plots
|
| 17 |
|
|
|
|
| 22 |
### Contour Plot
|
| 23 |
|
| 24 |
The contour plot shows the loss values and the regularization norms for the linear models on the dataset. The red path shows how the loss and regularization norm values
|
| 25 |
+
change across the selected regularization level range. You can see that the path intersects where the loss and regularization contours are parallel.
|
| 26 |
|
| 27 |
|
| 28 |
### Data Plot
|
|
|
|
| 33 |
### Regularization path plot
|
| 34 |
|
| 35 |
The regularization path plot shows regularization strength versus coefficient value for every covariate. Datasets with more than two covariates show only this plot. These match the values shown on the red regularization path in the contour plot.
|
|
|