请求将Prodigy JSONL/.spacy格式转换为CONLL格式
从Prodigy JSONL/.spacy转换为CONLL格式(适配Inception)
因为没有现成的转换工具适配你的关系分类标注结构,直接用Python脚本处理是最灵活的方案。以下脚本支持从Prodigy导出的JSONL文件或.spacy文件转换为兼容Inception的CONLL-U格式,同时保留实体标注(BIO格式)和语义关系标注。
核心思路
- 实体标注转换:将Prodigy的
spans字段转换为CONLL标准的BIO标签 - 关系标注转换:将Prodigy的
relations映射为CONLL-U中的head(父token ID)和deprel(关系标签) - 格式对齐:处理Prodigy token ID从0开始、CONLL-U ID从1开始的差异,补全CONLL-U所需的默认字段(如lemma、词性等,无数据则填
_)
完整转换脚本
import json import spacy from spacy.tokens import DocBin def process_prodigy_jsonl(input_path, output_path): with open(input_path, 'r', encoding='utf-8') as f_in, open(output_path, 'w', encoding='utf-8') as f_out: for line in f_in: if not line.strip(): continue item = json.loads(line) tokens = item['tokens'] spans = item.get('spans', []) relations = item.get('relations', []) # 初始化BIO标签 bio_tags = ['O'] * len(tokens) for span in spans: start_idx = span['token_start'] end_idx = span['token_end'] label = span['label'] bio_tags[start_idx] = f'B-{label}' for i in range(start_idx + 1, end_idx + 1): bio_tags[i] = f'I-{label}' # 初始化head和deprel,默认head为0(根节点),deprel为'root' heads = [0] * len(tokens) deprels = ['root'] * len(tokens) for rel in relations: # Prodigy token ID从0开始,CONLL-U从1开始,所以head和child都要+1 head_idx = rel['head'] child_idx = rel['child'] rel_label = rel['label'] heads[child_idx] = head_idx + 1 # CONLL-U的head是父节点的ID(从1开始) deprels[child_idx] = rel_label # 生成CONLL-U格式的每一行 for idx, token in enumerate(tokens): # CONLL-U字段:ID, FORM, LEMMA, UPOS, XPOS, FEATS, HEAD, DEPREL, DEPS, MISC token_id = idx + 1 form = token['text'] lemma = '_' # Prodigy标注无lemma,留空 upos = '_' # 无词性标注,留空 xpos = '_' feats = '_' head = heads[idx] deprel = deprels[idx] deps = '_' misc = f'Start={token["start"]}|End={token["end"]}' if 'start' in token else '_' line_parts = [str(token_id), form, lemma, upos, xpos, feats, str(head), deprel, deps, misc] f_out.write('\t'.join(line_parts) + '\n') # 样本间用空行分隔 f_out.write('\n') def process_spacy_file(input_path, output_path): nlp = spacy.blank('en') # 用空白模型加载DocBin doc_bin = DocBin().from_disk(input_path) docs = list(doc_bin.get_docs(nlp.vocab)) with open(output_path, 'w', encoding='utf-8') as f_out: for doc in docs: # 提取实体信息生成BIO标签 bio_tags = ['O'] * len(doc) for ent in doc.ents: start_idx = ent.start end_idx = ent.end - 1 # spacy的end是排他的,所以减1 label = ent.label_ bio_tags[start_idx] = f'B-{label}' for i in range(start_idx + 1, end_idx + 1): bio_tags[i] = f'I-{label}' # 提取关系信息(Prodigy导出的.spacy中,关系存储在doc.user_data['relations']) heads = [0] * len(doc) deprels = ['root'] * len(doc) relations = doc.user_data.get('relations', []) for rel in relations: head_idx = rel['head'] child_idx = rel['child'] rel_label = rel['label'] heads[child_idx] = head_idx + 1 deprels[child_idx] = rel_label # 生成CONLL-U行 for idx, token in enumerate(doc): token_id = idx + 1 form = token.text lemma = token.lemma_ if token.lemma_ != '-PRON-' else '_' upos = token.pos_ if token.pos_ else '_' xpos = token.tag_ if token.tag_ else '_' feats = token.morph if token.morph else '_' head = heads[idx] deprel = deprels[idx] deps = '_' misc = f'Start={token.idx}|End={token.idx + len(token)}' line_parts = [str(token_id), form, lemma, upos, xpos, str(feats), str(head), deprel, deps, misc] f_out.write('\t'.join(line_parts) + '\n') f_out.write('\n') # 使用示例 if __name__ == '__main__': # 转换JSONL到CONLL process_prodigy_jsonl('input_data.jsonl', 'output_conll.conllu') # 转换.spacy到CONLL # process_spacy_file('input_data.spacy', 'output_conll.conllu')
关键说明
- 实体BIO标注:遍历每个
span,对起始token标记B-<标签>,后续token标记I-<标签>,未标注的token为O - 关系映射:Prodigy的
head是父token的索引(0开始),转换为CONLL-U时要+1(CONLL-U ID从1开始),并将关系标签赋值给子token的deprel字段 - CONLL-U字段补全:没有的标注信息(如lemma、词性)用
_填充,兼容Inception的导入要求 - .spacy处理:Prodigy导出的.spacy文件中,关系会存在
doc.user_data['relations'],实体则存储在doc.ents,脚本会自动提取这些信息
适配Inception导入
生成的.conllu文件可以直接导入Inception:
- 进入Inception的项目,选择Import
- 选择Format: CONLL-U,上传生成的文件即可完成导入
内容的提问来源于stack exchange,提问作者Jonnyfoka
相关产品推荐
相关产品推荐

