d1-Omni-2B 信息抽取能力解析:3 步从发票读出金额、日期与文本 span

【免费下载链接】d1-Omni-2B

项目地址: https://ai.gitcode.com/hf_mirrors/LiquidAI/d1-Omni-2B
点击查看 免费下载

d1-Omni-2B 所属的 Liquid AI 多模态决策模型项目横幅

d1-Omni-2B 是 Liquid AI 开源的 2.17B 多模态决策模型,基于 LFM3-Omni-2B 底座构建,能像 OCR + 规则引擎那样从发票文本与图片中直接读出金额、日期和文本 span,但它是"一次前向传播读出校准答案"的抽取式模型:不逐 token 生成、不拼 prompt 模板,毫秒级返回带 found 概率的结构化结果(README.md)。本文带你从零理解它的抽取能力:问什么、怎么答、值从哪来。

1 分钟上手:从发票文本读出 4 个字段 📄

给 d1-Omni-2B 一段发票文本,同时提 4 个命名问题:开票方(text)、应付金额(number)、到期日(date)、税额(number),一次调用全部返回:

invoice = "ACME SA\nInvoice 2026-117\nTotal due: 1 234,50 EUR\nPay before 03/04/2026 (day/month/year)"
out = model.system_one(invoice, {
    "payee": {"type": "text", "instructions": "Who issued the invoice?"},
    "total": {"type": "number", "instructions": "How much is due?"},
    "due": {"type": "date", "instructions": "By when must it be paid?"},
    "vat": {"type": "number", "instructions": "How much VAT is charged?"},
})
a = out["answers"]
print(a["payee"]["text"], a["total"]["number"], a["due"]["date"], a["vat"]["number"])
# ACME SA 1234.5 2026-04-03 None

total 返回 1234.5——注意原文写作 1 234,50(欧洲式千分位空格 + 逗号小数),它被严格解析成了标准浮点数;due 被规范为 ISO 8601 的 2026-04-03;发票里没有税额,vat 直接返回 None 而不是编造。

每个抽取答案的字段:

字段含义
text / number / date按问题类型给出:原文文本 / 解析后的数值 / ISO 8601 日期
span值在状态文本中的字符区间 [start, end),即"原文 span"
found找到的校准概率;低于 0.5 时值返回 None
unitnumber 专属,如 EUR(原文写出的币种)
path状态为 JSON 时,值所在的 RFC6901 字段路径

它为什么"快":读出答案,而不是生成答案 ⚡

传统做法是让大模型生成 JSON,再解析、校验、重试。d1-Omni-2B 走的是另一条路(runner.py):

  • 状态(文本 / JSON / 图片 / 音频)+ 所有命名问题拼成一个 prompt,一次前向传播;
  • 每个问题的答案从它自己的答案槽位的 logits 上读出,不生成任何 token,usage 里 output_tokens 恒为 0;
  • 答案结构是定死的 schema——"JSON 写错格式"这件事在机制上不可能发生;
  • 所有概率都经过 calibration.json 校准,found、confidence 可以直接当阈值用。

速度上(warm 调用中位数,见 README.md):

场景NVIDIA H100AMD MI325XApple M5 Pro
1 个文本问题5.8 ms3.7 ms21.1 ms
3 个问题7.7 ms5.6 ms28.9 ms
384 px 图片12.1 ms8.6 ms40.2 ms
64 个状态批量1,154 /s1,697 /s110 /s

span 指针:值是怎么"指"出来的 🎯

这是抽取能力最核心的机制(pointer.py)。对 text / number / date 问题,模型不是写出值,而是对状态文本的每个 token 位置打一对分数(起点、终点):

  1. 先读 found 概率:低于 0.5 判"没找到",答案直接为 None;
  2. 找到后,选出最可能的起止位置对,且跨距受上限约束——text 最长 512 个 token,number 和 date 各 32 个(pointer.py);
  3. 把这对位置映射回原始文本,切出字符级 span,顺手去掉首尾空白,若 span 落在某个 JSON 字符串内部,还会还原其转义字符(json_spans.py)。

所以你拿到的 span 里的值,是原文的逐字切片——"读到的"永远等于"写着的",不会多出一个空格、少一位数字。

金额解析:1 234,50 怎么变成 1234.5 💶

number 答案 = span 原文 + 一个严格解析器(parsing.py),它只把"写着的值"读出来,不做算术、不做单位换算:

  • 小数点判断看证据:span 里写出的币种符号(EUR 用逗号、USD 用点)、文档里其他数字的写法、声明的 locale(en / fr / de……),证据互相矛盾时返回 None,绝不猜;
  • 千分位与小数并存:1 234,50、1.234,56 都能正确区分"哪个符号是分节、哪个是小数";
  • 币种与规模词:$、€、元、₹ 等会拆出来记入 unit;thousand / million / 万 / 亿 / crore 等规模词会被换算到数值上;
  • 括号负数:会计式 (123.45) 解析为负数。

日期解析:03/04/2026 到底是哪一天 📅

