美好的一天,
我正在尝试对被标记化为单独标记的带连字符的单词进行后处理,而这些单词本应是单个标记。例如:
Example:
Sentence: "up-scaled"
Tokens: ['up', '-', 'scaled']
Expected: ['up-scaled']
目前,我的解决方案是使用匹配器:
matcher = Matcher(nlp.vocab)
pattern = [{'IS_ALPHA': True, 'IS_SPACE': False},
{'ORTH': '-'},
{'IS_ALPHA': True, 'IS_SPACE': False}]
matcher.add('HYPHENATED', None, pattern)
def quote_merger(doc):
# this will be called on the Doc object in the pipeline
matched_spans = []
matches = matcher(doc)
for match_id, start, end in matches:
span = doc[start:end]
matched_spans.append(span)
for span in matched_spans: # merge into one token after collecting all matches
span.merge()
#print(doc)
return doc
nlp.add_pipe(quote_merger, first=True) # add it right after the tokenizer
doc = nlp(text)
但是,这会导致以下预期问题:
Example 2:
Sentence: "I know I will be back - I had a very pleasant time"
Tokens: ['i', 'know', 'I', 'will', 'be', 'back - I', 'had', 'a', 'very', 'pleasant', 'time']
Expected: ['i', 'know', 'I', 'will', 'be', 'back', '-', 'I', 'had', 'a', 'very', 'pleasant', 'time']
有没有一种方法可以只处理由字符之间没有空格的连字符分隔的单词?这样像“up-scaled”这样的词将被匹配并组合成一个标记,而不是“.. back - I ..”
非常感谢
编辑: 我试过发布的解决方案:Why does spaCy not preserve intra-word-hyphens during tokenization like Stanford CoreNLP does?
但是,我没有使用这个解决方案,因为它导致带有撇号 (') 的单词和带有小数的数字的错误标记化:
Sentence: "It's"
Tokens: ["I", "t's"]
Expected: ["It", "'s"]
Sentence: "1.50"
Tokens: ["1", ".", "50"]
Expected: ["1.50"]
这就是为什么我使用 Matcher 而不是尝试编辑正则表达式的原因。
最佳答案
Matcher 并不是真正适合这个的工具。您应该改为修改分词器。
如果您想保留其他所有内容的处理方式并且只更改连字符的行为,您应该修改现有的中缀模式并保留所有其他设置。当前的英文中缀模式定义在这里:
您可以在不定义自定义分词器的情况下添加新模式,但在不定义自定义分词器的情况下无法删除模式。因此,如果您注释掉连字符模式并定义自定义分词器:
import spacy
from spacy.tokenizer import Tokenizer
from spacy.lang.char_classes import ALPHA, ALPHA_LOWER, ALPHA_UPPER, CONCAT_QUOTES, LIST_ELLIPSES, LIST_ICONS
from spacy.util import compile_infix_regex
def custom_tokenizer(nlp):
infixes = (
LIST_ELLIPSES
+ LIST_ICONS
+ [
r"(?<=[0-9])[+\-\*^](?=[0-9-])",
r"(?<=[{al}{q}])\.(?=[{au}{q}])".format(
al=ALPHA_LOWER, au=ALPHA_UPPER, q=CONCAT_QUOTES
),
r"(?<=[{a}]),(?=[{a}])".format(a=ALPHA),
#r"(?<=[{a}])(?:{h})(?=[{a}])".format(a=ALPHA, h=HYPHENS),
r"(?<=[{a}0-9])[:<>=/](?=[{a}])".format(a=ALPHA),
]
)
infix_re = compile_infix_regex(infixes)
return Tokenizer(nlp.vocab, prefix_search=nlp.tokenizer.prefix_search,
suffix_search=nlp.tokenizer.suffix_search,
infix_finditer=infix_re.finditer,
token_match=nlp.tokenizer.token_match,
rules=nlp.Defaults.tokenizer_exceptions)
nlp = spacy.load("en")
nlp.tokenizer = custom_tokenizer(nlp)
print([t.text for t in nlp("It's 1.50, up-scaled haven't")])
# ['It', "'s", "'", '1.50', "'", ',', 'up-scaled', 'have', "n't"]
在初始化新的 Tokenizer 时,您确实需要提供当前的前缀/后缀/token_match 设置以保留现有的 tokenizer 行为。另见(德语,但非常相似):https://stackoverflow.com/a/57304882/461847
编辑添加(因为这看起来确实没有必要复杂,而且您确实应该能够重新定义中缀模式而无需加载全新的自定义分词器):
如果您刚刚加载模型(对于 v2.1.8)并且您还没有调用 nlp()
,您也可以只替换 infix_re.finditer
无需创建自定义分词器:
nlp = spacy.load('en')
nlp.tokenizer.infix_finditer = infix_re.finditer
有一个缓存错误应该有望在 v2.2 中得到修复,这将使它在任何时候都能正常工作,而不仅仅是新加载的模型。 (否则行为会非常困惑,这就是为什么创建自定义分词器是 v2.1.8 更好的通用建议。)
关于python - spaCy - 带连字符的单词的标记化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58105967/