l0-python 0.3.0__py3-none-any.whl → 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
l0/calibration.py CHANGED
@@ -30,10 +30,21 @@ class SparseCalibrationWeights(nn.Module):
30
30
  Lower bound of stretched concrete distribution
31
31
  zeta : float
32
32
  Upper bound of stretched concrete distribution
33
- init_keep_prob : float
34
- Initial probability of keeping each weight active
35
- init_weight_scale : float
36
- Initial scale for log weights (controls initial weight magnitude)
33
+ init_keep_prob : float or array-like
34
+ Initial probability of keeping each weight active.
35
+ If float, all weights use the same probability.
36
+ If array, must have shape (n_features,)
37
+ init_weights : float, array-like, or None
38
+ Initial weight values (on original scale, not log).
39
+ If float, all weights initialized to this value.
40
+ If array, must have shape (n_features,).
41
+ If None, defaults to 1.0 for all weights.
42
+ log_weight_jitter_sd : float
43
+ Standard deviation of noise added to log weights at start of fit() to break symmetry.
44
+ Set to 0 to disable jitter. Default is 0.0 (no jitter).
45
+ log_alpha_jitter_sd : float
46
+ Standard deviation of noise added to log_alpha at initialization to break symmetry.
47
+ Set to 0 to disable jitter. Default is 0.01 (following Louizos et al.).
37
48
  device : str or torch.device
38
49
  Device to run computations on ('cpu' or 'cuda')