date 答案统一为 ISO 8601。难点从来不是格式,而是日/月顺序,解析器(parsing.py)同样按证据裁决:

  • 文档里声明了格式(如示例中的 day/month/year,或 dd/mm/yyyy、jj/mm/aaaa 这类模板)→ 直接跟随;
  • 否则看声明的 locale(美式 → 月在前,欧式 → 日在前);
  • 再否则看文档里其他无歧义的日期(比如某处是 25/11/2026,25 只能是日);
  • 证据冲突时,宁可报歧义,也不硬猜。

它还认 16 种语言的月份名(法语 janvier、德语 Januar、俄语 января……)、星期名,以及中日韩写法 2026年10月20日、2026년 10월 20일;带时间的日期(2026-04-03T14:30+02:00)会原样保留时刻与时区,不补默认值。

给 JSON 状态抽取:答案自带字段路径 🔍

状态(state)不只能是字符串,还可以是任意 JSON 值。此时答案会多一个 path 字段——RFC6901 风格的路径(如 /header/total),精确指出值所在的字段(json_spans.py 负责定位 JSON 里每个字符串值的 span)。对票据流水线的自动化校验和人工抽检来说,span + path 意味着每个答案都能回溯到原始文档的具体位置。

从发票图片抽取:同一套问题,多一个 media 🖼️

发票是扫描件或照片?把图片放进 media,视觉编码器会把它展开成 token 混入序列,与文本走同一条前向路径(media.py)。除了 text / number / date 这类读取问题,图片还能直接做对象定位:

out = model.system_one(None, {
    "cats": {"type": "number", "instructions": "How many cats are there?", "range": [0, 10], "step": 1},
    "remote": {"type": "box", "instructions": "Where is the remote control on the left?"},
    "every_cat": {"type": "boxes", "instructions": "Every cat.", "max": 4},
}, media=[image])
# cats: 2.0  every_cat: 2 只  remote: {'top': 0.15, 'bottom': 0.24, 'left': 0.06, 'right': 0.27}
  • box:返回单个对象的四条边(占图片的归一化比例),可定位"发票金额栏在哪里"这类区域;
  • boxes:枚举图中所有实例,max 限制读取数量;
  • number + range + step:计数与数量估计,带 interval(p10–p90)置信区间。

一个提醒:span 指针指向的是文本状态的字符区间。当状态是图片(state=None)时,你得到的是解析好的值(金额、日期)和框坐标,而不是原文切片。

批量与性能调优:一张发票 5.8 ms,一千张发票一次调用 📦

  • 批量:system_one_batch([(state, questions[, media]), ...]) 一次调用处理多张发票,长度相近的请求自动拼进同一批;
  • set_size:多个问题共享同一次状态读取,默认 16(训练时的分组大小);调更大会更快,但文本任务上会损失一点精度(runner.py);
  • 图片像素上限:本 checkpoint 记录为 1,638,400 像素(config.json 中 d1_vision_max_pixels),大图会被等比缩小后再编码。

实用建议:生产里怎么用才稳 ✅

  1. 信任 found,别猜值:found < 0.5 即缺失(api.py)。建议在自己的票据样本上校准这个阈值,再决定哪些字段需要人工复核;
  2. 善用 unit:number 会自动带出币种(EUR、USD……),跨币种对账不用再解析原文;
  3. date 直接入仓:ISO 8601 是通用格式,下游系统零转换;
  4. 用 span / path 做审计:自动化流水线里可抽检"答案原文与文档是否一致",出问题时秒级定位;
  5. 按场景选模型:纯文本 / 单框任务上,无音频输入的 d1-2B 更强、校准更好;只有需要语音理解时才上 d1-Omni-2B(长语音段落精度会明显下降,拉丁文字之外的语言尤甚)。

关键文件速查

模块作用
runner.pysystem_one 引擎:拼装 prompt、一次前向、槽位读取
pointer.pyspan 指针:位置打分 → 最可能的起止位置 → 原文切片
parsing.py数值 / 日期的严格解析:币种、locale、日/月顺序
json_spans.pyJSON 状态中字符串值的 span 与 RFC6901 路径
api.py答案组装:found 阈值、unit、path
config.jsoncheckpoint 参数:d1_codes、d1_set_size、d1_vision_max_pixels
calibration.json概率校准表,让 found / confidence 可当阈值用

总结

d1-Omni-2B 的抽取能力 = 单次前向读出校准答案 + span 指针在状态文本里切出原文区间 + 严格解析器把"写着的值"读成标准数值和日期,JSON 状态还能额外给出字段 path。从一段发票文本、一份 JSON 到一张发票照片,它都能读出金额、日期和文本 span,单条文本决策只需毫秒级。对新手来说,最上手的一步就是先按"1 分钟上手"的 4 问题示例跑通,然后盯着 span 和 found 两个字段理解——你会立刻明白:这是一个可回溯、可校准、不会胡说的抽取模型,而不是一台只会生成 JSON 的黑盒。

【免费下载链接】d1-Omni-2B

项目地址: https://ai.gitcode.com/hf_mirrors/LiquidAI/d1-Omni-2B
点击查看 免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值