nanye commited on
Commit
ae86659
·
1 Parent(s): 24d4e3b

- allow user-specified range of regularization strengths

Browse files
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
- # todo - user defined reg levels
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(self.dataset, loss_type, regularizer_type)
218
- return self, None, None, self._generate_path_plot(path, self.dataset.feature_names)
 
 
 
 
 
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(self.plots_data.path, self.dataset.feature_names)
 
 
 
 
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(path: np.ndarray, feature_names: list[str] | None = None) -> Figure:
313
- reg_levels = np.linspace(0.001, 50, path.shape[0])
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
- default_dataset_type,
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="Custom", interactive=True,
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 as the regularization strength moves from 10^{-4} to 10^4 (log scale). You can see that the path intersects where the loss and regularization contours are parallel.
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.