sae-lens 6.9.1__py3-none-any.whl → 6.11.0__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.
sae_lens/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # ruff: noqa: E402
2
- __version__ = "6.9.1"
2
+ __version__ = "6.11.0"
3
3
 
4
4
  import logging
5
5
 
sae_lens/config.py CHANGED
@@ -169,8 +169,10 @@ class LanguageModelSAERunnerConfig(Generic[T_TRAINING_SAE_CONFIG]):
169
169
  eval_batch_size_prompts (int, optional): The batch size for evaluation, in prompts. Useful if evals cause OOM.
170
170
  logger (LoggingConfig): Configuration for logging (e.g. W&B).
171
171
  n_checkpoints (int): The number of checkpoints to save during training. 0 means no checkpoints.
172
- checkpoint_path (str): The path to save checkpoints. A unique ID will be appended to this path.
173
- verbose (bool): Whether to print verbose output.
172
+ checkpoint_path (str | None): The path to save checkpoints. A unique ID will be appended to this path. Set to None to disable checkpoint saving. (default is "checkpoints")
173
+ save_final_checkpoint (bool): Whether to include an additional final checkpoint when training is finished. (default is False).
174
+ output_path (str | None): The path to save outputs. Set to None to disable output saving. (default is "output")
175
+ verbose (bool): Whether to print verbose output. (default is True)
174
176
  model_kwargs (dict[str, Any]): Keyword arguments for `model.run_with_cache`
175
177
  model_from_pretrained_kwargs (dict[str, Any], optional): Additional keyword arguments to pass to the model's `from_pretrained` method.
176
178
  sae_lens_version (str): The version of the sae_lens library.
@@ -254,9 +256,13 @@ class LanguageModelSAERunnerConfig(Generic[T_TRAINING_SAE_CONFIG]):
254
256
 
255
257
  logger: LoggingConfig = field(default_factory=LoggingConfig)
256
258
 
257
- # Misc
259
+ # Outputs/Checkpoints
258
260
  n_checkpoints: int = 0
259
- checkpoint_path: str = "checkpoints"
261
+ checkpoint_path: str | None = "checkpoints"
262
+ save_final_checkpoint: bool = False
263
+ output_path: str | None = "output"
264
+
265
+ # Misc
260
266
  verbose: bool = True
261
267
  model_kwargs: dict[str, Any] = dict_field(default={})
262
268
  model_from_pretrained_kwargs: dict[str, Any] | None = dict_field(default=None)
