Spaces:
Sleeping
Sleeping
- added support for builtin datasets (2D or higher dimensional)
Browse files- fixed regularization path bug (weight increasing in strength)
- improve plots (use linspace instead of logspace for regularization strength, use average loss)
- backend/src/logic.py +40 -12
- backend/src/manager.py +19 -8
- frontends/gradio/main.py +154 -153
- usage.md +3 -3
backend/src/logic.py
CHANGED
|
@@ -19,6 +19,37 @@ class Dataset:
|
|
| 19 |
x1: list[float]
|
| 20 |
x2: list[float]
|
| 21 |
y: list[float]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
|
| 24 |
@dataclass
|
|
@@ -189,19 +220,15 @@ def compute_regularization_path(
|
|
| 189 |
loss_type: Literal["l1", "l2"],
|
| 190 |
regularizer_type: Literal["l1", "l2"],
|
| 191 |
) -> np.ndarray:
|
| 192 |
-
|
| 193 |
-
x2 = np.array(dataset.x2)
|
| 194 |
y = np.array(dataset.y)
|
| 195 |
-
|
| 196 |
-
X = np.stack([x1, x2], axis=1) # (N, 2)
|
| 197 |
-
|
| 198 |
-
w = cp.Variable(2)
|
| 199 |
lambd = cp.Parameter(nonneg=True)
|
| 200 |
|
| 201 |
if loss_type == "l2":
|
| 202 |
-
loss_expr = cp.sum_squares(y - X @ w)
|
| 203 |
elif loss_type == "l1":
|
| 204 |
-
loss_expr = cp.norm1(y - X @ w)
|
| 205 |
else:
|
| 206 |
raise ValueError(f"Unknown loss type: {loss_type}")
|
| 207 |
|
|
@@ -216,20 +243,21 @@ def compute_regularization_path(
|
|
| 216 |
problem = cp.Problem(objective)
|
| 217 |
|
| 218 |
# todo - user defined reg levels
|
| 219 |
-
reg_levels = np.
|
| 220 |
|
| 221 |
# solve with reg levels in descending order for using warm start
|
| 222 |
w_solutions = []
|
| 223 |
for reg_level in sorted(reg_levels, reverse=True):
|
| 224 |
lambd.value = reg_level
|
| 225 |
problem.solve(warm_start=True)
|
|
|
|
| 226 |
|
| 227 |
if w.value is None:
|
| 228 |
-
w_solutions.append(np.
|
| 229 |
else:
|
| 230 |
w_solutions.append(w.value.copy())
|
| 231 |
|
| 232 |
-
return np.array(w_solutions)
|
| 233 |
|
| 234 |
|
| 235 |
def compute_plot_values(
|
|
@@ -284,4 +312,4 @@ def compute_suggested_settings(
|
|
| 284 |
|
| 285 |
reg_levels = [i / 4 * opt_norm for i in range(1, 4)]
|
| 286 |
|
| 287 |
-
return w1_range, w2_range, reg_levels
|
|
|
|
| 19 |
x1: list[float]
|
| 20 |
x2: list[float]
|
| 21 |
y: list[float]
|
| 22 |
+
features: np.ndarray | None = None
|
| 23 |
+
feature_names: list[str] | None = None
|
| 24 |
+
|
| 25 |
+
@property
|
| 26 |
+
def X(self) -> np.ndarray:
|
| 27 |
+
return self.features if self.features is not None else np.column_stack([self.x1, self.x2])
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
PREDEFINED_DATASETS = ["Diabetes", "Synthetic (2 covariates)", "Synthetic (10 covariates)"]
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def load_predefined_dataset(name: str) -> Dataset:
|
| 34 |
+
from sklearn.datasets import load_diabetes, make_regression
|
| 35 |
+
from sklearn.preprocessing import StandardScaler
|
| 36 |
+
|
| 37 |
+
if name == "Diabetes":
|
| 38 |
+
data = load_diabetes()
|
| 39 |
+
X, y = data.data, data.target
|
| 40 |
+
names = list(data.feature_names)
|
| 41 |
+
elif name in PREDEFINED_DATASETS[1:]:
|
| 42 |
+
n_features = 2 if name == "Synthetic (2 covariates)" else 10
|
| 43 |
+
X, y = make_regression(
|
| 44 |
+
n_samples=100, n_features=n_features,
|
| 45 |
+
n_informative=min(5, n_features), noise=10, random_state=42,
|
| 46 |
+
)
|
| 47 |
+
names = [f"x{i + 1}" for i in range(n_features)]
|
| 48 |
+
else:
|
| 49 |
+
raise ValueError(f"Unknown predefined dataset: {name}")
|
| 50 |
+
X = StandardScaler().fit_transform(X)
|
| 51 |
+
y = y - np.mean(y)
|
| 52 |
+
return Dataset(X[:, 0].tolist(), X[:, 1].tolist(), y.tolist(), X, names)
|
| 53 |
|
| 54 |
|
| 55 |
@dataclass
|
|
|
|
| 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)
|
| 225 |
+
w = cp.Variable(X.shape[1])
|
|
|
|
|
|
|
|
|
|
| 226 |
lambd = cp.Parameter(nonneg=True)
|
| 227 |
|
| 228 |
if loss_type == "l2":
|
| 229 |
+
loss_expr = cp.sum_squares(y - X @ w)/(2*len(X))
|
| 230 |
elif loss_type == "l1":
|
| 231 |
+
loss_expr = cp.norm1(y - X @ w)/(len(X))
|
| 232 |
else:
|
| 233 |
raise ValueError(f"Unknown loss type: {loss_type}")
|
| 234 |
|
|
|
|
| 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 = []
|
| 250 |
for reg_level in sorted(reg_levels, reverse=True):
|
| 251 |
lambd.value = reg_level
|
| 252 |
problem.solve(warm_start=True)
|
| 253 |
+
#print(problem.status)
|
| 254 |
|
| 255 |
if w.value is None:
|
| 256 |
+
w_solutions.append(np.full(X.shape[1], np.nan))
|
| 257 |
else:
|
| 258 |
w_solutions.append(w.value.copy())
|
| 259 |
|
| 260 |
+
return np.array(w_solutions[::-1])
|
| 261 |
|
| 262 |
|
| 263 |
def compute_plot_values(
|
|
|
|
| 312 |
|
| 313 |
reg_levels = [i / 4 * opt_norm for i in range(1, 4)]
|
| 314 |
|
| 315 |
+
return w1_range, w2_range, reg_levels
|
backend/src/manager.py
CHANGED
|
@@ -19,6 +19,9 @@ from logic import (
|
|
| 19 |
compute_suggested_settings,
|
| 20 |
generate_dataset,
|
| 21 |
load_dataset_from_csv,
|
|
|
|
|
|
|
|
|
|
| 22 |
)
|
| 23 |
|
| 24 |
|
|
@@ -79,6 +82,8 @@ class Manager:
|
|
| 79 |
x2_col: int,
|
| 80 |
y_col: int,
|
| 81 |
) -> Dataset:
|
|
|
|
|
|
|
| 82 |
if dataset_type == "Generate":
|
| 83 |
x1, x2 = symbols("x1 x2")
|
| 84 |
allowed_locals = {
|
|
@@ -191,7 +196,7 @@ class Manager:
|
|
| 191 |
loss_type: str,
|
| 192 |
regularizer_type: str,
|
| 193 |
resolution: int,
|
| 194 |
-
) -> tuple[Manager, Figure, Figure, Figure]:
|
| 195 |
self.update_dataset(
|
| 196 |
dataset_type,
|
| 197 |
function,
|
|
@@ -207,6 +212,11 @@ class Manager:
|
|
| 207 |
y_col,
|
| 208 |
)
|
| 209 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
self.compute_plots_data(
|
| 211 |
loss_type,
|
| 212 |
regularizer_type,
|
|
@@ -218,8 +228,8 @@ class Manager:
|
|
| 218 |
|
| 219 |
contour_plot = self._generate_contour_plot(self.plots_data)
|
| 220 |
data_plot = self._generate_data_plot(self.dataset)
|
| 221 |
-
|
| 222 |
-
return self, contour_plot, data_plot,
|
| 223 |
|
| 224 |
@staticmethod
|
| 225 |
def _generate_contour_plot(plots_data: PlotsData) -> Figure:
|
|
@@ -299,16 +309,17 @@ class Manager:
|
|
| 299 |
return fig
|
| 300 |
|
| 301 |
@staticmethod
|
| 302 |
-
def
|
| 303 |
-
reg_levels = np.
|
| 304 |
|
| 305 |
fig, ax = plt.subplots(figsize=(8, 6))
|
| 306 |
ax.set_xlabel("Regularization Strength")
|
| 307 |
ax.set_ylabel("Weight")
|
| 308 |
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
|
|
|
| 312 |
ax.legend()
|
| 313 |
ax.grid(True)
|
| 314 |
return fig
|
|
|
|
| 19 |
compute_suggested_settings,
|
| 20 |
generate_dataset,
|
| 21 |
load_dataset_from_csv,
|
| 22 |
+
load_predefined_dataset,
|
| 23 |
+
PREDEFINED_DATASETS,
|
| 24 |
+
compute_regularization_path,
|
| 25 |
)
|
| 26 |
|
| 27 |
|
|
|
|
| 82 |
x2_col: int,
|
| 83 |
y_col: int,
|
| 84 |
) -> Dataset:
|
| 85 |
+
if dataset_type in PREDEFINED_DATASETS:
|
| 86 |
+
return load_predefined_dataset(dataset_type)
|
| 87 |
if dataset_type == "Generate":
|
| 88 |
x1, x2 = symbols("x1 x2")
|
| 89 |
allowed_locals = {
|
|
|
|
| 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,
|
| 202 |
function,
|
|
|
|
| 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,
|
|
|
|
| 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
|
| 235 |
def _generate_contour_plot(plots_data: PlotsData) -> Figure:
|
|
|
|
| 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")
|
| 318 |
|
| 319 |
+
for i in range(path.shape[1]):
|
| 320 |
+
label = feature_names[i] if feature_names else f"w{i + 1}"
|
| 321 |
+
ax.plot(reg_levels, path[:, i], label=label)
|
| 322 |
+
#ax.set_xscale("log")
|
| 323 |
ax.legend()
|
| 324 |
ax.grid(True)
|
| 325 |
return fig
|
frontends/gradio/main.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
from typing import Literal
|
| 2 |
|
| 3 |
import gradio as gr
|
| 4 |
-
from matplotlib.figure import Figure
|
| 5 |
|
| 6 |
import sys
|
| 7 |
from pathlib import Path
|
|
@@ -12,6 +11,7 @@ if str(backend_src) not in sys.path:
|
|
| 12 |
sys.path.append(str(backend_src))
|
| 13 |
|
| 14 |
from manager import Manager
|
|
|
|
| 15 |
|
| 16 |
CSS = """
|
| 17 |
.hidden-button {
|
|
@@ -53,6 +53,7 @@ def handle_dataset_type_change(dataset_type: Literal["Generate", "CSV"]):
|
|
| 53 |
def handle_generate_plots(
|
| 54 |
manager: Manager,
|
| 55 |
dataset_type: str,
|
|
|
|
| 56 |
function: str,
|
| 57 |
x1_range_input: str,
|
| 58 |
x2_range_input: str,
|
|
@@ -67,10 +68,10 @@ def handle_generate_plots(
|
|
| 67 |
loss_type: str,
|
| 68 |
regularizer_type: str,
|
| 69 |
resolution: int,
|
| 70 |
-
)
|
| 71 |
try:
|
| 72 |
-
|
| 73 |
-
dataset_type,
|
| 74 |
function,
|
| 75 |
x1_range_input,
|
| 76 |
x2_range_input,
|
|
@@ -86,6 +87,10 @@ def handle_generate_plots(
|
|
| 86 |
regularizer_type,
|
| 87 |
resolution,
|
| 88 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
except Exception as e:
|
| 90 |
raise gr.Error("Error generating plots: " + str(e))
|
| 91 |
|
|
@@ -111,7 +116,7 @@ def launch():
|
|
| 111 |
default_resolution = 100
|
| 112 |
|
| 113 |
manager = Manager()
|
| 114 |
-
manager, default_contour_plot, default_data_plot,
|
| 115 |
default_dataset_type,
|
| 116 |
default_function,
|
| 117 |
default_x1_range,
|
|
@@ -136,116 +141,127 @@ def launch():
|
|
| 136 |
|
| 137 |
with gr.Row():
|
| 138 |
with gr.Column(scale=2):
|
| 139 |
-
with gr.
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
|
|
|
| 145 |
|
| 146 |
with gr.Column(scale=1):
|
| 147 |
with gr.Tab("Data"):
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
)
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
)
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
inputs=[dataset_type],
|
| 236 |
-
outputs=[
|
| 237 |
-
function,
|
| 238 |
-
x1_textbox,
|
| 239 |
-
x2_textbox,
|
| 240 |
-
x_selection_method,
|
| 241 |
-
sigma,
|
| 242 |
-
nsample,
|
| 243 |
-
csv_file,
|
| 244 |
-
has_header,
|
| 245 |
-
x1_col,
|
| 246 |
-
x2_col,
|
| 247 |
-
y_col,
|
| 248 |
-
],
|
| 249 |
)
|
| 250 |
regenerate_plots_button1 = gr.Button("Regenerate Plots")
|
| 251 |
|
|
@@ -278,50 +294,35 @@ def launch():
|
|
| 278 |
with open(root_dir / "usage.md", "r") as f:
|
| 279 |
gr.Markdown(f.read())
|
| 280 |
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
x2_textbox,
|
| 311 |
-
x_selection_method,
|
| 312 |
-
sigma,
|
| 313 |
-
nsample,
|
| 314 |
-
csv_file,
|
| 315 |
-
has_header,
|
| 316 |
-
x1_col,
|
| 317 |
-
x2_col,
|
| 318 |
-
y_col,
|
| 319 |
-
loss_type_dropdown,
|
| 320 |
-
regularizer_type_dropdown,
|
| 321 |
-
resolution_slider,
|
| 322 |
-
],
|
| 323 |
-
outputs=[manager_state, main_plot, data_plot, strength_plot],
|
| 324 |
-
)
|
| 325 |
|
| 326 |
|
| 327 |
demo.launch(css=CSS)
|
|
|
|
| 1 |
from typing import Literal
|
| 2 |
|
| 3 |
import gradio as gr
|
|
|
|
| 4 |
|
| 5 |
import sys
|
| 6 |
from pathlib import Path
|
|
|
|
| 11 |
sys.path.append(str(backend_src))
|
| 12 |
|
| 13 |
from manager import Manager
|
| 14 |
+
from logic import PREDEFINED_DATASETS
|
| 15 |
|
| 16 |
CSS = """
|
| 17 |
.hidden-button {
|
|
|
|
| 53 |
def handle_generate_plots(
|
| 54 |
manager: Manager,
|
| 55 |
dataset_type: str,
|
| 56 |
+
dataset_name: str,
|
| 57 |
function: str,
|
| 58 |
x1_range_input: str,
|
| 59 |
x2_range_input: str,
|
|
|
|
| 68 |
loss_type: str,
|
| 69 |
regularizer_type: str,
|
| 70 |
resolution: int,
|
| 71 |
+
):
|
| 72 |
try:
|
| 73 |
+
result = manager.handle_generate_plots(
|
| 74 |
+
dataset_type if dataset_name == "Custom" else dataset_name,
|
| 75 |
function,
|
| 76 |
x1_range_input,
|
| 77 |
x2_range_input,
|
|
|
|
| 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),
|
| 92 |
+
gr.update(selected="contours" if show_2d else "path"),
|
| 93 |
+
gr.update(visible=show_2d))
|
| 94 |
except Exception as e:
|
| 95 |
raise gr.Error("Error generating plots: " + str(e))
|
| 96 |
|
|
|
|
| 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,
|
|
|
|
| 141 |
|
| 142 |
with gr.Row():
|
| 143 |
with gr.Column(scale=2):
|
| 144 |
+
with gr.Tabs() as plot_tabs:
|
| 145 |
+
with gr.Tab("Contours", id="contours") as contour_tab:
|
| 146 |
+
main_plot = gr.Plot(value=default_contour_plot)
|
| 147 |
+
with gr.Tab("Data", id="data") as data_tab:
|
| 148 |
+
data_plot = gr.Plot(value=default_data_plot)
|
| 149 |
+
with gr.Tab("Regularization path", id="path"):
|
| 150 |
+
path_plot = gr.Plot(value=default_path_plot)
|
| 151 |
|
| 152 |
with gr.Column(scale=1):
|
| 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",
|
| 163 |
+
choices=["Generate", "CSV"],
|
| 164 |
+
value=default_dataset_type,
|
| 165 |
+
interactive=True,
|
| 166 |
+
)
|
| 167 |
+
|
| 168 |
+
with gr.Row():
|
| 169 |
+
function = gr.Textbox(
|
| 170 |
+
label="Function (in terms of x1 and x2)",
|
| 171 |
+
value=default_function,
|
| 172 |
+
interactive=True,
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
with gr.Row():
|
| 176 |
+
x1_textbox = gr.Textbox(
|
| 177 |
+
label="x1 range",
|
| 178 |
+
value=default_x1_range,
|
| 179 |
+
interactive=True,
|
| 180 |
+
)
|
| 181 |
+
x2_textbox = gr.Textbox(
|
| 182 |
+
label="x2 range",
|
| 183 |
+
value=default_x2_range,
|
| 184 |
+
interactive=True,
|
| 185 |
+
)
|
| 186 |
|
| 187 |
+
with gr.Row():
|
| 188 |
+
x_selection_method = gr.Radio(
|
| 189 |
+
label="How to select x points",
|
| 190 |
+
choices=["Grid", "Random"],
|
| 191 |
+
value=default_x_selection_method,
|
| 192 |
+
interactive=True,
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
with gr.Row():
|
| 196 |
+
sigma = gr.Number(
|
| 197 |
+
label="Gaussian noise standard deviation",
|
| 198 |
+
value=default_sigma,
|
| 199 |
+
interactive=True,
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
with gr.Row():
|
| 203 |
+
nsample = gr.Slider(
|
| 204 |
+
label="Number of points",
|
| 205 |
+
value=default_num_points,
|
| 206 |
+
interactive=True,
|
| 207 |
+
minimum=2, # todo - set to 1 after fixing weird cases
|
| 208 |
+
maximum=100,
|
| 209 |
+
step=1,
|
| 210 |
+
)
|
| 211 |
+
|
| 212 |
+
with gr.Row():
|
| 213 |
+
csv_file = gr.File(
|
| 214 |
+
label="Upload CSV file - must have columns: (x1, x2, y)",
|
| 215 |
+
file_types=[".csv"],
|
| 216 |
+
visible=False,
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
with gr.Row():
|
| 220 |
+
has_header = gr.Checkbox(
|
| 221 |
+
label="CSV has header row",
|
| 222 |
+
value=default_has_header,
|
| 223 |
+
visible=False,
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
with gr.Row():
|
| 227 |
+
x1_col = gr.Number(
|
| 228 |
+
label="x1 column index (0-based)",
|
| 229 |
+
value=default_x1_col,
|
| 230 |
+
visible=False,
|
| 231 |
+
)
|
| 232 |
+
x2_col = gr.Number(
|
| 233 |
+
label="x2 column index (0-based)",
|
| 234 |
+
value=default_x2_col,
|
| 235 |
+
visible=False,
|
| 236 |
+
)
|
| 237 |
|
| 238 |
+
with gr.Row():
|
| 239 |
+
y_col = gr.Number(
|
| 240 |
+
label="y column index (0-based)",
|
| 241 |
+
value=default_y_col,
|
| 242 |
+
visible=False,
|
| 243 |
+
)
|
| 244 |
+
|
| 245 |
+
dataset_type.change(
|
| 246 |
+
fn=handle_dataset_type_change,
|
| 247 |
+
inputs=[dataset_type],
|
| 248 |
+
outputs=[
|
| 249 |
+
function,
|
| 250 |
+
x1_textbox,
|
| 251 |
+
x2_textbox,
|
| 252 |
+
x_selection_method,
|
| 253 |
+
sigma,
|
| 254 |
+
nsample,
|
| 255 |
+
csv_file,
|
| 256 |
+
has_header,
|
| 257 |
+
x1_col,
|
| 258 |
+
x2_col,
|
| 259 |
+
y_col,
|
| 260 |
+
],
|
| 261 |
)
|
| 262 |
+
dataset_name.change(
|
| 263 |
+
fn=lambda name: gr.update(visible=name == "Custom"),
|
| 264 |
+
inputs=[dataset_name], outputs=[custom_controls],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
)
|
| 266 |
regenerate_plots_button1 = gr.Button("Regenerate Plots")
|
| 267 |
|
|
|
|
| 294 |
with open(root_dir / "usage.md", "r") as f:
|
| 295 |
gr.Markdown(f.read())
|
| 296 |
|
| 297 |
+
for event in (
|
| 298 |
+
regenerate_plots_button1.click,
|
| 299 |
+
regenerate_plots_button2.click,
|
| 300 |
+
dataset_name.change,
|
| 301 |
+
):
|
| 302 |
+
event(
|
| 303 |
+
fn=handle_generate_plots,
|
| 304 |
+
inputs=[
|
| 305 |
+
manager_state,
|
| 306 |
+
dataset_type,
|
| 307 |
+
dataset_name,
|
| 308 |
+
function,
|
| 309 |
+
x1_textbox,
|
| 310 |
+
x2_textbox,
|
| 311 |
+
x_selection_method,
|
| 312 |
+
sigma,
|
| 313 |
+
nsample,
|
| 314 |
+
csv_file,
|
| 315 |
+
has_header,
|
| 316 |
+
x1_col,
|
| 317 |
+
x2_col,
|
| 318 |
+
y_col,
|
| 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],
|
| 325 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
|
| 327 |
|
| 328 |
demo.launch(css=CSS)
|
usage.md
CHANGED
|
@@ -6,7 +6,7 @@ In this visualizer, you visualize the loss and regularization of linear regressi
|
|
| 6 |
|
| 7 |
### Data
|
| 8 |
|
| 9 |
-
In the data tab, generate a dataset using a custom function or upload a
|
| 10 |
|
| 11 |
|
| 12 |
### Regularization
|
|
@@ -30,7 +30,7 @@ change as the regularization strength moves from 10^{-4} to 10^4 (log scale). Yo
|
|
| 30 |
The data plot shows the data points used to compute the loss values.
|
| 31 |
|
| 32 |
|
| 33 |
-
###
|
| 34 |
|
| 35 |
-
The
|
| 36 |
|
|
|
|
| 6 |
|
| 7 |
### Data
|
| 8 |
|
| 9 |
+
In the data tab, select a predefined regression dataset, or choose **Custom** to generate a dataset using a custom function or upload a CSV file. Predefined datasets have standardized covariates and a centered target. Make sure the function depends on `x1` and `x2`.
|
| 10 |
|
| 11 |
|
| 12 |
### Regularization
|
|
|
|
| 30 |
The data plot shows the data points used to compute the loss values.
|
| 31 |
|
| 32 |
|
| 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 |
|