Эта серия блоговЦель состоит в использовании двух ведущих библиотек обработки языка производственного уровня (John Snow Labs Apache Spark NLPиspaCy от Explosion AI), чтобы иметь дело с реальными сценариями обработки естественного языка (NLP), чтобы провести сравнение между ними. Обе библиотеки имеют открытый исходный код и лицензированы для коммерческого использования (Apache 2.0 и Массачусетский технологический институт). Оба имеют активную разработку, частые релизы и растущие сообщества.
Хотелось бы проанализировать и обозначить преимущества обеих библиотек, узнать, чем они отличаются для дата-сайентистов и разработчиков, и в каких ситуациях удобнее использовать ту или другую. Этот анализ надеется провести объективное исследование с определенным количеством субъективных решений (как в каждом приложении для понимания естественного языка) в несколько этапов.
Как бы просто это ни звучало, сравнение двух разных библиотек и проведение сопоставимых тестов могут быть очень сложными. Имейте в виду, что ваше приложение будет иметь другие сценарии, конвейеры данных, текстовые функции, настройку оборудования и некоторые нефункциональные требования, чем то, что сделано здесь.
Я предполагаю, что читатель уже знаком с концепциями НЛП и программированием. У вас может не быть необходимых знаний ни об одном из этих инструментов, но моя цель — сделать код как можно более понятным, сделать его как можно более читабельным, чтобы читатель не увяз в слишком многих деталях. Обе библиотеки имеют общедоступную документацию и полностью открыты. Так что советую сначала посмотретьspaCy 101иБыстрый старт Spark-NLPдокументация.
Об этих двух библиотеках
Исходный код Spark-NLP был открыт в октябре 2017 года. Как библиотека Spark, это собственное расширение Apache Spark. Он представляет набор этапов Spark ML Pipeline в виде оценщиков и преобразователей для работы с распределенными наборами данных.Spark NLP AnnotatorsВключены не только базовые функции, такие как токенизация, нормализация и тегирование частей речи, но и другие расширенные функции, такие как расширенный анализ тональности, проверка орфографии, статус утверждения и многое другое. Все это работает в Spark. в рамках ML. Spark-NLP написан на Scala, работает на JVM и использует оптимизацию и планы выполнения Spark. В настоящее время библиотека предоставляет API для Scala и Python.
spaCy — это популярная и простая в использовании библиотека Python для обработки естественного языка. это недавноОпубликована версия 2.0, который содержит множество моделей, таких как нейронные сети и распознавание сущностей. Он обеспечивает лучшую в отрасли точность и скорость, а также имеет активное сообщество с открытым исходным кодом. spaCy существует не менее трех лет, и его первый релиз на GitHub датируется началом 2015 года.
Spark-NLP в настоящее время не включает набор предварительно обученных моделей. А spaCy предоставляет предварительно обученные модели для семи (европейских) языков, поэтому пользователи могут быстро вводить целевые предложения и возвращать результаты без моделей обучения, включая сегментацию слов, ввод, части речи (POS), сходство, распознавание объектов. Подождите.
Обе библиотеки обеспечивают некоторый уровень настройки с помощью параметров, позволяют сохранять обученные конвейеры на диске и требуют от разработчиков разработки программ, использующих эти библиотеки в конкретных случаях использования. Искра НЛП позволяетВнедрение конвейера NLP как части конвейера машинного обучения Spark ML (от загрузки данных, NLP, проектирования функций, обучения модели, настройки гиперпараметров и оценки)что проще. В то же время, поскольку Spark может оптимизировать весь процесс выполнения конвейера, Spark НЛП работает быстрее.
Эталонное приложение
Программа, которую я написал здесь, будет предсказывать тег POS в исходном файле .txt. Большая часть очистки и подготовки данных выполняется последовательно. Оба приложения будут обучаться на одних и тех же данных и делать прогнозы на одних и тех же данных для максимально возможной сопоставимости.
Моя цель — проверить два столпа любой статистической процедуры:
1. Точность, мера того, насколько хорошо программа может правильно предсказать лингвистические особенности.
2. Производительность, а значит сколько мне ждать такой точности. и сколько входных данных я могу ввести в программу, прежде чем она рухнет или мой внук вырастет.
Чтобы сравнить эти показатели, мне нужно убедиться, что две библиотеки имеют максимальную сопоставимость. Я использовал следующую конфигурацию:
1. Настольный компьютер, операционная система Linux Mint. Он поставляется с жестким диском SSD и 16 ГБ оперативной памяти, а также с 4-ядерным процессором Intel i5-6600K с частотой 3,5 ГГц.
2. Данные для обучения, тестирования и с правильными результатами в формате NLTK POS (см. ниже).
3. Ноутбук Jupyter Python 3 с установленным spaCy 2.0.5.
4. Ноутбук Apache Zeppelin 0.7.3 с установленными Spark-NLP 1.3.0 и Apache Spark 2.1.1.
данные
Данные, используемые для обучения, тестирования и согласования, поступают изNational American Corpus. Я использовал письменный корпус MASC 3.0.2 для газетной части.
Я очистил данные с помощью инструмента, предоставленного корпусом (ANCtool). Хотя я могу использовать формат данных CoNLL, который содержит много информации о тегах, такой как термины, индексы и распознавание сущностей. Но я предпочитаю использовать формат данных NLTK, который включает теги Penn POS. Для моей цели достаточно. Данные выглядят так:
Neither|DT Davison|NNP nor|CC most|RBS other|JJ RxP|NNP opponents|NNSdoubt|VBP the|DT efficacy|NN of|IN medications|NNS .|.
Как видите, в обучающих данных содержится следующее:
- Обнаруженные границы предложений (новая строка, новое предложение)
- Результат сегментации слов (разделенных пробелами)
- Обнаруженный POS (разделенный знаком "|")
В необработанном текстовом файле все намешано, перемешано и без каких-либо стандартных границ.
Ниже приведены ключевые показатели ориентиры, которые мы хотим запустить тест.
Наборы эталонных данных
В этой статье мы будем использовать два эталонных набора данных. Первый очень маленький и используется для интерактивной отладки и экспериментов:
- Учебные данные: 36 файлов .txt общим объемом 77 КБ.
- Тестовые данные: 14 файлов .txt общим объемом 114 КБ.
- Нужно предсказать 21362 слова
Второй набор данных по-прежнему не «большие данные», а относительно большой набор данных, используемый для оценки типичных сценариев автономного приложения:
- Учебные данные: 72 файла .txt общим объемом 150 КБ.
- Два тестовых набора данных: 9225 файлов .txt, 75 МБ, 1125 файлов, 15 МБ.
- Нужно предсказать 13 миллионов слов
Обратите внимание, что здесь мы не оцениваем наборы данных «больших данных». Это связано с тем, что, хотя spaCy может использовать преимущества многоядерных процессоров, он не может изначально использовать кластеры, такие как Spark NLP. В результате Spark NLP на несколько порядков быстрее, чем spaCy, на терабайтных наборах данных с использованием кластеров. Точно так же база данных на мейнфрейме будет работать лучше, чем база данных MySQL, которую я установил здесь локально. Моя цель — оценить обе библиотеки на одной машине и использовать многоядерные возможности обеих библиотек. Это обычная ситуация при разработке, а также применимая к приложениям, которым не нужно работать с большими наборами данных.
Давайте начнем
Итак, приступим. Во-первых, мы должны импортировать соответствующие библиотеки и начать.
spaCy
import os
import io
import time
import re
import random
import pandas as pd
import spacy
nlp_model = spacy.load('en', disable=['parser', 'ner'])
nlp_blank = spacy.blank('en', disable=['parser', 'ner'])
Я отключил некоторые пайпы в spaCy, чтобы не засорять его ненужными парсерами. Я также использовал nlp_model в качестве эталона, который представляет собой предварительно обученную модель НЛП, предоставленную spaCy. Но я буду использовать nlp_blank, который будет более репрезентативным, это будет моя самообучающаяся модель.
Spark-NLP
import org.apache.spark.sql.expressions.Window
import org.apache.spark.ml.Pipeline
import com.johnsnowlabs.nlp._
import com.johnsnowlabs.nlp.annotators._
import com.johnsnowlabs.nlp.annotators.pos.perceptron._
import com.johnsnowlabs.nlp.annotators.sbd.pragmatic._
import com.johnsnowlabs.nlp.util.io.ResourceHelper
import com.johnsnowlabs.util.Benchmark
Первая проблема, с которой я столкнулся, заключалась в том, что я имел дело с тремя совершенно разными результатами токенизации, из-за чего было трудно определить, соответствует ли слово как тегам токенизации, так и тегам POS:
1. Токенизатор spaCy использует подход, основанный на правилах, и уже включает словарь, который содержит много общих сокращений для токенизации.
2. Токенизатор SparkNLP имеет свои правила токенизации.
3. Мои тренировочные и тестовые данные. Эти данные сегментированы в соответствии со стандартом ANC. Во многих случаях он разбивает слова совершенно иначе, чем токенизаторы этих двух библиотек.
Итак, чтобы преодолеть это, мне нужно решить, как сравнивать теги POS для совершенно другого набора тегов. Для Spark-NLP я оставлю все как есть. Его правила по умолчанию в основном соответствуют открытому стандартному формату сегментации слов ANC. Для spaCy мне нужно ослабить правила инфикса, чтобы повысить точность сопоставления сегментации слов, не используя слова сегментации «-».
spaCy
class DummyTokenMatch:
def __init__(я, содержимое):
self.start = лямбда: 0
self.end = лямбда: len(содержание)
def do_nothing(content):
вернуть [DummyTokenMatch (содержимое)]
model_tokenizer = nlp_model.tokenizer
nlp_blank.tokenizer = spacy.tokenizer.Tokenizer(nlp_blank.vocab, prefix_search=model_tokenizer.prefix_search,
suffix_search=model_tokenizer.suffix_search,
infix_finditer=ничего не делать,
token_match=model_tokenizer.token_match)
Обратите внимание: я передаю nlp_blank объект словаря, поэтому nlp_blank на самом деле не пуст. Этот объект словаря vocab содержит правила и политики английского языка, которые помогают нашей пустой модели помечать POS и токенизировать английские слова. Таким образом, у spaCy есть небольшое преимущество для начала, в то время как Spark-NLP не «знает» английский язык заранее.
конвейер обучения
Теперь наступает этап обучения. В spaCy мне нужно предоставить указанный формат обучающих данных, который выглядит так:
TRAIN_DATA = [
(«Мне нравятся зеленые яйца», {'метки': ['N', 'V', 'J', 'N']}),
("Ешьте голубую ветчину", {'метки': ['V', 'J', 'N']})
]
В то время как в Spark-NLP я должен предоставить папку с файлами данных .txt в формате «слово с разделителями | токен», который выглядит как данные обучения ANC. Итак, я просто передаю путь к папке тегу POS (т.е. PerceptronApproach).
Давайте загрузим тренировочные данные spaCy. В приведенном ниже коде я должен добавить несколько сгенерированных вручную исключений, правил и некоторых символов, потому что обучающему набору spaCy нужны чистые данные.
spaCy
start = time.time()
train_path = "./цель/обучение/"
train_files = sorted([train_path + f for f in os.listdir(train_path) if os.path.isfile(os.path.join(train_path, f))])
TRAIN_DATA = []
for file in train_files:
fo = io.open (файл, режим = 'r', кодировка = 'utf-8')
для строки в fo.readlines():
строка = строка.strip()
если строка == ":
Продолжать
строка_слов = []
line_tags = []
для пары в re.split("\\s+", строка):
тег = пара.полоса().разделить("|")
line_words.append(re.sub('(\w+)\.', '\1', tag[0].replace('$', ”).replace('-', ”).replace('\” , )))
line_tags.append(тег[-1])
TRAIN_DATA.append((' '.join(line_words), {'теги': line_tags}))
fo.close()
TRAIN_DATA[240] = ('Компания заявила, что единовременная поставка существенно устранит все будущие потери на установке .', {'tags': ['DT', 'NN', 'VBD', 'DT', 'JJ ', '-', 'NN', 'NN', 'MD', 'RB', 'VB', 'DT', 'JJ', 'NNS', 'IN', 'DT', 'NN', '.']})
n_iter=5
Теггер = nlp_blank.create_pipe('теггер')
tagger.add_label('-')
tagger.add_label('(')
tagger.add_label(')')
tagger.add_label('#')
tagger.add_label('...')
tagger.add_label("одноразовый")
nlp_blank.add_pipe(tagger)
optimizer = nlp_blank.begin_training()
for i in range(n_iter):
случайный. случайный (TRAIN_DATA)
потери = {}
для текста, аннотации в TRAIN_DATA:
nlp_blank.update(, [аннотации], sgd=оптимизатор, потери=потери)
печать(потери)
печать (время. время () - начало)
часы работы
{ 'Теггер': 5.773235303101046}
{'теггер': 1.138113870966123}
{'теггер': 0,46656132966405683}
{'теггер': 0,5513760568314119}
{'теггер': 0.2541630900934435}
Time to run: 122.11359786987305 seconds
Пришлось проделать дополнительную работу, чтобы обойти некоторые ямы. spaCy не позволяет мне использовать словарь моего токенизатора, потому что он содержит некрасивые символы. Например, spaCy не будет обучать предложения с тегами «большой экран» или «Нет», если они не указаны в теге vocab. Мне пришлось добавить эти символы в список словарей, чтобы spaCy мог найти их во время обучения.
Теперь давайте посмотрим, как строятся пайплайны в Spark-NLP.
Spark-NLP
val documentAssembler = new DocumentAssembler()
.etinputcol («текст»)
.setOutputCol("документ")
val tokenizer = new Tokenizer()
.setInputCols("документ")
.setOutputCol("токен")
.setPrefixPattern("\\A([^\\s\\p{L}\\d\\$\\.#]*)")
.addInfixPattern("(\\$?\\d+(?:[^\\s\\d]{1}\\d+)*)")
val posTagger = new PerceptronApproach()
.setInputCols («документ», «токен»)
.setOutputCol("поз")
.setCorpusPath("/home/saif/nlp/comparison/target/training")
.setNIterations(5)
val finisher = new Finisher()
.setInputCols("токен", "поз")
.setOutputAsArray (истина)
val pipeline = new Pipeline()
.setStages (массив (
документАссемблер,
токенизатор,
постегггь,
финишер
))
val model = Benchmark.time("Время обучения модели") {
pipe.fit(данные)
}
Как видите, построение пайплайна — это очень линейный процесс: настраиваем ассемблер документов, который делает целевой текстовый столбец целью последующих аннотаторов (т. и символические формы в качестве входных данных.
Мне пришлось обновить шаблон префикса и добавить новый шаблон инфикса для соответствия датам и числам так же, как ANC (это может стать значением по умолчанию в следующей версии Spark NLP). Как видите, каждый компонент пайплайна находится под контролем пользователя, здесь нет неявного знания словарного запаса или английского языка, в отличие от spaCy.
corpusPath из PerceptronApproach передается в папку, содержащую текстовые файлы с разделителями вертикальной чертой. Аннотатор финишера упаковывает результат POS и токенизации, чтобы его можно было использовать на следующем шаге. Как видно из названия SetOutputAsArray(), он возвращает массив, а не объединенную строку, но для его обработки требуются вычислительные затраты.
Данные, передаваемые в функцию fit(), не имеют значения, потому что единственный обучаемый тегировщик НЛП — это PerceptronApproach. И этот теггер обучается с помощью внешней POS Corpora.
часы работы
Time to train model: 3.167619593sec
В качестве примечания в конвейер можно внедрить SentenceDetector или SpellChecker. В некоторых случаях это может помочь повысить точность POS, сообщая модели, где заканчивается предложение.
Что дальше?
На данный момент мы инициализировали библиотеки, загрузили данные и обучили модель токенизатора с использованием обеих библиотек. Обратите внимание, что spaCy поставляется с предварительно обученным токенизатором, поэтому этот шаг может не понадобиться, если ваши текстовые данные поступают из языка (например, английского) и домена (например, сводки новостей), где spaCy был обучен. Но для того, чтобы сгенерированные символы лучше соответствовали нашему корпусу АНК, модификация инфикса причастия очень важна. Благодаря 5 итерациям Spark-NLP обучается более чем в 38 раз быстрее, чем spaCy.
В следующем выпуске этой серии мы рассмотрим код, точность и производительность, запустив конвейер NLP с моделью, которую мы только что обучили.