You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请求将Prodigy JSONL/.spacy格式转换为CONLL格式

从Prodigy JSONL/.spacy转换为CONLL格式(适配Inception)

因为没有现成的转换工具适配你的关系分类标注结构,直接用Python脚本处理是最灵活的方案。以下脚本支持从Prodigy导出的JSONL文件或.spacy文件转换为兼容Inception的CONLL-U格式,同时保留实体标注(BIO格式)和语义关系标注。

核心思路

  1. 实体标注转换:将Prodigy的spans字段转换为CONLL标准的BIO标签
  2. 关系标注转换:将Prodigy的relations映射为CONLL-U中的head(父token ID)和deprel(关系标签)
  3. 格式对齐:处理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:

  1. 进入Inception的项目,选择Import
  2. 选择Format: CONLL-U,上传生成的文件即可完成导入

内容的提问来源于stack exchange,提问作者Jonnyfoka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 12:05:21