SpaCy SpanCat模型训练成功但无预测结果求助
核心问题
训练SpanCat模型过程显示成功,但测试时doc.spans始终为空,同一批数据在NER模型上可正常生成预测。以下是具体问题排查和修复方案:
1. 数据转换逻辑错误(最可能原因)
你的转换函数中存在关键错误:将整个文档作为Span添加到了spans列表中,而非标注的实体span。
错误代码片段:
spans.append(Span(doc, 0, len(doc), label=label)) # 错误:添加全文档span
这会导致模型学习的目标是"将整个文本标注为某标签",而非识别具体的实体span。正确的做法应该是将char_span得到的实体span添加到spans列表中。
修复后的转换函数:
def convert_to_docbin(input, output_path="./train.spacy", lang='en'): """ Convert a pair of text annotations into DocBin then save """ nlp = spacy.blank(lang) db = DocBin() for text, annotations in input: doc = nlp(text) spans = [] for start, end, label in annotations['entities']: # 仅添加标注的实体span,而非全文档 span = doc.char_span(start, end, label=label) if span is not None: # 确保span有效,避免字符索引与token边界不匹配的问题 spans.append(span) # 创建SpanGroup并关联到doc.spans["sc"] doc.spans["sc"] = SpanGroup(doc, name="sc", spans=spans) db.add(doc) db.to_disk(output_path)
注意:添加
if span is not None的判断,避免因字符索引与token边界不匹配导致无效span,影响模型训练。
2. 配置文件潜在问题
(1)Span候选生成范围不足
你的spancat.suggester设置为sizes = [1,2,3],仅生成1-3个token长度的候选span。如果你的实体长度超过3个token,模型将无法生成对应的候选span,自然无法预测。
根据你的实体长度调整sizes,比如如果有更长的实体,改为:
[components.spancat.suggester] @misc = "spacy.ngram_suggester.v1" sizes = [1,2,3,4,5] # 覆盖实体可能的长度范围
(2)阈值设置过高
配置中threshold = 0.5,如果模型预测的置信度低于0.5,会被过滤掉。可以尝试降低阈值(比如0.3)测试:
[components.spancat] threshold = 0.3
(3)标签初始化路径问题
配置中[initialize.components.spancat.labels]的path = null,虽然训练时可能自动从数据中加载标签,但显式指定标签路径更稳妥。可以先从数据中提取所有标签保存为labels.txt,然后配置:
[initialize.components.spancat.labels] @readers = "spacy.read_labels.v1" path = "./labels.txt" require = true
3. 训练过程验证
检查训练日志中的spans_sc_f指标:
- 如果f1值接近0,说明模型根本没学到有效特征,大概率是数据转换错误导致的;
- 如果f1值正常,那问题可能出在测试阶段的阈值或候选生成范围。
测试代码优化
测试时可以直接查看doc.spans["sc"](而非整个doc.spans),同时打印置信度确认:
for text in df['text_cleaned']: doc = nlp(text) # 直接访问指定spans_key的结果 predicted_spans = doc.spans.get("sc", []) print(f"Text: {text}") print("Predicted spans:") for span in predicted_spans: print(f"- {span.text} ({span.label_}, confidence: {span.score:.2f})")
内容的提问来源于stack exchange,提问作者user21351798

