Natural Language Processing (NLP) в PySpark: токенизация, стоп-слова, N-граммы

Обработка естественного языка (Natural Language Processing, NLP) является перспективным направлением Data Science и Big Data. Сегодня мы расскажем вам о применении методов NLP в PySpark. В этой статье вы узнаете об обычной токенизации и на основе регулярных выражений, стоп-словах русского и английского языков, а также о N-граммах в PySpark.

Токенизация в PySpark

Токенизация — это процесс разбиения текста на текстовые единицы (чаще всего слова). В PySpark за это отвечают Tokenizer и RegexTokenizer. Создадим DataFrame, который состоит из простых предложений, а также определим функцию (udf), которая будет считать количество слов в списке.

from pyspark.ml.feature import Tokenizer, RegexTokenizer
from pyspark.sql.functions import col, udf
from pyspark.sql.types import IntegerType
sentenceDataFrame = spark.createDataFrame([
    (0, "Привет я слышал о NLP PySpark"),
    (1, "Как же хочется попасть на Big Data курсы"),
    (2, "Модели,линейной,регрессии,очень,хороши")
], ["id", "sentence"])
countTokens = udf(lambda words: len(words), IntegerType())

Tokenizer разбивает текст на слова, разделённые пробелом. Ниже код на Python это демонстрирует. Как видим, последнюю строку он посчитал за один токен, потому что в этом тексте нет пробелов.

tokenizer = Tokenizer(inputCol="sentence", outputCol="words")
tokenized = tokenizer.transform(sentenceDataFrame)
tokenized.select("sentence", "words")\
    .withColumn("tokens", countTokens(col("words"))).show(truncate=False)
+-------------------------------------------------+------+
|words                                            |tokens|
+-------------------------------------------------+------+
|[привет, я, слышал, о, nlp, pyspark]             |6     |
|[как, же, хочется, попасть, на, big, data, курсы]|8     |
|[модели,линейной,регрессии,очень,хороши]         |1     |
+-------------------------------------------------+------+

На практике лучше применять RegexTokenizer, который позволяет проводить токенизацию на основе регулярных выражений. Параметр pattern используется в качестве разделителя. По умолчанию pattern имеет значение "\\s+" (пробел), т.е. ничем не отличается от обычного Tokenizer. Но мы можем разбить текст на слова с учётом запятых:

regexTokenizer = RegexTokenizer(inputCol="sentence", outputCol="words", pattern=r"[,\s]")
regexTokenized = regexTokenizer.transform(sentenceDataFrame)
regexTokenized.select("sentence", "words") \
    .withColumn("tokens", countTokens(col("words"))).show(truncate=False)
+-------------------------------------------------+------+
|words                                            |tokens|
+-------------------------------------------------+------+
|[привет, я, слышал, о, nlp, pyspark]             |6     |
|[как, же, хочется, попасть, на, big, data, курсы]|8     |
|[модели, линейной, регрессии, очень, хороши]     |5     |
+-------------------------------------------------+------+

Следует, однако, быть острожным с регулярными выражениями, если работаете с кириллицей. Например, \W (не слово) найдёт не только знаки препинания и цифры, но и кириллические буквы.

Удаление стоп-слов

Многие алгоритмы Machine Learning очень чувствительны к шумам. В NLP к таким шумам относятся стоп-слова. Чаще всего это слова, которые не несут большой информативной нагрузки. В PySpark для удаления стоп-слов используется класс StopWordsRemover.

StopWordsRemover принимает список слов, а не строку (str), поэтому предварительно следует токенизировать текст, как это было описано выше. Вот так в Python выглядит удаление стоп-слов для английского языка:

from pyspark.ml.feature import StopWordsRemover

sentenceData = spark.createDataFrame([
    (0, ["I", "saw", "the", "red", "balloon"]),
    (1, ["Mary", "had", "a", "little", "lamb"])
], ["id", "raw"])
remover = StopWordsRemover(inputCol="raw", outputCol="filtered")
remover.transform(sentenceData).show()
+---+----------------------------+--------------------+
|id |raw                         |filtered            |
+---+----------------------------+--------------------+
|0  |[I, saw, the, red, balloon] |[saw, red, balloon] |
|1  |[Mary, had, a, little, lamb]|[Mary, little, lamb]|
+---+----------------------------+--------------------+

Для русского языка в PySpark есть специальный список стоп-слов, доступный через loadDefaultStopWords(language). Например, для токенизированного выше текста на русском (regexTokenized) удаление стоп-слов выглядит следующим образом:

rus_stopwords = StopWordsRemover.loadDefaultStopWords("russian")
remover = StopWordsRemover(inputCol="words",
                           outputCol="filtered",
                           stopWords=rus_stopwords)
remover.transform(regexTokenized).show()
+-------------------------------------------------+--------------------------------------------+
|words                                            |filtered                                    |
+-------------------------------------------------+--------------------------------------------+
|[привет, я, слышал, о, nlp, pyspark]             |[привет, слышал, nlp, pyspark]              |
|[как, же, хочется, попасть, на, big, data, курсы]|[хочется, попасть, big, data, курсы]        |
|[модели, линейной, регрессии, очень, хороши]     |[модели, линейной, регрессии, очень, хороши]|
+-------------------------------------------------+--------------------------------------------+

Разбиение на N-граммы

Ещё один полезным инструментом NLP является разбиение слов на N-граммы — группы последовательных слов, которые можно извлечь из текстов. N-граммы позволяют сохранить контекст, поскольку становится известно, какое слово идёт следующим.

Для этого в PySpark используется Ngram, который принимает список слов, поэтому текст следует также предварительно следует токенизировать. Ngram не зависит от языка. Вот так в Python выглядит извлечение биграмм (би = 2):

from pyspark.ml.feature import NGram

wordDataFrame = spark.createDataFrame([
    (0, ["Привет", "Я", "слышал", "о", "Spark"]),
    (1, ["PySpark", "NLP", "это", "очень", "просто", "если", "подумать"]),
    (2, ["Модели", "линейной", "регрессии", "просты"])
], ["id", "words"])
ngram = NGram(n=2, inputCol="words", outputCol="ngrams")
ngramDataFrame = ngram.transform(wordDataFrame)
ngramDataFrame.select("ngrams").show()
+---------------------------------------------------------------------------+
|ngrams                                                                     |
+---------------------------------------------------------------------------+
|[Привет Я, Я слышал, слышал о, о Spark]                                    |
|[PySpark NLP, NLP это, это очень, очень просто, просто если, если подумать]|
|[Модели линейной, линейной регрессии, регрессии просты]                    |
+---------------------------------------------------------------------------+

На этом примере видно, что «регрессия» теперь ассоциируется со словом «линейная».

О предобработке текстов в рамках NLP с помощью PySpark вы узнаете на специализированном курсе «NLP – обработка естественного языка с Python» в нашем лицензированном учебном центре обучения и повышения квалификации разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве.

Источники

  1. https://spark.apache.org/docs/latest/ml-features.html

Поиск по сайту