在 spacy 中用 ## 替换的数字的正确 POS 标签

Pyf*_*sch 6 python pos-tagger spacy

gigaword 数据集是一个巨大的语料库,用于训练抽象摘要模型。它包含如下摘要:

spain 's colonial posts #.## billion euro loss
taiwan shares close down #.## percent
Run Code Online (Sandbox Code Playgroud)

我想用spacy处理这些摘要,并为每个令牌获取正确的 pos 标签。问题是数据集中的所有数字都被替换为 # 符号,spacy 不会将其归类为数字 ( NUM ) 而是其他标签。

>>> import spacy
>>> from spacy.tokens import Doc
>>> nlp = spacy.load("en_core_web_sm")
>>> nlp.tokenizer = lambda raw: Doc(nlp.vocab, words=raw.split(' '))
>>> text = "spain 's colonial posts #.## billion euro loss"
>>> doc = nlp(text)
>>> [(token.text, token.pos_) for token in doc]
[('spain', 'PROPN'), ("'s", 'PART'), ('colonial', 'ADJ'), ('posts', 'NOUN'), ('#.##', 'PROPN'), ('billion', 'NUM'), ('euro', 'PROPN'), ('loss', 'NOUN')]
Run Code Online (Sandbox Code Playgroud)

有没有办法自定义 POS 标记器,以便将所有仅由 #-sign 和点组成的标记分类为数字?

我知道你用你自己的替换了 spacy POS 标记器,或者用额外的数据为你的域微调它,但我没有标记训练数据,其中所有数字都被替换为 #,我想尽可能少地更改标记器. 我更喜欢有一个总是被识别为数字的正则表达式或固定的标记列表。

Wal*_*oss 1

如果#用数字替换呢?

\n\n

在这个答案的第一个版本中,我选择了 digital 9,因为它让我想起了大约 30 年前使用的 COBOL 数字字段格式......但后来我查看了数据集,并意识到为了正确的 NLP 处理,应该至少弄清楚几件事:

\n\n
    \n
  • 序数词(第 1、第 2、...)
  • \n
  • 日期
  • \n
\n\n

序数词需要对任何数字选择进行特殊处理,但数字1会产生合理的日期,但年份除外(当然,1111 可能会或可能不会被解释为有效年份,但我们要谨慎行事)。11/11/2020显然比99/99/9999...

\n\n

这是代码:

\n\n
import re\n\nic = re.IGNORECASE\nsubs = [\n    (re.compile(r\'\\b1(nd)\\b\', flags=ic), r\'2\\1\'),  # 1nd -> 2nd\n    (re.compile(r\'\\b1(rd)\\b\', flags=ic), r\'3\\1\'),  # 1rd -> 3rd\n    (re.compile(r\'\\b1(th)\\b\', flags=ic), r\'4\\1\'),  # 1th -> 4th\n    (re.compile(r\'11(st)\\b\', flags=ic), r\'21\\1\'),  # ...11st -> ...21st\n    (re.compile(r\'11(nd)\\b\', flags=ic), r\'22\\1\'),  # ...11nd -> ...22nd\n    (re.compile(r\'11(rd)\\b\', flags=ic), r\'23\\1\'),  # ...11rd -> ...23rd\n    (re.compile(r\'\\b1111\\b\'), \'2020\')              # 1111 -> 2020\n]\n\ntext = \'\'\'spain \'s colonial posts #.## billion euro loss\n#nd, #rd, #th, ##st, ##nd, ##RD, ##TH, ###st, ###nd, ###rd, ###th.\nID=#nd#### year=#### OK\'\'\'\n\ntext = text.replace(\'#\', \'1\')\nfor pattern, repl in subs:\n    text = re.sub(pattern, repl, text)\n\nprint(text)\n# spain \'s colonial posts 1.11 billion euro loss\n# 2nd, 3rd, 4th, 21st, 22nd, 23RD, 11TH, 121st, 122nd, 123rd, 111th.\n# ID=1nd1111 year=2020 OK\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果语料库的预处理将任何数字转换为 a #,则此转换不会丢失任何信息。一些 \xe2\x80\x9ctrue\xe2\x80\x9d#会变成1,但与不被识别的数字相比,这可能是一个小问题。此外,在对数据集的大约 500000 行进行目视检查时,我无法找到 \xe2\x80\x9ctrue\xe2\x80\x9d 的任何候选者#

\n\n

注意:上面的正则表达式代表\xe2\x80\x9cword border\xe2\x80\x9d,即(单词)和(非单词)字符\\b之间的边界,其中单词字符是任何字母数字字符(更多信息请点击此处)。替换中的代表第一组,即第一对括号(更多信息请参见此处)。使用保留所有文本的大小写,这对于像. 后来我发现您的数据集已标准化为所有小写字母,但我决定保持通用。\\w\\W\\1\\12nd

\n\n

#如果您需要从词性中获取带 s 的文本,那么很简单

\n\n
token.text.replace(\'0\',\'#\').replace(\'1\',\'#\').replace(\'2\',\'#\').replace(\'3\',\'#\').replace(\'4\',\'#\')\n
Run Code Online (Sandbox Code Playgroud)\n