#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, мне любопытно узнать:
- Где и как
Doc
объект хранится в памяти именно для того, чтобы позволить вышеуказанному работать. - Если
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
объект. Это также помогает людям не писать код, который почти работает — — почти правильный часто является наихудшим типом неправильного.