正确地使用Unicode撇号分词英语缩略词

4
如何修改默认的Spacy(v3.0.5)分词器,以便在使用unicode apostrophes(而不是')时正确拆分英语缩略词。
import spacy

nlp = spacy.load('en_core_web_sm')
apostrophes = ["'",'\u02B9', '\u02BB', '\u02BC', '\u02BD', '\u02C8', '\u02CA', '\u02CB', '\u0060', '\u00B4']
for apo in apostrophes:
    text = f"don{apo}t"
    print([t for t in nlp(text)])
>>> 
 [do, n't]
 [donʹt]
 [donʻt]
 [donʼt]
 [donʽt]
 [donˈt]
 [donˊt]
 [donˋt]
 [don`t]
 [don´t]

所有示例的期望输出是[do,n't] 我的最佳猜测是使用所有可能的撇号变体扩展默认的tokenizer_exceptions。但是,由于Tokenizer特殊情况不允许修改文本,因此这种方法不起作用。
import spacy 
from spacy.util import compile_prefix_regex, compile_suffix_regex, compile_infix_regex

nlp = spacy.load('en_core_web_sm')

apostrophes = ['\u02B9', '\u02BB', '\u02BC', '\u02BD', '\u02C8', '\u02CA', '\u02CB', '\u0060', '\u00B4']
default_rules = nlp.Defaults.tokenizer_exceptions
extended_rules = default_rules.copy()
for key, val in default_rules.items():
    if "'" in key:
        for apo in apostrophes:
            extended_rules[key.replace("'", apo)] = val

rules = nlp.Defaults.tokenizer_exceptions
infix_re = compile_infix_regex(nlp.Defaults.infixes)
prefix_re = compile_prefix_regex(nlp.Defaults.prefixes)
suffix_re = compile_suffix_regex(nlp.Defaults.suffixes)

nlp.tokenizer =  spacy.tokenizer.Tokenizer(
        nlp.vocab,
        rules = extended_rules,
        prefix_search=prefix_re.search,
        suffix_search=suffix_re.search,
        infix_finditer=infix_re.finditer,
    )
            
apostrophes = ["'",'\u02B9', '\u02BB', '\u02BC', '\u02BD', '\u02C8', '\u02CA', '\u02CB', '\u0060', '\u00B4']
for apo in apostrophes:
    text = f"don{apo}t"
    print([t for t in nlp(text)])

>>> ValueError: [E997] Tokenizer special cases are not allowed to modify the text. This would map ':`(' to ':'(' given token attributes '[{65: ":'("}]'.
1个回答

3

你只需要添加一个异常处理而不更改文本内容。

import spacy 

nlp = spacy.load('en_core_web_sm')

from spacy.attrs import ORTH, NORM
case = [{ORTH: "do"}, {ORTH: "n`t", NORM: "not"}]
tokenizer = nlp.tokenizer
tokenizer.add_special_case("don`t", case)

doc =  nlp("I don`t believe in bugs")

print(list(doc))
# => [I, do, n`t, believe, in, bugs]

如果您想更改文本,则应将其作为预处理步骤完成。

是的,这个方法可以工作。但是撇号不仅存在于单词“don't”中。它可能出现在任何地方,因此我需要写下所有可能出现的撇号。使用这种方法似乎需要做很多工作。 - gustavz
如果你想要更简单的方法,你可以使用正则表达式来替换奇怪的标点符号作为预处理步骤,类似于 n[<weird-apostrophes>]t。这样做不会保留原始输入,但这似乎并不重要? - polm23
1
是的,我现在已经实现了这个功能,作为一个完整的字符串预处理步骤,在使用分词器之前替换所有的撇号。 我本来希望将这个功能包含在分词器中,而不是有一个专门的额外预处理步骤,但显然这是解决它的最好方法。 - gustavz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接