Рубрики
Без рубрики

Python для НЛП: Начало работы с библиотекой Stanford CoreNLP

Автор оригинала: Usman Malik.

Это девятая статья в моей серии статей по Python для НЛП. В предыдущей статье мы видели , как библиотека шаблонов Python может использоваться для выполнения различных задач НЛП, начиная от токенизации и заканчивая POS-тегированием, а также классификацией текста и анализом настроений. До этого мы исследовали библиотеку TextBlob для выполнения аналогичных задач обработки естественного языка.

В этой статье мы рассмотрим библиотеку Stanford CoreNLP , которая является еще одной чрезвычайно удобной библиотекой для обработки естественного языка. Мы увидим различные особенности StanfordCoreNLP с помощью примеров. Поэтому, прежде чем тратить время впустую, давайте начнем.

Настройка среды

Процесс установки Stanford CoreNLP не так прост, как другие библиотеки Python. На самом деле Stanford CoreNLP-это библиотека, которая на самом деле написана на Java. Поэтому убедитесь, что в вашей системе установлена Java. Вы можете свободно скачать последнюю версию Java .

После установки Java вам необходимо загрузить JAR-файлы для библиотек Stanford CoreNLP. Файл JAR содержит модели, которые используются для выполнения различных задач НЛП. Чтобы загрузить JAR-файлы для английских моделей, загрузите и распакуйте папку, расположенную на официальном сайте Stanford CoreNLP .

Следующее, что вам нужно сделать, это запустить сервер, который будет обслуживать запросы, отправленные оболочкой Python в библиотеку Stanford CoreNLP. Перейдите к пути, по которому вы распаковали папку JAR files. Перейдите в папку и выполните следующую команду в командной строке:

$ java -mx6g -cp "*" edu.stanford.nlp.pipeline.StanfordCoreNLPServer -timeout 10000

Приведенная выше команда инициирует сервер Stanford CoreNLP. Параметр -mx6g указывает, что объем используемой сервером памяти не должен превышать 6 гигабайт. Важно отметить, что вы должны работать под управлением 64-битной системы, чтобы иметь кучу размером до 6 ГБ. Если вы используете 32-разрядную систему, вам, возможно, придется уменьшить объем памяти, выделенной для сервера.

После выполнения приведенной выше команды вы должны увидеть следующие выходные данные:

[main] INFO CoreNLP - --- StanfordCoreNLPServer#main() called ---
[main] INFO CoreNLP - setting default constituency parser
[main] INFO CoreNLP - warning: cannot find edu/stanford/nlp/models/srparser/englishSR.ser.gz
[main] INFO CoreNLP - using: edu/stanford/nlp/models/lexparser/englishPCFG.ser.gz instead
[main] INFO CoreNLP - to use shift reduce parser download English models jar from:
[main] INFO CoreNLP - http://stanfordnlp.github.io/CoreNLP/download.html
[main] INFO CoreNLP -     Threads: 8
[main] INFO CoreNLP - Starting server...
[main] INFO CoreNLP - StanfordCoreNLPServer listening at /0:0:0:0:0:0:0:0:9000

Сервер работает на порту 9000.

Теперь последний шаг – установить оболочку Python для библиотеки Stanford CoreNLP. Оболочка, которую мы будем использовать, – это pycorenlp . Следующий сценарий загружает библиотеку-оболочку:

$ pip install pycorenlp

Теперь мы все готовы подключиться к серверу Stanford CoreNLP и выполнить необходимые задачи НЛП.

Чтобы подключиться к серверу, мы должны передать адрес сервера Stanford CoreNLP, который мы инициализировали ранее, классу StanfordCoreNLP модуля pycorenlp . Возвращенный объект затем можно использовать для выполнения задач НЛП. Посмотрите на следующий сценарий:

from pycorenlp import StanfordCoreNLP

nlp_wrapper = StanfordCoreNLP('http://localhost:9000')

Выполнение задач НЛП

В этом разделе мы кратко рассмотрим использование библиотеки Stanford CoreNLP для выполнения общих задач НЛП.

Лемматизация, POS-маркировка и распознавание именованных сущностей

Лемматизация, маркировка частей речи и распознавание именованных сущностей-самые основные задачи НЛП. Библиотека Stanford CoreNLP поддерживает функциональность конвейера, которая может быть использована для выполнения этих задач структурированным образом.

В следующем сценарии мы создадим аннотатор, который сначала разбивает документ на предложения, а затем далее разбивает предложения на слова или токены. Затем слова аннотируются тегами распознавания POS и именованных сущностей.

doc = "Ronaldo has moved from Real Madrid to Juventus. While messi still plays for Barcelona"
annot_doc = nlp_wrapper.annotate(doc,
    properties={
        'annotators': 'ner, pos',
        'outputFormat': 'json',
        'timeout': 1000,
    })

