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
Download modeling_autojev.py from inclusionAI/LemonDecider: direct link, hf CLI and curl.
- Browser
- Download file 4.55 kB
-
https://huggingface.co/inclusionAI/LemonDecider/resolve/main/modeling_autojev.py
- Command line
-
hf download hf://inclusionAI/LemonDecider/modeling_autojev.py
-
curl -L -o modeling_autojev.py https://huggingface.co/inclusionAI/LemonDecider/resolve/main/modeling_autojev.py
4.55 kB
| """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 | |
| 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") | |