在 spaCy 的自定义句子边界中使用 POS 和 PUNCT 标记
Using POS and PUNCT tokens in custom sentence boundaries in spaCy
我正在尝试使用 spaCy 将句子拆分为子句,以便使用 MLLib 进行分类。我已经搜索了两种我认为是最佳方法的解决方案之一,但运气不太好。
选项:将使用文档中的标记,即 token.pos_
匹配 SCONJ
并拆分为一个句子。
选项:将使用任何 spaCy 具有的值字典创建一个列表,它标识为 SCONJ
1 的问题是我只有 .text、.i 而没有 .pos_ 作为自定义边界(据我所知,在解析器之前需要 运行。
2 的问题是我似乎找不到字典。这也是一种非常 hacky 的方法。
import deplacy
from spacy.language import Language
# Uncomment to visualise how the tokens are labelled
# deplacy.render(doc)
custom_EOS = ['.', ',', '!', '!']
custom_conj = ['then', 'so']
@Language.component("set_custom_boundaries")
def set_custom_boundaries(doc):
for token in doc[:-1]:
if token.text in custom_EOS:
doc[token.i + 1].is_sent_start = True
if token.text in custom_conj:
doc[token.i].is_sent_start = True
return doc
def set_sentence_breaks(doc):
for token in doc:
if token == "SCONJ":
doc[token.i].is_sent_start = True
def main():
text = "In the add user use case, we need to consider speed and reliability " \
"so use of a relational DB would be better than using SQLite. Though " \
"it may take extra effort to convert @Bot"
nlp = spacy.load("en_core_web_sm")
nlp.add_pipe("set_custom_boundaries", before="parser")
doc = nlp(text)
# for token in doc:
# print(token.pos_)
print("Sentences:", [sent.text for sent in doc.sents])
if __name__ == "__main__":
main()
当前输出
句子:['In the add user use case,',
'我们需要考虑速度和可靠性,
'so the use of a relational DB would be better than using SQLite.',
'Though it may take extra effort to convert @Bot']
我建议不要尝试对 is_sent_starts
做任何巧妙的事情 - 虽然它是用户可访问的,但它确实不打算以这种方式使用,并且有 at least one unresolved issue 与之相关.
由于您只需要对其他一些分类器进行这些划分,那么您只需要获取字符串就足够了,对吧?在那种情况下,我建议您 运行 像往常一样使用 spaCy 管道,然后在 SCONJ 标记上拆分句子(如果仅使用 SCONJ 对您的用例有效)。类似于:
out = []
for sent in doc.sents:
last = sent[0].i
for tok in sent:
if tok.pos_ == "SCONJ":
out.append(doc[last:tok.i])
last = tok.i + 1
out.append(doc[last:sent[-1].i])
或者,如果这还不够好,您可以使用依存分析来识别子句,以查找子句中的动词(例如,通过它们与 SCONJ 的关系),保存子句,然后根据词根添加另一个句子.
我正在尝试使用 spaCy 将句子拆分为子句,以便使用 MLLib 进行分类。我已经搜索了两种我认为是最佳方法的解决方案之一,但运气不太好。
选项:将使用文档中的标记,即
token.pos_
匹配SCONJ
并拆分为一个句子。选项:将使用任何 spaCy 具有的值字典创建一个列表,它标识为
SCONJ
1 的问题是我只有 .text、.i 而没有 .pos_ 作为自定义边界(据我所知,在解析器之前需要 运行。
2 的问题是我似乎找不到字典。这也是一种非常 hacky 的方法。
import deplacy
from spacy.language import Language
# Uncomment to visualise how the tokens are labelled
# deplacy.render(doc)
custom_EOS = ['.', ',', '!', '!']
custom_conj = ['then', 'so']
@Language.component("set_custom_boundaries")
def set_custom_boundaries(doc):
for token in doc[:-1]:
if token.text in custom_EOS:
doc[token.i + 1].is_sent_start = True
if token.text in custom_conj:
doc[token.i].is_sent_start = True
return doc
def set_sentence_breaks(doc):
for token in doc:
if token == "SCONJ":
doc[token.i].is_sent_start = True
def main():
text = "In the add user use case, we need to consider speed and reliability " \
"so use of a relational DB would be better than using SQLite. Though " \
"it may take extra effort to convert @Bot"
nlp = spacy.load("en_core_web_sm")
nlp.add_pipe("set_custom_boundaries", before="parser")
doc = nlp(text)
# for token in doc:
# print(token.pos_)
print("Sentences:", [sent.text for sent in doc.sents])
if __name__ == "__main__":
main()
当前输出
句子:['In the add user use case,',
'我们需要考虑速度和可靠性,
'so the use of a relational DB would be better than using SQLite.',
'Though it may take extra effort to convert @Bot']
我建议不要尝试对 is_sent_starts
做任何巧妙的事情 - 虽然它是用户可访问的,但它确实不打算以这种方式使用,并且有 at least one unresolved issue 与之相关.
由于您只需要对其他一些分类器进行这些划分,那么您只需要获取字符串就足够了,对吧?在那种情况下,我建议您 运行 像往常一样使用 spaCy 管道,然后在 SCONJ 标记上拆分句子(如果仅使用 SCONJ 对您的用例有效)。类似于:
out = []
for sent in doc.sents:
last = sent[0].i
for tok in sent:
if tok.pos_ == "SCONJ":
out.append(doc[last:tok.i])
last = tok.i + 1
out.append(doc[last:sent[-1].i])
或者,如果这还不够好,您可以使用依存分析来识别子句,以查找子句中的动词(例如,通过它们与 SCONJ 的关系),保存子句,然后根据词根添加另一个句子.