В приведенном выше сценарии у нас есть документ с двумя предложениями. Мы используем метод annotate объекта-оболочки Stanford CoreNLP, который мы инициализировали ранее. Метод принимает три параметра. Параметр annotator принимает тип аннотации, которую мы хотим выполнить над текстом. Мы передаем 'near, post' в качестве значения параметра annotator , который указывает, что мы хотим аннотировать ваш документ для POS-тегов и именованных сущностей.

Переменная output Format определяет формат, в котором вы хотите получить аннотированный текст. Возможные значения: json для объектов JSON, xml для формата XML, text для обычного текста и serialize для сериализованных данных.

Последний параметр-это тайм-аут в миллисекундах, который определяет время, в течение которого оболочка должна ждать ответа от сервера перед тайм-аутом.

В выходных данных вы должны увидеть объект JSON следующим образом:

{'sentences': [{'index': 0, 'entitymentions': [{'docTokenBegin': 0, 'docTokenEnd': 1, 'tokenBegin': 0, 'tokenEnd': 1, 'text': 'Ronaldo', 'characterOffsetBegin': 0, 'characterOffsetEnd': 7, 'ner': 'PERSON'}, {'docTokenBegin': 4, 'docTokenEnd': 6, 'tokenBegin': 4, 'tokenEnd': 6, 'text': 'Real Madrid', 'characterOffsetBegin': 23, 'characterOffsetEnd': 34, 'ner': 'ORGANIZATION'}, {'docTokenBegin': 7, 'docTokenEnd': 8, 'tokenBegin': 7, 'tokenEnd': 8, 'text': 'Juventus', 'characterOffsetBegin': 38, 'characterOffsetEnd': 46, 'ner': 'ORGANIZATION'}], 'tokens': [{'index': 1, 'word': 'Ronaldo', 'originalText': 'Ronaldo', 'lemma': 'Ronaldo', 'characterOffsetBegin': 0, 'characterOffsetEnd': 7, 'pos': 'NNP', 'ner': 'PERSON', 'before': '', 'after': ' '}, {'index': 2, 'word': 'has', 'originalText': 'has', 'lemma': 'have', 'characterOffsetBegin': 8, 'characterOffsetEnd': 11, 'pos': 'VBZ', 'ner': 'O', 'before': ' ', 'after': ' '}, {'index': 3, 'word': 'moved', 'originalText': 'moved', 'lemma': 'move', 'characterOffsetBegin': 12, 'characterOffsetEnd': 17, 'pos': 'VBN', 'ner': 'O', 'before': ' ', 'after': ' '}, {'index': 4, 'word': 'from', 'originalText': 'from', 'lemma': 'from', 'characterOffsetBegin': 18, 'characterOffsetEnd': 22, 'pos': 'IN', 'ner': 'O', 'before': ' ', 'after': ' '}, {'index': 5, 'word': 'Real', 'originalText': 'Real', 'lemma': 'real', 'characterOffsetBegin': 23, 'characterOffsetEnd': 27, 'pos': 'JJ', 'ner': 'ORGANIZATION', 'before': ' ', 'after': ' '}, {'index': 6, 'word': 'Madrid', 'originalText': 'Madrid', 'lemma': 'Madrid', 'characterOffsetBegin': 28, 'characterOffsetEnd': 34, 'pos': 'NNP', 'ner': 'ORGANIZATION', 'before': ' ', 'after': ' '}, {'index': 7, 'word': 'to', 'originalText': 'to', 'lemma': 'to', 'characterOffsetBegin': 35, 'characterOffsetEnd': 37, 'pos': 'TO', 'ner': 'O', 'before': ' ', 'after': ' '}, {'index': 8, 'word': 'Juventus', 'originalText': 'Juventus', 'lemma': 'Juventus', 'characterOffsetBegin': 38, 'characterOffsetEnd': 46, 'pos': 'NNP', 'ner': 'ORGANIZATION', 'before': ' ', 'after': ''}, {'index': 9, 'word': '.', 'originalText': '.', 'lemma': '.', 'characterOffsetBegin': 46, 'characterOffsetEnd': 47, 'pos': '.', 'ner': 'O', 'before': '', 'after': ' '}]}, {'index': 1, 'entitymentions': [{'docTokenBegin': 14, 'docTokenEnd': 15, 'tokenBegin': 5, 'tokenEnd': 6, 'text': 'Barcelona', 'characterOffsetBegin': 76, 'characterOffsetEnd': 85, 'ner': 'ORGANIZATION'}], 'tokens': [{'index': 1, 'word': 'While', 'originalText': 'While', 'lemma': 'while', 'characterOffsetBegin': 48, 'characterOffsetEnd': 53, 'pos': 'IN', 'ner': 'O', 'before': ' ', 'after': ' '}, {'index': 2, 'word': 'messi', 'originalText': 'messi', 'lemma': 'messus', 'characterOffsetBegin': 54, 'characterOffsetEnd': 59, 'pos': 'NNS', 'ner': 'O', 'before': ' ', 'after': ' '}, {'index': 3, 'word': 'still', 'originalText': 'still', 'lemma': 'still', 'characterOffsetBegin': 60, 'characterOffsetEnd': 65, 'pos': 'RB', 'ner': 'O', 'before': ' ', 'after': ' '}, {'index': 4, 'word': 'plays', 'originalText': 'plays', 'lemma': 'play', 'characterOffsetBegin': 66, 'characterOffsetEnd': 71, 'pos': 'VBZ', 'ner': 'O', 'before': ' ', 'after': ' '}, {'index': 5, 'word': 'for', 'originalText': 'for', 'lemma': 'for', 'characterOffsetBegin': 72, 'characterOffsetEnd': 75, 'pos': 'IN', 'ner': 'O', 'before': ' ', 'after': ' '}, {'index': 6, 'word': 'Barcelona', 'originalText': 'Barcelona', 'lemma': 'Barcelona', 'characterOffsetBegin': 76, 'characterOffsetEnd': 85, 'pos': 'NNP', 'ner': 'ORGANIZATION', 'before': ' ', 'after': ''}]}]}

