Решение NLP задач при помощи spacy


      Введение

    Обработка естественного языка сейчас используется повсеместно: стремительно развиваются голосовые интерфейсы и чат-боты, разрабатываются модели для обработки больших текстовых данных, продолжает развиваться машинный перевод.

    В этой статье рассматривается библиотека SpaCy, которая на данный момент является одним из самых популярных и удобных решений при обработке текста в Python. Её функционал позволяет решать очень широкий спектр задач: от определения частей речи и выделения именованных сущностей до создания собственных моделей для анализа.

    Обработка данных в SpaCy происходит следующим образом: загруженный для обработки текст последовательно проходит через различные компоненты обработки и сохраняется как экземпляр объекта Doc:

    Doc является центральной структурой данных в SpaCy, именно в нём хранятся последовательности слов или, как их ещё называют, токенов. Внутри объекта Doc можно выделить два других типа объекта: Token и Span. Token представляет собой ссылку на отдельные слова документа, а Span – ссылку на последовательность из нескольких слов (их можно создавать самостоятельно):


    Ещё одной важной структурой данных является объект Vocab, который хранит набор справочных таблиц, общий для всех документов. Это позволяет экономить память и обеспечивать единый источник информации для всех обрабатываемых документов.

    Токены документов связаны с объектом Vocab через хеш, используя который можно получить начальные формы слов или другие лексические атрибуты токенов:


    

    1. Операции с синтаксисом


    Для более сложных операций по обработке текста используются модели, специально натренированные для задач, связанных с синтаксисом, выделением именованных сущностей и работы со значениями слов. Например, для английского языка существует 3 официальных модели, различающихся размером. Для русского языка на настоящий момент официальная модель ещё не обучена, однако уже есть модель ru2 из сторонних источников, которая умеет работать с синтаксисом.

    Для демонстрации возможностей SpaCy, рассмотрим модель для английского языка (en_core_web_sm). С использованием этой модели мы можем для каждого из токенов получить часть речи, роль в предложении и токен, от которого он зависит:

import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("New Apple MacBook set launch tomorrow")

for token in doc:
    token_text = token.text
    token_pos = token.pos_
    token_dep = token.dep_
    token_head = token.head.text
    print(f"{token_text:<12}{token_pos:<10}" \
          f"{token_dep:<10}{token_head:<12}")

New         PROPN     compound  MacBook     
Apple       PROPN     compound  MacBook     
MacBook     PROPN     nsubj     set         
set         VERB      ROOT      set         
to          PART      aux       launch      
launch      VERB      xcomp     set         
tomorrow    NOUN      npadvmod  launch 

    Для расшифровки названий тегов можно воспользоваться функций explain:

print(spacy.explain("aux"))
print(spacy.explain("PROPN"))
auxiliary
proper noun

    Здесь на экран выводятся расшифровки аббревиатур, из которых мы можем узнать, что aux обозначает вспомогательную частицу (auxiliary), а PROPN – имя собственное (proper noun).

    В SpaCy также реализована возможность узнать начальную форму слова для любого из токенов (для местоимений используется -PRON-):

import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("I saw a movie yesterday")
print(' '.join([token.lemma_ for token in doc]))

'-PRON- see a movie yesterday'

      2. Выделение именованных сущностей


        Для решения данной задачи мною были протестированы еще ряд библиотек, помимо Spacy:

    Библиотека NLTK - это классическая библиотека для обработки естественного языка, она проста в использовании, не требует длительного изучения и выполняет 99% задач, которые могут возникнуть при решении студенческих задач.

import nltk
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
nltk.download('maxent_ne_chunker')
nltk.download('words')
for sent in nltk.sent_tokenize(english_text):
   for chunk in nltk.ne_chunk(nltk.pos_tag(nltk.word_tokenize(sent))):
      if hasattr(chunk, 'label'):
         print(chunk)

    Output:
(GPE Indian/JJ)
(ORGANIZATION PHP/NNP)
(GPE Laravel/NNP)
(PERSON Need/NNP)
(ORGANIZATION SAP/NNP)
(ORGANIZATION FI/NNP)

    Как мы видим, NLTK неплохо справился со своей задачей, однако для того, чтобы сделать результат более «богатым» нам придется обучать свой собственный tagger (или выбрать другой из достаточно широкого списка). Но, стоит ли это делать в 2020 году если есть более простые решения? 

        DeepPavlov - это библиотека с открытым исходным кодом, построенная TensorFlow и Keras.
    Разработчики предполагают использование системы преимущественно для «диалоговых» систем, чат-ботов и т.д., но библиотека превосходно подходит и для решения исследовательских задач. Использовать ее без серьезной работы по «кастомизации» решения — задача, похоже, даже не предполагаемая создателями из МФТИ.
            Странный и нелогичный подход к архитектуре кода, противоречащий дзен Python, тем не менее приносит хорошие результаты, если потратить достаточно времени, чтобы с ним разобраться.

from deeppavlov import configs, build_model
from deeppavlov import build_model, configs

ner_model = build_model(configs.ner.ner_ontonotes_bert, download=True)
result = ner_model([english_text])
for i in range(len(result[0][0])):
     if result [1][0][i] != 'O':
         print(result[0][0][i], result[1][0][i])

    Output:
7 B-DATE
days I-DATE
Indian B-NORP
Laravel B-PRODUCT
Codeigniter B-PRODUCT
daily B-DATE
4 B-TIME
hours I-TIME
Monday B-DATE
27th I-DATE
Jan I-DATE
6 B-DATE
months I-DATE
FI B-PRODUCT
AREAWe I-PRODUCT
10 B-DATE
days I-DATE
noon B-TIME
tomorrow B-DATE


    Результат предсказуемый, понятный, подробный и один из лучших. Сама модель также может использоваться напрямую в Hugging Face Transformers, что снимает, во многом, претензии к архитектуре кода.

        Почему SpaCy 

   Spacy - это Python-библиотека с открытым исходным кодом для обработки естественного языка, она издается под лицензией MIT. Как правило, каждый, кто сталкивается с необходимостью решения каких-то задач для обработки естественного языка, рано или поздно узнает об этой библиотеке. Большинство функций доступно «из коробки», разработчики заботятся о том, чтобы библиотека была удобна в использовании.
      Spacy предлагает 18 меток (tags) которыми отмечаются именованные сущности, равно как и простой способ дообучить свою собственную модель. Также стоит отметить отличную документацию, огромное сообщество и хорошую поддержку — и станет понятно, почему это решение стало таким популярным в последние пару лет.

         Для работы с текстом требуется выделить сущности, упомянутые в тексте. Чтобы получить список именованных сущностей в документе, используется атрибут doc.ents, а для получения метки для этой сущности – атрибут ent.label_:

import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("Apple is looking at buying U.K. startup for 1$ billion")
for ent in doc.ents:
    print(ent.text, ent.label_)


Apple ORG
U.K. GPE
1$ billion MONEY

    Здесь также можно использовать атрибут explain, чтобы узнать расшифровки меток именованных сущностей:

print(spacy.explain("GPE"))

    Countries, cities, states

А функция displacy поможет наглядно обозначить списки сущностей прямо в тексте:

from spacy import displacy
displacy.render(doc, style='ent', jupyter=True)


    Вывод


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