umeleti commited on
Commit
1d27642
·
verified ·
1 Parent(s): c037bd4

Upload folder using huggingface_hub

Browse files
wong_baseline_resnet18/config.json CHANGED
@@ -1,14 +1,21 @@
1
  {
 
2
  "architectures": [
3
- "BaselineClassifierForImageClassification"
4
  ],
5
- "model_type": "baseline_classifier",
 
 
 
 
 
 
 
 
 
 
6
  "auto_map": {
7
- "AutoConfig": "hf_model.BaselineClassifierConfig",
8
- "AutoModel": "hf_model.BaselineClassifierForImageClassification"
9
- },
10
- "arch": "resnet18",
11
- "num_classes": 4,
12
- "dropout_p": 0.2,
13
- "pretrained": false
14
  }
 
1
  {
2
+ "arch": "resnet18",
3
  "architectures": [
4
+ "SNGPForImageClassification"
5
  ],
6
+ "cov_momentum": 0.999,
7
+ "dtype": "float32",
8
+ "length_scale": 1.0,
9
+ "mean_field": true,
10
+ "model_type": "sngp_classifier",
11
+ "n_power_iterations_sn": 1,
12
+ "num_classes": 8,
13
+ "pretrained": false,
14
+ "rff_dim": 1024,
15
+ "ridge_penalty": 0.001,
16
+ "transformers_version": "4.57.3",
17
  "auto_map": {
18
+ "AutoConfig": "hf_sngp_model.SNGPConfig",
19
+ "AutoModel": "hf_sngp_model.SNGPForImageClassification"
20
+ }
 
 
 
 
21
  }
