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