File size: 7,576 Bytes
61f0dbc
 
 
 
 
 
 
 
08aacbd
 
61f0dbc
 
 
 
 
08aacbd
61f0dbc
08aacbd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61f0dbc
 
 
08aacbd
 
 
 
 
 
61f0dbc
 
 
08aacbd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5aaf6c0
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
---
language:
- en
library_name: transformers
base_model: LiquidAI/LFM2.5-350M
tags:
- schema-extraction
- json-extraction
- structured-output
- information-extraction
- lora
- edge-ai
- schemalm
---

# SchemaLM v2 β€” LFM2.5 350M

A 350M parameter model fine-tuned for **JSON-schema-conditioned information extraction**.  
Give it a JSON Schema and any unstructured text β€” it returns a JSON object that matches the schema exactly.

**Base model:** [`LiquidAI/LFM2.5-350M`](https://huggingface.co/LiquidAI/LFM2.5-350M) (SSM hybrid, ideal for edge/CPU deployment)

---

## Benchmark Results

| Metric | Score |
|---|---|
| Overall score (v2 training benchmark) | **97.7%** |
| JSON validity | **100%** |
| Schema conformance | **100%** |
| Field extraction accuracy | **97.6%** |
| Internal benchmark cases | **96.2%** |

Evaluated on a 25-case internal benchmark covering easy/medium/hard extraction across 7 domains.

| Domain | Avg Accuracy |
|---|---|
| Software Projects | ~74% |
| Governance / Compliance | ~75% |
| HR | ~85% |
| Education | ~81% |
| Sales | ~55% |
| Healthcare | ~53% |
| Support (phone) | ~61% |

---

## Training

- **Dataset:** 49K synthetic schema-extraction examples (93 schemas, 29 domains, nested object/array coverage)
- **Method:** PEFT/LoRA SFT β€” rank 32, alpha 64, ~2% trainable parameters
- **Epochs:** 5 | **LR:** 2e-4 (cosine) | **Max seq:** 768 tokens
- **Precision:** bf16 on Ampere/Hopper, fp16 otherwise

---

## Usage

The model follows the **ChatML format**: system message = JSON Schema, user message = unstructured text, assistant response = JSON.

### Basic (Transformers)

```python
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch, json

model_id = "senthil090/schemalm-v2-lfm2-350m"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
    model_id, torch_dtype=torch.bfloat16, device_map="auto"
)
model.eval()

def extract(schema: dict, text: str, max_new_tokens: int = 256) -> dict:
    messages = [
        {"role": "system", "content": json.dumps(schema)},
        {"role": "user",   "content": text},
    ]
    prompt = tokenizer.apply_chat_template(
        messages, tokenize=False, add_generation_prompt=True
    )
    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
    with torch.no_grad():
        out = model.generate(
            **inputs,
            max_new_tokens=max_new_tokens,
            do_sample=False,
            repetition_penalty=1.08,
        )
    raw = tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
    return json.loads(raw.strip())
```

---

### Example 1 β€” Sprint planning note β†’ structured task

```python
schema = {
    "type": "object",
    "properties": {
        "task_title":    {"type": "string"},
        "assignee":      {"type": "string"},
        "priority":      {"type": "string", "enum": ["critical", "high", "medium", "low"]},
        "story_points":  {"type": "integer"},
        "status":        {"type": "string", "enum": ["todo", "in_progress", "done"]},
        "epic":          {"type": "string"},
    },
    "required": ["task_title", "assignee", "priority"],
}

text = (
    "Assign the Redis caching implementation to Priya for Sprint 14. "
    "It's high priority under the Performance epic, estimated 5 story points. "
    "Mark it as in progress since she already started."
)

result = extract(schema, text)
# β†’ {
#   "task_title": "Redis caching implementation",
#   "assignee": "Priya",
#   "priority": "high",
#   "story_points": 5,
#   "status": "in_progress",
#   "epic": "Performance"
# }
```

---

### Example 2 β€” Nested support ticket (nested objects + arrays)

```python
schema = {
    "type": "object",
    "properties": {
        "customer": {
            "type": "object",
            "properties": {
                "name":    {"type": "string"},
                "company": {"type": "string"},
                "email":   {"type": "string", "format": "email"},
            },
            "required": ["name", "company"],
        },
        "issue": {
            "type": "object",
            "properties": {
                "summary":  {"type": "string"},
                "severity": {"type": "string", "enum": ["low", "medium", "high", "critical"]},
            },
            "required": ["summary", "severity"],
        },
        "actions": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "owner": {"type": "string"},
                    "task":  {"type": "string"},
                },
                "required": ["owner", "task"],
            },
        },
    },
    "required": ["customer", "issue"],
}

text = (
    "Support note: customer Priya Shah from Velocity Corp, email priya@velocity.io. "
    "Dashboard charts won't load β€” severity high. "
    "Action: Sarah to open an engineering ticket. Marcus to send ETA to customer."
)

result = extract(schema, text)
# β†’ {
#   "customer": {"name": "Priya Shah", "company": "Velocity Corp", "email": "priya@velocity.io"},
#   "issue": {"summary": "Dashboard charts won't load", "severity": "high"},
#   "actions": [
#     {"owner": "Sarah", "task": "open an engineering ticket"},
#     {"owner": "Marcus", "task": "send ETA to customer"}
#   ]
# }
```

---

### Example 3 β€” Compliance finding with date and enum fields

```python
schema = {
    "type": "object",
    "properties": {
        "finding_id":   {"type": "string"},
        "standard":     {"type": "string", "enum": ["ISO27001", "SOC2", "HIPAA", "GDPR", "PCI-DSS"]},
        "clause":       {"type": "string"},
        "severity":     {"type": "string", "enum": ["critical", "major", "minor", "observation"]},
        "description":  {"type": "string"},
        "due_date":     {"type": "string", "format": "date"},
        "owner":        {"type": "string"},
        "status":       {"type": "string", "enum": ["open", "in_remediation", "closed"]},
    },
    "required": ["standard", "severity", "description", "due_date"],
}

text = (
    "Audit finding AF-2024-089: ISO27001 clause A.9.4.2 violation β€” "
    "privileged accounts are not protected with MFA. Severity: major. "
    "Remediation owner: Chen, due by 2024-09-30. Status: in_remediation."
)

result = extract(schema, text)
# β†’ {
#   "finding_id": "AF-2024-089",
#   "standard": "ISO27001",
#   "clause": "A.9.4.2",
#   "severity": "major",
#   "description": "Privileged accounts are not protected with MFA",
#   "due_date": "2024-09-30",
#   "owner": "Chen",
#   "status": "in_remediation"
# }
```

---

### Example 4 β€” CPU inference (no GPU)

```python
model = AutoModelForCausalLM.from_pretrained(
    "senthil090/schemalm-v2-lfm2-350m",
    torch_dtype=torch.float32,  # fp32 for CPU
)
model.eval()
# Same extract() function as above β€” just slower (~5–15s per call on a modern CPU)
```

---

## Limitations

- Best on extraction tasks (reading text and pulling fields) β€” not a general-purpose chat model
- Weaker on very long schemas (>50 fields) or schemas with complex `pattern`/`format` constraints
- Sales numeric extraction and phone-call transcripts have lower accuracy (~50–60%) β€” targeted in v2.1 hardcase training
- Not suitable for code generation, reasoning, or creative tasks

## Citation

```
@misc{schemalm-v2,
  title  = {SchemaLM v2 β€” JSON Schema-Conditioned Information Extraction},
  author = {Senthil},
  year   = {2025},
  url    = {https://huggingface.co/senthil090/schemalm-v2-lfm2-350m}
}
```