我正在使用Spacy,它在如何拆分句子方面存在问题。出于某种原因,有些句子被分成两部分或树状部分,而不是一部分,所以当我试图用TensorFlow训练模型时,我会得到一个错误,即向量的维度不匹配。
我已经尝试将标记符更改为空白标记符,如此链接所示。所以,我的类看起来像(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
这种获取句子的方法就是从这里开始的,因为我想检索用Benepar解析的句子,但其中一些句子被分为几个句子,而不是获取整个解析的句子。
出于某种原因,对于某些句子,它们被分为2或3部分:
示例1:一只刚刚剃掉头发的黑绵羊
它被分为:【一只刚刚剃掉头发的害群之马】
示例2:路标上写着s.3rd av。并且没有左转
它分为:[路标上写着s.,3rd av.,禁止左转]
示例3:道路中间的单向专用标志
它分为:[一个转弯的方式,只有在路中间的标志]
示例4:两头奶牛站在树前的草地上
它分为:【两头奶牛站在树前的草地上,’s】
示例5:一辆30号德比车在右侧车门半开的情况下转弯
它分为:[一辆德比车,30号车右车门半开着转弯]
我还有其他类似于示例1的句子,它们只被正确地拆分为一个句子。例如:
一只站在草地上的背绵羊
它分为:【一只站在草地上的背绵羊】
因此,当我用list(parsed_caption.sents)[0]
获得一个标题的sents时,我得到的是整个标题,而不是标题的一部分。
问题出在哪里?
-----更新以尝试更好地解释我的问题。-----
我有一个句子列表,我想用Benepar解析这些句子,然后把它们作为工具的输入,看看我是否能得到更好的结果。由于该工具的anaconda环境的限制,我需要使用2019版本,它是在Python 3.6上工作的版本。
我遇到的问题是,有些句子是分开的,Benepar将它们解析为独立的块(而不是整个句子一起),当我试图用list(parsed_caption.sents)[0]
(因为应该只有一个元素,整个句子)获得它们时,我只得到一个块,当我运行实验时,我得到一个错误,即原始句子和解析的句子的大小不匹配。
我有超过61.6万句的训练句子。它们中的大多数都被解析得很好,没有被分成块,如果我这样做了:
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]
来获取句子,我只会解析句子的一大块。我只期待一句话,不再期待了。
由于只得到时间,当我训练模型和句子时,现在是行动的时候了被选中我得到一个错误,因为的长度现在是行动的时间和时间不相同。
我不会在您的阶段使用自定义标记器,为了确保不拆分空白,请使用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。(我不明白是怎么回事/为什么,但可以。)
您的问题是benepar可靠地返回一个句子,但spaCy没有,这会导致Tensorflow的维度问题。
如果以上任何一项都是错误的,请纠正我。
假设这是正确的,令牌化器与您的问题无关。它决定了如何将字符串拆分为单词,而不是如何定义句子。(拥有不同数量的代币也可能导致维度问题,但听起来这不是你的问题。)
如果你只想从spaCy获得一段文本的令牌列表,你可以这样做:
tokens = list(nlp(text))
就是这样。如果你已经知道每个输入应该是一个句子,那么你就不需要使用句子迭代器。
此外,我注意到您链接到的说明来自旧版本的文档,与最新版本的benepar不兼容。使用最新版本和当前的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
字符的几行(不确切,只是为了显示一个示例):
(Sn (NPn (DT a)n (NN restaurant)) (VPn (VBZ has) (NPn (JJ modern) (JJ wooden) (NNS tables) (CC and) (NNS chairs))))
在这种情况下,所有的句子都得到了正确的处理,我可以在原始标签向量和解析向量的长度没有差异的情况下训练模型,因为现在,使用Spacy被拆分为几个部分的句子不再被拆分为多个部分。
我希望这能帮助使用相同版本工具的人。
致以最良好的问候。