Если вы внимательно посмотрите на приведенный выше скрипт, то сможете найти теги СООБЩЕНИЙ, именованные сущности и лемматизированную версию каждого слова.

Лемматизация

Давайте теперь рассмотрим аннотированные результаты. Сначала мы напечатаем лемматизацию для слов в двух предложениях нашего набора данных:

for sentence in annot_doc["sentences"]:
    for word in sentence["tokens"]:
        print(word["word"] + " => " + word["lemma"])

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

Ronaldo=>Ronaldo
has=>have
moved=>move
from=>from
Real=>real
Madrid=>Madrid
to=>to
Juventus=>Juventus
.=>.
While=>while
messi=>messus
still=>still
plays=>play
for=>for
Barcelona=>Barcelona

Например, вы можете видеть , что слово moved было лемматизировано в move , точно так же слово plays было лемматизировано в play .

POS-маркировка

Точно так же мы можем найти POS-теги для каждого слова. Посмотрите на следующий сценарий:

for sentence in annot_doc["sentences"]:
    for word in sentence["tokens"]:
        print (word["word"] + "=>" + word["pos"])

В выходных данных вы должны увидеть следующие результаты:

Ronaldo=>NNP
has=>VBZ
moved=>VBN
from=>IN
Real=>JJ
Madrid=>NNP
to=>TO
Juventus=>NNP
.=>.
While=>IN
messi=>NNS
still=>RB
plays=>VBZ
for=>IN
Barcelona=>NNP

Набор тегов, используемый для POS-тегов, является набором тегов Penn Treebank и может быть найден здесь .

Распознавание Именованных Сущностей

Чтобы найти именованные сущности в нашем документе, мы можем использовать следующий скрипт:

for sentence in annot_doc["sentences"]:
    for word in sentence["tokens"]:
        print (word["word"] + "=>" + word["ner"])

Вывод выглядит следующим образом:

Ronaldo=>PERSON
has=>O
moved=>O
from=>O
Real=>ORGANIZATION
Madrid=>ORGANIZATION
to=>O
Juventus=>ORGANIZATION
.=>O
While=>O
messi=>O
still=>O
plays=>O
for=>O
Barcelona=>ORGANIZATION

Мы видим , что Роналду был идентифицирован как ЧЕЛОВЕК в то время как Барселона была идентифицирована как Организация , что в данном случае правильно.

Анализ настроений

Чтобы найти смысл предложения, все, что вам нужно, – это передать sentiment в качестве значения для свойства annotators . Посмотрите на следующий сценарий:

doc = "I like this chocolate. This chocolate is not good. The chocolate is delicious. Its a very tasty chocolate. This is so bad"
annot_doc = nlp_wrapper.annotate(doc,
    properties={
       'annotators': 'sentiment',
       'outputFormat': 'json',
       'timeout': 1000,
    })

Чтобы найти сентимент, мы можем перебирать каждое предложение, а затем использовать свойство sentiment Value для поиска сентимента. Значение sentiment возвращает значение от 1 до 4, где 1 соответствует очень негативному настроению, а 4-очень позитивному. Свойство sentiment может быть использовано для получения sentiment в вербальной форме, то есть positive , negative или neutral .

Следующий сценарий находит тональность для каждого предложения в документе, который мы определили выше.

for sentence in annot_doc["sentences"]:
    print ( " ".join([word["word"] for word in sentence["tokens"]]) + " => " \
        + str(sentence["sentimentValue"]) + " = "+ sentence["sentiment"])

Выход:

I like this chocolate . => 2 = Neutral
This chocolate is not good . => 1 = Negative
The chocolate is delicious . => 3 = Positive
Its a very tasty chocolate . => 3 = Positive
This is so bad => 1 = Negative

Вывод

Stanford CoreNLP – еще одна чрезвычайно удобная библиотека для обработки естественного языка. В этой статье мы изучили, как настроить среду для запуска StanfordCoreNLP. Затем мы изучили использование библиотеки StanfordCoreNLP для общих задач НЛП, таких как лемматизация, POS-тегирование и распознавание именованных сущностей, и, наконец, мы завершили статью сентиментальным анализом с использованием StanfordCoreNLP.