信源:论文 arXiv:2507.18546,代码 github.com/fastino-ai/GLiNER2。下文机制与 API 以仓库实现为准;论文表格数字仅作零样本参考。
先摆问题:信息抽取为什么常常做成pipeline
从一段非结构化文本里拿出结构化字段——人名地名、情感标签、发票上的金额和日期、实体之间的关系——在 NLP 里叫信息抽取(IE)。工程里常见做法是:NER 一个模型、分类又一个、抽 JSON 再调 LLM 或专用模型、关系抽取再挂 GLiREL 一类东西。每加一种任务,就多一套部署、多一轮延迟,标签稍微变一下还可能要重训或重配。
第一代 GLiNER(2024)已经说明:用小型 encoder(双向 Transformer),把「实体类型」写进输入里,可以在 CPU 上做零样本 NER,效果和当时不少 LLM 提示词方案接近,体量大约一两亿参数。但 GLiNER 只管 NER;后面社区又长出 GLiClass(分类)、GLiREL(关系)等分支,能力散了。
GLiNER2 要做的事很直白:在保持「小模型 + CPU 能跑」的前提下,把 实体识别、文本分类、层级结构化抽取、关系抽取 收进 同一个模型、同一套 Schema 接口,并且允许 一次前向传播 里组合多个任务。论文把它叫 schema-driven:你先声明「要抽什么」,模型按声明好的 prompt 模板去读文本,而不是每次写一大段自然语言指令去赌 LLM 格式稳定。
模型在干什么
先说一句容易误解的:GLiNER2 没有 GPT 那种自回归 decoder,不会一个字一个字「生成 JSON」。它是 Encoder + 若干任务头:把 schema 和正文拼成一条序列,过 DeBERTa 编码;NER/结构化/关系在 候选 span 上打分匹配;分类在 标签 embedding 上出 logit。下面按任务拆开,参考论文 Appendix A 和源码。
1. 统一输入:前半段是「任务说明书」,后半段是正文
论文抽象写法:
[Task Prompt] ⊕ [SEP] ⊕ [Input Text]
落到仓库里,SchemaTransformer._format_input_with_mapping 实际拼的是:
[schema_1 的 token 串] [SEP_STRUCT] [schema_2 的 token 串] [SEP_STRUCT] … [SEP_TEXT] [正文按词切开后的 token 串]
- 每个子任务(entities、某个 classification、某个 structure、某个 relation)各自占一段 schema token;
[SEP_STRUCT]/[SEP_TEXT]是库内常量(processor.py里SEP_STRUCT、SEP_TEXT),作用和论文的[SEP]一样:别让任务说明和正文在注意力里糊成一团。
例子(概念上) :NER + 情感,正文 Tim Cook unveiled iPhone 15. Analysts are optimistic.
[P] entities [E] person [E] company [E] product [SEP_STRUCT]
[P] sentiment [L] positive [L] negative [L] neutral [SEP_TEXT]
tim cook unveiled iphone 15 analysts are optimistic
(真实字符串会再经 HF tokenizer 切成 subword;特殊 token 会进词表。)
2. 特殊 token:任务侧的「锚点」
| 库内 / 论文 | 出现在哪 | 模型拿来干什么 |
|---|---|---|
[P] | 每个子任务开头 | 结构化/关系:count 头读它的 hidden state,预测有几个实例(0–19) |
[E] | NER 每个类型前 | 该位置 hidden → 实体类型向量,去和正文 span 打分 |
[L] | 分类每个标签前 | 该位置 hidden → 标签向量,过 classifier MLP 得 logit |
[C] | 结构化每个字段前 | 字段向量;配合 count_embed 变成「第 k 个实例的第 j 个字段」 |
[R] | 关系每个槽位前 | 与 [C] 类似,常见 head / tail 两槽,走同一套 span 匹配 |
这些 token 会 add_special_tokens 进 DeBERTa 词表,和 encoder 一起训。
3. NER:在正文「词」上滑窗,和每个 [E] 比相似度
输入示例
- Schema:
person,company,product - 正文:
Apple CEO Tim Cook announced iPhone 15.
做法(推理时,engine.py → _extract_entities)
- 正文先被
WhitespaceTokenSplitter切成 词(apple,ceo,tim,cook, …),每个词再切成 subword,聚成 一词一向量(默认取该词第一个 subword 的 hidden,token_pooling="first")。 compute_span_rep在所有 连续词片段 上滑窗,宽度 ≤max_width(配置里多为 8 个词),得到每个候选 span 的向量(SpanRepLayer,来自 GLiNER 家族的markerV0)。- 每个
[E] person/[E] company… 在 encoder 输出里取对应位置的 hidden,当作 类型向量。 - 算
sigmoid(span_emb · type_emb)(实现里是einsum批量点积),超过threshold的 span 保留。 - 用
start_mapping/end_mapping把 词级 span 映回 字符 start/end(include_spans=True时)。
这一条例子里期望发生什么(零样本,不保证全对)
| 类型 | 可能抽到的 span | 对应词级片段 |
|---|---|---|
person | Tim Cook | tim + cook(连续两词) |
company | Apple | apple |
product | iPhone 15 | iphone + 15 |
NER 没有「先找 mention 再分类」的两阶段流水线,而是 所有候选 span 同时对每种类型打分;同一 span 理论上可多种类型都过阈值(靠描述和训练压这种情况)。
4. 文本分类:每个 [L] 一个 logit,不碰 span
输入示例
- 任务
sentiment,标签positive/negative/neutral - 正文:
This laptop has amazing performance but terrible battery life.
做法(engine.py → _extract_classification_result)
- encoder 跑完后,在 schema 段找到每个
[L]后面的标签名位置,取出 hidden →cls_embeds。 - 共享的
classifier(model.py里两层 MLP,hidden → 2×hidden → 1)对每个标签出一个 标量 logit。 - 单标签:softmax,取 argmax(上例可能偏
negative)。 - 多标签:sigmoid,保留 ≥
cls_threshold的标签(可多个)。
分类 只看整段正文在 encoder 里的上下文,不要求标签对应某个字符 span;所以中文连续无空格时,分类往往比 NER 稳(见下文「中文与 span」)。
5. 结构化抽取:先数有几「行」,再每行填列
输入示例
- Structure
product,字段name::str,price - 正文:
iPhone 15 costs $999. Galaxy costs $899.
序列形状(概念)
[P] product [C] name [C] price [SEP_TEXT] iphone 15 costs $999 galaxy costs $899
做法(三步,论文 Appendix + model.py / engine.py)
- 数实例:
count_pred(MLP,[P]hidden → 20 类)预测 K,例如 K=2。推理用 argmax;训练用 gold count 的交叉熵。 - 造 K 套字段向量:
count_embed(layers.py里CountLSTM/CountLSTMv2等)把每个[C]的 embedding 扩展成 K 份——第 1 份偏向「第一个 product」,第 2 份偏向「第二个」,以此类推,靠 GRU + 位置 embedding 区分实例,而不是靠 decoder 逐字段生成。 - 每字段对齐 span:对第 k 个实例的第 j 个字段,用与 NER 相同的
span_rep和字段向量点积,在正文词上找最高分片段 →name≈iPhone 15/Galaxy,price≈$999/$899。
字段带 choices(枚举)时,在推理侧当成分类子问题处理(cls_fields 映射),仍落在 span/标签打分框架里。
6. 关系抽取:同一套 span 机,两个(或多个)槽位填 head/tail
输入示例
- 关系
works_for - 正文:
John works for Apple Inc.
序列形状
[P] works_for [R] head [R] tail [SEP_TEXT] john works for apple inc
做法
count_pred预测有几条关系实例(常为 1)。count_embed为head、tail各生成实例 k 下的向量。- 分别在正文 span 上为
head、tail找最佳片段 →('John', 'Apple Inc.')。 _extract_relations把两槽拼成元组;include_spans时 head/tail 各带字符坐标。
关系在实现里 不是 单独的图神经网络,而是 多槽位的结构化 span 匹配。
7. 多任务组合:多个 schema 段 + 一次 encoder
create_schema().entities(...).classification(...).structure(...) 会在 schema_tokens_list 里得到 多段 prompt,中间用 [SEP_STRUCT] 连接,正文只出现 一次。
encoder 只对 input_idsforward 一次;extract_embeddings_from_batch 再按位置切出:
- 正文各 词 的向量列表 → 共用一份
span_rep; - 每个子任务 schema 上
[P]/[E]/[L]/[C]/[R]的向量 → 分别走分类头或 span 匹配。
因此组合任务的算力 ≈ 一次编码 + 多个轻量头,而不是「任务数个完整模型」。
代码里对应什么:从 schema 到字典结果
整体可以看成 五条流水线(没有独立「解码器」模块,解码 = 在候选 span / 标签上取 argmax 或阈值过滤):
模块对照表
| 阶段 | 文件 / 类 | 做什么 |
|---|---|---|
| Schema 构建 | inference/schema.py → Schema | 链式 .entities().classification().structure(),产出 JSON 形任务声明 |
| 词切分 | processor.py → WhitespaceTokenSplitter | 正则切词 + 字符起止;中文连续串易并成一词(见「中文与 span」) |
| 序列拼装 | SchemaTransformer._format_input_with_mapping | schema + [SEP_TEXT] + 正文词 → subword → input_ids |
| 批处理 | PreprocessedBatch / collate | padding、text_word_indices 供 fast gather |
| 编码器 | Extractor.encoder | AutoModel,默认 microsoft/deberta-v3-base;可选 FlashDeberta |
| 词级特征 | extract_embeddings_from_batch | 从 subword hidden 汇聚到词(first/mean/max) |
| Schema 特征 | 同上,读 schema_special_indices | 每个 [P][E][L][C][R] 处的 hidden |
| Span 特征 | compute_span_rep + SpanRepLayer | 词序列上所有长度 ≤8 的片段 → (num_spans, hidden) |
| 分类头 | Extractor.classifier | 每个标签 hidden → 1 维 logit |
| 计数头 | Extractor.count_pred | [P] hidden → 20 类(实例数 0–19) |
| 多实例展开 | layers.CountLSTM* → count_embed | 字段 embedding × 预测个数 K → (K, num_fields, hidden) |
| 训练损失 | forward → _compute_sample_loss | 分类 BCE;结构 BCE on span 网格;count CE |
| 推理入口 | inference/engine.py → GLiNER2 | extract / batch_extract 调 _extract_from_batch |
| 后处理 | RegexValidator(可选) | 在 span 已抽出后做正则过滤(见「Schema 与 RegexValidator」) |
分词与「特征」两层含义
第一层:词(word) ——WhitespaceTokenSplitter 产出,是 NER/结构/关系的 span 单位。max_len 限制的是词数。
第二层:子词(subword) ——DeBERTa tokenizer.tokenize(每个词),encoder 实际吃的是 subword 序列。text_word_first_positions 记录「这个词的第一个 subword 在序列里的下标」,后面用 gather 一次取出各词代表向量。
因此:不是 jieba 之类的分词;中文要自己预处理时,应在进模型 之前 用空格把词隔开,且训推一致(见「中文与 span」)。
Span 怎么从 hidden 变出来(compute_span_rep)
对长度为 T 的词序列,枚举所有 (start, end) 且 end - start < max_width 的片段;SpanRepLayer 把片段内各词向量合成 一个 span 向量(GLiNER markerV0:以边界词为主的 MLP 组合,不引入跨样本注意力)。
训练时 compute_struct_loss 在 (实例, 字段, start, width) 四维网格上打 BCE;推理时 _find_spans 在得分张量上找峰值并映射回字符串。
「解码器」在本项目里指什么
| 常见含义 | GLiNER2 有没有 |
|---|---|
| 自回归 Transformer decoder(GPT 式) | 无 |
| 把 hidden 变成 JSON 字符串 | 无,直接出 Python dict |
| 分类 softmax / 多标签 sigmoid | 有,classifier + _extract_classification_result |
| Span 解码(选 start/width) | 有,_find_spans + 字符映射 |
走读示例:一次 extract_entities 调用了谁
extractor.extract_entities(text, ["company", "person"])
engine.py:create_schema().entities(...)→extract→batch_extract(batch_size=1)processor.transform_and_format:schema →schema_tokens_list;正文 →text_tokens+input_idsmodel._encode_batch:encoder(input_ids)→extract_embeddings_from_batchcompute_span_rep(token_embs):正文词向量 →span_info_extract_span_result:count_pred对 entities 常预测 ≥1;对每个[E]类型跑 span 打分_format_spans:组装{'entities': {'company': [...], 'person': [...]}}
训练时同一条 input_ids 走 Extractor.forward(PreprocessedBatch),用 gold 标注算 classification_loss / structure_loss / count_loss 之和。
用户 API 与核心类的关系
GLiNER2(engine.py):继承Extractor(model.py),对外暴露extract_*、batch_*、load_adapter。SchemaTransformer(processor.py):训练/推理共用的「schema → 张量」编译器;Extractor构造时挂self.processor,编码后由 processor 负责 拆词向量 / schema 向量。
能做什么(按任务说)
论文和 README 对齐的能力可以收成四块;关系抽取在库里有 API,训练数据里也有,但论文 zero-shot 基准主要报的是 分类 + NER(层级结构在论文里写明缺标准 zero-shot benchmark,未做同口径横评)。
- 命名实体识别(NER)
标签可以只是一串字符串,也可以是{类型: "自然语言描述"},用描述收窄语义(医疗、法务场景常用)。可选返回 置信度、字符级 span。 - 文本分类
单标签或多标签;多标签时调cls_threshold。多个分类任务可以在一次extract里并列声明。 - 层级 / 结构化抽取
用字段语法字段名::类型::描述,类型str或list;可用[选项1|选项2]做枚举约束。适合「一张表多行」「一个公司块 + 多个产品块」这类半结构化输出。 - 关系抽取
输出(head, tail)元组列表,关系名可带描述。README 示例:works_for、located_in等。 - 组合
create_schema()链式挂上 entity、classification、structure、relations,一次extract(text, schema)返回多键结果。适合日志解析、工单分拣、合规字段预审等 字段集合固定 的场景。
怎么用(最短路径)
安装
# 不跑本地模型:schema 校验、训练数据工具、云 API 客户端
pip install gliner2
# 本地推理 / 训练(会拉 torch、transformers)
pip install gliner2[local]
gliner2/__init__.py 对 GLiNER2 做了懒加载:没装 [local] 时 import 不会立刻拖进 torch。
本地推理
from gliner2 import GLiNER2
extractor = GLiNER2.from_pretrained("fastino/gliner2-base-v1")
text = "Apple CEO Tim Cook announced iPhone 15 in Cupertino yesterday."
result = extractor.extract_entities(
text,
["company", "person", "product", "location"],
)
# {'entities': {'company': ['Apple'], 'person': ['Tim Cook'], ...}}
带描述、置信度、span 的参数见仓库 tutorial/ 下各篇;批量场景用 batch_extract_entities / batch_extract,可开 num_workers 做预处理并行。
Schema 组合(多任务一次调用)
schema = (
extractor.create_schema()
.entities(["person", "organization"])
.classification("sentiment", ["positive", "negative", "neutral"])
)
result = extractor.extract(text, schema)
结构化字段用 extract_json 或 schema builder 的 structure 段;关系用 relations(...)。
云 API
GLiNER2.from_api() 走 Pioneer 托管的大模型(如 XL 1B),适合本机不想下权重、也没有 GPU 的情况;和「205M 本地小模型」是不同部署选项,不是替代关系。
微调
仓库带 training/ 与 GLiNER2Trainer(tutorial/9-training.md)。垂直场景建议 全量微调 base 权重,让 encoder 与任务头一起对齐域内标注;论文训练集约 25 万 条,混合真实文档与 GPT-4o 标注/合成数据,多任务一起训。仓库也支持 LoRA 挂多域 adapter(tutorial/10-lora_adapters.md),本篇不展开。
Schema:接口怎么写
Schema 会直接编成输入里的 Task Prompt。改标签名、改字段,等于改这次 forward 在找什么。Schema 类不依赖 torch,可在没装 [local] 的机器上先写好、校验。
三种写法,选一种主路径:
extractor.extract_entities(text, ["person", "company"])
extractor.classify_text(text, {"sentiment": ["positive", "negative", "neutral"]})
extractor.extract_json(text, {"contact": ["name::str", "email::str", "phone"]})
schema = (
extractor.create_schema()
.entities({"person": "人名", "organization": "机构名"})
.classification("sentiment", ["positive", "negative", "neutral"])
.structure("contact")
.field("name", dtype="str")
.field("email", dtype="str")
.field("phone")
.relations(["works_for"])
)
result = extractor.extract(text, schema)
字典 / JSON 用 Schema.from_dict() / from_json(),适合配置中心;to_dict() 导出,与模型权重 同版本发布。
结构化字段串:字段名::类型::描述,或 字段名::[选项A|选项B]::类型。str 单值,list 可多段,choices 做枚举。同一 structure 多实例:训练 JSONL 里用 多个同名 parent 的列表,每个 dict 一行实例。
组合任务共享正文编码,建议从最小 schema 起,指标够了再加字段。include_confidence / include_spans 对组合调用全局生效;批量用 batch_extract(texts, schema, batch_size=..., num_workers=...),num_workers 只并行预处理。
RegexValidator
挂在 structure 字段上,span 抽出 之后 做正则过滤,不训练进网络:
from gliner2 import RegexValidator
email_v = RegexValidator(r"^[\w.-]+@[\w.-]+.\w+$")
schema = (
extractor.create_schema()
.structure("contact")
.field("email", dtype="str", validators=[email_v])
)
适用邮箱、电话、编号形态;不能替代 Luhn、身份证校验位等业务规则。
中文与 span
NER、结构、关系都要字符级 span。库内 WhitespaceTokenSplitter 先切 词,再 HF tokenizer 切 子词;span 在词上滑窗,再映回字符坐标。batch_extract 的 max_len 限制 词数,超出静默丢弃尾部。
连续汉字 无空格 时,正则常把整段并成一个「词」,词级 span 很难标人名/地名。分类受影响小,NER / structure / 关系 / 中文 PII 受影响大。
import jieba
from gliner2 import GLiNER2
extractor = GLiNER2.from_pretrained("fastino/gliner2-multi-v1")
def zh_for_gliner(text: str) -> str:
return " ".join(jieba.cut(text))
spaced = zh_for_gliner("张三在北京的阿里巴巴工作")
result = extractor.extract_entities(spaced, ["人名", "地名", "机构"])
gliner2-multi-v1(mDeBERTa)对中文子词更合适,但切词器逻辑未变,仍建议预分词。include_spans 坐标相对 传入字符串(含空格);JSONL 的 input 须与推理用同一套分词,标注写在 spaced 文本上。字级插空格是权宜:每字一词,但 span 窗口约 8 词,长机构名吃力。
PII 场景
GLiNER2 做 PII 是 可声明类型的 span 检测,典型链路:
原文 → GLiNER2(PII schema)→ 掩码/替换/审计 → 存储或 LLM
适合姓名、电话、邮箱、证件号、银行卡片段、地址等类型相对固定;数据不出内网、CPU 批量扫;与规则引擎并存(模型候选 + 正则确认)。
pii_schema = extractor.create_schema().entities({
"person_name": "自然人姓名,含中文与英文名",
"phone": "手机号或座机",
"email": "电子邮件",
"id_number": "身份证、护照等证件号",
"bank_card": "银行卡或信用卡号",
"address": "可定位到个人的详细地址",
})
零样本能跑,合规口径(什么算地址、是否含公司名)往往要靠 描述 + 少量全量微调。Presidio 等可把 GLiNER2 当自定义 NER 后端,业务侧把 extract 转成 (start, end, type);仓库不带 Presidio 适配器。
不是 DLP 全套;长文超 max_len 须分段;多数 PII 管线只需 NER + 正则,不必四类任务全开。
微调:JSONL 与全量训练
零样本不够时,用 JSONL + GLiNER2Trainer全量微调(encoder 与任务头一起训)。一行一条:
{"input": "待分析正文", "output": { ... 标注 ... }}
output 常用键:entities、classifications、json_structures、relations,与推理 schema 语义对齐。关系标注硬约束:同一种关系名,全文实例字段名须与第一次出现一致(不能第一条 head/tail,后面改成 from/to)。json_structures 多实例用列表里多个同名 parent;允许某类为空,但不能所有任务都空。
from gliner2.training.data import InputExample
from gliner2.training.trainer import GLiNER2Trainer, TrainingConfig
examples = [
InputExample(
text="John works at Google in California.",
entities={"person": ["John"], "company": ["Google"], "location": ["California"]},
),
]
model = GLiNER2.from_pretrained("fastino/gliner2-base-v1")
config = TrainingConfig(
output_dir="./output",
num_epochs=10,
batch_size=8,
encoder_lr=1e-5,
task_lr=5e-4,
)
GLiNER2Trainer(model, config).train(train_data=examples)
# trainer.train(train_data="train.jsonl")
中文微调:input 用与推理相同的预分词;entities 字符串须能在 input 中字面找到。建议顺序:10~20 条零样本 → 难字段加描述或 RegexValidator → 几百~几千条 JSONL 全量微调 → holdout 对比。
批量推理与上线
大队列别逐条 extract 硬循环:
results = extractor.batch_extract(
texts,
schema,
batch_size=32,
num_workers=4,
threshold=0.5,
include_confidence=True,
)
batch_size 按显存/CPU 调;max_len 超出静默截断,长文须分段并处理 span 偏移。GPU 可试 quantize=True、compile=True;CPU 生产以 base-v1 + 合理 batch 为主。论文 CPU 分类约百毫秒级/次(标签 5~50),实测为准。
| 部署 | 适合 |
|---|---|
from_pretrained 本地 | 不出网、可控延迟、schema 固定 |
from_api() | 不想管权重与机器 |
| 全量微调权重 | 单域垂直、标注口径稳定 |
在 RAG / Agent 里放哪
GLiNER2 管「从文本里抠字段」;LLM 管「用字段和上下文做决策」。常见落点:
原文/用户消息 → 分段 → GLiNER2(PII 或元数据)→ 掩码或打标 → 索引 / 再送 LLM
入库前扫 PII;RAG chunk 打实体、时间、产品名做过滤或重排;工单用分类路由;工具返回的大段文本用固定 structure schema 抽 JSON 再进 LLM。不适合替代检索、规划,或单次超长上下文(须分段)。
闭集、高频、要 span → GLiNER2;开放域偶尔抽 → LLM;不要让 LLM 再抽一遍已确定的字段。
论文里的效果与效率(怎么读这些数)
分类(Table 2) :七个公开集上,GLiNER2(205M)平均准确率 0.72,高于 GLiClass(0.63)和 DeBERTa-v3 zero-shot(0.69),低于 GPT-4o(0.84)。意图类(如 Banking77)涨得明显;个别集仍略输 DeBERTa。
NER(Table 3,CrossNER) :平均 F1 0.590,接近 GPT-4o(0.599),略低于专注 NER 的 GLiNER-M(0.615)——论文的解释是:GLiNER2 是多任务通用模型,NER 略让一点算预期内的 trade-off。
延迟(Table 4,CPU 分类) :标签数从 5 增到 50,GLiNER2 大约 130–208 ms 量级;DeBERTa 按「每标签一次 forward」线性涨,20 标签时论文报 约 6.8× 更慢。GPT-4o 走 API,同一表里大约 2.6× 慢于 GLiNER2/GLiClass 的 CPU 路径。
读表时注意三点:零样本设定(不是你在域内微调后的上限);上下文 论文对比里 GLiNER2 为 2048 tokens(初代 GLiNER 512);层级结构 缺统一公开基准,别拿「没表」当「一定更强或更弱」。
LLM 时代为什么还要 GLiNER2(不是二选一)
大模型用提示词也能抽字段,很多团队已经这么干了。GLiNER2 存在的理由,论文和工程实践里其实是 分工,不是「谁取代谁」:
- 成本和时延
高 QPS、短文本、标签集合固定的抽取(客服意图、PII 扫描、日志字段),用小 encoder 在 CPU 上跑,单条毫秒级、无按 token 计费,和「每条都调 7B+ API」账不一样。 - 数据不出域
医疗、金融、政务常见要求是 原文不出内网。205M 模型本地加载即可推理;论文 Table 1 把「CPU 部署 / 无 API 费用 / 隐私」和 7B 级开放 LLM 的差异写得很直白。 - 输出形态稳定
LLM 抽取往往要再解析 JSON、修格式、对 hallucination 做护栏。GLiNER2 的输出是 schema 绑定的字典结构(实体列表、标签、字段槽位),适合下游直接进规则引擎、检索索引或数据库列——不是万能,但在「字段 closed-set」任务上路径更短。 - 和 LLM 配合,而不是对立
常见拆法:GLiNER2 做第一道结构化(实体、分类、固定槽位),LLM 做开放推理、长文档摘要、需要世界知识的步骤。 - GLiNER 生态的收口
以前 NER / 分类 / 关系可能要三套权重;GLiNER2 用一套接口和一次 forward 组合,运维面更简单——这是产品向的「独特之处」,不是声称全面超越 GPT-4o。
不适合硬上的情况也要说清楚:开放式抽取、schema 每周大变、超长文档、强推理型 IE(需要多跳阅读与世界知识),仍更适合 LLM 或专门 pipeline;GLiNER2 的强项是 声明式 schema + 高效 encoder + 本地可部署。
和相近方案比一眼(避免捧杀)
| 路线 | 特点 | 和 GLiNER2 的差别(简述) |
|---|---|---|
| spaCy / 传统 NLP | 成熟、快,标签多为训练时固定 | 零样本换标签能力弱,多任务要多套模型 |
| LLM 提示词抽取 | 灵活、懂开放域 | GPU/API 成本、格式与幻觉要额外治理 |
| 初代 GLiNER | NER 强、CPU 友好 | 只有 NER;上下文 512 |
| GLiNER2 | 多任务 + schema 组合 | 参数 ~205M–340M;NER 专模略强仍可能,但省管线 |
读源码时建议看的文件
| 路径 | 内容 |
|---|---|
gliner2/inference/engine.py | 对外 GLiNER2、batch 推理、create_schema |
gliner2/inference/schema.py | Schema 构建与校验 |
gliner2/processor.py | SchemaTransformer、prompt 编解码、PreprocessedBatch |
gliner2/model.py | Extractor 网络结构、loss、forward |
gliner2/layers.py | count / span 相关层 |
tutorial/*.md | 官方按任务拆的用法说明 |
gliner2/api_client.py | 云端 API 客户端 |
包版本以仓库 __init__.py 的 __version__ 为准(撰写时看到为 1.3.1)。
收束
GLiNER2 是 Encoder + 任务头:schema 与正文一次编码,分类走 logit,NER/结构/关系走词级 span 匹配,没有自回归 decoder,直接出 Python dict。闭集字段、要本地跑、要稳定 JSON 结构时值得试;开放域和长推理仍靠 LLM。中文 NER 依赖 multi-v1 与训推一致的预分词;PII 是 NER + 规则复核;垂直场景用 JSONL 全量微调比裸零样本稳得多。
字段名基本固定、量大、要低延迟或不能出网,可先装 gliner2[local] 用十几条业务句跑 schema;开放式理解长报告,GLiNER2 更适合前置过滤器,而不是唯一大脑。
信源
- Zaratiana, U., Pasternak, G., Hurn-Maloney, O. B. G., & Lewis, A. GLiNER2: An Efficient Multi-Task Information Extraction System with Schema-Driven Interface. arXiv:2507.18546, 2025.
- 代码仓库:github.com/fastino-ai/GLiNER2