@@ -394,6 +400,7 @@ class LanguageModelSAERunnerConfig(Generic[T_TRAINING_SAE_CONFIG]):
394
400
  return SAETrainerConfig(
395
401
  n_checkpoints=self.n_checkpoints,
396
402
  checkpoint_path=self.checkpoint_path,
403
+ save_final_checkpoint=self.save_final_checkpoint,
397
404
  total_training_samples=self.total_training_tokens,
398
405
  device=self.device,
399
406
  autocast=self.autocast,
@@ -618,7 +625,8 @@ class PretokenizeRunnerConfig:
618
625
  @dataclass
619
626
  class SAETrainerConfig:
620
627
  n_checkpoints: int
621
- checkpoint_path: str
628
+ checkpoint_path: str | None
629
+ save_final_checkpoint: bool
622
630
  total_training_samples: int
623
631
  device: str
624
632
  autocast: bool
@@ -8,13 +8,18 @@ from typing import Any, Generic
8
8
 
9
9
  import torch
10
10
  import wandb
11
+ from safetensors.torch import save_file
11
12
  from simple_parsing import ArgumentParser
12
13
  from transformer_lens.hook_points import HookedRootModule
13
14
  from typing_extensions import deprecated
14
15
 
15
16
  from sae_lens import logger
16
17
  from sae_lens.config import HfDataset, LanguageModelSAERunnerConfig
17
- from sae_lens.constants import ACTIVATIONS_STORE_STATE_FILENAME, RUNNER_CFG_FILENAME
18
+ from sae_lens.constants import (
19
+ ACTIVATIONS_STORE_STATE_FILENAME,
20
+ RUNNER_CFG_FILENAME,
21
+ SPARSITY_FILENAME,
22
+ )
18
23
  from sae_lens.evals import EvalConfig, run_evals
19
24
  from sae_lens.load_model import load_model
20
25
  from sae_lens.saes.batchtopk_sae import BatchTopKTrainingSAEConfig
@@ -185,11 +190,47 @@ class LanguageModelSAETrainingRunner:
185
190
  self._compile_if_needed()
186
191
  sae = self.run_trainer_with_interruption_handling(trainer)
187
192
 
193
+ if self.cfg.output_path is not None:
194
+ self.save_final_sae(
195
+ sae=sae,
196
+ output_path=self.cfg.output_path,
197
+ log_feature_sparsity=trainer.log_feature_sparsity,
198
+ )
199
+
188
200
  if self.cfg.logger.log_to_wandb:
189
201
  wandb.finish()
190
202
 
191
203
  return sae
192
204
 
205
+ def save_final_sae(
206
+ self,
207
+ sae: TrainingSAE[Any],
208
+ output_path: str,
209
+ log_feature_sparsity: torch.Tensor | None = None,
210
+ ):
211
+ base_output_path = Path(output_path)
212
+ base_output_path.mkdir(exist_ok=True, parents=True)
213
+
214
+ weights_path, cfg_path = sae.save_inference_model(str(base_output_path))
215
+
216
+ sparsity_path = None
217
+ if log_feature_sparsity is not None:
218
+ sparsity_path = base_output_path / SPARSITY_FILENAME
219
+ save_file({"sparsity": log_feature_sparsity}, sparsity_path)
220
+
221
+ runner_config = self.cfg.to_dict()
222
+ with open(base_output_path / RUNNER_CFG_FILENAME, "w") as f:
223
+ json.dump(runner_config, f)
224
+
225
+ if self.cfg.logger.log_to_wandb:
226
+ self.cfg.logger.log(
227
+ self,
228
+ weights_path,
229
+ cfg_path,
230
+ sparsity_path=sparsity_path,
231
+ wandb_aliases=["final_model"],
232
+ )
233
+
193
234
  def _set_sae_metadata(self):
194
235
  self.sae.cfg.metadata.dataset_path = self.cfg.dataset_path
195
236
  self.sae.cfg.metadata.hook_name = self.cfg.hook_name
@@ -247,20 +288,24 @@ class LanguageModelSAETrainingRunner:
247
288
  sae = trainer.fit()
248
289
 
249
290
  except (KeyboardInterrupt, InterruptedException):
250
- logger.warning("interrupted, saving progress")
251
- checkpoint_path = Path(self.cfg.checkpoint_path) / str(
252
- trainer.n_training_samples
253
- )
254
- self.save_checkpoint(checkpoint_path)
255
- logger.info("done saving")
291
+ if self.cfg.checkpoint_path is not None:
292
+ logger.warning("interrupted, saving progress")
293
+ checkpoint_path = Path(self.cfg.checkpoint_path) / str(
294
+ trainer.n_training_samples
295
+ )
296
+ self.save_checkpoint(checkpoint_path)
297
+ logger.info("done saving")
256
298
  raise
257
299
 
258
300
  return sae
259
301
 
260
302
  def save_checkpoint(
261
303
  self,
262
- checkpoint_path: Path,
304
+ checkpoint_path: Path | None,
263
305
  ) -> None:
306
+ if checkpoint_path is None:
307
+ return
308
+
264
309
  self.activations_store.save(
265
310
  str(checkpoint_path / ACTIVATIONS_STORE_STATE_FILENAME)
266
311
  )
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from typing import Any
2
+ from typing import Any, Literal
3
3
 
4
4
  import numpy as np
5
5
  import torch
@@ -187,13 +187,29 @@ class JumpReLUSAE(SAE[JumpReLUSAEConfig]):
187
187
  class JumpReLUTrainingSAEConfig(TrainingSAEConfig):
188
188
  """
189
189
  Configuration class for training a JumpReLUTrainingSAE.
190
+
191
+ - jumprelu_init_threshold: initial threshold for the JumpReLU activation
192
+ - jumprelu_bandwidth: bandwidth for the JumpReLU activation
193
+ - jumprelu_sparsity_loss_mode: mode for the sparsity loss, either "step" or "tanh". "step" is Google Deepmind's L0 loss, "tanh" is Anthropic's sparsity loss.
194
+ - l0_coefficient: coefficient for the l0 sparsity loss
195
+ - l0_warm_up_steps: number of warm-up steps for the l0 sparsity loss
196
+ - pre_act_loss_coefficient: coefficient for the pre-activation loss. Set to None to disable. Set to 3e-6 to match Anthropic's setup. Default is None.
197
+ - jumprelu_tanh_scale: scale for the tanh sparsity loss. Only relevant for "tanh" sparsity loss mode. Default is 4.0.
190
198
  """
191
199
 
192
200
  jumprelu_init_threshold: float = 0.01
193
201
  jumprelu_bandwidth: float = 0.05
202
+ # step is Google Deepmind, tanh is Anthropic
203
+ jumprelu_sparsity_loss_mode: Literal["step", "tanh"] = "step"
194
204
  l0_coefficient: float = 1.0
195
205
  l0_warm_up_steps: int = 0
196
206
 
207
+ # anthropic's auxiliary loss to avoid dead features
208
+ pre_act_loss_coefficient: float | None = None
209
+
210
+ # only relevant for tanh sparsity loss mode
211
+ jumprelu_tanh_scale: float = 4.0
212
+
197
213
  @override
198
214
  @classmethod
199
215
  def architecture(cls) -> str:
@@ -267,9 +283,35 @@ class JumpReLUTrainingSAE(TrainingSAE[JumpReLUTrainingSAEConfig]):
267
283
  sae_out: torch.Tensor,
268
284
  ) -> dict[str, torch.Tensor]:
