SpaCy

Материал из NLPub
Перейти к навигации Перейти к поиску
Well.svg Данная статья была создана в рамках учебного задания.
Студент: Участник:Алексей Паульс
Преподаватель: Антон Алексеев
Срок: 10 декабря 2019

В настоящее время задание завершено и проверено. Данная страница может свободно правиться другими участниками NLPub.


spaCy — это open-source библиотека для NLP, написанная на Python и Cython. В отличие от NLTK, который широко используется для преподавания и исследований, spaCy фокусируется на предоставлении программного обеспечения для разработки.


Установка

pip

$ pip install -U spacy

conda

$ conda install -c conda-forge spacy

Compile from source

python -m pip install -U pip                   # update pip
git clone https://github.com/explosion/spaCy   # clone spaCy
cd spaCy                                       # navigate into directory
python -m venv .env                            # create environment in .env
source .env/bin/activate                       # activate virtual environment
export PYTHONPATH='pwd'                        # set Python path to spaCy directory
pip install -r requirements.txt                # install all requirements
python setup.py build_ext --inplace            # compile spaCy

Ubuntu

$ sudo apt-get install build-essential python-dev git

macOS / OS X

Установите актуальную версию XCode, включая “Command Line Tools”. macOS и OS X поставляются с предустановленным Python и git.

Windows

Для установки spaCy потребуется установить Visual C++ Build Tools или Visual Studio Express соответствующую версии, используемой для компиляции вашего интерпретатора Python. Для официальных дистрибутивов соответствующие версии указаны в таблице:

Distribution Version
Python 2.7 Visual Studio 2008
Python 3.4 Visual Studio 2010
Python 3.5+ Visual Studio 2015

Установка и запуск spaCy с поддержкой GPU

Для того, чтобы установить spaCy с поддержкой GPU, используйте следующие спецификации: spacy[cuda], spacy[cuda90], spacy[cuda91], spacy[cuda92] или spacy[cuda100]. Если вы знаете вашу версию cuda, используйте более явную спецификацию, например:

$ pip install -U spacy[cuda92]

После установки нужно активировать использование GPU с помощью вызова spacy.prefer_gpu или spacy.require_gpu() в скрипте до загрузки моделей. Исключение require_gpu будет вызвано, если GPU не доступен. Пример:

import spacy

spacy.prefer_gpu()
nlp = spacy.load("en_core_web_sm")

Архитектура

Центральными структурами данных в spaCy являются Doc и Vocab. Объект Doc хранит последовательности токенов и все их аннотации. Объект Vocab хранит набор справочных таблиц, что делает общую информацию доступной для всех документов. При централизованном хранении строк, векторов слов и лексических атрибутов отстутствует необходимость хранения нескольких копий этих данных. Это экономит память и обеспечивает единый источник правды.

Текстовые аннотации также предназначены для обеспечения единого источника правды: объект Doc владеет данными, а Span и Token являются представлениями, указывающими на них. Объект Doc создается объектом Tokenizer, а затем модифицируется in-place компонентами pipeline. Объект Language координирует эти компоненты. Он берет необработанный текст и отправляет его по pipeline, возвращая аннотированный документ.

SpaCy architecture.jpeg

Использование

Загрузка моделей

Для использования spaCy нужно скачать предобученную статистическую языковую модель для соответствующего языка:

python -m spacy download en_core_web_sm
# При загрузке в Jupyter Notebook понадобится перезапустить ядро

И загрузить ее в скрипт, для этого есть два способа:

import spacy

nlp = spacy.load("en_core_web_sm")  # используя встроенный загрузчик
nlp = en_core_web_sm.load()  # импортируя явно (как Python модуль)

На данный момент поддерживаются модели для 10 языков и одна мультиязычная. Для некоторых языков (например, английского) доступны модели, предобученные на разных данных.

Русскоязычные модели

На данный момент официальная модель для русского языка не готова, но есть модели из сторонних источников:

Pipeline

Когда вы вызваете nlp на тексте, spaCy разбивает этот текст на токены, получая объект Doc (содержит последовательность объектов класса Token и другую полезную информацию). Затем Doc последовательно обрабатывается в несколько шагов — это называется pipeline. В стандартных моделях pipeline состоит из следущюих компонент: tagger, parser и entity recognizer. Каждая компонента принимает на вход объект Doc и возвращает его расширенную версию (с новыми аттрибутами для каждого токена или для всего Doc в целом).

SpaCy pipeline.jpg

Pipeline зависит от языковой модели (компоненты могут требовать дополнительную информацию о токенах) и его базовая версия определяется в метаданных модели.

В примере ниже используется nlp.pipe, чтобы обработать входящий iterable объект (коллекция текстов) как поток, и его аргумент disable, чтобы исключить из обработки некоторые этапы. В pipeline остался только один компонент — ner, который извлекает из текста именованные сущности.

