Feature Extraction
Transformers
Safetensors
autojev_decision
jev
decision
autojev
typed-decision
classification
distillation
custom_code
Instructions to use inclusionAI/LemonDecider with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use inclusionAI/LemonDecider with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="inclusionAI/LemonDecider", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("inclusionAI/LemonDecider", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Upload modeling_autojev.py with huggingface_hub
Browse files- modeling_autojev.py +86 -0
modeling_autojev.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""AutoJev-style full-weight decision model. No autoregressive generation head.
|
| 2 |
+
|
| 3 |
+
The checkpoint's backbone is copied byte-for-byte from Qwen, while readout rows
|
| 4 |
+
are selected from its original LM head. T is used in postprocessing, not CE loss.
|
| 5 |
+
"""
|
| 6 |
+
from dataclasses import dataclass
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
from transformers.utils import ModelOutput
|
| 10 |
+
from transformers.models.qwen3_5.modeling_qwen3_5 import Qwen3_5Model, Qwen3_5PreTrainedModel
|
| 11 |
+
from .configuration_autojev import AutoJevConfig
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
@dataclass
|
| 15 |
+
class AutoJevOutput(ModelOutput):
|
| 16 |
+
loss: torch.Tensor | None = None
|
| 17 |
+
logits: torch.Tensor | None = None
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class AutoJevForDecisionClassification(Qwen3_5PreTrainedModel):
|
| 21 |
+
config_class = AutoJevConfig
|
| 22 |
+
base_model_prefix = "backbone"
|
| 23 |
+
|
| 24 |
+
def __init__(self, config):
|
| 25 |
+
super().__init__(config)
|
| 26 |
+
self.backbone = Qwen3_5Model(config)
|
| 27 |
+
self.readout = torch.nn.Linear(config.text_config.hidden_size, config.max_options, bias=False)
|
| 28 |
+
self.post_init()
|
| 29 |
+
|
| 30 |
+
def get_input_embeddings(self):
|
| 31 |
+
return self.backbone.get_input_embeddings()
|
| 32 |
+
|
| 33 |
+
def set_input_embeddings(self, value):
|
| 34 |
+
self.backbone.set_input_embeddings(value)
|
| 35 |
+
|
| 36 |
+
def forward(self, input_ids=None, attention_mask=None, option_counts=None,
|
| 37 |
+
labels=None, return_dict=True, **kwargs):
|
| 38 |
+
if input_ids is None or input_ids.ndim != 2 or input_ids.shape[1] == 0:
|
| 39 |
+
raise ValueError("input_ids must be a nonempty [batch, length] tensor")
|
| 40 |
+
if input_ids.shape[1] > self.config.max_input_length:
|
| 41 |
+
raise ValueError("Input exceeds max_input_length; truncation is not allowed")
|
| 42 |
+
if attention_mask is None:
|
| 43 |
+
attention_mask = torch.ones_like(input_ids)
|
| 44 |
+
if attention_mask.shape != input_ids.shape or not bool((attention_mask[:, -1] == 1).all()):
|
| 45 |
+
raise ValueError("Left padding required: last prompt position must be valid")
|
| 46 |
+
if option_counts is None:
|
| 47 |
+
raise ValueError("option_counts is required for each input")
|
| 48 |
+
counts = torch.as_tensor(option_counts, device=input_ids.device)
|
| 49 |
+
if counts.dtype == torch.bool or counts.is_floating_point() or counts.shape != (input_ids.shape[0],):
|
| 50 |
+
raise ValueError("option_counts must contain one integer per row")
|
| 51 |
+
if not bool(((counts >= 1) & (counts <= self.config.max_options)).all()):
|
| 52 |
+
raise ValueError("Each question must contain 1..255 options")
|
| 53 |
+
if kwargs.pop("use_cache", False) or kwargs.get("past_key_values") is not None:
|
| 54 |
+
raise ValueError("Decision forward does not accept KV-cache decoding")
|
| 55 |
+
output = self.backbone(input_ids=input_ids, attention_mask=attention_mask,
|
| 56 |
+
use_cache=False, return_dict=True, **kwargs)
|
| 57 |
+
# Qwen3_5Model.last_hidden_state is AFTER language_model.norm (RMSNorm).
|
| 58 |
+
hidden = output.last_hidden_state[:, -1, :]
|
| 59 |
+
logits = self.readout(hidden).float()
|
| 60 |
+
invalid = torch.arange(self.config.max_options, device=logits.device)[None] >= counts[:, None]
|
| 61 |
+
logits = logits.masked_fill(invalid, -1e9)
|
| 62 |
+
loss = None
|
| 63 |
+
if labels is not None:
|
| 64 |
+
labels = labels.to(logits.device)
|
| 65 |
+
if labels.shape == (len(counts),):
|
| 66 |
+
if labels.is_floating_point() or labels.dtype == torch.bool or not bool(((labels >= 0) & (labels < counts)).all()):
|
| 67 |
+
raise ValueError("Hard targets must be valid option indices")
|
| 68 |
+
loss = F.cross_entropy(logits, labels.long())
|
| 69 |
+
elif labels.shape == logits.shape:
|
| 70 |
+
target = labels.float()
|
| 71 |
+
if not bool(torch.isfinite(target).all()) or bool((target < 0).any()):
|
| 72 |
+
raise ValueError("Invalid soft targets")
|
| 73 |
+
if not torch.allclose(target.sum(-1), torch.ones(len(counts), device=target.device), atol=1e-6, rtol=0):
|
| 74 |
+
raise ValueError("Soft targets must sum to one")
|
| 75 |
+
if bool((target.masked_select(invalid) != 0).any()):
|
| 76 |
+
raise ValueError("Soft targets assign mass to invalid options")
|
| 77 |
+
loss = -(target * F.log_softmax(logits, dim=-1)).sum(-1).mean()
|
| 78 |
+
else:
|
| 79 |
+
raise ValueError("labels must be [batch] or [batch,255]")
|
| 80 |
+
if not return_dict:
|
| 81 |
+
return (loss, logits) if loss is not None else (logits,)
|
| 82 |
+
return AutoJevOutput(loss=loss, logits=logits)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
AutoJevConfig.register_for_auto_class()
|
| 86 |
+
AutoJevForDecisionClassification.register_for_auto_class("AutoModel")
|