269
285
  """Calculate architecture-specific auxiliary loss terms."""
270
- l0 = torch.sum(Step.apply(hidden_pre, self.threshold, self.bandwidth), dim=-1) # type: ignore
271
- l0_loss = (step_input.coefficients["l0"] * l0).mean()
272
- return {"l0_loss": l0_loss}
286
+
287
+ threshold = self.threshold
288
+ W_dec_norm = self.W_dec.norm(dim=1)
289
+ if self.cfg.jumprelu_sparsity_loss_mode == "step":
290
+ l0 = torch.sum(
291
+ Step.apply(hidden_pre, threshold, self.bandwidth), # type: ignore
292
+ dim=-1,
293
+ )
294
+ l0_loss = (step_input.coefficients["l0"] * l0).mean()
295
+ elif self.cfg.jumprelu_sparsity_loss_mode == "tanh":
296
+ per_item_l0_loss = torch.tanh(
297
+ self.cfg.jumprelu_tanh_scale * feature_acts * W_dec_norm
298
+ ).sum(dim=-1)
299
+ l0_loss = (step_input.coefficients["l0"] * per_item_l0_loss).mean()
300
+ else:
301
+ raise ValueError(
302
+ f"Invalid sparsity loss mode: {self.cfg.jumprelu_sparsity_loss_mode}"
303
+ )
304
+ losses = {"l0_loss": l0_loss}
305
+
306
+ if self.cfg.pre_act_loss_coefficient is not None:
307
+ losses["pre_act_loss"] = calculate_pre_act_loss(
308
+ self.cfg.pre_act_loss_coefficient,
309
+ threshold,
310
+ hidden_pre,
311
+ step_input.dead_neuron_mask,
312
+ W_dec_norm,
313
+ )
314
+ return losses
273
315
 
274
316
  @override
275
317
  def get_coefficients(self) -> dict[str, float | TrainCoefficientConfig]:
@@ -310,3 +352,21 @@ class JumpReLUTrainingSAE(TrainingSAE[JumpReLUTrainingSAEConfig]):
310
352
  threshold = state_dict["threshold"]
311
353
  del state_dict["threshold"]
312
354
  state_dict["log_threshold"] = torch.log(threshold).detach().contiguous()