import spacy

texts = [
    "Net income was $9.4 million compared to the prior year of $2.7 million.",
    "Revenue exceeded twelve billion dollars, with a loss of $1b.",
]

nlp = spacy.load("en_core_web_sm")
for doc in nlp.pipe(texts, disable=["tagger", "parser"]):
    print([(ent.text, ent.label_) for ent in doc.ents])

Output:

[('$9.4 million', 'MONEY'), ('the prior year', 'DATE'), ('$2.7 million', 'MONEY')]
[('twelve billion dollars', 'MONEY'), ('1b', 'MONEY')]

Встроенные компоненты pipeline

String Component name Description
tagger Tagger Присваивает токенам метки частей речи (part-of-speech-tagging).
parser DependencyParser Присваивает токенам метки зависимостей токенов друг от друга (dependency labels).
ner EntityRecognizer Присваивает тексту именованные сущности (named entities).
entity_linker EntityLinker Присваивает именованным сущностям ID из базы знаний (связывает сущности). Должен располагаться после entity recognizer.
textcat TextCategorizer Присваивает тексту категории.
entity_ruler EntityRuler Присваивает тексту именованные сущности, найденные по шаблонам (pattern rules).
sentencizer Sentencizer Добавляет основанное на правилах разбиение предложения без нахождения зависимостей между токенами.
merge_noun_chunks merge_noun_chunks Объединяет последовательности существительных в единый токен. Должен располагаться после tagger и parser.
merge_entities merge_entities Объединяет все сущности в единый токен. Должен располагаться после ner.
merge_subtokens merge_subtokens Объединяет subtokens, полученные parser в единый токен. Должен располагаться после parser.

Tokenization

Токенизация — это задача разделения текста на значимые сегменты, называемые токенами. Входные данные для токенизатора представляют собой текст в кодировке Unicode, а выходные данные — объект Doc.

Токенизация в spaCy не “деструктивная” — по токенам можно легко восстановить исходный текст.

Этот скрипт подсчитывает количество вхождений токенов в текст:

import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("Apple is looking at buying U.K. startup for $1 billion. \
                 This is a very promising startup.")
vocab = {}
for token in doc:
    if token.text not in vocab.keys():
      vocab[token.text] = 1
    else:
      vocab[token.text] += 1
print(vocab)

Output:

{'Apple': 1, 'is': 2, 'looking': 1, 'at': 1, 'buying': 1, 'U.K.': 1, 'startup': 2, 'for': 1, '$': 1, '1': 1, 'billion': 1, '.': 2, 'This': 1, 'a': 1, 'very': 1, 'promising': 1}

Обратите внимание на то, что “$” и “1” - два разных токена, хотя в тексте написаны слитно, а “U.K.” - один токен, что знаки пунктуации не входят в состав токенов и т.д.

Также spaCy позволяет добавлять специальные правила для токенизации и создавать свой токенизатор — эти процедуры описаны в официальной документации.

POS Tagger

После токенизации spaCy может разметить Doc. На этом этапе используется статистическая модель, которая позволяет spaCy предсказать, какой тег или метка наиболее вероятно применимы в этом контексте.

Лингвистические аннотации доступны как атрибуты объекта Token. Как и многие библиотеки NLP, spaCy кодирует все строки в хеш-значения, чтобы уменьшить использование памяти и повысить эффективность. Поэтому, чтобы получить читаемое строковое представление атрибута, нам нужно добавить подчеркивание _ к его имени:

import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("Apple is looking at buying U.K. startup for $1 billion")

for token in doc:
    print(token.text, token.lemma_, token.pos_, token.tag_, token.dep_,
            token.shape_, token.is_alpha, token.is_stop)

Output:

Apple Apple PROPN NNP nsubj Xxxxx True False
is be AUX VBZ aux xx True True
looking look VERB VBG ROOT xxxx True False
at at ADP IN prep xx True True
buying buy VERB VBG pcomp xxxx True False
U.K. U.K. PROPN NNP compound X.X. False False
startup startup NOUN NN dobj xxxx True False
for for ADP IN prep xxx True True
$ $ SYM $ quantmod $ False False
1 1 NUM CD compound d False False
billion billion NUM CD pobj xxxx True False

Скрипт выше выводит следующую информацию о токенах:

TEXT LEMMA POS TAG DEP SHAPE ALPHA STOP
Токен без изменений Нормальная форма Coarse part-of-speech Fine-grained part-of-speech Роль в отношении зависимости Обобщенная форма токена (отражает орфографические признаки) Состоит ли токен из букв Является ли стоп-словом

Полный список аттрибутов класса Token и их описаний можно найти в официальной документации.

