Использование spaCy и Python для обнаружения сходства между предложениями

В этом разделе описаны основные шаги по определению сходства между двумя предложениями с использованием модуля обработки естественного языка под названием spaCy. Следующее руководство основано на реализации Python. Это особенно полезно для сопоставления вводимых пользователем данных с доступными вопросами для FAQ-бота.

Учитывая следующие предложения:

  1. Как мне вступить в гильдию?
  2. Как мне добавить друзей?
  3. Что такое маунт?
  4. Как повысить свой боевой рейтинг?
  5. Могу ли я атаковать верхом?

Какие предложения похожи друг на друга? С человеческой точки зрения мы можем легко определить, что предложение 3 и предложение 5 имеют некоторое сходство друг с другом, поскольку оба содержат слово «гора». Что касается компьютеров, они должны полагаться на сравнение векторов слов (вложения слов), которые представляют многомерное значение каждого слова. Например, вектор слова для слова «банан» представлен следующим образом:

Эти векторы слов генерируются с использованием алгоритма word2vec, который можно обучить с помощью любых библиотек с открытым исходным кодом, таких как Gensim или FastText. К счастью, spaCy имеет собственные встроенные векторы слов, готовые к использованию (применимо только для определенных языков и моделей).

Всего в этом руководстве пять разделов:

  1. Настройка и установка
  2. Использование и вызовы API
  3. Пользовательские функции
  4. Оценка
  5. Заключение

Давайте начнем!

Установка и установка

Как указано на официальном сайте, spaCy совместим с 64-битным CPython 2.7 /3.5+ и работает в Unix / Linux, macOS / OS X и Windows. Последние выпуски spaCy доступны через «pip и conda. Пожалуйста, обратитесь к странице Быстрый запуск, если у вас возникли проблемы с его установкой.

Python

В этом руководстве я буду использовать Python 3.7.1, установленный в виртуальной среде.

модуль spaCy

Ознакомьтесь со следующими командами и запустите их в командной строке:

  • Установка через pip для тех, у кого нет GPU
pip install spacy
  • Установка через conda
conda install -c conda-forge spacy
  • Обновление Spacy через pip
pip install -U spacy
  • Установка через pip для тех, у кого есть GPU
pip install -U spacy[cuda92]

Для этого урока я использую spaCy 2.0.18. После того, как вы установили spaCy, вам необходимо загрузить языковую модель, прежде чем вы сможете ее использовать.

Языковая модель

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

python -m spacy download en_core_web_lg

Если вы хотите получить конкретную версию модели, используйте следующую команду (пример для загрузки маленькой английской модели версии 2.1.0):

python -m spacy download en_core_web_sm-2.1.0 --direct

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

После того, как вы все установили, давайте протестируем доступные нам основные вызовы API.

Модуль импорта и загрузки

Первый шаг - импортировать модуль spaCy и загрузить только что загруженную языковую модель. Я использую Jupyter Notebook и запускаю следующий код:

Если у вас есть проблема с символической ссылкой, вы можете использовать этот код для загрузки модели:

Stopwords

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

#assign the default stopwords list to a variable
STOP_WORDS = spacy.lang.en.stop_words.STOP_WORDS

Начиная с версии 2.0.11, он включает некоторый синтаксический сахар, который позволяет добавлять или удалять запрещающие слова.

  • Проверить текущие стоп-слова:
#nlp refers to the name of the model loaded, change the name accordingly
#nlp = en_core_web_lg.load() or nlp = spacy.load("en_core_web_lg")
print(nlp.Defaults.stop_words)
  • Добавьте один стоп-слово:
nlp.Defaults.stop_words.add("add")
  • Добавьте несколько игнорируемых слов:
nlp.Defaults.stop_words |= {"stop","word",}
  • Удалите один стоп-слово:
nlp.Defaults.stop_words.remove("remove")
  • Удалите несколько игнорируемых слов:
nlp.Defaults.stop_words -= {"stop", "word"}

Соответствие подобия

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

doc1 = nlp("How do I turn sound on/off?")
doc2 = nlp("How do I obtain a pet?")
doc1.similarity(doc2)

Вы должны получить следующий результат, хотя ваши результаты могут отличаться в зависимости от используемой версии:

0.8680366536690709

Значение варьируется от 0 до 1, где 1 означает, что оба предложения одинаковы, а 0 означает отсутствие сходства между обоими предложениями. Как видите, результат довольно высок, даже если предложения не кажутся связанными с человеческой точки зрения. Это связано с тем, что оба предложения начинаются с «Как мне» и заканчиваются символом «?». Однако могут быть случаи, когда в предложениях нет общих слов, но они все же имеют большое сходство. Чтобы решить эту проблему, нам нужно предварительно обработать текст на соответствующие части. Например, если бы мы тестировали, мы использовали следующий образец:

doc1 = nlp("turn sound on/off")
doc2 = nlp("obtain a pet")
doc1.similarity(doc2)

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

0.49538299705127853

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

