Image Segmentation
Transformers
Safetensors
English
falcon_x
feature-extraction
falcon-x
vision-language
custom_code
Instructions to use JonathanJMK/FALCON with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use JonathanJMK/FALCON with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-segmentation", model="JonathanJMK/FALCON", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("JonathanJMK/FALCON", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download safety.py from JonathanJMK/FALCON: direct link, hf CLI and curl.
- Browser
- Download file 10.3 kB
-
https://huggingface.co/JonathanJMK/FALCON/resolve/main/safety.py
- Command line
-
hf download hf://JonathanJMK/FALCON/safety.py
-
curl -L -o safety.py https://huggingface.co/JonathanJMK/FALCON/resolve/main/safety.py
10.3 kB
| """Structured Safety Adapter (SSA) with binary presence supervision.""" | |
| from __future__ import annotations | |
| import math | |
| from dataclasses import dataclass | |
| import torch | |
| from torch import Tensor, nn | |
| from torch.nn import functional as F | |
| from .capabilities import ALL_SAFETY_HEADS, SafetyCapabilities | |
| # These indices are the canonical Falcon-X annotation order. The paper's | |
| # "main charge" is represented by the dataset label ``explosive``. | |
| COMPONENT_TYPES = ("detonator", "explosive", "battery") | |
| LINK_PAIRS = ((2, 0), (2, 1), (0, 1)) | |
| SAFETY_TOKEN_TYPES = ( | |
| "scene_risk", | |
| "presence_detonator", | |
| "presence_explosive", | |
| "presence_battery", | |
| "link_battery_detonator", | |
| "link_battery_explosive", | |
| "link_detonator_explosive", | |
| ) | |
| class SafetyOutput: | |
| slots: Tensor | |
| attention: Tensor | |
| presence_logits: Tensor | |
| presence_probabilities: Tensor | |
| link_logits: Tensor | |
| link_probabilities: Tensor | |
| risk_logit: Tensor | |
| risk_probability: Tensor | |
| tokens: Tensor | |
| capabilities: SafetyCapabilities = SafetyCapabilities() | |
| def prediction(self, batch_index: int = 0) -> dict: | |
| """Serialize unavailable heads as null, never plausible-looking numbers.""" | |
| return { | |
| "risk": float(self.risk_probability[batch_index].detach().cpu()) | |
| if self.capabilities.risk | |
| else None, | |
| "presence": [ | |
| float(value.detach().cpu()) if enabled else None | |
| for value, enabled in zip( | |
| self.presence_probabilities[batch_index], | |
| self.capabilities.presence, | |
| strict=True, | |
| ) | |
| ], | |
| "links": [ | |
| float(value.detach().cpu()) if enabled else None | |
| for value, enabled in zip( | |
| self.link_probabilities[batch_index], self.capabilities.links, strict=True | |
| ) | |
| ], | |
| "capabilities": self.capabilities.as_dict(), | |
| } | |
| def scalar_values(self) -> Tensor: | |
| """Values in the seven-token paper order: risk, presence, links.""" | |
| return torch.cat( | |
| ( | |
| self.risk_probability.unsqueeze(-1), | |
| self.presence_probabilities, | |
| self.link_probabilities, | |
| ), | |
| dim=-1, | |
| ) | |
| class StructuredSafetyAdapter(nn.Module): | |
| """Map a variable proposal set to three typed component slots and seven tokens.""" | |
| def __init__( | |
| self, | |
| model_dim: int, | |
| token_dim: int, | |
| *, | |
| head_hidden_dim: int | None = None, | |
| capabilities: SafetyCapabilities = ALL_SAFETY_HEADS, | |
| ) -> None: | |
| super().__init__() | |
| hidden_dim = head_hidden_dim or model_dim | |
| self.model_dim = model_dim | |
| self.token_dim = token_dim | |
| self.capabilities = capabilities | |
| self.component_queries = nn.Parameter(torch.empty(3, model_dim)) | |
| nn.init.normal_(self.component_queries, std=0.02) | |
| self.presence_head = nn.Linear(model_dim, 1) | |
| self.link_heads = nn.ModuleList( | |
| [ | |
| nn.Sequential( | |
| nn.Linear(2 * model_dim, hidden_dim), | |
| nn.GELU(), | |
| nn.Linear(hidden_dim, 1), | |
| ) | |
| for _ in LINK_PAIRS | |
| ] | |
| ) | |
| self.risk_head = nn.Sequential( | |
| nn.Linear(3 * model_dim + len(LINK_PAIRS), hidden_dim), | |
| nn.GELU(), | |
| nn.Linear(hidden_dim, 1), | |
| ) | |
| self.value_projection = nn.Linear(1, token_dim) | |
| self.type_embedding = nn.Embedding(len(SAFETY_TOKEN_TYPES), token_dim) | |
| self.freeze_unavailable_heads() | |
| def freeze_unavailable_heads(self) -> None: | |
| """Reapply after a training-stage change has enabled the shared adapter.""" | |
| modules = [ | |
| (self.presence_head, any(self.capabilities.presence)), | |
| (self.risk_head, self.capabilities.risk), | |
| ] | |
| modules.extend(zip(self.link_heads, self.capabilities.links, strict=True)) | |
| for module, enabled in modules: | |
| if not enabled: | |
| for parameter in module.parameters(): | |
| parameter.requires_grad = False | |
| def _component_slots( | |
| self, | |
| region_embeddings: Tensor, | |
| valid: Tensor, | |
| ) -> tuple[Tensor, Tensor]: | |
| batch, proposals, channels = region_embeddings.shape | |
| if channels != self.model_dim: | |
| raise ValueError(f"expected region dimension {self.model_dim}, received {channels}") | |
| if valid.shape != (batch, proposals): | |
| raise ValueError("valid must have shape [batch, proposals]") | |
| if proposals == 0: | |
| slots = region_embeddings.new_zeros((batch, 3, channels)) | |
| attention = region_embeddings.new_zeros((batch, 3, 0)) | |
| return slots, attention | |
| scores = torch.einsum("cd,bnd->bcn", self.component_queries, region_embeddings) | |
| scores = scores / math.sqrt(float(channels)) | |
| expanded_valid = valid[:, None, :].to(dtype=torch.bool) | |
| scores = scores.masked_fill(~expanded_valid, torch.finfo(scores.dtype).min) | |
| attention = scores.softmax(dim=-1) * expanded_valid.to(dtype=scores.dtype) | |
| attention = attention / attention.sum(dim=-1, keepdim=True).clamp_min(1e-12) | |
| slots = torch.einsum("bcn,bnd->bcd", attention, region_embeddings) | |
| return slots, attention | |
| def forward( | |
| self, | |
| region_embeddings: Tensor, | |
| valid: Tensor | None = None, | |
| ) -> SafetyOutput: | |
| if region_embeddings.ndim != 3: | |
| raise ValueError("region_embeddings must have shape [batch, proposals, channels]") | |
| if valid is None: | |
| valid = torch.ones( | |
| region_embeddings.shape[:2], | |
| dtype=torch.bool, | |
| device=region_embeddings.device, | |
| ) | |
| else: | |
| valid = valid.to(device=region_embeddings.device, dtype=torch.bool) | |
| slots, attention = self._component_slots(region_embeddings, valid) | |
| presence_logits = ( | |
| self.presence_head(slots).squeeze(-1) | |
| if any(self.capabilities.presence) | |
| else slots.new_zeros(slots.shape[:2]) | |
| ) | |
| link_logits = torch.stack( | |
| [ | |
| head(torch.cat((slots[:, left], slots[:, right]), dim=-1)).squeeze(-1) | |
| if enabled | |
| else slots.new_zeros(slots.shape[0]) | |
| for head, (left, right), enabled in zip( | |
| self.link_heads, LINK_PAIRS, self.capabilities.links, strict=True | |
| ) | |
| ], | |
| dim=-1, | |
| ) | |
| link_probabilities = link_logits.sigmoid() * slots.new_tensor(self.capabilities.links) | |
| # Risk is explicitly conditioned on every slot and the three inferred links. | |
| risk_input = torch.cat((slots.flatten(start_dim=1), link_probabilities), dim=-1) | |
| risk_logit = ( | |
| self.risk_head(risk_input).squeeze(-1) | |
| if self.capabilities.risk | |
| else slots.new_zeros(slots.shape[0]) | |
| ) | |
| risk_probability = risk_logit.sigmoid() * int(self.capabilities.risk) | |
| presence_probabilities = presence_logits.sigmoid() * slots.new_tensor( | |
| self.capabilities.presence | |
| ) | |
| scalar_values = torch.cat( | |
| ( | |
| risk_probability.unsqueeze(-1), | |
| presence_probabilities, | |
| link_probabilities, | |
| ), | |
| dim=-1, | |
| ) | |
| type_ids = torch.tensor( | |
| self.capabilities.token_indices, device=slots.device, dtype=torch.long | |
| ) | |
| tokens = self.value_projection(scalar_values[:, type_ids].unsqueeze(-1)) | |
| tokens = tokens + self.type_embedding(type_ids).unsqueeze(0) | |
| return SafetyOutput( | |
| slots=slots, | |
| attention=attention, | |
| presence_logits=presence_logits, | |
| presence_probabilities=presence_probabilities, | |
| link_logits=link_logits, | |
| link_probabilities=link_probabilities, | |
| risk_logit=risk_logit, | |
| risk_probability=risk_probability, | |
| tokens=tokens, | |
| capabilities=self.capabilities, | |
| ) | |
| def loss( | |
| output: SafetyOutput, | |
| *, | |
| risk: Tensor, | |
| presence: Tensor, | |
| links: Tensor, | |
| ) -> dict[str, Tensor]: | |
| """Stage-2/3 objective: presence BCE with logits and risk/link mean L1.""" | |
| risk_target = risk.to(output.risk_probability).reshape_as(output.risk_probability) | |
| presence_logits = output.presence_logits.float() | |
| presence_target = presence.to( | |
| device=presence_logits.device, dtype=torch.float32 | |
| ).reshape_as(presence_logits) | |
| link_target = links.to(output.link_probabilities).reshape_as(output.link_probabilities) | |
| if not output.capabilities.risk: | |
| risk_target = torch.full_like(risk_target, float("nan")) | |
| presence_target = presence_target.masked_fill( | |
| ~torch.tensor(output.capabilities.presence, device=presence_target.device), float("nan") | |
| ) | |
| link_target = link_target.masked_fill( | |
| ~torch.tensor(output.capabilities.links, device=link_target.device), float("nan") | |
| ) | |
| def finite_l1(prediction: Tensor, target: Tensor) -> Tensor: | |
| finite = torch.isfinite(target) | |
| if finite.any(): | |
| return F.l1_loss(prediction[finite], target[finite]) | |
| # Keep a differentiable zero when a sample has no annotated links. | |
| return prediction.sum() * 0.0 | |
| risk_loss = finite_l1(output.risk_probability, risk_target) | |
| observed_presence = torch.isfinite(presence_target) | |
| if observed_presence.any(): | |
| presence_loss = F.binary_cross_entropy_with_logits( | |
| presence_logits[observed_presence], presence_target[observed_presence] | |
| ) | |
| else: | |
| presence_loss = presence_logits.sum() * 0.0 | |
| link_loss = finite_l1(output.link_probabilities, link_target) | |
| return { | |
| "risk": risk_loss, | |
| "presence": presence_loss, | |
| "links": link_loss, | |
| } | |