355
+
356
+
357
+ def calculate_pre_act_loss(
358
+ pre_act_loss_coefficient: float,
359
+ threshold: torch.Tensor,
360
+ hidden_pre: torch.Tensor,
361
+ dead_neuron_mask: torch.Tensor | None,
362
+ W_dec_norm: torch.Tensor,
363
+ ) -> torch.Tensor:
364
+ """
365
+ Calculate Anthropic's pre-activation loss, except we only calculate this for latents that are actually dead.
366
+ """
367
+ if dead_neuron_mask is None or not dead_neuron_mask.any():
368
+ return hidden_pre.new_tensor(0.0)
369
+ per_item_loss = (
370
+ (threshold - hidden_pre).relu() * dead_neuron_mask * W_dec_norm
371
+ ).sum(dim=-1)
372
+ return pre_act_loss_coefficient * per_item_loss.mean()
@@ -22,6 +22,7 @@ from sae_lens.saes.sae import (
22
22
  from sae_lens.training.activation_scaler import ActivationScaler
23
23
  from sae_lens.training.optim import CoefficientScheduler, get_lr_scheduler
24
24
  from sae_lens.training.types import DataProvider
25
+ from sae_lens.util import path_or_tmp_dir
25
26
 
26
27
 
27
28
  def _log_feature_sparsity(
@@ -40,7 +41,7 @@ def _update_sae_lens_training_version(sae: TrainingSAE[Any]) -> None:
40
41
  class SaveCheckpointFn(Protocol):
41
42
  def __call__(
42
43
  self,
43
- checkpoint_path: Path,
44
+ checkpoint_path: Path | None,
44
45
  ) -> None: ...
45
46
 
46
47
 
@@ -187,12 +188,8 @@ class SAETrainer(Generic[T_TRAINING_SAE, T_TRAINING_SAE_CONFIG]):
187
188
  )
188
189
  self.activation_scaler.scaling_factor = None
189
190
 
190
- # save final inference sae group to checkpoints folder
191
- self.save_checkpoint(
192
- checkpoint_name=f"final_{self.n_training_samples}",
193
- wandb_aliases=["final_model"],
194
- save_inference_model=True,
195
- )
191
+ if self.cfg.save_final_checkpoint:
192
+ self.save_checkpoint(checkpoint_name=f"final_{self.n_training_samples}")
196
193
 
197
194
  pbar.close()
198
195
  return self.sae
@@ -201,32 +198,31 @@ class SAETrainer(Generic[T_TRAINING_SAE, T_TRAINING_SAE_CONFIG]):
201
198
  self,
202
199
  checkpoint_name: str,
203
200
  wandb_aliases: list[str] | None = None,
204
- save_inference_model: bool = False,
205
201
  ) -> None:
206
- checkpoint_path = Path(self.cfg.checkpoint_path) / checkpoint_name
207
- checkpoint_path.mkdir(exist_ok=True, parents=True)
208
-
209
- save_fn = (
210
- self.sae.save_inference_model
211
- if save_inference_model
212
- else self.sae.save_model
213
- )
214
- weights_path, cfg_path = save_fn(str(checkpoint_path))
202
+ checkpoint_path = None
203
+ if self.cfg.checkpoint_path is not None or self.cfg.logger.log_to_wandb:
204
+ with path_or_tmp_dir(self.cfg.checkpoint_path) as base_checkpoint_path:
205
+ checkpoint_path = base_checkpoint_path / checkpoint_name
206
+ checkpoint_path.mkdir(exist_ok=True, parents=True)
215
207
 
216
- sparsity_path = checkpoint_path / SPARSITY_FILENAME
217
- save_file({"sparsity": self.log_feature_sparsity}, sparsity_path)
208
+ weights_path, cfg_path = self.sae.save_model(str(checkpoint_path))
218
209
 
219
- activation_scaler_path = checkpoint_path / ACTIVATION_SCALER_CFG_FILENAME
220
- self.activation_scaler.save(str(activation_scaler_path))
210
+ sparsity_path = checkpoint_path / SPARSITY_FILENAME
211
+ save_file({"sparsity": self.log_feature_sparsity}, sparsity_path)
221
212
 
222
- if self.cfg.logger.log_to_wandb:
223
- self.cfg.logger.log(
224
- self,
225
- weights_path,
226
- cfg_path,
227
- sparsity_path=sparsity_path,
228
- wandb_aliases=wandb_aliases,
229
- )
213
+ activation_scaler_path = (
214
+ checkpoint_path / ACTIVATION_SCALER_CFG_FILENAME
215
+ )
216
+ self.activation_scaler.save(str(activation_scaler_path))
217
+
218
+ if self.cfg.logger.log_to_wandb:
219
+ self.cfg.logger.log(
220
+ self,
221
+ weights_path,
222
+ cfg_path,
223
+ sparsity_path=sparsity_path,
224
+ wandb_aliases=wandb_aliases,
225
+ )
230
226
 
