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

for循环break语句行为不符预期,CoNLL数据spaCy匹配出错

CoNLL2003 NER数据依赖解析匹配逻辑错误排查

我第一次提问,麻烦多包涵。我写了一段处理CoNLL2003 NER任务数据的代码,目的是把预分词的token拼接成字符串后传入spaCy做依赖解析,再给原数据添加依赖关系(dep)和中心词(head)列,写入新文件。

因为spaCy的分词结果和原数据不一致,我用嵌套循环从上次匹配的位置开始查找对应token。没加else分支时代码正常,但添加后,原本预期只有不匹配的token输出no_dep/no_head,后续匹配的token仍输出正确结果,实际却是所有后续token都输出no_dep/no_head。

原始代码

import pandas as pd
import spacy

df = pd.read_csv('../../../Data/conll2003.dev.conll', sep='\t', on_bad_lines='skip', header=None)

nlp = spacy.load('en_core_web_sm')
nlp.max_length = 1500000

all_tokens = []

for token in df[0]:
    all_tokens.append(str(token))

string = ' '.join(all_tokens)

doc = nlp(string)

token_tuples = tuple(enumerate(doc))

outfile = open('./conll2003.dev.syntax_corrupt.conll', 'w')
i = 0 ## initiate by looking at the first token in the doc
for x, token in enumerate(df[0]):
    for num, tok in token_tuples[i:]: ## we add this step to ensure that the for loop always looks from the last token that was a match, since doc is longer
        ## than df[0], otherwise it would at some point start looking from earlier tokens since spacy has more tokens and if there is an accidental match, it
        ## would provide the wrong dep and head
        if token == tok.text:
            i = num ## get the number from the token tuples as new starting point
            outfile.write(str(df[0][x]) + '\t' + str(df[1][x]) + '\t' + str(df[2][x]) + '\t' + str(df[3][x]) + '\t' + str(tok.dep_) + '\t' + str(tok.head.text) + '\n')
            break
        else:
            outfile.write(str(df[0][x]) + '\t' + str(df[1][x]) + '\t' + str(df[2][x]) + '\t' + str(df[3][x]) + '\t' + 'no_dep' + '\t' + 'no_head' + '\n')
            break
outfile.close()

输入输出示例

输入文件片段

LONDON  NNP B-NP    B-LOC
1996-08-30  CD  I-NP    O

West    NNP B-NP    B-MISC

无else分支时的输出

LONDON  NNP B-NP    B-LOC   nmod    Simmons
West    NNP B-NP    B-MISC  nmod    Indian

期望输出

LONDON  NNP B-NP    B-LOC   nmod    Simmons
1996-08-30      CD      I-NP    no_dep  no_head
West    NNP B-NP    B-MISC  nmod    Indian

实际输出

LONDON  NNP B-NP    B-LOC   no_dep  no_head
1996-08-30  CD  I-NP    O   no_dep  no_head
West    NNP B-NP    B-MISC  no_dep  no_head

(注:实际输出第一行原本有正确结果,问题从第二行开始)

错误原因

  • else分支的break逻辑错误:当第一个spaCy token和当前df token不匹配时,else分支直接break终止内层循环,且没有更新i的位置。这导致下一次处理df的下一个token时,依然从原来的i位置开始查找,而该位置的spaCy token和新的df token还是不匹配,持续触发else分支,最终所有后续结果都输出no_dep/no_head。
  • 匹配成功后i的更新错误:当前代码匹配成功后设置i = num,但num是当前spaCy token的索引,下一次循环会从同一个索引开始,导致重复匹配同一个spaCy token。正确的做法应该是i = num + 1,让下一次从下一个spaCy token开始查找。
  • 未处理spaCy分词拆分原token的情况:当spaCy将一个原df token拆分为多个token时,代码没有跳过这些拆分后的token,直接判定为不匹配,不符合实际需求。

修正后的代码

import pandas as pd
import spacy

df = pd.read_csv('../../../Data/conll2003.dev.conll', sep='\t', on_bad_lines='skip', header=None)

nlp = spacy.load('en_core_web_sm')
nlp.max_length = 1500000

all_tokens = [str(token) for token in df[0]]
string = ' '.join(all_tokens)
doc = nlp(string)

token_tuples = tuple(enumerate(doc))
outfile = open('./conll2003.dev.syntax_corrupt.conll', 'w')

i = 0  # 起始查找位置
doc_len = len(token_tuples)

for x, token in enumerate(df[0]):
    matched = False
    # 从当前i位置开始遍历spaCy的token
    while i < doc_len:
        num, tok = token_tuples[i]
        if token == tok.text:
            # 匹配成功,写入结果并更新查找位置
            out_line = f"{df[0][x]}\t{df[1][x]}\t{df[2][x]}\t{df[3][x]}\t{tok.dep_}\t{tok.head.text}\n"
            outfile.write(out_line)
            i = num + 1  # 下一次从下一个token开始查找
            matched = True
            break
        else:
            # spaCy拆分了原token,跳过当前spaCy token
            i += 1
    # 遍历完所有spaCy token都没匹配到,写入no_dep/no_head
    if not matched:
        out_line = f"{df[0][x]}\t{df[1][x]}\t{df[2][x]}\t{df[3][x]}\tno_dep\tno_head\n"
        outfile.write(out_line)

outfile.close()

代码说明

  • 把内层for循环改成while循环,方便灵活控制i的递增,处理spaCy拆分原token的情况。
  • 增加matched标记,只有遍历完所有剩余spaCy token都没匹配到,才写入no_dep/no_head。
  • 匹配成功后将i设为num + 1,确保下一次从下一个spaCy token开始查找,避免重复匹配。
  • 当spaCy token和原df token不匹配时,递增i跳过该token,继续往后查找可能的匹配项。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 07:50:28