wong_baseline_resnet18/hf_sngp_model.py ADDED
@@ -0,0 +1,394 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HuggingFace-compatible SNGP (Spectral-normalized Neural Gaussian Process) model wrapper.
3
+ Enables model loading without dependency on source code.
4
+ """
5
+
6
+ import math
7
+ from typing import Optional, Tuple
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from torch.nn.utils import spectral_norm
13
+ from transformers import PreTrainedModel, PretrainedConfig
14
+ from transformers.utils import logging
15
+ from torchvision.models import (
16
+ resnet18, resnet34, resnet50,
17
+ ResNet18_Weights, ResNet34_Weights, ResNet50_Weights,
18
+ vit_b_16, vit_b_32, vit_l_16, vit_l_32, vit_h_14,
19
+ ViT_B_16_Weights, ViT_B_32_Weights, ViT_L_16_Weights,
20
+ ViT_L_32_Weights, ViT_H_14_Weights,
21
+ )
22
+
23
+ logger = logging.get_logger(__name__)
24
+
25
+
26
+ class SNGPConfig(PretrainedConfig):
27
+ """Configuration class for SNGP model."""
28
+ model_type = "sngp_classifier"
29
+
30
+ def __init__(
31
+ self,
32
+ arch: str = "resnet18",
33
+ num_classes: int = 2,
34
+ rff_dim: int = 1024,
35
+ length_scale: float = 1.0,
36
+ ridge_penalty: float = 1e-3,
37
+ cov_momentum: float = 0.999,
38
+ mean_field: bool = True,
39
+ n_power_iterations_sn: int = 1,
40
+ pretrained: bool = False,
41
+ **kwargs
42
+ ):
43
+ super().__init__(**kwargs)
44
+ self.arch = arch
45
+ self.num_classes = num_classes
46
+ self.rff_dim = rff_dim
47
+ self.length_scale = length_scale
48
+ self.ridge_penalty = ridge_penalty
49
+ self.cov_momentum = cov_momentum
50
+ self.mean_field = mean_field
51
+ self.n_power_iterations_sn = n_power_iterations_sn
52
+ self.pretrained = pretrained
53
+
54
+
55
+ def apply_spectral_norm_to_convs(
56
+ module: nn.Module,
57
+ n_power_iterations: int = 1,
58
+ skip_if_has_weight_orig: bool = False
59
+ ) -> None:
60
+ """
61
+ Recursively apply spectral normalization to Conv/Linear layers.
62
+
63
+ Args:
64
+ module: Module to apply spectral norm to
65
+ n_power_iterations: Power iterations for spectral norm
66
+ skip_if_has_weight_orig: If True, skip applying SN to layers that already have weight_orig
67
+ (useful when loading checkpoints with pre-existing SN)
68
+ """
69
+ for name, child in module.named_children():
70
+ if isinstance(child, (nn.Conv2d, nn.Linear)):
71
+ # Skip if already has spectral norm applied
72
+ if hasattr(child, 'weight_u'):
73
+ continue
74
+ # Skip if weight_orig exists (pre-existing spectral norm from checkpoint)
75
+ if skip_if_has_weight_orig and hasattr(child, 'weight_orig'):
76
+ continue
77
+
78
+ sn = spectral_norm(child, n_power_iterations=n_power_iterations)
79
+ setattr(module, name, sn)
80
+ else:
81
+ apply_spectral_norm_to_convs(child, n_power_iterations=n_power_iterations, skip_if_has_weight_orig=skip_if_has_weight_orig)
82
+
83
+
84
+ class RandomFeatureGaussianProcess(nn.Module):
85
+ """
86
+ RFF-GP output layer for uncertainty quantification.
87
+
88
+ Uses Random Fourier Features with Gaussian Process posterior to provide:
89
+ - Mean-field logits (calibrated predictions)
90
+ - Raw logits (unscaled)
91
+ - Predictive variance (uncertainty estimates)
92
+ """
93
+
94
+ def __init__(
95
+ self,
96
+ in_dim: int,
97
+ num_classes: int,
98
+ rff_dim: int = 1024,
99
+ length_scale: float = 1.0,
100
+ ridge_penalty: float = 1e-3,
101
+ cov_momentum: float = 0.999,
102
+ mean_field: bool = True,
103
+ dtype: torch.dtype = torch.float32,
104
+ device: Optional[torch.device] = None,
105
+ ):
106
+ super().__init__()
107
+ self.in_dim = in_dim
108
+ self.num_classes = num_classes
109
+ self.rff_dim = rff_dim
110
+ self.length_scale = length_scale
111
+ self.ridge = ridge_penalty
112
+ self.cov_momentum = cov_momentum
113
+ self.mean_field = mean_field
114
+
115
+ # Random Fourier feature parameters (fixed)
116
+ W = torch.randn(in_dim, rff_dim, dtype=dtype) / length_scale
117
+ b = 2 * math.pi * torch.rand(rff_dim, dtype=dtype)
118
+ self.register_buffer("W", W)
119
+ self.register_buffer("b", b)
120
+
121
+ # Linear classifier over RFFs (learned)
122
+ self.classifier = nn.Linear(rff_dim, num_classes, bias=True)
123
+
124
+ # EMA covariance of features
125
+ C = torch.zeros(rff_dim, rff_dim, dtype=dtype)
126
+ self.register_buffer("cov_ema", C)
127
+ self.register_buffer("num_updates", torch.tensor(0, dtype=torch.long))
128
+
129
+ # Identity for covariance computation
130
+ eye = torch.eye(rff_dim, dtype=dtype)
131
+ self.register_buffer("I", eye)
132
+
133
+ self.rff_scale = math.sqrt(2.0 / rff_dim)
134
+
135
+ @torch.no_grad()
136
+ def _update_cov(self, phi: torch.Tensor) -> None:
137
+ """Update exponential moving average of feature covariance."""
138
+ B = phi.shape[0]
139
+ batch_cov = (phi.T @ phi) / max(1, B)
140
+ if self.num_updates == 0:
141
+ self.cov_ema.copy_(batch_cov)
142
+ else:
143
+ self.cov_ema.mul_(self.cov_momentum).add_(
144
+ (1.0 - self.cov_momentum) * batch_cov
145
+ )
146
+ self.num_updates += 1
147
+
148
+ def _features(self, x: torch.Tensor) -> torch.Tensor:
149
+ """Compute Random Fourier Features: phi(x) = sqrt(2/m) * cos(x W + b)."""
150
+ proj = x @ self.W + self.b
151
+ phi = torch.cos(proj) * self.rff_scale
152
+ return phi
153
+
154
+ def forward(
155
+ self,
156
+ x: torch.Tensor,
157
+ update_cov: bool = True
158
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
159
+ """
160
+ Args:
161
+ x: Input features [B, in_dim]
162
+ update_cov: Update covariance during training
163
+
164
+ Returns:
165
+ mean_field_logits: Calibrated logits
166
+ raw_logits: Uncalibrated logits
167
+ pred_var: Predictive variance [B, 1]
168
+ """
169
+ phi = self._features(x)
170
+
171
+ # Update covariance during training
172
+ if self.training and update_cov:
173
+ with torch.no_grad():
174
+ self._update_cov(phi)
175
+
176
+ raw_logits = self.classifier(phi)
177
+
178
+ # Compute predictive variance via GP approximation
179
+ with torch.no_grad():
180
+ A = (self.cov_ema + self.ridge * self.I).to(phi.dtype).to(phi.device)
181
+ L = torch.linalg.cholesky(A)
182
+ phi_T = phi.T
183
+ y = torch.linalg.solve_triangular(L, phi_T, upper=False)
184
+ z = torch.linalg.solve_triangular(L.T, y, upper=True)
185
+ solved = z.T
186
+ pred_var = (phi * solved).sum(dim=1, keepdim=True)
187
+ pred_var = torch.clamp(pred_var, min=0.0)
188
+
189
+ # Apply mean-field logit correction
190
+ if self.mean_field:
191
+ denom = torch.sqrt(1.0 + pred_var)
192
+ mean_field_logits = raw_logits / denom
193
+ else:
194
+ mean_field_logits = raw_logits
195
+
196
+ return mean_field_logits, raw_logits, pred_var
197
+
198
+
199
+ class SNGPClassifier(nn.Module):
200
+ """
201
+ ResNet backbone (torchvision) with spectral normalization + RFF-GP head.
202
+ """
203
+
204
+ def __init__(
205
+ self,
206
+ num_classes: int,
207
+ arch: str = "resnet18",
208
+ pretrained: bool = False,
209
+ rff_dim: int = 1024,
210
+ length_scale: float = 1.0,
211
+ ridge_penalty: float = 1e-3,
212
+ cov_momentum: float = 0.999,
213
+ mean_field: bool = True,
214
+ n_power_iterations_sn: int = 1,
215
+ apply_spectral_norm: bool = True,
216
+ ):
217
+ super().__init__()
218
+ self.num_classes = num_classes
219
+
220
+ # --- Backbone ---
221
+ if arch in {"resnet18", "resnet34", "resnet50"}:
222
+ if arch == "resnet18":
223
+ weights = ResNet18_Weights.IMAGENET1K_V1 if pretrained else None
224
+ base = resnet18(weights=weights)
225
+ feat_dim = base.fc.in_features
226
+ elif arch == "resnet34":
227
+ weights = ResNet34_Weights.IMAGENET1K_V1 if pretrained else None
228
+ base = resnet34(weights=weights)
229
+ feat_dim = base.fc.in_features
230
+ elif arch == "resnet50":
231
+ weights = ResNet50_Weights.IMAGENET1K_V1 if pretrained else None
232
+ base = resnet50(weights=weights)
233
+ feat_dim = base.fc.in_features
234
+
235
+ # Remove original classifier
236
+ modules = list(base.children())[:-1] # keep up to global avgpool
237
+ self.backbone = nn.Sequential(*modules) # outputs [B, feat_dim, 1, 1]
238
+
239
+ # Pool + flatten
240
+ self.pool = nn.Identity() # resnet already has avgpool at [-2]
241
+ self.flatten = nn.Flatten()
242
+
243
+ elif arch in {"vit_b_16", "vit_b_32", "vit_l_16", "vit_l_32", "vit_h_14"}:
244
+ # Construct ViT with optional ImageNet weights
245
+ if arch == "vit_b_16":
246
+ weights = ViT_B_16_Weights.IMAGENET1K_V1 if pretrained else None
247
+ base = vit_b_16(weights=weights)
248
+ elif arch == "vit_b_32":
249
+ weights = ViT_B_32_Weights.IMAGENET1K_V1 if pretrained else None
250
+ base = vit_b_32(weights=weights)
251
+ elif arch == "vit_l_16":
252
+ weights = ViT_L_16_Weights.IMAGENET1K_V1 if pretrained else None
253
+ base = vit_l_16(weights=weights)
254
+ elif arch == "vit_l_32":
255
+ weights = ViT_L_32_Weights.IMAGENET1K_V1 if pretrained else None
256
+ base = vit_l_32(weights=weights)
257
+ elif arch == "vit_h_14":
258
+ weights = ViT_H_14_Weights.IMAGENET1K_V1 if pretrained else None
259
+ base = vit_h_14(weights=weights)
260
+
261
+ # Grab the incoming feature size from the existing head, then strip it
262
+ # torchvision ViT uses a Heads block -> final Linear; we read its in_features
263
+ feat_dim = None
264
+ for m in base.heads.modules():
265
+ if isinstance(m, nn.Linear):
266
+ feat_dim = m.in_features
267
+ break
268
+ if feat_dim is None:
269
+ # Fallback to hidden_dim if present
270
+ feat_dim = getattr(base, "hidden_dim", 768)
271
+
272
+ base.heads = nn.Identity() # expose class-token representation [B, feat_dim]
273
+
274
+ self.backbone = base # forward now returns [B, feat_dim]
275
+ self.pool = nn.Identity() # no pooling for ViT
276
+ self.flatten = nn.Identity()
277
+
278
+ else:
279
+ raise ValueError(f"Unsupported arch: {arch}")
280
+
281
+ # Apply spectral norm to all convs/linears in the backbone
282
+ # Skip this if loading from checkpoint where weights are already in spectral norm form
283
+ if apply_spectral_norm:
284
+ apply_spectral_norm_to_convs(self.backbone, n_power_iterations=n_power_iterations_sn)
285
+
286
+
287
+ # --- RFF-GP head ---
288
+ self.gp_head = RandomFeatureGaussianProcess(
289
+ in_dim=feat_dim,
290
+ num_classes=num_classes,
291
+ rff_dim=rff_dim,
292
+ length_scale=length_scale,
293
+ ridge_penalty=ridge_penalty,
294
+ cov_momentum=cov_momentum,
295
+ mean_field=mean_field,
296
+ )
297
+
298
+ def forward(self, x: torch.Tensor, update_cov: bool = True):
299
+ """
300
+ Returns:
301
+ mean_field_logits, raw_logits, pred_var
302
+ """
303
+ feats = self.backbone(x)
304
+
305
+ # Some backbones may return tuples (e.g., aux outputs). Keep the main tensor.
306
+ if isinstance(feats, (tuple, list)):
307
+ feats = feats[0]
308
+
309
+ # ResNet: [B, C, 1, 1] -> flatten to [B, C]
310
+ if feats.dim() == 4:
311
+ feats = self.pool(feats) # no-op for your ResNet setup, keeps [B, C, 1, 1]
312
+ feats = self.flatten(feats) # -> [B, C]
313
+
314
+ # Transformer variants that might return sequences: [B, N, D]
315
+ elif feats.dim() == 3:
316
+ # Prefer class token if present; otherwise fallback to mean-pool the sequence
317
+ feats = feats[:, 0] if getattr(self, "use_cls_token", True) else feats.mean(dim=1)
318
+
319
+ # ViT (torchvision with heads=Identity) already returns [B, D]; nothing to do for dim()==2
320
+
321
+ return self.gp_head(feats, update_cov=update_cov)
322
+
323
+
324
+ class SNGPForImageClassification(PreTrainedModel):
325
+ """
326
+ HuggingFace-compatible wrapper for SNGP.
327
+
328
+ Load with:
329
+ from transformers import AutoModel
330
+ model = AutoModel.from_pretrained("org/my-model", trust_remote_code=True)
331
+ """
332
+ config_class = SNGPConfig
333
+ base_model_prefix = "model"
334
+
335
+ def __init__(self, config: SNGPConfig):
336
+ super().__init__(config)
337
+ self.model = SNGPClassifier(
338
+ num_classes=config.num_classes,
339
+ arch=config.arch,
340
+ pretrained=config.pretrained,
341
+ rff_dim=config.rff_dim,
342
+ length_scale=config.length_scale,
343
+ ridge_penalty=config.ridge_penalty,
344
+ cov_momentum=config.cov_momentum,
345
+ mean_field=config.mean_field,
346
+ n_power_iterations_sn=config.n_power_iterations_sn,
347
+ apply_spectral_norm=False, # No spectral norm - weights are pre-normalized
348
+ )
349
+
350
+ @classmethod
351
+ def from_pretrained(cls, pretrained_model_name_or_path, *args, **kwargs):
352
+ """Load model from pretrained checkpoint."""
353
+ # Use the standard HuggingFace loading mechanism
354
+ # The checkpoint has weight_orig, weight_u, weight_v which match the spectral_norm structure
355
+ return super().from_pretrained(pretrained_model_name_or_path, *args, **kwargs)
356
+
357
+ def _load_from_state_dict(
358
+ self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs
359
+ ):
360
+ """Standard state dict loading - checkpoint has spectral norm components."""
361
+ # Call parent implementation
362
+ super()._load_from_state_dict(
363
+ state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs
364
+ )
365
+
366
+ def forward(
367
+ self,
368
+ pixel_values: torch.Tensor,
369
+ return_dict: bool = True,
370
+ update_cov: bool = True,
371
+ ):
372
+ """
373
+ Args:
374
+ pixel_values: Input tensor [B, 3, 224, 224]
375
+ return_dict: Whether to return dict
376
+ update_cov: Update GP covariance during training
377
+
378
+ Returns:
379
+ Dict with mean_field_logits, raw_logits, pred_var
380
+ """
381
+ mean_field_logits, raw_logits, pred_var = self.model(
382
+ pixel_values,
383
+ update_cov=update_cov
384
+ )
385
+
386
+ if return_dict:
387
+ return {
388
+ "mean_field_logits": mean_field_logits,
389
+ "logits": mean_field_logits, # For compatibility
390
+ "raw_logits": raw_logits,
391
+ "pred_var": pred_var,
392
+ }
393
+
394
+ return mean_field_logits, raw_logits, pred_var
wong_baseline_resnet18/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d5dfb0db9315f52443525eff435be1abeecbc02176fd22fe3cf3a0940f43617c
3
+ size 55279776
wong_baseline_resnet18/pytorch_model.bin CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:8d264d0db12dc0986effc3ab363039c8ea7606800d670442f1760b22697391dc
3
- size 44793931
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a55edc192f6af09e3f9d6221a6c01c82c3f968b283398b65c23e77684b08ffd6
3
+ size 55465855