Dependency parsing

spaCy обладает быстрым и точным синтаксическим анализатором зависимостей и имеет богатый API для навигации по дереву. Синтаксический анализатор также обеспечивает обнаружение границ предложений и позволяет перебирать базовые словосочетания (chunks). Вы можете проверить, был ли объект Doc проанализирован с помощью атрибута doc.is_parsed, который возвращает логическое значение.

Noun chunks

import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("Autonomous cars shift insurance liability toward manufacturers")
for chunk in doc.noun_chunks:  # Iterate over the base noun phrases in the document
    print(f"{chunk.text}, {chunk.root.text}, {chunk.root.dep_}, {chunk.root.head.text}")

Output:

Autonomous cars, cars, nsubj, shift
insurance liability, liability, dobj, shift
manufacturers, manufacturers, pobj, toward

Скрипт выше выводит следующую информацию о chunks:

TEXT ROOT.TEXT ROOT.DEP_ ROOT.HEAD.TEXT
Сама фраза Ключевое слово Тип синтаксического отношения Главное слово по отношению к root


spaCy использует термины head и child, чтобы описать слова, связанные одной дугой в дереве зависимостей.


Навигация по дереву зависимостей

import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("Autonomous cars shift insurance liability toward manufacturers")
for token in doc:
    print(token.text, token.dep_, token.head.text, token.head.pos_,
            [child for child in token.children])

Форматированный output:

TEXT DEP HEAD TEXT HEAD POS CHILDREN
Autonomous amod cars NOUN
cars nsubj shift VERB Autonomous
shift ROOT shift VERB cars, liability, toward
insurance compound liability NOUN
liability dobj shift VERB insurance
toward prep shift NOUN manufacturers
manufacturers pobj toward ADP

Так как синтаксические зависимости представляются в виде дерева, у каждого слова только один head

Named Entity Recognition

Именованная сущность — это «объект реального мира», которому присвоено имя, например, человек, страна, продукт или название книги. spaCy может распознавать различные виды именованных сущностей в документе, запрашивая прогноз у модели. Поскольку модели являются статистическими и сильно зависят от примеров, на которых они были обучены, это не всегда работает идеально, и может потребоваться некоторая настройка позже, в зависимости от вашего варианта использования.

Стандартный способ доступа к аннотациям сущностей — это doc.ents, где хранится последовательность объектов Span. Тип сущности доступен либо в виде значения хеш-функции, либо в виде строки, используя атрибуты ent.label и ent.label_. Объект Span является последовательностью токенов (можно итерироваться по сущности или получить ее текстовую форму, как будто это один токен).

Вы также можете получить доступ к аннотациям объекта Token, используя атрибуты token.ent_iob (начало, середина или конец сущности) и token.ent_type (тип сущности, если не задан - вернется пустая строка).

import spacy

nlp = spacy.load("en_core_web_sm")
# Не забываем, что ner входит в стандартный pipeline, поэтому явно указывать его не нужно
doc = nlp("San Francisco considers banning sidewalk delivery robots")

# document level
ents = [(e.text, e.start_char, e.end_char, e.label_) for e in doc.ents]
print("Сущности: ", ents)

# token level
ent_san = [doc[0].text, doc[0].ent_iob_, doc[0].ent_type_]
ent_francisco = [doc[1].text, doc[1].ent_iob_, doc[1].ent_type_]
print("Токен 'Sun': ", ent_san)
print("Токен 'Francisco': ", ent_francisco)

Output:

Сущности:  [('San Francisco', 0, 13, 'GPE')]
Токен 'Sun':  ['San', 'B', 'GPE']
Токен 'Francisco':  ['Francisco', 'I', 'GPE']

Настройка pipeline

Отключение компонент

# Disable loading:
nlp = spacy.load("en_core_web_sm", disable=["tagger", "parser"])

# Disable for processing:
for doc in nlp.pipe(texts, disable=["tagger", "parser"]):
    # Do something with the doc here

# Disable for block:
# 1. Use as a contextmanager
with nlp.disable_pipes("tagger", "parser"):
    doc = nlp("I won't be tagged and parsed")
doc = nlp("I will be tagged and parsed")

# 2. Restore manually
disabled = nlp.disable_pipes("ner")
doc = nlp("I won't have named entities")
disabled.restore()

Удаление и замена компонент

nlp.remove_pipe("parser")
nlp.rename_pipe("ner", "entityrecognizer")
nlp.replace_pipe("tagger", my_custom_tagger)

Добавление компонент

nlp.add_pipe(my_component, name="print_info", last=True)

Создание своей компоненты pipeline

В качестве примера создадим компонент, который получает Doc и печатает некоторую информацию о нем: количество токенов, части речи для токенов и условное сообщение на основе длины документа:

