SpaCy: как документ продолжает существовать и работать после удаления?

#python #spacy

#python #spacy

Вопрос:

Из документации:

Документ — это последовательность объектов-токенов. Доступ к предложениям и именованным объектам, экспорт аннотаций в массивы numpy, сериализация без потерь в сжатые двоичные строки. Объект Doc содержит массив структур TokenC. Объекты Token уровня Python и 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, мне любопытно узнать:

  1. Где и как Doc объект хранится в памяти именно для того, чтобы позволить вышеуказанному работать.
  2. Если token переменная может вызывать этот объект со всеми его функциональными возможностями в 80 байтах, почему doc переменная должна быть более чем вдвое больше размера в 184?

Ответ №1:

Ну, вы можете найти код здесь:https://github.com/explosion/spaCy/tree/master/spacy/tokens . Это в Cython, поэтому есть несколько дополнительных концепций, но вы все равно можете счесть это полезным.

Короткий ответ заключается в том, что объекты Span и Token содержат ссылку на Doc , и эта ссылка сохраняет Doc объект живым даже после удаления вашей doc переменной. Это позволяет вам продолжать запрашивать документ.

doc Однако в Span нем нет никаких ссылок на его Token или, в целом, на его объекты. Эти объекты являются строго временными: новый Token экземпляр создается заново каждый раз, когда вы пишете doc[i] . Взгляните на __getitem__ реализацию в doc.pyx , чтобы увидеть, как это происходит.

Ранние версии spaCy кэшировали Token объекты, надеясь повысить эффективность для некоторых шаблонов доступа. Однако это создает цикл ссылок между doc и его токенами, что приводит к путанице при подсчете ссылок. Есть способы обойти это (используя слабые ссылки), но в конечном итоге из-за чистой стоимости оно того не стоит — лучше просто делать простую вещь и каждый раз создавать новый Token объект. Это также помогает людям не писать код, который почти работает — — почти правильный часто является наихудшим типом неправильного.