Spacy 分句不一致
Spacy divides sentences inconsistently
我正在使用 Spacy,但我对它如何拆分句子有疑问。出于某种原因,一些句子被分成两个或树部分而不是一个,所以当我尝试使用 TensorFlow 训练模型时,我得到一个错误,即向量的维度不匹配。
我已尝试将标记器更改为空白标记器,如 this link 中所示。所以,我的 class 看起来像(注释掉了 Benepar 组件):
class BeneparAnnotator(SyntaxAnnotator):
class WhitespaceTokenizer:
def __init__(self, vocab):
self.vocab = vocab
def __call__(self, text):
words = text.split(" ")
spaces = [True] * len(words)
# Avoid zero-length tokens
for i, word in enumerate(words):
if word == "":
words[i] = " "
spaces[i] = False
# Remove the final trailing space
if words[-1] == " ":
words = words[0:-1]
spaces = spaces[0:-1]
else:
spaces[-1] = False
return Doc(self.vocab, words=words, spaces=spaces)
def __init__(self):
self.nlp = spacy.load('en')
# self.nlp.add_pipe(BeneparComponent("benepar_en2"))
self.nlp.tokenizer = self.WhitespaceTokenizer(self.nlp.vocab)
self.extract_arc_representation = False
... other stuff ...
def ... other stuff ...
现在,当解析句子时:
def parse_sentences(nlp, captions: List[str]) -> List[Span]:
parsed_sentences = []
for caption in tqdm(captions, desc="Parsing sentences"):
parsed_caption = nlp(caption)
if len(list(parsed_caption.sents)) > 1: # This if is for debug
length = len(list(parsed_caption.sents))
as_list = list(parsed_caption.sents)
pass
parsed_sentence = list(parsed_caption.sents)[0]
parsed_sentences.append(parsed_sentence)
return parsed_sentences
这种获取句子的方式取自here,因为我想检索用 Benepar 解析的句子,但由于其中一些被分成几个句子,而不是获取整个解析的句子我得到几个已解析的句子。
并且出于某种原因,某些句子被分成 2 或 3 个部分:
例1:一只黑羊刚剃完毛
分为:[害群之马,刚剃完毛]
示例 2:路牌上写着 s。第三大道禁止左转
分为:[路牌上写着 s., 3rd av., and no left turn]
例3:一转路只在马路中间标志
分为:[一个转弯路,只在路中间签到]
例4:两只牛站在树前的草地上
分为:[两头牛站在树前的草地上]
示例 5:一辆 30 号德比车在右门微开的情况下切入弯道
它分为:[一辆德比车,看到 30 号右门半开着切入一个角落]
我还有其他类似于 示例 1 的句子,它们仅在一个句子中正确拆分。例如:
一只站在草丛中的背羊
分为:[站在草丛中的一只背羊]
因此,当我得到 sents 一个带有 list(parsed_caption.sents)[0]
的字幕时,我得到的是整个字幕,而不是字幕的一部分。
问题出在哪里?
----- 更新以尝试更好地解释我的问题。 -----
我有一个句子列表,我想用 Benepar 解析这些句子,然后将它们用作工具的输入,看看我是否能得到更好的结果。由于该工具的 anaconda 环境的限制,我需要使用适用于 Python 3.6.
的 2019 版本
我遇到的问题是一些句子被拆分并且 Benepar 将它们解析为独立的块(而不是整个句子在一起)并且当我尝试用 list(parsed_caption.sents)[0]
来获取它们时(因为应该只有一个元素,整个句子)并且我只得到一个块,当我 运行 实验时我得到一个错误,原始句子的大小和解析的句子不匹配。
我有超过 616K 个句子用于训练集。他们中的大多数都被很好地解析并且没有被分成块,如果我这样做:
caption = list(parsed_caption.sents)[0]
然后:
caption._.parse_string
我有 Benepar 的完整解析句子。
假设我有这句话:The time for action is now.
我在 list(parsed_caption.sents)[0]
中的预期内容应该是:
[The time for action is now.]
所以当我想用 caption._.parse_string
得到解析后的句子时,我得到:
(S (NP (NP (DT The) (NN time)) (PP (IN for) (NP (NN action)))) (VP (VBZ is) (ADVP (RB now))) (. .))
但问题是对于某些句子,它们被划分为(例如):
[The time, for action is now.]
因此,如果我尝试获取带有 list(parsed_caption.sents)[0]
的句子,我将只会解析句子的一大块。我只期待1句话,没有更多。
As a result of only getting 时间,当我训练模型和句子The time for action is now. is selected 我得到一个错误,因为长度 The time for action is now. and The time is not the same.
我不会在您的阶段使用自定义分词器,并且肯定不会拆分空格,使用 spacy 常规分词器:
from spacy.tokenizer import Tokenizer
self.nlp = spacy.load("en")
self.nlp.tokenizer=Tokenizer(self.nlp.vocab)
如果您想查看代币:
def _TokoniezData(self,sentence):
self.doc = self.nlp(sentence)
self._tokonized=[]
for token in self.doc:
self._tokonized.append((token.text ,token.lemma_, token.pos_, token.tag_, token.dep_,
token.shape_, token.is_alpha, token.is_stop,token.head,token.left_edge,token.right_edge,token.ent_type_))
对您在这里尝试做的事情感到很困惑,但这是我的理解:
- 您有一个纯文本文档列表。你知道每一个都是一个句子。
- 您以某种方式将句子传递给 benepar 以进行解析。
- 您还想将 spaCy 令牌传递给 Tensorflow。 (我不明白 how/why 但还好。)
你的问题是 benepar 可靠地 returns 一个句子,但 spaCy 没有,这会导致 Tensorflow 的维度问题。
以上如有不妥欢迎指正
假设这是正确的,分词器与您的问题无关。它决定了一个字符串如何被分割成单词,而不是句子是如何定义的。 (拥有不同数量的令牌也可能导致维度问题,但听起来这不是你的问题。)
如果你只是想要一个来自 spaCy 的一段文本的标记列表,你可以这样做:
tokens = list(nlp(text))
就是这样。如果您已经知道每个输入应该是一个句子,则不需要使用句子迭代器。
我还注意到您链接到的说明来自旧版本的文档,并不适用于最新版本的 benepar。使用最新版本和 the current README 我能够编写下面的示例代码,它将输入文本毫无问题地拆分为一个句子。 (我确实收到了来自 torch 的警告,但这似乎是一些内部福利。)
import benepar, spacy
nlp = spacy.load('en_core_web_md')
nlp.add_pipe("benepar", config={"model": "benepar_en3"})
doc = nlp("a black sheep having just had it s haired shaved off")
for sent in doc.sents:
print(sent)
print(sent._.parse_string)
print("---")
我终于找到了满足我需求的解决方案。
由于工具 Berkeley Neural Parser 提供了两个选项(NLTK 和 Spacy),我尝试了 NLTK 选项并设法实现了我的目标。由于环境的限制我不得不使用支持Python 3.6.
的2019版本
使用 NLTK 我只需要做:
import benepar
class BeneparAnnotator(SyntaxAnnotator):
def __init__(self):
self.parser = benepar.Parser("benepar_en2")
self.extract_arc_representation = False
....
然后:
for caption in tqdm(captions, desc="Parsing sentences"):
parsed_caption = parser.parse(caption)
tree_as_string = parsed_caption.pformat(margin=500, indent=0, nodesep='', parens='()', quotes=False)
parsed_sentences.append(tree_as_string)
在tree_as_string
中你会得到解析后的句子:
(S (NP (DT a) (NN restaurant)) (VP (VBZ has) (NP (JJ modern) (JJ wooden) (NNS tables) (CC and) (NNS chairs))))
您必须调整 margin
参数来设置短语的长度,因为如果短语比边距长,pformat 会用 \n
字符将其分成几行这样(不准确,仅举个例子):
(S\n (NP\n (DT a)\n (NN restaurant)) (VP\n (VBZ has) (NP\n (JJ modern) (JJ wooden) (NNS tables) (CC and) (NNS chairs))))
在这种情况下,所有句子都得到了正确处理,我可以在原始标签向量和解析向量的长度没有差异的情况下训练模型,因为现在用 Spacy 分成几个部分的句子不再被分割分为几个部分。
我希望这对使用相同版本工具的人有所帮助。
此致。
我正在使用 Spacy,但我对它如何拆分句子有疑问。出于某种原因,一些句子被分成两个或树部分而不是一个,所以当我尝试使用 TensorFlow 训练模型时,我得到一个错误,即向量的维度不匹配。
我已尝试将标记器更改为空白标记器,如 this link 中所示。所以,我的 class 看起来像(注释掉了 Benepar 组件):
class BeneparAnnotator(SyntaxAnnotator):
class WhitespaceTokenizer:
def __init__(self, vocab):
self.vocab = vocab
def __call__(self, text):
words = text.split(" ")
spaces = [True] * len(words)
# Avoid zero-length tokens
for i, word in enumerate(words):
if word == "":
words[i] = " "
spaces[i] = False
# Remove the final trailing space
if words[-1] == " ":
words = words[0:-1]
spaces = spaces[0:-1]
else:
spaces[-1] = False
return Doc(self.vocab, words=words, spaces=spaces)
def __init__(self):
self.nlp = spacy.load('en')
# self.nlp.add_pipe(BeneparComponent("benepar_en2"))
self.nlp.tokenizer = self.WhitespaceTokenizer(self.nlp.vocab)
self.extract_arc_representation = False
... other stuff ...
def ... other stuff ...
现在,当解析句子时:
def parse_sentences(nlp, captions: List[str]) -> List[Span]:
parsed_sentences = []
for caption in tqdm(captions, desc="Parsing sentences"):
parsed_caption = nlp(caption)
if len(list(parsed_caption.sents)) > 1: # This if is for debug
length = len(list(parsed_caption.sents))
as_list = list(parsed_caption.sents)
pass
parsed_sentence = list(parsed_caption.sents)[0]
parsed_sentences.append(parsed_sentence)
return parsed_sentences
这种获取句子的方式取自here,因为我想检索用 Benepar 解析的句子,但由于其中一些被分成几个句子,而不是获取整个解析的句子我得到几个已解析的句子。
并且出于某种原因,某些句子被分成 2 或 3 个部分:
例1:一只黑羊刚剃完毛
分为:[害群之马,刚剃完毛]
示例 2:路牌上写着 s。第三大道禁止左转
分为:[路牌上写着 s., 3rd av., and no left turn]
例3:一转路只在马路中间标志
分为:[一个转弯路,只在路中间签到]
例4:两只牛站在树前的草地上
分为:[两头牛站在树前的草地上]
示例 5:一辆 30 号德比车在右门微开的情况下切入弯道
它分为:[一辆德比车,看到 30 号右门半开着切入一个角落]
我还有其他类似于 示例 1 的句子,它们仅在一个句子中正确拆分。例如:
一只站在草丛中的背羊
分为:[站在草丛中的一只背羊]
因此,当我得到 sents 一个带有 list(parsed_caption.sents)[0]
的字幕时,我得到的是整个字幕,而不是字幕的一部分。
问题出在哪里?
----- 更新以尝试更好地解释我的问题。 -----
我有一个句子列表,我想用 Benepar 解析这些句子,然后将它们用作工具的输入,看看我是否能得到更好的结果。由于该工具的 anaconda 环境的限制,我需要使用适用于 Python 3.6.
的 2019 版本我遇到的问题是一些句子被拆分并且 Benepar 将它们解析为独立的块(而不是整个句子在一起)并且当我尝试用 list(parsed_caption.sents)[0]
来获取它们时(因为应该只有一个元素,整个句子)并且我只得到一个块,当我 运行 实验时我得到一个错误,原始句子的大小和解析的句子不匹配。
我有超过 616K 个句子用于训练集。他们中的大多数都被很好地解析并且没有被分成块,如果我这样做:
caption = list(parsed_caption.sents)[0]
然后:
caption._.parse_string
我有 Benepar 的完整解析句子。
假设我有这句话:The time for action is now.
我在 list(parsed_caption.sents)[0]
中的预期内容应该是:
[The time for action is now.]
所以当我想用 caption._.parse_string
得到解析后的句子时,我得到:
(S (NP (NP (DT The) (NN time)) (PP (IN for) (NP (NN action)))) (VP (VBZ is) (ADVP (RB now))) (. .))
但问题是对于某些句子,它们被划分为(例如):
[The time, for action is now.]
因此,如果我尝试获取带有 list(parsed_caption.sents)[0]
的句子,我将只会解析句子的一大块。我只期待1句话,没有更多。
As a result of only getting 时间,当我训练模型和句子The time for action is now. is selected 我得到一个错误,因为长度 The time for action is now. and The time is not the same.
我不会在您的阶段使用自定义分词器,并且肯定不会拆分空格,使用 spacy 常规分词器:
from spacy.tokenizer import Tokenizer
self.nlp = spacy.load("en")
self.nlp.tokenizer=Tokenizer(self.nlp.vocab)
如果您想查看代币:
def _TokoniezData(self,sentence):
self.doc = self.nlp(sentence)
self._tokonized=[]
for token in self.doc:
self._tokonized.append((token.text ,token.lemma_, token.pos_, token.tag_, token.dep_,
token.shape_, token.is_alpha, token.is_stop,token.head,token.left_edge,token.right_edge,token.ent_type_))
对您在这里尝试做的事情感到很困惑,但这是我的理解:
- 您有一个纯文本文档列表。你知道每一个都是一个句子。
- 您以某种方式将句子传递给 benepar 以进行解析。
- 您还想将 spaCy 令牌传递给 Tensorflow。 (我不明白 how/why 但还好。)
你的问题是 benepar 可靠地 returns 一个句子,但 spaCy 没有,这会导致 Tensorflow 的维度问题。
以上如有不妥欢迎指正
假设这是正确的,分词器与您的问题无关。它决定了一个字符串如何被分割成单词,而不是句子是如何定义的。 (拥有不同数量的令牌也可能导致维度问题,但听起来这不是你的问题。)
如果你只是想要一个来自 spaCy 的一段文本的标记列表,你可以这样做:
tokens = list(nlp(text))
就是这样。如果您已经知道每个输入应该是一个句子,则不需要使用句子迭代器。
我还注意到您链接到的说明来自旧版本的文档,并不适用于最新版本的 benepar。使用最新版本和 the current README 我能够编写下面的示例代码,它将输入文本毫无问题地拆分为一个句子。 (我确实收到了来自 torch 的警告,但这似乎是一些内部福利。)
import benepar, spacy
nlp = spacy.load('en_core_web_md')
nlp.add_pipe("benepar", config={"model": "benepar_en3"})
doc = nlp("a black sheep having just had it s haired shaved off")
for sent in doc.sents:
print(sent)
print(sent._.parse_string)
print("---")
我终于找到了满足我需求的解决方案。
由于工具 Berkeley Neural Parser 提供了两个选项(NLTK 和 Spacy),我尝试了 NLTK 选项并设法实现了我的目标。由于环境的限制我不得不使用支持Python 3.6.
的2019版本使用 NLTK 我只需要做:
import benepar
class BeneparAnnotator(SyntaxAnnotator):
def __init__(self):
self.parser = benepar.Parser("benepar_en2")
self.extract_arc_representation = False
....
然后:
for caption in tqdm(captions, desc="Parsing sentences"):
parsed_caption = parser.parse(caption)
tree_as_string = parsed_caption.pformat(margin=500, indent=0, nodesep='', parens='()', quotes=False)
parsed_sentences.append(tree_as_string)
在tree_as_string
中你会得到解析后的句子:
(S (NP (DT a) (NN restaurant)) (VP (VBZ has) (NP (JJ modern) (JJ wooden) (NNS tables) (CC and) (NNS chairs))))
您必须调整 margin
参数来设置短语的长度,因为如果短语比边距长,pformat 会用 \n
字符将其分成几行这样(不准确,仅举个例子):
(S\n (NP\n (DT a)\n (NN restaurant)) (VP\n (VBZ has) (NP\n (JJ modern) (JJ wooden) (NNS tables) (CC and) (NNS chairs))))
在这种情况下,所有句子都得到了正确处理,我可以在原始标签向量和解析向量的长度没有差异的情况下训练模型,因为现在用 Spacy 分成几个部分的句子不再被分割分为几个部分。
我希望这对使用相同版本工具的人有所帮助。
此致。