Пользовательские функции

Назначение пользовательских функций - повысить точность за счет предварительной обработки входных или выходных данных.

Удаление стоп-слов с помощью цикла for

Мы начнем с функции удаления игнорируемых слов. Посмотрите следующий код:

  1. Преобразование текста в нижний регистр (соответствие без учета регистра).
  2. Объявите переменную списка для хранения результатов.
  3. Переберите каждое слово.
  4. Проверьте, не найдено ли слово в списке стоп-слов.
  5. Добавить слово в переменную списка.
  6. Соедините слова в предложение и верните результат.

Удаление игнорируемых слов с помощью понимания списка

Мы можем дополнительно оптимизировать наш код, изменив цикл for на понимание списка:

Как видите, код намного менее подробен. Давайте проверим скорость, чтобы увидеть разницу. Я буду использовать волшебную функцию %timeit, доступную для Jupyter Notebook. Вы можете реализовать свою собственную функцию для хронометрирования результатов:

sample = "Thanks for the cool story bro!"
%timeit remove_stopwords(sample)
%timeit remove_stopwords_fast(sample)

Я получил следующий результат (может отличаться в зависимости от машины):

6.87 ms ± 274 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
6.54 ms ± 70.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Обратите внимание, что бывают случаи, когда цикл for работает лучше, чем понимание списка. Также нужно учитывать читабельность вашего кода. Убедитесь, что вы не слишком оптимизировали свой код ради небольшой выгоды.

Удаление местоимений

Вы можете легко определить, какие слова являются местоимениями, используя вызов token.lemma_:

Если вы хотите получить только форму лемматизации слова, вы можете изменить код следующим образом:

Удалите игнорируемые слова, знаки препинания и местоимения

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

Вычислить сходство с функциями предварительной обработки

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

Оценка

Когда у вас есть готовые функции, вы можете легко вызвать их и сравнить вводимые пользователем данные со списком вопросов в текстовом файле или базе данных. Я буду использовать несколько примеров часто задаваемых вопросов из игры от Yoozoo Games под названием Legacy of Discord - Furious Wings. Список доступен по следующей ссылке. Вопросы включают:

...
What can I do if there is emergent game issue?
What can I do if my Flash Player version is too old?
What can I do if the game stuck on loading page?
What can I do if the Flash Player crashed?
What can I do if I can't login?
How could I get the chance to play the game?
Will my Beta Release account be deleted? Can I recharge during Beta Release?
Can I get high quality souls with my low quality souls?
How many qualities of Hero Souls there are?
What's the usage of Hero Soul?
Why some players get better reward from Conquest?
What's the benefit if a guild occupied a mine area?
Why can't I attack some players' mine?
Is all the mining area the same?
Why other player get better reward than I do?
Can I control the skills manually in Arena?
Why I can't explore a certain stage?
Can I challenge the stage I already passed?
What's the usage of Mount?
...

Я собираюсь протестировать несколько различных образцов входных данных и оценить результаты. Результат основан на трех с наивысшими значениями.

Версия игры престолов

Версия ключевого слова является ключевым фактором высокого сходства.

'What can I do if my Flash Player version is too old?' #0.7180225711646898
'How could I get the chance to play the game?' #0.6954108074024377
"Why can't I enter the game?" #0.6835874250781667

Есть ли применение для креплений?

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

"What's the usage of Mount?" #0.8644284544385071
'How many sockets does a equipment have?' #0.5640873317116436
'How do I change audio setting?' #0.5242988083830281

я ученик

Случайное предложение, не имеющее отношения ни к одному из вопросов в списке.

"What's the highest level I can reach?" #0.42903297878853114
"Why I can't explore a certain stage?" #0.40936269689262966
'How do I gain Activity points?' #0.3974547468215102

Kawaii desu ne

Я использую ромадзи в форме 可愛 い で す ね просто для развлечения. Как я уже упоминал, я использую языковую модель для английского текста. Использование другого языка приведет к аналогичным результатам с очень низким сходством.

'How many types of Heroes in LOA III?' #0.2616090637978352
'How to be VIP?' #0.15710692304217794
'Can I recycle the gem I already used?' #0.14802936240064615

Заключение

Поздравляю с этим! В этом руководстве продемонстрирована только основная концепция использования spaCy для вычисления сходства. В зависимости от вашего сценария использования вы можете и дальше использовать его. Например, простой вызов API для определения трех наиболее похожих вопросов на основе ввода данных пользователем. Возможности безграничны при условии, что у вас есть правильный образ мышления и знания в их использовании. Не стесняйтесь экспериментировать со spaCy, поскольку существует гораздо больше встроенных функций. Мы встретимся снова в следующем уроке!

Ссылка

  1. Https://spacy.io/usage/vectors-similarity
  2. Https://spacy.io/models
  3. Https://support.gtarcade.com/faq?gid=182&cid=651
  4. Https://stackoverflow.com/questions/41170726/add-remove-stop-words-with-spacy