文档是一系列令牌对象。访问句子并命名 实体,将注释导出到numpy数组,无损地序列化为 压缩的二进制字符串。 Doc对象包含一个TokenC数组 结构。 Python级的Token和Span对象是此视图 数组,即它们本身并不拥有数据。
这是有道理的,但我很想知道它到底是如何工作的,特别是因为,正如我在下面显示的那样,一个人可以删除Doc
对象(或至少指向它的变量)它),然后继续工作。
import spacy
nlp = spacy.load('en_core_web_sm')
from sys import getsizeof
doc = nlp('King Henry VIII married six times.')
print(doc)
print(getsizeof(doc))
token = doc[0]
print(token)
print(getsizeof(token))
span = doc[:3]
del doc
span.merge() # This updates the vestigial doc despite deletion.
print(token)
print(getsizeof(token)) # Same size as before, being just a pointer.
print(token.doc) # Doc can be retrieved.
print(getsizeof(token.doc))
输出:
King Henry VIII married six times.
184
King
80
King Henry VIII
80
King Henry VIII married six times.
184
鉴于我对Python的基本了解,我很想知道:
Doc
对象在何处以及如何精确地存储在内存中,以使上述内容正常工作。token
变量可以使用80个字节的所有功能来调用此对象,为什么doc
变量的大小应比184时的大小大一倍?答案 0 :(得分:2)
好吧,您可以在这里找到代码:https://github.com/explosion/spaCy/tree/master/spacy/tokens。它在Cython中,因此还有一些其他概念,但是您可能仍然会发现它很有用。
简短的回答是Span
和Token
对象确实保存了对Doc
的引用,即使删除了您的{ Doc
变量。这样一来,您就可以继续查询文档。
然而,doc
没有对其doc
或Span
对象的引用。这些对象严格来说是瞬态的:每次编写Token
时都会重新创建一个新的Token
实例。看看doc[i]
中的__getitem__
实现,看看会发生这种情况。
spaCy的早期版本确实缓存了doc.pyx
对象,希望提高某些访问模式的效率。但是,这会在Token
及其令牌之间创建一个参考周期,这会破坏参考计数。有多种方法可以解决此问题(使用弱引用),但最终的净成本使其不值得-只做简单的事情,并每次创建一个新的doc
对象,效果更好。这也有助于人们编写几乎有效的代码---几乎正确通常是最糟糕的错误类型。