Feature Extraction
Transformers
Safetensors
lemondecider_decision
decision-engine
typed-decision
choice
classification
Merge
qwen3.5
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
Replace v1 autojev runtime with LemonDecider v2 package
Browse files- configuration_autojev.py +0 -32
- modeling_autojev.py +0 -86
- sft_receipt.json +0 -18
configuration_autojev.py
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
"""Configuration for a Qwen backbone with a dynamic-option decision head."""
|
| 2 |
-
import math
|
| 3 |
-
from transformers.models.qwen3_5.configuration_qwen3_5 import Qwen3_5Config
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
class AutoJevConfig(Qwen3_5Config):
|
| 7 |
-
model_type = "autojev_decision"
|
| 8 |
-
|
| 9 |
-
def __init__(self, codes=None, token_ids=None, max_options=255, temperature=1.0,
|
| 10 |
-
base_model="Qwen/Qwen3.8-27B", initialization_step=0,
|
| 11 |
-
max_input_length=8192, **kwargs):
|
| 12 |
-
super().__init__(**kwargs)
|
| 13 |
-
if max_options != 255:
|
| 14 |
-
raise ValueError("This artifact uses exactly 255 option positions")
|
| 15 |
-
if not math.isfinite(temperature) or temperature <= 0:
|
| 16 |
-
raise ValueError("temperature must be positive and finite")
|
| 17 |
-
self.max_options = max_options
|
| 18 |
-
self.codes = list(codes or [])
|
| 19 |
-
self.token_ids = list(token_ids or [])
|
| 20 |
-
if self.codes or self.token_ids:
|
| 21 |
-
if len(self.codes) != max_options or len(set(self.codes)) != max_options:
|
| 22 |
-
raise ValueError("Expected 255 unique answer codes")
|
| 23 |
-
if len(self.token_ids) != max_options or len(set(self.token_ids)) != max_options:
|
| 24 |
-
raise ValueError("Expected 255 unique answer token IDs")
|
| 25 |
-
if any(type(i) is not int or not 0 <= i < self.text_config.vocab_size for i in self.token_ids):
|
| 26 |
-
raise ValueError("Answer token IDs outside base vocabulary")
|
| 27 |
-
self.temperature = float(temperature)
|
| 28 |
-
self.base_model = base_model
|
| 29 |
-
self.initialization_step = initialization_step
|
| 30 |
-
self.max_input_length = int(max_input_length)
|
| 31 |
-
if self.max_input_length < 1:
|
| 32 |
-
raise ValueError("max_input_length must be positive")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
modeling_autojev.py
DELETED
|
@@ -1,86 +0,0 @@
|
|
| 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")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sft_receipt.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"step": 24,
|
| 3 |
-
"dev_accuracy_t1": 0.7735191583633423,
|
| 4 |
-
"best_step": 3,
|
| 5 |
-
"best_dev_accuracy_t1": 0.7804877758026123,
|
| 6 |
-
"lr": 2.2827515498423204e-07,
|
| 7 |
-
"examples_seen": 6144,
|
| 8 |
-
"temperature_fit": {
|
| 9 |
-
"method": "NLL scan on dev 574 under fork _known() semantics, T grid [0.30,5.00]x0.05 (from cached T=1 probs)",
|
| 10 |
-
"best_T": 0.6,
|
| 11 |
-
"dev_nll_at_T": 0.56265,
|
| 12 |
-
"dev_brier_at_T": 0.28261,
|
| 13 |
-
"dev_nll_at_1": 0.6016,
|
| 14 |
-
"dev_brier_at_1": 0.29885,
|
| 15 |
-
"bound": false,
|
| 16 |
-
"reason": "best_T=0.60 != serving T=1.0; NOT applied — public-set re-eval required before adoption (dev acc unchanged at T*, argmax-invariant except noul boundary)"
|
| 17 |
-
}
|
| 18 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|