【问题标题】:SpaCy -- intra-word hyphens. How to treat them one word?SpaCy——词内连字符。如何对待他们一个字?
【发布时间】:2019-03-19 13:12:31
【问题描述】:

以下是作为答复question 提供的代码;

import spacy
from spacy.tokenizer import Tokenizer
from spacy.util import compile_prefix_regex, compile_infix_regex, compile_suffix_regex
import re

nlp = spacy.load('en')

infixes = nlp.Defaults.prefixes + (r"[./]", r"[-]~", r"(.'.)")

infix_re = spacy.util.compile_infix_regex(infixes)

def custom_tokenizer(nlp):
    return Tokenizer(nlp.vocab, infix_finditer=infix_re.finditer)

nlp.tokenizer = custom_tokenizer(nlp)

s1 = "Marketing-Representative- won't die in car accident."
s2 = "Out-of-box implementation"

for s in s1,s2:
    doc = nlp("{}".format(s))
    print([token.text for token in doc])

结果

$python3 /tmp/nlp.py  
['Marketing-Representative-', 'wo', "n't", 'die', 'in', 'car', 'accident', '.']  
['Out-of-box', 'implementation']  

以下使用的第一个 (r"[./]") 和最后一个 (r"(.'.)") 模式是什么?

infixes = nlp.Defaults.prefixes + (r"[./]", r"[-]~", r"(.'.)")

编辑:我希望拆分如下;

那个

叶海亚

笔记本电脑盖

.


我希望 spacy 将连字符内的单词视为一个标记,而不会对其他拆分规则产生负面影响。

“那是 Yahya 的笔记本电脑保护套。3.14!”

[“That”、“is”、“Yahya”、“'s”、“laptop-cover”、“.”、“3.14”、“!”](预期)

默认情况下,

import spacy
nlp = spacy.load('en_core_web_md')
for token in nlp("That is Yahya's laptop-cover. 3.14!"):
    print (token.text)

SpaCy 给予;

["That", "is", "Yahya", "'s", "laptop", "-", "cover", ".", "3.14", "!"]

然而,

from spacy.util import compile_infix_regex
infixes = nlp.Defaults.prefixes + tuple([r"[-]~"])
infix_re = spacy.util.compile_infix_regex(infixes)
nlp.tokenizer = spacy.tokenizer.Tokenizer(nlp.vocab, infix_finditer=infix_re.finditer)
for token in nlp("That is Yahya's laptop-cover. 3.14!"):
    print (token.text)

给予;

["That", "is", "Yahya", "'", "s", "laptop-cover.", "3.14", "!"]

【问题讨论】:

  • 我想你只需要知道explained here
  • @WiktorStribiżew 该链接没有帮助,我已经浏览过了。 'infixes' 被分配了所有 nlp.Defaults.prefixes 加上以下三个模式(r"[./]", r"[-]~", r"(.'.)")。中间一个显然是为了包含 HYPH,这样词内连字符就不会被单独标记,另外两种模式是做什么用的?
  • 请将您的代码粘贴为文本,而不是图像。然后,添加确切的要求,将我的问题下与实际问题相关的所有 cmets 移至 question。然后,我们将看看可以做些什么。它已经开始看起来像一个新问题了。
  • 实际问题只是关于 (r"[./]", r"[-]~", r"(.'.)") 中的模式,你回答得很好。后来,我发现通过更新中缀,我遇到了其他问题,例如“boy's”->(“bo”,“y's”)。我希望我能问另一个我不能问的单独问题。

标签: nlp tokenize spacy


【解决方案1】:

注意:要查看保留连字符的自定义标记器,请查看答案的底部。