231
227
  if self.save_checkpoint_fn is not None:
232
228
  self.save_checkpoint_fn(checkpoint_path=checkpoint_path)
sae_lens/util.py CHANGED
@@ -1,5 +1,8 @@
1
1
  import re
2
+ import tempfile
3
+ from contextlib import contextmanager
2
4
  from dataclasses import asdict, fields, is_dataclass
5
+ from pathlib import Path
3
6
  from typing import Sequence, TypeVar
4
7
 
5
8
  K = TypeVar("K")
@@ -45,3 +48,18 @@ def extract_layer_from_tlens_hook_name(hook_name: str) -> int | None:
45
48
  """
46
49
  hook_match = re.search(r"\.(\d+)\.", hook_name)
47
50
  return None if hook_match is None else int(hook_match.group(1))
51
+
52
+
53
+ @contextmanager
54
+ def path_or_tmp_dir(path: str | Path | None):
55
+ """Context manager that yields a concrete Path for path.
56
+
57
+ - If path is None, creates a TemporaryDirectory and yields its Path.
58
+ The directory is cleaned up on context exit.
59
+ - Otherwise, yields Path(path) without creating or cleaning.
60
+ """
61
+ if path is None:
62
+ with tempfile.TemporaryDirectory() as td:
63
+ yield Path(td)
64
+ else:
65
+ yield Path(path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sae-lens
3
- Version: 6.9.1
3
+ Version: 6.11.0
4
4
  Summary: Training and Analyzing Sparse Autoencoders (SAEs)
5
5
  License: MIT
6
6
  Keywords: deep-learning,sparse-autoencoders,mechanistic-interpretability,PyTorch
@@ -1,12 +1,12 @@
1
- sae_lens/__init__.py,sha256=YTdinwk5L_K9nK5pBJIKiJTT_bLudiY_uvF_osio3gs,3588
1
+ sae_lens/__init__.py,sha256=87qzm9CROR5ywD55DSxpt_ut24G4O8MR2zGYDtHEA0Y,3589
2
2
  sae_lens/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  sae_lens/analysis/hooked_sae_transformer.py,sha256=vRu6JseH1lZaEeILD5bEkQEQ1wYHHDcxD-f2olKmE9Y,14275
4
4
  sae_lens/analysis/neuronpedia_integration.py,sha256=Gx1W7hUBEuMoasNcnOnZ1wmqbXDd1pSZ1nqKEya1HQc,4962
5
5
  sae_lens/cache_activations_runner.py,sha256=cNeAtp2JQ_vKbeddZVM-tcPLYyyfTWL8NDna5KQpkLI,12583
6
- sae_lens/config.py,sha256=IrjbsKBbaZoFXYrsPJ5xBwIqi9uZJIIFXjV_uoErJaE,28176
6
+ sae_lens/config.py,sha256=IdRXSKPfYY3hwUovj-u83eep8z52gkJHII0mY0KseYY,28739
7
7
  sae_lens/constants.py,sha256=CSjmiZ-bhjQeVLyRvWxAjBokCgkfM8mnvd7-vxLIWTY,639
8
8
  sae_lens/evals.py,sha256=4hanbyG8qZLItWqft94F4ZjUoytPVB7fw5s0P4Oi0VE,39504
9
- sae_lens/llm_sae_training_runner.py,sha256=exxNX_OEhdiUrlgmBP9bjX9DOf0HUcNQGO4unKeDjKM,13713
9
+ sae_lens/llm_sae_training_runner.py,sha256=sJTcDX1bUJJ_jZLUT88-8KUYIAPeUGoXktX68PsBqw0,15137
10
10
  sae_lens/load_model.py,sha256=C8AMykctj6H7tz_xRwB06-EXj6TfW64PtSJZR5Jxn1Y,8649
11
11
  sae_lens/loading/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  sae_lens/loading/pretrained_sae_loaders.py,sha256=CVzHntSUKR1X3_gAqn8K_Ajq8D85qBrmrgEgU93IV4A,49609
@@ -17,7 +17,7 @@ sae_lens/registry.py,sha256=nhy7BPSudSATqW4lo9H_k3Na7sfGHmAf9v-3wpnLL_o,1490
17
17
  sae_lens/saes/__init__.py,sha256=jVwazK8Q6dW5J6_zFXPoNAuBvSxgziQ8eMOjGM3t-X8,1475
18
18
  sae_lens/saes/batchtopk_sae.py,sha256=CyaFG2hMyyDaEaXXrAMJC8wQDW1JoddTKF5mvxxBQKY,3395
19
19
  sae_lens/saes/gated_sae.py,sha256=qcmM9JwBA8aZR8z_IRHV1_gQX-q_63tKewWXRnhdXuo,8986
20
- sae_lens/saes/jumprelu_sae.py,sha256=3xkhBcCol2mEpIBLceymCpudocm2ypOjTeTXbpiXoA4,10794
20
+ sae_lens/saes/jumprelu_sae.py,sha256=HHBF1sJ95lZvxwP5vwLSQFKdnJN2KKYK0WAEaLTrta0,13399
21
21
  sae_lens/saes/sae.py,sha256=gdUZuLaOHQrPjbDj-nZI813B6-_mNAnV9i9z4qTnpHk,38255
22
22
  sae_lens/saes/standard_sae.py,sha256=9UqYyYtQuThYxXKNaDjYcyowpOx2-7cShG-TeUP6JCQ,5940
23
23
  sae_lens/saes/topk_sae.py,sha256=CXMBI6CFvI5829bOhoQ350VXR9d8uFHUDlULTIWHXoU,8686
@@ -28,12 +28,12 @@ sae_lens/training/activation_scaler.py,sha256=seEE-2Qd2JMHxqgnsNWPt-DGtYGZxWPnOw
28
28
  sae_lens/training/activations_store.py,sha256=2EUY2abqpT5El3T95sypM_JRDgiKL3VeT73U9SQIFGY,32903
29
29
  sae_lens/training/mixing_buffer.py,sha256=vDpYG5ZE70szDvBsRKcNHEES3h_WTKJ16qDYk5jPOVA,2015
30
30
  sae_lens/training/optim.py,sha256=TiI9nbffzXNsI8WjcIsqa2uheW6suxqL_KDDmWXobWI,5312
31
- sae_lens/training/sae_trainer.py,sha256=6HPf5wtmY1wMUTkLFRg9DujNMMXJkVMPdAhB2svvlkk,15368
31
+ sae_lens/training/sae_trainer.py,sha256=Jh5AyBGtfZjnprv9H3k0p_luWWnM7YFjlmHuO1W_J6U,15465
32
32
  sae_lens/training/types.py,sha256=qSjmGzXf3MLalygG0psnVjmhX_mpLmL47MQtZfe7qxg,81
33
33
  sae_lens/training/upload_saes_to_huggingface.py,sha256=r_WzI1zLtGZ5TzAxuG3xa_8T09j3zXJrWd_vzPsPGkQ,4469
34
34
  sae_lens/tutorial/tsea.py,sha256=fd1am_XXsf2KMbByDapJo-2qlxduKaa62Z2qcQZ3QKU,18145
35
- sae_lens/util.py,sha256=mCwLAilGMVo8Scm7CIsCafU7GsfmBvCcjwmloI4Ly7Y,1718
36
- sae_lens-6.9.1.dist-info/LICENSE,sha256=DW6e-hDosiu4CfW0-imI57sV1I5f9UEslpviNQcOAKs,1069
37
- sae_lens-6.9.1.dist-info/METADATA,sha256=ELrgBpTtDALXmLtQuJSS7-ui_pf-DJbAyL4uUr5N-UU,5244
38
- sae_lens-6.9.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
39
- sae_lens-6.9.1.dist-info/RECORD,,
35
+ sae_lens/util.py,sha256=lW7fBn_b8quvRYlen9PUmB7km60YhKyjmuelB1f6KzQ,2253
36
+ sae_lens-6.11.0.dist-info/LICENSE,sha256=DW6e-hDosiu4CfW0-imI57sV1I5f9UEslpviNQcOAKs,1069
37
+ sae_lens-6.11.0.dist-info/METADATA,sha256=yEZL6kzNBUKjzd0d73Hiup6SpLNtNEGUOgFu0MwgVpo,5245
38
+ sae_lens-6.11.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
39
+ sae_lens-6.11.0.dist-info/RECORD,,