무엇인가
GLiNER2는 스타트업 Fastino가 2025년 7월 공개한 스키마 기반 정보 추출 라이브러리입니다(EMNLP 2025 데모 논문). 뽑고 싶은 것의 이름을 ["company", "person", "product"]처럼 글자로 주면 재학습 없이 원문에서 그 조각을 집어 옵니다. 개체명·텍스트 분류·관계·구조화 레코드를 모델 하나가 한 번의 순방향 계산으로 처리하고, 답은 언제나 원문의 글자 위치입니다. 지어낸 말이 나올 수 없는 구조입니다.
뿌리는 2023년 GLiNER(NAACL 2024)의 "라벨도 입력이다"라는 발상입니다. 그전까지 개체명 모델은 라벨을 출력층의 고정된 칸으로 뒀기 때문에 라벨 하나를 더하려면 다시 학습해야 했습니다. GLiNER는 라벨 이름을 문장과 함께 인코더에 넣어 라벨과 단어 조각을 같은 공간에서 비교합니다. GLiNER2는 이것을 개체 밖으로 넓혀 분류·관계·레코드를 한 줄의 스키마로 묶었고, 2026년 8월에는 구조를 바꾼 GLiNER2.5(경계 쌍 제안 + 재정렬, 논문 없이 블로그로 발표)가 같은 라이브러리에 들어왔습니다. 체크포인트는 7,400만~3억 4천만 파라미터이고 Hugging Face fastino/ 아래에 있습니다.
어떻게 동작하나
( [P] 이름 ( [E] person [E] company ... ) ) [SEP_TEXT] 본문 단어
DeBERTa-v3 / mDeBERTa-v3, 라벨 토큰과 단어가 서로를 본다
분류는 [L] 점수, 추출은 라벨 벡터 · 조각 벡터 내적
| 단계 | GLiNER2 (스팬) | GLiNER2.5 (경계) |
|---|---|---|
| 후보 조각 | 시작 단어 × 폭(최대 8단어) 격자를 전부 채점 | 단어 사이 틈마다 시작·끝·안쪽 점수, 문서마다 후보 192개만 추림 |
| 점수 | 조각 벡터와 라벨 벡터의 내적 → 시그모이드 | 후보 하나씩 재정렬(내적 + 길이·경계 특징) |
| 레코드 개수 | [P] 토큰으로 개수(0~19)를 먼저 예측, CountLSTM이 레코드별 질의 생성 | 개수 예측 없이 레코드 헤드가 묶음, 학습은 헝가리안 매칭 |
| 비우기 | 임계값만 | 기권 헤드가 라벨을 통째로 닫는다 |
| 긴 이름 | 8단어 넘는 조각은 구조상 못 뽑는다 | 길이 제한 없음 |
라벨이 입력의 짧은 토큰 몇 개이므로 라벨이 늘어도 지연은 거의 그대로이고, 비용은 본문 길이에 비례합니다. 긴 문서는 *_long API가 384단어 창·64단어 겹침으로 나눠 읽고 위치를 원문 기준으로 되돌립니다. 2.5와 함께 들어온 Classifier(라벨 간 제약을 만족하는 조합 탐색)와 JointIE(개체·관계 타입 제약 빔 탐색)는 결과를 일관된 그래프로 맞춰 줍니다.
빠른 시작
pip install "gliner2[local]" # 로컬 추론 (PyTorch 포함). 기본 설치는 스키마·API 클라이언트만
pip install kiwipiepy # 한국어 조사 자르기용
from gliner2 import AutoExtractor
model = AutoExtractor.from_pretrained("fastino/gliner2-multi-v1") # 한국어는 구형 multi가 실측 우위
text = "삼성전자 이재용 회장이 어제 수원에서 갤럭시 S26을 공개했다."
schema = (model.create_schema()
.entities({"person": "사람 이름", "company": "회사·기관 이름", "location": "지명"})
.classification("sentiment", ["positive", "negative", "neutral"]))
result = model.extract(text, schema, include_spans=True) # text[start:end] == 결과
한국어는 공백 단위로 뽑히므로 '이재용 회장이', '수원에서'처럼 끝에 조사가 붙어 나옵니다. 뽑은 조각의 끝 형태소가 조사·어미면 잘라 냅니다.
from kiwipiepy import Kiwi
kiwi = Kiwi()
def trim_josa(span: str) -> str:
end = len(span)
for t in reversed(kiwi.tokenize(span)):
if t.tag.startswith(("J", "E", "VCP")) and t.start > 0:
end = min(end, t.start)
else:
break
return span[:end].rstrip()
trim_josa("수원에서") # '수원'
코어닷투데이 실측
2026년 11월 8일, Apple M1 Ultra. KLUE 개체명 검증셋에서 무작위로 고른 300문장(정답 863개, 인물·장소·기관·날짜·시간·수량)에 두 다국어 체크포인트를 제로샷으로 돌렸습니다. GLiNER는 PyTorch CPU 16스레드, 비교용 Qwen3.5는 같은 기계의 Ollama(GPU)에서 온도 0으로 돌렸습니다. 엄격 F1은 (시작, 끝, 유형)이 글자 단위로 같아야 맞습니다.
| 설정 (영어 라벨, 임계값 0.3) | gliner2-multi-v1 (스팬) | gliner2.5-multi-v1 (경계) |
|---|---|---|
| 공백 그대로 (기본값) | 34.9 | 29.9 |
| 공백 + 끝 조사 자르기 | 62.5 | 54.0 |
| 공백 + 자르기 + 라벨 설명 | 63.0 | 58.1 |
| Kiwi 형태소로 나눠 입력 | 55.5 | 53.5 |
| 음절 단위 (char) | 30.2 | 34.0 |
| 300문장 처리 시간 (CPU) | 9.4초 | 8.4초 |
- 조사가 점수를 갈랐습니다. 기본값의 엄격 F1은 34.9였지만, 끝에 조사 1~3글자가 붙은 것까지 봐주면 68.6이었습니다. 모델은 개체를 찾고 있었고 경계에 조사가 붙었을 뿐입니다. 후처리 한 줄로 +27.6점.
- 로컬 LLM과 비겼고 15배 빨랐습니다. Qwen3.5 9B는 61.0(4B는 47.4), 300문장에 141.3초. 9B의 답 810개 중 84개는 '오후 2 시', '박 해 일'처럼 띄어쓰기를 바꾼 복사라 위치를 다시 맞춰 채점했고, 원문에 아예 없는 말은 2개였습니다.
- 신형이 한국어에서는 구형보다 낮았습니다. 경계 모델은 수량에 약했고, 기권 헤드가 '어제'(날짜) 같은 라벨을 통째로 닫았습니다. Fastino 자체 표에서도 2.5의 다국어 추출 평균은 47.56→46.40으로 내려갔습니다.
- 라벨 설명은 뜻은 바꾸지만 경계 규칙은 못 바꿨습니다. 수량 설명은 효과가 컸고(스팬 60.7→63.7, 경계 46.0→56.5), "직함·호칭은 빼라"는 설명에도 인물은 '백 대표', '윤균상 씨'로 뽑혔습니다(61.3→60.0). 설명을 붙이면 처리 시간은 두 배가 됐습니다.
- 긴 이름은 경계 구조만 찾았습니다. 12단어 영문 법률명·13단어 영문 주소를 스팬 구조는 못 찾았고, 경계 구조는 '정보통신망 이용촉진 및 정보보호 등에 관한 법률'까지 0.997점으로 정확히 뽑았습니다.
- 라벨 수는 지연에 거의 영향이 없었습니다. 40단어 문장 하나에 라벨 1개 115ms, 32개 146ms. 본문 25단어→400단어는 110ms→509ms. 영어 전용
gliner2.5-small-v1은 300문장에 5.1초였습니다.
언제 쓰고, 언제 피하나
| 상황 | 구성 |
|---|---|
| 한국어 짧은 글의 개체명, 반출 불가 | gliner2-multi-v1 + 끝 조사 자르기, 임계값 0.3부터 |
| 한국어 법령·계약·공시 (긴 고유명) | gliner2.5-multi-v1 + 조사 자르기 또는 형태소 분할 |
| 대량 개인정보 가리기 | gliner2-privacy-filter-PII-multi + 정규식 검사기 + 필드별 임계값 |
| 영어, 속도 최우선 | gliner2.5-small-v1 |
| 라벨 체계가 고정되고 학습 데이터가 있음 | 위 모델 + LoRA 어댑터 미세조정 (경계 규칙 문제를 푸는 길) |
| 답이 원문에 없다 (계산·요약·추론·정규화) | GLiNER 단독은 피한다. 뽑은 뒤 규칙이나 LLM을 붙인다 |
한계와 주의
- 공식 지원 언어에 한국어가 없습니다.
gliner2-multi-v1모델 카드의 언어는 프랑스어·영어·스페인어·독일어·이탈리아어·포르투갈어 여섯 개이고, 논문·블로그에 한국어 평가도 없습니다. 위 실측은 KLUE 한 데이터셋의 주석 규칙(호칭 빼기 등)에 묶여 있으며 300문장은 작은 표본입니다. - 결과 단위가 공백 '단어'입니다. 한국어에서는 조사 후처리가 사실상 필수입니다. 음절(
char) 분할기는 중국어용이라 한국어에서는 오히려 떨어졌습니다. - 스팬 구조(GLiNER2)는 8단어 넘는 조각을 뽑지 못합니다. 형태소로 쪼개면 토큰 수가 늘어 이 벽에 더 빨리 닿습니다.
- 확신도는 순위로만 믿습니다. 임계값은 자기 데이터 100~300건으로 정합니다. 기본 0.5보다 0.3이 재현율에서 나은 경우가 많았습니다.
- README가 가리키는 구조 문서(
docs/boundary_architecture.md등)가 저장소에 없습니다. 2.5 구조는 코드(2.1.0)와 공개 체크포인트 설정이 사실상 유일한 1차 자료입니다. 공개 2.5 체크포인트는 문서 전체가 후보 풀 하나를 공유하는 설정(candidate_pool="shared")입니다. - 비슷한 이름에 주의합니다. 'GLiGuard'라는 가드레일 모델이 Fastino(arXiv 2605.07982)와 Hivetrace(arXiv 2605.05277) 두 곳에서 따로 나왔습니다.



