fix: derive KLUE NER labels from dataset, not hardcoded order
KLUE NER 의 라벨 id 순서는 B-DT(0) … I-TI(11), O(12) 로 'O' 가 맨 뒤다. 스크립트는 ["O"] + [B-/I- ...] 로 재구성해 써서 전체가 한 칸씩 밀렸다. tokenize_and_align 은 데이터셋 id 를 그대로 넘기는데 build_metrics 가 어긋난 순서로 해석하면서, 실제 O 구간이 I-TI 엔티티로 집계됐다. 영향 — micro support 14,257(실제 엔티티 수) → 67,056 으로 부풀고 F1 도 함께 올랐다. 학습된 klue_ner_large 를 올바른 매핑으로 재평가하면 0.8123 이며 보고돼 온 0.8750 이 아니다. 전 차수 성적서의 support 62,760 (max_length 절단분 차이) 도 같은 상태였다. 모델 자체는 데이터셋 id 공간에서 일관되게 학습돼 재학습이 필요없다. 잘못된 것은 지표 해석뿐이다. 라벨 목록을 데이터셋에서 직접 가져와 유일한 출처로 삼는다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
be25b6c053
commit
37f61c1bea
@ -26,11 +26,22 @@ from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
# KLUE NER 태그 6종. BIO 로 12개 + O = 13 labels (전 차수와 동일).
|
||||
# KLUE NER 태그 6종. 성적서 게재 순서이며 출력 정렬에만 쓴다.
|
||||
TAGS = ["DT", "LC", "OG", "PS", "QT", "TI"]
|
||||
LABELS = ["O"] + [f"{p}-{t}" for t in TAGS for p in ("B", "I")]
|
||||
LABEL2ID = {label: i for i, label in enumerate(LABELS)}
|
||||
ID2LABEL = {i: label for label, i in LABEL2ID.items()}
|
||||
|
||||
|
||||
def label_names(dataset) -> list[str]:
|
||||
"""라벨 목록은 데이터셋에서 직접 가져온다. 코드에서 다시 만들지 않는다.
|
||||
|
||||
KLUE NER 의 id 순서는 B-DT(0) … I-TI(11), **O(12)** 로 'O' 가 맨 뒤다.
|
||||
이를 `["O"] + [B-/I- ...]` 로 재구성하면 전체가 한 칸씩 밀려, 실제 O 구간이
|
||||
I-TI 엔티티로 집계된다. 그러면 micro support 가 14,257(실제 엔티티 수) 에서
|
||||
67,056 으로 부풀고 F1 도 함께 올라간다(0.8123 → 0.8750). 전 차수 성적서의
|
||||
support 62,760 이 이 상태였다.
|
||||
|
||||
데이터셋을 유일한 출처로 삼아 그 경로를 막는다.
|
||||
"""
|
||||
return list(dataset.features["ner_tags"].feature.names)
|
||||
|
||||
|
||||
def tokenize_and_align(examples, tokenizer, max_length: int):
|
||||
@ -129,10 +140,15 @@ def main() -> int:
|
||||
print("학습 %d건 / 평가 %d건"
|
||||
% (len(dataset["train"]), len(dataset["validation"])))
|
||||
|
||||
labels = label_names(dataset["train"])
|
||||
label2id = {label: i for i, label in enumerate(labels)}
|
||||
id2label = {i: label for label, i in label2id.items()}
|
||||
print("라벨 %d종 (데이터셋 기준): %s" % (len(labels), ", ".join(labels)))
|
||||
|
||||
source = args.out_dir if args.eval_only else args.model
|
||||
tokenizer = AutoTokenizer.from_pretrained(str(source))
|
||||
model = AutoModelForTokenClassification.from_pretrained(
|
||||
str(source), num_labels=len(LABELS), id2label=ID2LABEL, label2id=LABEL2ID)
|
||||
str(source), num_labels=len(labels), id2label=id2label, label2id=label2id)
|
||||
|
||||
encoded = dataset.map(
|
||||
lambda batch: tokenize_and_align(batch, tokenizer, args.max_length),
|
||||
@ -156,7 +172,7 @@ def main() -> int:
|
||||
train_dataset=encoded["train"],
|
||||
eval_dataset=encoded["validation"],
|
||||
data_collator=DataCollatorForTokenClassification(tokenizer),
|
||||
compute_metrics=build_metrics(ID2LABEL),
|
||||
compute_metrics=build_metrics(id2label),
|
||||
)
|
||||
|
||||
if not args.eval_only:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user