import spacy

def my_component(doc):
    print("After tokenization, this doc has {} tokens.".format(len(doc)))
    print("The part-of-speech tags are:", [token.pos_ for token in doc])
    if len(doc) < 10:
        print("This is a pretty short document.")
    return doc

nlp = spacy.load("en_core_web_sm")
nlp.add_pipe(my_component, name="print_info", last=True)
print(nlp.pipe_names)  # ['tagger', 'parser', 'ner', 'print_info']
doc = nlp("This is a sentence.")

Output:

['tagger', 'parser', 'ner', 'print_info']
After tokenization, this doc has 5 tokens.
The part-of-speech tags are: ['DET', 'AUX', 'DET', 'NOUN', 'PUNCT']
This is a pretty short document.

Другой способ — создать класс, как это показано здесь.

Word vectors

spaCy позволяет работать с векторными представлениями слов, сравнивать слова по их представлениями и т.д.

Модели со встроенными векторами слов делают их доступными в качестве атрибута Token.vector. Doc.vector и Span.vector по умолчанию будут усреднены по всем векторам токенов. Вы также можете проверить, есть ли у токена назначенный вектор, и получить L2 норму, которую можно использовать для нормализации векторов. > Чтобы сделать модели компактными и быстрыми, маленькие модели spaCy (все названия, оканчивающиеся на sm) поставляются без векторов слов и содержат только контекстно-зависимые тензоры. Скачивайте и используйте модели, названия которых оканчиваются на md или lg.

python -m spacy download en_core_web_lg

import spacy

nlp = spacy.load("en_core_web_md")
tokens = nlp("dog cat afskfsd")

print("Tokens info:")
for token in tokens:
    print(token.text, token.has_vector, token.vector_norm, token.is_oov)

print("\nTokens similarities:")
for i in range(len(tokens)):
    for j in range(i + 1, len(tokens)):
        print(tokens[i].text, tokens[j].text, tokens[i].similarity(tokens[j]))

print("\nToken similarity to itself:")
print(tokens[0].text, tokens[0].text, tokens[0].similarity(tokens[0]))

Output:

Tokens info:
dog True 7.0336733 False
cat True 6.6808186 False
afskfsd False 0.0 True

Tokens similarities:
dog cat 0.80168545
dog afskfsd 0.0
cat afskfsd 0.0

Token similarity to itself:
dog dog 1.0

Обучение моделей

SpaCy позволяет не только загрузить предобученные статистические модели для разных задач, но и обучить свои. В примере ниже показан процесс обучения модели для Dependency Parsing.

Для большинства целей лучший способ обучения — через интерфейс командной строки. Команда spacy train учитывает многие детали. Также, вы можете подготовить свои данные, используя команду spacy convert, которая принимает многие распространенные форматы данных, включая .iob для именованных объектов и формат CoNLL для зависимостей:

git clone https://github.com/UniversalDependencies/UD_Spanish-AnCora
mkdir ancora-json
python -m spacy convert UD_Spanish-AnCora/es_ancora-ud-train.conllu ancora-json
python -m spacy convert UD_Spanish-AnCora/es_ancora-ud-dev.conllu ancora-json
mkdir models
python -m spacy train es models ancora-json/es_ancora-ud-train.json ancora-json/es_ancora-ud-dev.json

Примеры обучения моделей для других задач есть в документации.

Сохранение и загрузка

Если вы изменяли pipeline, vocabulary, vectors и entities или обновляли модель, вы в конечном итоге захотите сохранить свой прогресс — например, все, что находится в вашем объекте nlp. Это означает, что вам придется переводить его содержимое и структуру в формат, который можно сохранить, например, в файл или строку байтов. Этот процесс называется сериализацией. SpaCy поставляется со встроенными методами сериализации и поддерживает протокол Pickle.

Все классы-контейнеры (Language(nlp), Doc, Vocab и StringStore) имеют следующие методы:

METHOD RETURNS EXAMPLE
to_bytes bytes data = nlp.to_bytes()
from_bytes object nlp.from_bytes(data)
to_disk - nlp.to_disk("/path")
from_disk object nlp.from_disk("/path")

Визуализация

В spaCy есть встроенные функции для визуализации зависимостей и сущностей в браузере или Jupyter Nootbook:

import spacy
from spacy import displacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("This is a sentence.")
displacy.serve(doc, style="dep")

Output: SpaCy displacy dep.jpg

import spacy
from spacy import displacy

text = "Great Piano Academy is situated in Mayfair or the City of London and has world-class piano instructors."

nlp = spacy.load("en_core_web_sm")
doc = nlp(text)
displacy.serve(doc, style="ent")

Output: SpaCy displacy ner.jpg

Ссылки

См. также

Примечания