这里定义了一个自定义标记器,它使用一组内置 (nlp.Defaults.prefixes) 和自定义 ([./][-]~(.'.)) 模式将文本标记为标记。

nlp.Defaults.prefixes + (r"[./]", r"[-]~", r"(.'.)")是元组拼接操作,结果如下

('§', '%', '=', '—', '–', '\\+(?![0-9])', '…', '……', ',', ':', ';', '\\!', '\\?', '¿', '؟', '¡', '\\(', '\\)', '\\[', '\\]', '\\{', '\\}', '<', '>', '_', '#', '\\*', '&', '。', '?', '!', ',', '、', ';', ':', '~', '·', '।', '،', '؛', '٪', '\\.\\.+', '…', "\\'", '"', '”', '“', '`', '‘', '´', '’', '‚', ',', '„', '»', '«', '「', '」', '『', '』', '(', ')', '〔', '〕', '【', '】', '《', '》', '〈', '〉', '\\$', '£', '€', '¥', '฿', 'US\\$', 'C\\$', 'A\\$', '₽', '﷼', '₴', '[\\u00A6\\u00A9\\u00AE\\u00B0\\u0482\\u058D\\u058E\\u060E\\u060F\\u06DE\\u06E9\\u06FD\\u06FE\\u07F6\\u09FA\\u0B70\\u0BF3-\\u0BF8\\u0BFA\\u0C7F\\u0D4F\\u0D79\\u0F01-\\u0F03\\u0F13\\u0F15-\\u0F17\\u0F1A-\\u0F1F\\u0F34\\u0F36\\u0F38\\u0FBE-\\u0FC5\\u0FC7-\\u0FCC\\u0FCE\\u0FCF\\u0FD5-\\u0FD8\\u109E\\u109F\\u1390-\\u1399\\u1940\\u19DE-\\u19FF\\u1B61-\\u1B6A\\u1B74-\\u1B7C\\u2100\\u2101\\u2103-\\u2106\\u2108\\u2109\\u2114\\u2116\\u2117\\u211E-\\u2123\\u2125\\u2127\\u2129\\u212E\\u213A\\u213B\\u214A\\u214C\\u214D\\u214F\\u218A\\u218B\\u2195-\\u2199\\u219C-\\u219F\\u21A1\\u21A2\\u21A4\\u21A5\\u21A7-\\u21AD\\u21AF-\\u21CD\\u21D0\\u21D1\\u21D3\\u21D5-\\u21F3\\u2300-\\u2307\\u230C-\\u231F\\u2322-\\u2328\\u232B-\\u237B\\u237D-\\u239A\\u23B4-\\u23DB\\u23E2-\\u2426\\u2440-\\u244A\\u249C-\\u24E9\\u2500-\\u25B6\\u25B8-\\u25C0\\u25C2-\\u25F7\\u2600-\\u266E\\u2670-\\u2767\\u2794-\\u27BF\\u2800-\\u28FF\\u2B00-\\u2B2F\\u2B45\\u2B46\\u2B4D-\\u2B73\\u2B76-\\u2B95\\u2B98-\\u2BC8\\u2BCA-\\u2BFE\\u2CE5-\\u2CEA\\u2E80-\\u2E99\\u2E9B-\\u2EF3\\u2F00-\\u2FD5\\u2FF0-\\u2FFB\\u3004\\u3012\\u3013\\u3020\\u3036\\u3037\\u303E\\u303F\\u3190\\u3191\\u3196-\\u319F\\u31C0-\\u31E3\\u3200-\\u321E\\u322A-\\u3247\\u3250\\u3260-\\u327F\\u328A-\\u32B0\\u32C0-\\u32FE\\u3300-\\u33FF\\u4DC0-\\u4DFF\\uA490-\\uA4C6\\uA828-\\uA82B\\uA836\\uA837\\uA839\\uAA77-\\uAA79\\uFDFD\\uFFE4\\uFFE8\\uFFED\\uFFEE\\uFFFC\\uFFFD\\U00010137-\\U0001013F\\U00010179-\\U00010189\\U0001018C-\\U0001018E\\U00010190-\\U0001019B\\U000101A0\\U000101D0-\\U000101FC\\U00010877\\U00010878\\U00010AC8\\U0001173F\\U00016B3C-\\U00016B3F\\U00016B45\\U0001BC9C\\U0001D000-\\U0001D0F5\\U0001D100-\\U0001D126\\U0001D129-\\U0001D164\\U0001D16A-\\U0001D16C\\U0001D183\\U0001D184\\U0001D18C-\\U0001D1A9\\U0001D1AE-\\U0001D1E8\\U0001D200-\\U0001D241\\U0001D245\\U0001D300-\\U0001D356\\U0001D800-\\U0001D9FF\\U0001DA37-\\U0001DA3A\\U0001DA6D-\\U0001DA74\\U0001DA76-\\U0001DA83\\U0001DA85\\U0001DA86\\U0001ECAC\\U0001F000-\\U0001F02B\\U0001F030-\\U0001F093\\U0001F0A0-\\U0001F0AE\\U0001F0B1-\\U0001F0BF\\U0001F0C1-\\U0001F0CF\\U0001F0D1-\\U0001F0F5\\U0001F110-\\U0001F16B\\U0001F170-\\U0001F1AC\\U0001F1E6-\\U0001F202\\U0001F210-\\U0001F23B\\U0001F240-\\U0001F248\\U0001F250\\U0001F251\\U0001F260-\\U0001F265\\U0001F300-\\U0001F3FA\\U0001F400-\\U0001F6D4\\U0001F6E0-\\U0001F6EC\\U0001F6F0-\\U0001F6F9\\U0001F700-\\U0001F773\\U0001F780-\\U0001F7D8\\U0001F800-\\U0001F80B\\U0001F810-\\U0001F847\\U0001F850-\\U0001F859\\U0001F860-\\U0001F887\\U0001F890-\\U0001F8AD\\U0001F900-\\U0001F90B\\U0001F910-\\U0001F93E\\U0001F940-\\U0001F970\\U0001F973-\\U0001F976\\U0001F97A\\U0001F97C-\\U0001F9A2\\U0001F9B0-\\U0001F9B9\\U0001F9C0-\\U0001F9C2\\U0001F9D0-\\U0001F9FF\\U0001FA60-\\U0001FA6D]', '[/.]', '-~', "(.'.)")

如您所见,这些都是正则表达式,用于处理单词中的标点符号、中缀。见Spacy tokenizer algorithm

算法可以总结如下:

  1. 遍历以空格分隔的子字符串
  2. 检查我们是否为此子字符串明确定义了规则。如果我们这样做,请使用它。
  3. 否则,请尝试使用前缀。
  4. 如果我们使用了前缀,则返回到循环的开头,这样特殊情况总是优先。
  5. 如果我们没有使用前缀,请尝试使用后缀。
  6. 如果我们不能使用前缀或后缀,请寻找“中缀”——比如连字符等。
  7. 一旦我们不能再使用任何字符串,请将其作为单个令牌处理。

现在,当我们处于中缀处理步骤时,这些正则表达式也用于根据这些模式将文本拆分为标记。

例如[/.] 很重要,因为如果不添加它,abc.def/ghi 将是一个单独的标记,但添加了模式后,它将被拆分为 'abc', '.', 'def', '/', 'ghi'

[-]~(与-~ 相同)匹配- 并想在之后匹配~,但由于它不存在,- 被跳过并且不会发生拆分,你得到整个'Marketing-Representative-' 令牌。但是请注意,如果您在句子中有 'Marketing-~Representative-',并且您使用 -~ 正则表达式,您将得到 ['Marketing', '-~', 'Representative-'] 作为结果,因为会有匹配。

.'. 正则表达式匹配任何字符 + ' + 任何字符。点匹配正则表达式中的任何字符。因此,规则只是将这些标记从句子中标记(拆分)出来(例如n't、r'd 等)

回答编辑

添加新规则时应非常小心,并检查它们是否与已添加的规则重叠。

例如当您添加r"\b's\b" 以拆分Genetive case 撇号-s 时,您应该“覆盖”nlp.Defaults.prefixes 中的"\\'" 规则。如果您不打算将 ' 匹配为中缀,请将其删除,或者通过将 nlp.Defaults.prefixes 附加到这些规则来优先考虑您的自定义规则,反之亦然。

查看示例代码:

import re
import spacy
from spacy.tokenizer import Tokenizer

nlp = spacy.load("en_core_web_md")
infixes = tuple([r"'s\b", r"(?<!\d)\.(?!\d)"]) +  nlp.Defaults.prefixes
infix_re = spacy.util.compile_infix_regex(infixes)

def custom_tokenizer(nlp):
    return Tokenizer(nlp.vocab, infix_finditer=infix_re.finditer)

nlp.tokenizer = custom_tokenizer(nlp)
doc = nlp(u"That is Yahya's laptop-cover. 3.14!")
print([t.text for t in doc])

输出:['That', 'is', 'Yahya', "'s", 'laptop-cover', '.', '3.14', '!']

详情

  • r"'s\b" - 匹配 's 后跟单词边界
  • r"(?&lt;!\d)\.(?!\d) - 匹配前面或后面没有数字的 .

并且如果您想使用将连字符单词保留为单个标记的自定义标记器,您将不得不重新定义infixesr"(?&lt;=[{a}])(?:{h})(?=[{a}])".format(a=ALPHA, h=HYPHENS), 行说明了这一点,您需要摆脱它。由于它是唯一包含-|–|—|--|---|——|~ 字符串的项目,因此从infixes 中删除该项目并重新编译中缀模式会更容易:

import spacy
from spacy.tokenizer import Tokenizer
from spacy.util import compile_infix_regex

nlp = spacy.load("en_core_web_sm")

inf = list(nlp.Defaults.infixes)
inf = [x for x in inf if '-|–|—|--|---|——|~' not in x] # remove the hyphen-between-letters pattern from infix patterns
infix_re = compile_infix_regex(tuple(inf))

def custom_tokenizer(nlp):
    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.tokenizer = custom_tokenizer(nlp)
doc = nlp("That is Yahya's laptop-cover. 3.14!")
print([t.text for t in doc])
# => ['That', 'is', 'Yahya', "'s", 'laptop-cover', '.', '3.14', '!']

【讨论】:

  • r"'s\b" 仅停止将 "'s" 拆分为 ("'", "s")。但是,像“他们会”这样的案例被错误地拆分为(“他们”、“'”、“ll”)。因此,我将模式修改为 r"'\w{1,2}\b" 解决了这个问题。 ^^ 我希望我不会遇到更多类似的问题。您会看到连字符内问题得到解决,但结果出现了许多其他问题。
  • Further r"n?'\w{1,2}\b" 确保 "aren't" 不被拆分为 ("aren", "'t")
  • @ZeeshanAli 那你为什么要保留"\\'" 规则呢?如果您不想拆分 ',请将其删除。
  • 如果我删除它,"'s" 将被拆分为 ("'", "s") :( 我需要 SpaCy 将连字符内的单词视为一个单词,并保留所有其他拆分规则原样。
  • 不知道你是怎么做到的,infixes = tuple([r"(?&lt;!\d)\.(?!\d)"]) + tuple(x for x in nlp.Defaults.prefixes if x != "\\'") 产生 - 我猜 - 预期的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-17
  • 2022-12-17
  • 1970-01-01
  • 2014-06-03
  • 2010-09-05
相关资源
最近更新 更多