Sentence Similarity
sentence-transformers
Safetensors
greenleaf_embed
embeddings
legal
retrieval
feature-extraction
mteb
mleb
legal-tech
case-law
contracts
judicialmind
custom_code
Instructions to use judicialmind/greenleaf-law-embed-tiny with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- sentence-transformers
How to use judicialmind/greenleaf-law-embed-tiny with sentence-transformers:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("judicialmind/greenleaf-law-embed-tiny", trust_remote_code=True) sentences = [ "That is a happy person", "That is a happy dog", "That is a very happy person", "Today is a sunny day" ] embeddings = model.encode(sentences) similarities = model.similarity(embeddings, embeddings) print(similarities.shape) # [4, 4] - Notebooks
- Google Colab
- Kaggle
| """GreenLeaf Law Embed — quantization modules. | |
| Provides native int8 and binary quantization for embedding vectors, | |
| enabling efficient storage and fast similarity search without | |
| requiring post-hoc compression. | |
| Quantizers use straight-through estimation: the forward pass applies | |
| hard quantization while gradients flow through the soft (continuous) | |
| approximation during training. | |
| """ | |
| import torch | |
| import numpy as np | |
| from typing import Literal | |
| from sentence_transformers.models import Module | |
| class BaseQuantizer(torch.nn.Module): | |
| """Base quantizer with straight-through gradient estimation. | |
| During inference (hard=True), applies discrete quantization. | |
| During training, gradients bypass the discretization step so | |
| the model can learn through the quantization bottleneck. | |
| """ | |
| def __init__(self, hard: bool = True): | |
| """ | |
| Args: | |
| hard: If True, output is discretized. If False, output is | |
| the soft continuous approximation. | |
| """ | |
| super().__init__() | |
| self._hard = hard | |
| def _hard_quantize(self, x, *args, **kwargs) -> torch.Tensor: | |
| raise NotImplementedError | |
| def _soft_quantize(self, x, *args, **kwargs) -> torch.Tensor: | |
| raise NotImplementedError | |
| def forward(self, x, *args, **kwargs) -> torch.Tensor: | |
| soft = self._soft_quantize(x, *args, **kwargs) | |
| if not self._hard: | |
| return soft | |
| # Straight-through estimator: forward uses hard quantization, | |
| # backward passes gradients through the soft path unchanged. | |
| return ( | |
| self._hard_quantize(x, *args, **kwargs).detach() | |
| + soft - soft.detach() | |
| ) | |
| class Int8EmbeddingQuantizer(BaseQuantizer): | |
| """Quantizes embeddings to signed 8-bit integers via tanh scaling. | |
| The soft path applies tanh to squash values into [-1, 1], then | |
| the hard path scales to [-128, 127] and rounds to integers. | |
| """ | |
| def __init__(self, hard: bool = True): | |
| super().__init__(hard=hard) | |
| self.qmin = -128 | |
| self.qmax = 127 | |
| def _soft_quantize(self, x, *args, **kwargs): | |
| return torch.tanh(x) | |
| def _hard_quantize(self, x, *args, **kwargs): | |
| soft = self._soft_quantize(x) | |
| int_x = torch.round(soft * self.qmax) | |
| return torch.clamp(int_x, self.qmin, self.qmax) | |
| class BinaryEmbeddingQuantizer(BaseQuantizer): | |
| """Quantizes embeddings to {-1, +1} via sign function. | |
| The soft path uses scaled tanh as a differentiable approximation | |
| to the sign function. The hard path applies the actual sign. | |
| """ | |
| def __init__(self, hard: bool = True, scale: float = 1.0): | |
| super().__init__(hard) | |
| self._scale = scale | |
| def _soft_quantize(self, x, *args, **kwargs): | |
| return torch.tanh(self._scale * x) | |
| def _hard_quantize(self, x, *args, **kwargs): | |
| return torch.where(x >= 0, 1.0, -1.0) | |
| class PackedBinaryEncoder: | |
| """Packs binary {-1, +1} embeddings into uint8 bit-arrays. | |
| Each embedding dimension maps to 1 bit, reducing storage by 32x | |
| compared to float32. Useful for large-scale retrieval with | |
| Hamming distance. | |
| """ | |
| def __call__(self, x: torch.Tensor) -> torch.Tensor: | |
| bits = np.where(x.cpu().numpy() >= 0, True, False) | |
| packed = np.packbits(bits, axis=-1) | |
| return torch.from_numpy(packed).to(x.device) | |
| class FlexibleQuantizer(Module): | |
| """Sentence-transformers module that applies optional quantization. | |
| Default behavior: pass through raw float embeddings (bf16/fp32). | |
| Quantization is applied only when explicitly requested. | |
| Supported modes: | |
| - None: raw float embeddings (default, no quantization) | |
| - "int8": signed 8-bit integer embeddings | |
| - "binary": {-1, +1} binary embeddings | |
| - "ubinary": packed binary as uint8 bit-arrays | |
| """ | |
| def __init__(self): | |
| super().__init__() | |
| self._int8_quantizer = Int8EmbeddingQuantizer() | |
| self._binary_quantizer = BinaryEmbeddingQuantizer() | |
| self._packed_binary_encoder = PackedBinaryEncoder() | |
| def forward( | |
| self, | |
| features: dict[str, torch.Tensor], | |
| quantization: Literal["int8", "binary", "ubinary"] | None = None, | |
| **kwargs, | |
| ) -> dict[str, torch.Tensor]: | |
| if quantization is None: | |
| # Default: return raw float embeddings, no quantization | |
| return features | |
| elif quantization == "int8": | |
| features["sentence_embedding"] = self._int8_quantizer( | |
| features["sentence_embedding"] | |
| ) | |
| elif quantization == "binary": | |
| features["sentence_embedding"] = self._binary_quantizer( | |
| features["sentence_embedding"] | |
| ) | |
| elif quantization == "ubinary": | |
| features["sentence_embedding"] = self._packed_binary_encoder( | |
| features["sentence_embedding"] | |
| ) | |
| else: | |
| raise ValueError( | |
| f"Unknown quantization mode: '{quantization}'. " | |
| f"Supported modes: 'int8', 'binary', 'ubinary'." | |
| ) | |
| return features | |
| def load( | |
| cls, | |
| model_name_or_path: str, | |
| subfolder: str = "", | |
| token: bool | str | None = None, | |
| cache_folder: str | None = None, | |
| revision: str | None = None, | |
| local_files_only: bool = False, | |
| **kwargs, | |
| ): | |
| return cls() | |
| def save(self, output_path: str, *args, **kwargs) -> None: | |
| return | |