39
50
  """
@@ -44,8 +55,10 @@ class SparseCalibrationWeights(nn.Module):
44
55
  beta: float = 2 / 3,
45
56
  gamma: float = -0.1,
46
57
  zeta: float = 1.1,
47
- init_keep_prob: float = 0.5,
48
- init_weight_scale: float = 1.0,
58
+ init_keep_prob: float | np.ndarray = 0.5,
59
+ init_weights: float | np.ndarray | None = None,
60
+ log_weight_jitter_sd: float = 0.0,
61
+ log_alpha_jitter_sd: float = 0.01,
49
62
  device: str | torch.device = "cpu",
50
63
  ):
51
64
  super().__init__()
@@ -53,25 +66,65 @@ class SparseCalibrationWeights(nn.Module):
53
66
  self.beta = beta
54
67
  self.gamma = gamma
55
68
  self.zeta = zeta
69
+ self.log_weight_jitter_sd = log_weight_jitter_sd
70
+ self.log_alpha_jitter_sd = log_alpha_jitter_sd
56
71
  self.device = torch.device(device)
57
72
 
58
- # Log weights to ensure positivity via exp transformation
59
- self.log_weight = nn.Parameter(
60
- torch.normal(
61
- mean=0.0,
62
- std=init_weight_scale,
63
- size=(n_features,),
64
- device=self.device,
73
+ # Initialize weights (on original scale)
74
+ if init_weights is None:
75
+ # Default: all weights start at 1.0
76
+ weight_values = torch.ones(n_features, device=self.device)
77
+ elif isinstance(init_weights, (int, float)):
78
+ # Scalar: all weights start at this value
79
+ weight_values = torch.full(
80
+ (n_features,), float(init_weights), device=self.device
65
81
  )
66
- )
82
+ else:
83
+ # Array: use provided values
84
+ weight_values = torch.tensor(
85
+ init_weights, dtype=torch.float32, device=self.device
86
+ )
87
+ if weight_values.shape != (n_features,):
88
+ raise ValueError(
89
+ f"init_weights array must have shape ({n_features},), "
90
+ f"got {weight_values.shape}"
91
+ )
92
+
93
+ # Convert to log space to ensure positivity via exp transformation
94
+ # Add small epsilon to avoid log(0)
95
+ self.log_weight = nn.Parameter(torch.log(weight_values + 1e-8))
67
96
 
68
97
  # L0 gate parameters
69
- mu = torch.log(torch.tensor(init_keep_prob / (1 - init_keep_prob)))
70
- self.log_alpha = nn.Parameter(
71
- torch.normal(
72
- mu.item(), 0.01, size=(n_features,), device=self.device
98
+ # Handle init_keep_prob as scalar or array
99
+ if isinstance(init_keep_prob, (int, float)):
100
+ # Scalar: broadcast to all features
101
+ keep_prob_values = torch.full(
102
+ (n_features,), float(init_keep_prob), device=self.device
73
103
  )
74
- )
104
+ else:
105
+ # Array: use provided values
106
+ keep_prob_values = torch.tensor(
107
+ init_keep_prob, dtype=torch.float32, device=self.device
108
+ )
109
+ if keep_prob_values.shape != (n_features,):
110
+ raise ValueError(
111
+ f"init_keep_prob array must have shape ({n_features},), "
112
+ f"got {keep_prob_values.shape}"
113
+ )
114
+ # Clip to valid probability range to avoid log(0) or log(inf)
115
+ keep_prob_values = keep_prob_values.clamp(1e-6, 1 - 1e-6)
116
+
117
+ # Convert probabilities to log_alpha
118
+ mu = torch.log(keep_prob_values / (1 - keep_prob_values))
119
+ # Add jitter to break symmetry (if specified)
120
+ if self.log_alpha_jitter_sd > 0:
121
+ jitter = (
122
+ torch.randn(n_features, device=self.device)
123
+ * self.log_alpha_jitter_sd
124
+ )
125
+ self.log_alpha = nn.Parameter(mu + jitter)
126
+ else:
127
+ self.log_alpha = nn.Parameter(mu)
75
128
 
76
129
  # Cache for sparse tensor conversion
77
130
  self._cached_M_torch: torch.sparse.Tensor | None = None
@@ -312,8 +365,14 @@ class SparseCalibrationWeights(nn.Module):
312
365
  # No grouping - all targets weighted equally
313
366
  group_weights = torch.ones_like(y)
314
367
 
315
- # Initialize weights
316
- nn.init.normal_(self.log_weight, 0, 0.5)
368
+ # Add jitter to weights to break symmetry (if jitter_sd > 0)
369
+ if self.log_weight_jitter_sd > 0:
370
+ with torch.no_grad():
371
+ jitter = (
372
+ torch.randn_like(self.log_weight)
373
+ * self.log_weight_jitter_sd
374
+ )
375
+ self.log_weight.data += jitter
317
376
 
318
377
  # Setup optimizer
319
378
  optimizer = torch.optim.Adam([self.log_weight, self.log_alpha], lr=lr)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: l0-python
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: L0 regularization for sparse neural networks and intelligent sampling
5
5
  Author-email: PolicyEngine <hello@policyengine.org>
6
6
  License: MIT License
@@ -36,7 +36,7 @@ Classifier: License :: OSI Approved :: MIT License
36
36
  Classifier: Programming Language :: Python :: 3
37
37
  Classifier: Programming Language :: Python :: 3.13
38
38
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
39
- Requires-Python: >=3.13
39
+ Requires-Python: >=3.11
40
40
  Description-Content-Type: text/markdown
41
41
  License-File: LICENSE
42
42
  Requires-Dist: torch>=2.0.0
@@ -1,12 +1,12 @@
1
1
  l0/__init__.py,sha256=ezGVLGbdU_jAElAJSdLKjSWCnZRILAZi8qMBPjcHwQc,1192
2
- l0/calibration.py,sha256=H-5bo0SOCQXNBFuAQCzfBxw9-j4Ta0DcmJ66Hv-3Qtc,14860
2
+ l0/calibration.py,sha256=EnIhQPkC6yvRp0-o1qG3SCVUp6pIqwkqkx3DX3uWdG0,17838
3
3
  l0/distributions.py,sha256=GvVoDasdxoDlPPdzt33lFzkuJgQsfkEn9e-abxru3nM,6325
4
4
  l0/gates.py,sha256=54OORMg9qSsdbIyzYIL9t9CCHWsKbumSKl_d4-YFuNU,11443
5
5
  l0/layers.py,sha256=j_DPEFdfUc37ojtoK2ufGzVYzZ_DwusqTcJXCVxZS68,13785
6
6
  l0/penalties.py,sha256=okqXgX2eGUmg5prcDclicTQALPPPF9FA5eQP09V4GTE,9569
7
7
  l0/sparse.py,sha256=UVpkrrJBnQ9iSNDoWK5F_sr43tQareWXzYHyMq_cKss,9726
8
- l0_python-0.3.0.dist-info/licenses/LICENSE,sha256=LoyFJ8qmpwWPp5RmxFGHhdBxsemvp_BqGyiCgNAZHlk,1069
9
- l0_python-0.3.0.dist-info/METADATA,sha256=sBDt2EahzEyNWSBsufl0geYWY5mwq9JMkSTVGppjPkg,6833
10
- l0_python-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- l0_python-0.3.0.dist-info/top_level.txt,sha256=brP2SoElgmGhNJk-qRFEQZ5UE-d1e87Tr60hSSh8n34,3
12
- l0_python-0.3.0.dist-info/RECORD,,
8
+ l0_python-0.3.1.dist-info/licenses/LICENSE,sha256=LoyFJ8qmpwWPp5RmxFGHhdBxsemvp_BqGyiCgNAZHlk,1069
9
+ l0_python-0.3.1.dist-info/METADATA,sha256=nsOQQL-M3CKPcWkU_cEtzVNf3T1Rku1it7sm2rh2xoI,6833
10
+ l0_python-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ l0_python-0.3.1.dist-info/top_level.txt,sha256=brP2SoElgmGhNJk-qRFEQZ5UE-d1e87Tr60hSSh8n34,3
12
+ l0_python-0.3.1.dist-info/RECORD,,