【问题标题】:Preprocessing of Labelled Text Data标记文本数据的预处理
【发布时间】:2020-07-25 21:34:40
【问题描述】:

要为 NER 训练 NLP 模型,必须拥有在文本上具有命名实体标签的文本数据。在许多情况下,这是由字符偏移量给出的(例如。 ("Android Pay expands to Canada", [(0, 11, 'PRODUCT'), (23, 30, 'GPE')]))、BILUO 格式(例如(["Facebook", "released", "React", "in", "2014"], ["U-ORG", "O", "U-TECHNOLOGY", "O", "U-DATE"]))或类似的东西(示例取自 spaCy101)。当想要对这些数据进行预处理时,将标签保持在正确的位置很重要。例如。删除停用词或操作空白字符和标记将被删除。我的问题是:

是否有一种数据结构可以在将标签移动到新文本中的正确位置的同时对带标签的文本数据进行预处理?

如果在 python 中已经有这个实现,我也会对此感兴趣。否则我可能也愿意自己编写代码。

我使用 NER 作为这个问题的动机,但如果有更通用的数据结构可以同时存储不同类型的标签,我也会感兴趣。

例如,我想做一些这样的操作,使我的所有文档都小写。

import spacy
nlp = spacy.blank('en')
doc = nlp('Hello World! This is an amazing day!')
doc.text = doc.text.lower()

这是不可能的并返回错误。

AttributeError: attribute 'text' of 'spacy.tokens.doc.Doc' objects is not writable

【问题讨论】:

  • 如果 Spacy 的标记器满足您的需求,我相信它的 Token 类会跟踪原始文档中每个标记的索引,这应该有助于对齐标签。至少,如果您遇到困难,这可能是一个很好的起点。
  • 我认为 spacy Docs 和 Tokens 在创建后不应该被修改。一个我已经无法轻松完成的简单示例是将所有文本更改为小写。
  • 您能否提供更多细节或问题的示例? (只是为了确保我理解正确。)
  • 我在问题中添加了一个我想对此类对象执行的操作示例。但我不仅希望能够切换到小写。理想情况下,我希望能够进行所有标准的文本预处理操作(去除停用词、词形还原、词干提取、空白清理……)。

标签: python data-structures nlp preprocessor named-entity-recognition


【解决方案1】:

如果我没记错的话,spaCy 的设计使其Token 类跟踪相对于原始文档的字符偏移量(例如,参见token.idx)。

如果您需要进行一些自定义预处理,这仍然会被处理。例如,如果您以here 为例:

print([t.idx for t in doc])

这应该给你:

['hello', '-', 'world.', ':)']
[0, 5, 6, 13]

如果您真的想要或需要拥有自己的自定义属性,spaCy 允许您通过 ._ 命名空间设置自定义属性和方法,您可以阅读更多关于 here 的信息。

希望其中一个选项能够满足您的需求。如果没有,您也许可以从 spaCy 的源代码和文档中寻找灵感?

编辑 1:示例

import spacy


nlp = spacy.load('en_core_web_sm')

text = "Sam is going to the store."

labels = [(0, 3, "PER"),
          (20, 25, "LOC")]

extra_stopwords = ['hi']

for word in extra_stopwords:
    nlp.vocab[word].is_stop = True

doc = nlp(text)

# add Span(start, end, label) to doc.ents
for start, end, label in labels:
    span = doc.char_span(start, end, label)

    # I forgot char_span only assigns the label to the span
    # might be safer to use the ._ namespace I mentioned earlier
    # instead of .ent_type_, but the idea should be the same
    for token in span:
        token.ent_type_ = label

# examples of things you can access
print([(token,
        token.ent_type_,
        token.lemma_.lower(),
        token.is_stop)
      for token in doc])

编辑 2:

如果你真的想得到偏移的偏移量,你可以使用上面的方法并循环遍历标记,跟踪你的位置,并从中构造一个新的Doc 对象。您必须决定如何处理添加空白;为了简单起见,我只是在下面使用了一种幼稚的方法。

from spacy.tokens import Doc


# example
def preprocessing(token):
    """Takes a token and returns the processed string."""
    return token.lemma_.lower()


labels_new = []
words = []
spaces = []
offset = 0


for i, token in enumerate(doc):
    token_new = preprocessing(token)
    words.append(token_new)

    if token.ent_type_:
        labels_new.append((offset,
                           offset + len(token_new),
                           token.ent_type_))

    offset += len(token_new)

    if len(token.whitespace_) == 1:
        spaces.append(True)

    else:
        spaces.append(False)


doc_new = Doc(nlp.vocab, words, spaces)

那么你将拥有:

print([(token, token.idx) for token in doc_new])

# [(sam, 0), (be, 4), (go, 7), (to, 10), (the, 13), (store, 17), (., 22)]

和:

print(labels_new)

# [(0, 3, 'PER'), (12, 17, 'LOC')]

(当然,您必须像以前一样将这些重新分配给每个令牌。)

【讨论】:

  • 问题是我想从标记数据开始,然后进行预处理。用例是:我有一个标记文本的数据集。现在我想加载这些文本并对其进行一些预处理以使其成为标准格式。对于标签,我采用了原始文本。在我的代码中,我现在想更改文本,同时仍将标签保持在正确的位置。我看不出您的提案如何做到这一点。
  • 我添加了一个示例,不完全确定您是否有无法涵盖的更具体的需求?
  • 也许我应该在我的问题中更好地指定“正确的位置”。我想修改文本,以便在新位置上获得带有标签的新文本。例如。一个修改后的文档,我可以调用 doc.text 来获取新文本和 token.idx 来获取新文本中的索引。
  • 我明白,我只是无法想象做这种事情的动机。如果目标是让 (start, end, label) span 与适当的已处理令牌配对,那么您已经拥有 token/token.ent_type_。需要知道“处理过的”字符偏移对我来说似乎很不寻常,这就是为什么我对动机感到好奇。
  • 我想将修改后的文档用作其他模型的训练数据。
猜你喜欢
  • 1970-01-01
  • 2017-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多