CAFF / caff /csv.py
MrDhifallah's picture
Upload folder using huggingface_hub
634ebe8 verified
Raw
History Blame Contribute Delete
2.98 kB
"""
caff/csv.py
===========
Contextual Summary Vector (CSV) β€” paper Β§6.2, Equation 14.
The CSV is the parameter-free, permutation-invariant encoding of
the previously retained set S_{β„“-1} that breaks the Context
Blindness Error (Theorem 1).
Equation 14 (verbatim from paper):
z_{β„“-1} = β”Œβ”€ (1/|S_{β„“-1}|) Ξ£_{(h,r,t) ∈ S_{β„“-1}} e_r, if S_{β„“-1} β‰  βˆ…
└─ 0_d otherwise
Properties (paper Β§6.2):
β€’ Parameter-free (no trainable weights)
β€’ Permutation-invariant (mean is symmetric)
β€’ Linear projection of the RRC: z = E^T Β· Z (Property 1)
β€’ Injective on the simplex when rank(E) = |R| (Lemma 2)
β€’ Noise-stable: E[||zΜƒ - z||Β²] = σ²d / |S_{β„“-1}| (Proposition 2)
"""
from __future__ import annotations
import torch
import torch.nn as nn
from .encoders import RelationEmbeddingCache
class CSV(nn.Module):
"""Contextual Summary Vector encoder (Eq. 14).
Stateless. Looks up frozen relation embeddings from the cache
and computes the mean (or max-pool, for ablation Β§10.1) over
the retained set.
Parameters
----------
relation_cache : RelationEmbeddingCache
Pre-encoded frozen relation embeddings, |R| Γ— d.
pool : {"mean", "max"}
Pooling strategy. Paper default is "mean" (Eq. 14).
Ablation Β§10.1 reports max-pool yields -0.5 acc.
"""
def __init__(
self,
relation_cache: RelationEmbeddingCache,
pool: str = "mean",
) -> None:
super().__init__()
assert pool in {"mean", "max"}, f"pool must be mean or max, got {pool}"
self.relation_cache = relation_cache
self.pool = pool
self.d = relation_cache.embeddings.shape[1]
@property
def device(self) -> torch.device:
return self.relation_cache.embeddings.device
def forward(self, retained_relations: list[list[str]]) -> torch.Tensor:
"""Compute CSV for a batch of retained sets.
Parameters
----------
retained_relations : list of list of str
For each item in the batch, the list of relation names
in S_{β„“-1}. Empty inner list = empty retained set β†’
CSV returns the zero vector (Eq. 14, second case).
Returns
-------
Tensor of shape (B, d).
"""
batch_size = len(retained_relations)
z = torch.zeros(batch_size, self.d, device=self.device)
for b, relations in enumerate(retained_relations):
if len(relations) == 0:
# Eq. 14, empty case: z = 0_d
continue
embeds = self.relation_cache.get_batch(relations) # (|S|, d)
if self.pool == "mean":
z[b] = embeds.mean(dim=0)
elif self.pool == "max":
z[b] = embeds.max(dim=0).values
return z