Repository navigation
Conversation
…s.jacobians schema
JacobianLens.load() dropped the top-level `provenance` dict that
Neuronpedia's converted J++ lenses carry, so the released
Qwen3.5-0.8B artifact loaded with empty metadata, and the J++ Lens
package's own `parameters.jacobians` + `config` schema was rejected.
- Keep recorded provenance verbatim under metadata["provenance"] and
normalise model name/revision, method, lrp_mode, target layer,
relative end-transport layer, capture point and stream reduction onto
the metadata keys validate_model() reads. Explicit `metadata` keys
win; no revision is invented when none was recorded; unreloadable
leaves are dropped and named in dropped_fields.
- Accept the J++ package schema (parameters.jacobians, source_layers,
config) and the {"J", "config"} variant; read d_model and
num_prompts_trained_on from the config; mark the lens
converted_from="jpp_lens_artifact". Only lens_type="jacobian" loads.
- validate_model() resolves relative_end_transport_layer against
n_layers and checks recorded capture_point="block_output" and
stream_reduce="none".
- merge() refuses imported J++ maps (method="jpp" or the sentinel):
they are expert-combined, not prompt means.
- Tests with synthetic payloads for both schemas, round trips, merge
refusal and the new validate_model checks; docs section on published
J++ lenses.
Part 1 of TransformerLensOrg#1991.
arrdel
left a comment
There was a problem hiding this comment.
Checked this against safety-research/jpp_lens (workspace_lens/config.py and workspace_lens/lenses/base_lens.py). The schema split (J + provenance, J + config, and parameters.jacobians + config), the merge() guard on non-additive maps, and the relative_end_transport_layer / capture_point / stream_reduce checks in validate_model() all match the upstream package.
Two edge cases against jpp_lens's on-disk format worth tightening:
-
LensConfigdefaultsd_modelto0, notNone. Inworkspace_lens/config.py(LensConfig), the untrained default isd_model: int = 0, soconfig.to_dict()writes"d_model": 0rather than omitting the key. In both_from_artifact_payloadand_from_jpp_payload, the fallback check isif d_model is None: d_model = _matrix_width(path, jacobians). When a file carries"d_model": 0,0 is not Noneskips_matrix_widthand passesd_model=0intocls(...), which then raisesjacobians[0] has shape (d, d), expected (0, 0). Checkingif not d_model:(orif d_model is None or int(d_model) <= 0:) in both loaders handles both an omitted key and the dataclass default0. -
source_layersconsistency check in_from_jpp_payload.BaseLens.save()always writes top-level"source_layers": self.source_layersalongside"parameters", andLensParameters.__post_init__inbase_lens.pyenforcessorted(per_layer) == sorted(source_layers)._from_jpp_payload's docstring notes that the file holds thesource_layerslist, but the loader ignorespayload.get("source_layers"). Checking thatsorted(jacobians)agrees withpayload["source_layers"]when present (and raisingValueErroron mismatch, asBaseLens.loaddoes) prevents silently loading a truncated or mismatched layer dict.
…against the jacobians
|
Thanks, both are right and fixed in 820159d. Both loaders now treat |
|
Closing as superseded by #2014 |
Description
JacobianLens.load()reads the matrices of the published J++ lenses but drops their top-levelprovenancedict, so the released Qwen3.5-0.8B file onneuronpedia/jacobian-lensloads withmetadata == {}. The authors' own file format fromsafety-research/jpp_lens(parameters.jacobians+config) is rejected outright. This is part 1 of #1991 (loader and provenance); the filtered-readout API from part 2 can follow separately.What changes:
load()keeps a recordedprovenancedict verbatim undermetadata["provenance"]and normalises the values it records (model name, revision when present, method, lrp_mode, target layer or relative end-transport layer, capture point, stream reduction) onto the metadata keysvalidate_model()already reads. Explicitmetadatakeys win. Nothing is invented: a file with no recorded revision loads withoutmodel_revision. Leaves thatweights_only=Truecould not reload are dropped and named indropped_fields.load()accepts the J++ Lens package schema (parameters.jacobians,source_layers,config) and its{"J", "config"}variant, readingd_modelandnum_prompts_trained_onfrom the config, keeping the config undermetadata["provenance"]["source_config"], and marking the lensconverted_from: "jpp_lens_artifact". Onlylens_type == "jacobian"files load.validate_model()resolvesrelative_end_transport_layeragainst the model and checks a recordedcapture_point(block_output) andstream_reduce(none) against the single-stream block-output contract.merge()refuses imported J++ maps (method == "jpp"or the sentinel). They combine clustered expert Jacobians with learned weights, so they are not prompt means and carry no additive statistics.Checked against the real pinned artifact (
neuronpedia/jacobian-lens@e166deb5,qwen3.5-0.8b/jpp/.../Qwen3.5-0.8B_jpp_lens.pt, SHA-25639bf7690...): metadata is now populated, save/load round trips exactly, andmerge()refuses it.Fixes part 1 of #1991
Type of change
Checklist: