Token Classification
Transformers
PyTorch
Chinese
named-entity-recognition
ner
ernie
crf
chinese-nlp
person-name-extraction
financial-documents
Instructions to use warfbro/Human-Name-extraction with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use warfbro/Human-Name-extraction with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="warfbro/Human-Name-extraction")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("warfbro/Human-Name-extraction", device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 6,869 Bytes
fa655ab | 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 | """
双模型集成人名提取
- 模型A (finetune_best.pt) + 模型B (frozen_best.pt) 分别提取
- 一致 → 直接输出
- 不一致 → LLM 裁定
用法: python ensemble_extract.py <input.xlsx> [output.xlsx]
"""
import sys
import torch
import openpyxl
import re
from config import BIO_ID2LABEL, BIO_LABELS
from model import ErnieCRF, ErnieCRF2
from config import ERNIE_LOCAL, CHECKPOINT, CHECKPOINT_FROZEN
def clean_names(names: list) -> list:
cleaned = []
for n in names:
n = re.sub(r"等\d*人?$", "", n)
if n.strip():
cleaned.append(n.strip())
return cleaned
def expand_bracket_names(names: list, title: str) -> list:
expanded = []
for name in names:
idx = title.find(name)
if idx == -1:
expanded.append(name)
continue
found = False
# 英文(中文)
if idx > 0 and title[idx - 1] == "(":
right = idx + len(name)
if right < len(title) and title[right] == ")":
left = idx - 2
if left >= 0 and re.match(r"[A-Za-z]", title[left]):
start = left
while start > 0 and re.match(r"[A-Za-z]", title[start - 1]):
start -= 1
found = True
expanded.append(title[start:right + 1])
# 中文(英文)
if not found:
right = idx + len(name)
if right < len(title) and title[right] == "(":
close = title.find(")", right)
if close != -1 and re.match(r"[A-Za-z]", title[right + 1:close]):
found = True
expanded.append(title[idx:close + 1])
if not found:
expanded.append(name)
# 去子串
deduped = []
for n in expanded:
if not any(n != other and n in other for other in expanded):
deduped.append(n)
return deduped
def postprocess(names, title):
names = clean_names(names)
names = expand_bracket_names(names, title)
names = [n for n in names if n in title]
return names
def model_extract(title, model, tokenizer, device):
chars = list(title)
ids = [tokenizer.cls_token_id]
for c in chars:
ids.extend(tokenizer.encode(c, add_special_tokens=False))
ids.append(tokenizer.sep_token_id)
input_ids = torch.tensor([ids], device=device)
mask = torch.ones_like(input_ids)
with torch.no_grad():
preds = model(input_ids, mask)[0]
preds = preds[1:1 + len(chars)]
names, cur = [], []
for char, lid in zip(title, preds):
tag = BIO_ID2LABEL.get(lid, "O")
if tag == "B-PER":
if cur:
names.append("".join(cur))
cur = [char]
elif tag == "I-PER" and cur:
cur.append(char)
else:
if cur:
names.append("".join(cur))
cur = []
if cur:
names.append("".join(cur))
return names
def load_models(device):
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(ERNIE_LOCAL)
model_a = ErnieCRF(ERNIE_LOCAL, len(BIO_LABELS)).to(device)
model_a.load_state_dict(torch.load(CHECKPOINT, map_location=device, weights_only=True))
model_a.eval()
model_b = ErnieCRF(ERNIE_LOCAL, len(BIO_LABELS)).to(device)
model_b.load_state_dict(torch.load(CHECKPOINT_FROZEN, map_location=device, weights_only=True))
model_b.eval()
return tokenizer, model_a, model_b
def main():
if len(sys.argv) < 2:
print("用法: python ensemble_extract.py <input.xlsx> [output.xlsx]")
sys.exit(1)
input_xlsx = sys.argv[1]
output_xlsx = sys.argv[2] if len(sys.argv) > 2 else "提取结果_集成.xlsx"
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"加载双模型 ({device})...")
tokenizer, model_a, model_b = load_models(device)
print(f"读取: {input_xlsx}")
wb = openpyxl.load_workbook(input_xlsx)
ws = wb.active
out_wb = openpyxl.Workbook()
out_ws = out_wb.active
out_ws.append(["A列:原数据", "B列:提取人名", "C列:方法", "D列:姓名字数"])
stats = {"一致": 0, "LLM裁定": 0, "未检出": 0}
disagreements = [] # (row_idx, title, names_a, names_b)
results_cache = {} # title → final_names
for row_idx, row in enumerate(ws.iter_rows(min_row=2, min_col=1, max_col=12, values_only=True)):
title = str(row[0]) if row[0] else ""
org = str(row[11]) if len(row) > 11 and row[11] else ""
if not title:
continue
clean_title = title
if org and org in title:
clean_title = title.replace(org, "").replace(" ", " ").strip()
# 双模型分别提取
raw_a = model_extract(clean_title, model_a, tokenizer, device)
raw_b = model_extract(clean_title, model_b, tokenizer, device)
names_a = postprocess(raw_a, title)
names_b = postprocess(raw_b, title)
name_str_a = "、".join(names_a) if names_a else ""
name_str_b = "、".join(names_b) if names_b else ""
if name_str_a == name_str_b:
# 一致,直接输出
stats["一致"] += 1
if not name_str_a:
stats["未检出"] += 1
name_len = "、".join(str(len(n)) for n in names_a) if names_a else "0"
out_ws.append([title, name_str_a, "一致", name_len])
else:
# 不一致,缓存等 LLM
disagreements.append((title, name_str_a, name_str_b))
print(f"一致: {stats['一致']} 条 (含未检出)")
print(f"不一致需LLM裁定: {len(disagreements)} 条")
if disagreements:
from llm_resolver import resolve_batch
print(f"\n开始LLM裁定...")
final_names = resolve_batch(disagreements)
for (title, na, nb), final in zip(disagreements, final_names):
stats["LLM裁定"] += 1
names = [n.strip() for n in final.split("、") if n.strip()] if final else []
# 后处理
names = [n for n in names if n in title]
name_str = "、".join(names) if names else ""
name_len = "、".join(str(len(n)) for n in names) if names else "0"
out_ws.append([title, name_str, "LLM裁定", name_len])
out_wb.save(output_xlsx)
print(f"\n=== 统计 ===")
print(f"一致: {stats['一致']} 条")
print(f"LLM裁定: {stats['LLM裁定']} 条")
print(f"未检出: {stats['未检出']} 条")
print(f"已保存: {output_xlsx}")
if __name__ == "__main__":
main()
|