Skip to content

Commit f6b0e7d

Browse files
committed
collinear dataset and vision models now work for ndim > 1 also why did it download CIFAR10 to data sure hope this doesn't happen in the future and I don't accidentally include it with the repo
1 parent f3158e0 commit f6b0e7d

File tree

10 files changed

+167
-23
lines changed

10 files changed

+167
-23
lines changed

visualbench/benchmark.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ def __init__(
6363

6464
self._reference_images: dict[str, torch.Tensor] = {}
6565
"""images to always include in visualizations"""
66-
self._image_keys: python_tools.SortedSet[str] = python_tools.SortedSet()
66+
self._image_keys: python_tools.SortedSet = python_tools.SortedSet()
6767
"""keys to display as images"""
68-
self._image_lowest_keys: python_tools.SortedSet[str] = python_tools.SortedSet()
68+
self._image_lowest_keys: python_tools.SortedSet = python_tools.SortedSet()
6969
"""keys to display images corresponding to lowest loss found so far"""
70-
self._plot_keys: python_tools.SortedSet[str] = python_tools.SortedSet()
70+
self._plot_keys: python_tools.SortedSet = python_tools.SortedSet()
7171
"""keys to display line charts for"""
7272

7373
self._basis: torch.Tensor | None = None

visualbench/models/vision.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ class TinyWideConvNet(nn.Module):
7676
def __init__(self, in_size:int | Sequence[int], in_channels:int, out_channels:int, act_cls: Callable = nn.ReLU, dropout=0.5):
7777
super().__init__()
7878
if isinstance(in_size, int): in_size = (in_size, )
79-
ndim = len(in_size)
79+
self.ndim = len(in_size)
8080

81-
Conv = ConvNd(ndim)
82-
MaxPool = MaxPoolNd(ndim)
83-
Dropout = DropoutNd(ndim)
81+
Conv = ConvNd(self.ndim)
82+
MaxPool = MaxPoolNd(self.ndim)
83+
Dropout = DropoutNd(self.ndim)
8484

8585
self.c1 = nn.Sequential(
8686
Conv(in_channels, 8, kernel_size=5), # ~37
@@ -105,7 +105,8 @@ def forward(self, x):
105105
if x.ndim == 2: x = x.unsqueeze(1)
106106
x = self.c1(x)
107107
x = self.c2(x)
108-
x = self.c3(x).mean(-1)
108+
dims = [-i for i in range(1, self.ndim+1)]
109+
x = self.c3(x).mean(dims)
109110
return self.linear(x)
110111

111112

@@ -114,11 +115,11 @@ class TinyLongConvNet(nn.Module):
114115
def __init__(self, in_size:int | Sequence[int], in_channels:int, out_channels:int, act_cls: Callable = nn.ReLU, dropout=0.0):
115116
super().__init__()
116117
if isinstance(in_size, int): in_size = (in_size, )
117-
ndim = len(in_size)
118+
self.ndim = len(in_size)
118119

119-
Conv = ConvNd(ndim)
120-
Dropout = DropoutNd(ndim)
121-
BatchNorm = BatchNormNd(ndim)
120+
Conv = ConvNd(self.ndim)
121+
Dropout = DropoutNd(self.ndim)
122+
BatchNorm = BatchNormNd(self.ndim)
122123

123124
self.c1 = nn.Sequential(
124125
Conv(in_channels, 4, kernel_size=2, bias=False),
@@ -158,7 +159,9 @@ def forward(self, x):
158159
if x.ndim == 2: x = x.unsqueeze(1)
159160
x = self.c1(x)
160161
x = self.c2(x)
161-
x = self.c3(x).mean(-1)
162+
163+
dims = [-i for i in range(1, self.ndim+1)]
164+
x = self.c3(x).mean(dims)
162165
return self.linear(x)
163166

164167

@@ -258,10 +261,10 @@ class MobileNet(nn.Module):
258261
def __init__(self, in_size:int | Sequence[int], in_channels:int, out_channels:int, act_cls: Callable = nn.ReLU, dropout=0.5):
259262
super().__init__()
260263
if isinstance(in_size, int): in_size = (in_size, )
261-
ndim = len(in_size)
264+
self.ndim = len(in_size)
262265

263-
Conv = ConvNd(ndim)
264-
Dropout = DropoutNd(ndim)
266+
Conv = ConvNd(self.ndim)
267+
Dropout = DropoutNd(self.ndim)
265268

266269
self.c1 = nn.Sequential(
267270
Conv(in_channels, 32, kernel_size=3, stride=2, padding=1),
@@ -297,7 +300,8 @@ def forward(self, x):
297300
x = self.c1(x)
298301
x = self.c2(x)
299302
x = self.c3(x)
300-
return x.mean(-1)
303+
dims = [-i for i in range(1, self.ndim+1)]
304+
return x.mean(dims)
301305

302306
def convblocknd(in_channels, out_channels, kernel_size, stride, padding, act_cls, bn: bool, dropout:float|None, transpose=False, ndim:int=2):
303307
ConvCls = ConvTransposeNd(ndim) if transpose else ConvNd(ndim)

visualbench/runs/benchmark_benchmark.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,29 @@ def logger_fn(value: float):
134134

135135
self.run_optimizer = run_optimizer
136136

137+
def quickrun(self):
138+
opt = lambda p, lr: torch.optim.SGD(p, lr)
139+
self.run_optimizer(opt, "SGD", tune=True, max_dim=None)
140+
141+
opt = lambda p, lr: torch.optim.SGD(p, lr, momentum=0.9, nesterov=True)
142+
self.run_optimizer(opt, "NAG(0.95)", tune=True, max_dim=None)
143+
144+
opt = lambda p, lr: torch.optim.Adam(p, lr)
145+
self.run_optimizer(opt, "Adam", tune=True, max_dim=None)
146+
147+
opt = lambda p, lr: torch.optim.Adam(p, lr, betas=(0.95, 0.95))
148+
self.run_optimizer(opt, "Adam(0.95,0.95)", tune=True, max_dim=None)
149+
150+
opt = lambda p, lr: torch.optim.Adagrad(p, lr)
151+
self.run_optimizer(opt, "Adagrad", tune=True, max_dim=None)
152+
153+
opt = lambda p, lr: torch.optim.RMSprop(p, lr)
154+
self.run_optimizer(opt, "RMSprop", tune=True, max_dim=None)
155+
156+
opt = lambda p, lr: tz.Optimizer(p, tz.m.SOAP(), tz.m.LR(lr))
157+
self.run_optimizer(opt, "SOAP", tune=True, max_dim=None)
158+
159+
137160
def run(self, stochastic=True, non_stochastic=True, vr=True, qn=True, newton=True, zo=True, noop=True):
138161
if noop: self.run_noop()
139162
if stochastic: self.run_stochastic()

visualbench/runs/mlbench.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ def run_mls(self):
7979
# ------------------------ Online Logistic regression ------------------------ #
8080
# ndim = 385
8181
# 5s. ~ 1m. 40s.
82-
bench = tasks.datasets.Covertype(models.MLP([54, 7]), batch_size=1).to(CUDA_IF_AVAILABLE)
83-
bench_name = 'MLS - Covertype BS-1 - Online Logistic Regression'
82+
bench = tasks.Collinear(models.MLP([32, 10]), batch_size=1).to(CUDA_IF_AVAILABLE)
83+
bench_name = 'MLS - Ill-conditioned logistic regression BS-1'
8484
self.run_bench(bench, bench_name, passes=10_000, sec=600, test_every=50, metrics='test loss', vid_scale=None)
8585

8686
# --------------------------- Matrix factorization --------------------------- #
@@ -94,6 +94,12 @@ def run_mls(self):
9494
bench_name = 'MLS - MovieLens BS-32 - Matrix Factorization'
9595
self.run_bench(bench, bench_name, passes=10_000, sec=600, test_every=50, metrics='test loss', vid_scale=None)
9696

97+
# ------------------------------ MLP (Colinear) ------------------------------ #
98+
model = models.MLP([32, 64, 96, 128, 256, 10])
99+
bench = tasks.Collinear(model, batch_size=64, test_batch_size=4096).cuda()
100+
bench_name = 'MLS - Colinear BS-64 - MLP(32-64-96-128-256-10)'
101+
self.run_bench(bench, bench_name, passes=10_000, sec=600, test_every=100, metrics='test loss', vid_scale=None)
102+
97103
# ------------------------------- RNN (MNIST-1D) ------------------------------ #
98104
# ndim = 20,410
99105
# 11s. ~ 3m. 30s.

visualbench/runs/optimizer_benchmark.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ def run_mls(self):
251251
# ndim = 56,874
252252
# 9.4s ~ 2m. 28s.
253253
bench = tasks.datasets.Mnist1d(
254-
models.MLP([40, 64,96,128,256, 10], act_cls=nn.ELU),
254+
models.MLP([40, 64, 96, 128, 256, 10], act_cls=nn.ELU),
255255
batch_size=64
256256
).to(CUDA_IF_AVAILABLE)
257257
bench_name = "MLS - MNIST-1D BS-64 - MLP(40-64-96-128-256-10)"

visualbench/tasks/datasets/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from importlib.util import find_spec
22
from typing import TYPE_CHECKING
33

4+
from .dataset import DatasetBenchmark
5+
from .ill import Collinear
46
from .mnist1d import Mnist1d, Mnist1dAutoencoding
57
from .other import WDBC
68
from .seg1d import SynthSeg1d

visualbench/tasks/datasets/dataset.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from torch import nn
66

77
from ...benchmark import Benchmark
8-
from ...utils import CUDA_IF_AVAILABLE, totensor
8+
from ...utils import CUDA_IF_AVAILABLE, torch_tools, totensor
99
from ...utils import normalize as _normalize
1010
from ...utils.light_dataloader import TensorDataLoader
1111

@@ -191,6 +191,10 @@ def _norm(x: torch.Tensor, normalize):
191191
self.resolution = resolution
192192
self.boundary_act = boundary_act
193193

194+
def set_model(self, model: torch.nn.Module):
195+
self.model = model.to(self.device)
196+
self._initial_state_dict = None #torch_tools.copy_state_dict(self.state_dict(), device='cpu')
197+
194198
def reset(self):
195199
super().reset()
196200
for module in self.modules():

visualbench/tasks/datasets/ill.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
2+
import numpy as np
3+
import torch
4+
from torch.nn import functional as F
5+
from .dataset import DatasetBenchmark
6+
def generate_correlated_logistic_data(
7+
n_samples=100_000,
8+
n_features=32,
9+
n_classes=10,
10+
n_correlated=768,
11+
correlation=0.99,
12+
seed=0
13+
):
14+
assert n_classes >= 2
15+
generator = np.random.default_rng(seed)
16+
17+
X = generator.standard_normal(size=(n_samples, n_features))
18+
weights = generator.uniform(-2, 2, size=(n_features, n_classes))
19+
20+
used_pairs = set()
21+
n_correlated = min(n_correlated, n_features * (n_features - 1) // 2)
22+
23+
for _ in range(n_correlated):
24+
idxs = None
25+
while idxs is None or idxs in used_pairs:
26+
pair = generator.choice(n_features, size=2, replace=False)
27+
pair.sort()
28+
idxs = tuple(pair)
29+
30+
used_pairs.add(idxs)
31+
idx1, idx2 = idxs
32+
33+
noise = generator.standard_normal(n_samples) * np.sqrt(1 - correlation**2)
34+
X[:, idx2] = correlation * X[:, idx1] + noise
35+
36+
w = generator.integers(1, 51)
37+
cls = generator.integers(0, n_classes)
38+
weights[idx1, cls] = w
39+
weights[idx2, cls] = -w
40+
41+
logits = X @ weights
42+
43+
logits -= logits.max(axis=1, keepdims=True)
44+
exp_logits = np.exp(logits)
45+
probabilities = exp_logits / exp_logits.sum(axis=1, keepdims=True)
46+
47+
y_one_hot = generator.multinomial(1, pvals=probabilities)
48+
y = np.argmax(y_one_hot, axis=1)
49+
50+
X -= X.mean(0, keepdims=True)
51+
X /= X.std(0, keepdims=True)
52+
53+
return X, y.astype(np.int64)
54+
55+
56+
class Collinear(DatasetBenchmark):
57+
"""Synthetic dataset with a lot of multicollinearity"""
58+
def __init__(
59+
self,
60+
model,
61+
batch_size=None,
62+
test_batch_size=None,
63+
n_samples=100_000,
64+
n_features=32,
65+
n_classes=10,
66+
n_correlated=768,
67+
correlation=0.99,
68+
criterion = F.cross_entropy,
69+
train_split = 0.8,
70+
seed=0,
71+
):
72+
X, y = generate_correlated_logistic_data(n_samples=n_samples, n_features=n_features, n_classes=n_classes, n_correlated=n_correlated, correlation=correlation, seed=seed)
73+
super().__init__(
74+
(X, y),
75+
model=model,
76+
criterion=criterion,
77+
batch_size=batch_size,
78+
test_batch_size=test_batch_size,
79+
train_split=train_split,
80+
dtypes=(torch.float32, torch.long),
81+
)

visualbench/tasks/datasets/sklearn.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ class CaliforniaHousing(DatasetBenchmark):
1818
"""
1919
def __init__(
2020
self,
21-
model,
22-
criterion=F.mse_loss,
21+
model: torch.nn.Module,
22+
criterion: Callable = F.mse_loss,
2323
batch_size: int | None = None,
2424
test_batch_size: int | None = None,
2525
train_split=0.8,

visualbench/utils/pl_tools.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from typing import Any
2+
from collections.abc import Sequence, Callable
3+
import polars as pl
4+
from sklearn.preprocessing import StandardScaler, LabelEncoder
5+
import numpy as np
6+
7+
def load_classification_csv(file, target_cols: str | Sequence[str], one_hot_cols: str | Sequence[str] | None = None, scaler: Any = StandardScaler()):
8+
df = pl.read_csv(file)
9+
10+
if one_hot_cols is not None:
11+
df = df.with_columns(df.to_dummies(one_hot_cols)).drop(one_hot_cols)
12+
13+
X = df.select(pl.exclude(target_cols)).to_numpy()
14+
y = df.select(target_cols).to_numpy()
15+
16+
y = np.stack([LabelEncoder().fit_transform(t) for t in y.T], -1)
17+
18+
X = scaler.fit_transform(np.asarray(X))
19+
return X, y
20+
21+
22+
def plot_corr(X):
23+
import seaborn as sns
24+
sns.heatmap(pl.DataFrame(X).corr(), cmap='coolwarm')

0 commit comments

Comments
 (0)