Время прочтения: 4 мин.

Для задач NLP, как мы знаем, недостаточно положить в модель голый текст. Датасет возьмем с кеггла (https://www.kaggle.com/rishisankineni/text-similarity) — оставим тренировочную часть и попробуем предобработать данные для наших моделей. Надеюсь, это моя не последняя статья и далее мы разберем уже конкретные модели, но сегодня остановимся на предобработке.

Импортируем нужные библиотеки и создаем Спарк-сессию:

from pyspark.sql import SparkSession
import pyspark.sql.functions as f

spark = SparkSession.builder \
        .master("local[*]") \
        .appName('testForNTA') \
        .getOrCreate()

Возьмем данные с соревнования по определению тональности текстов. Загружаем датасет (подробнее о работе с данными и их загрузке можно прочитать в первой статье):

df = spark.read.format('csv') \
               .option('header', True) \
               .load('text-similarity.csv')

Для переименования столбца можем сделать его select, выделить нужный столбец и сопоставить нужный alias. Переименуем наш столбец description_x для удобства в text:

df = df.select(f.col('description_x').alias('text'))

Посмотрим на наши данные:

df.show(5, vertical = True)

Для начала рассмотрим разбивку на токены и TF-IDF (тк у нас ознакомительная статья, разбирать это не будем, ознакомиться с математическим смыслом можно тут: https://ru.wikipedia.org/wiki/TF-IDF). Импортируем нужные библиотеки:

from pyspark.ml.feature import HashingTF, IDF, Tokenizer

В следующем сегменте кода мы начинаем с набора предложений. Мы разбиваем каждое предложение на слова, используя Tokenizer. Для каждого предложения (набора слов) мы используем HashingTFхеширование предложения в вектор признаков. Мы используем IDFдля масштабирования векторов признаков; это обычно повышает производительность при использовании текста в качестве функций. Затем наши векторы признаков можно было бы передать алгоритму обучения.

Перейдем к коду, начнем с набора текста. Разобъем кадлное предложение на слова, используя Tokenizer.

Давайте разобьем наш текст на отдельные токены и запишем их в столбец text_words. В Tokenizer передаем столбец входных данных и название результирующего столбца. Далее с помощью transform модифицируем наши данные:

tokenizer = Tokenizer(inputCol = 'text', outputCol = 'text_words')
data_words = tokenizer.transform(df)
data_words.show(5)

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

Далее используем хеширование в вектор признаков, для масштабирования используем IDF, затем наши признаки можно было бы передавать алгоритму машинного обучения.

hash_tf = HashingTF(inputCol = 'text_words', outputCol = 'text_words_features', numFeatures = 20)
data_featurized = hash_tf.transform(data_words)
data_featurized.show(5)

В features запишем конечный результат:

idf = IDF(inputCol = 'text_words_features', outputCol = 'features')
idf_model = idf.fit(data_featurized)
data_scaled = idf_model.transform(data_featurized)

Посмотрим на результат:

data_scaled.show(5)

Давайте разберем еще один метод предобработки — Word2Vec. Почитать подробнее можно тут: https://ru.wikipedia.org/wiki/Word2vec Он преобразует каждое слово в уникальные вектор заданного размера. По сути, на выходе мы получаем векторное представление слов на естественном языке. После этого мы можем использовать, допустим, для расчета сходства документов или других NLP-задач. Давайте обработаем наш текст, импортируем нужное, важное и загрузим датасет заново.

from pyspark.ml.feature import Word2Vec

df = spark.read.format('csv') \
               .option('header', True) \
               .load('text-similarity.csv')

df = df.select(f.col('description_x').alias('text'))

Разбить текст можем и обычным сплитом, создадим 1 колонку:

df = df.withColumn('text_list', (f.split(df['text'], ' ')))

При создании Word2Vec можем задать размерность нашего вектора, давайте сделаем его равным 3:

word2Vec = Word2Vec(vectorSize = 3, minCount = 0, inputCol = 'text_list', outputCol = 'result')
model = word2Vec.fit(df)
result = model.transform(df)

Давайте проверим результат:

result.show(5)

Убедимся, что в колонке result лежат трехмерные вектора:

result.take(1)[0]['result']

Отлично, все получилось.

Перейдем к следующему методу — CountVectorizer, о нем читаем тут: https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html Он помогает преобразовать набор документов (текствовых), в векторы количества токенов с этом тексте. Модель создает разреженную матрицу для наших документов.

Если кратко, на выходе мы получаем таблицу со столбцами-словами, которые встречаются в тексте, а на пересечении, соответственно, их количество в определенном тексте. Импортируем нужное.

from pyspark.ml.feature import CountVectorizer

df = spark.read.format('csv') \
               .option('header', True) \
               .load('text-similarity.csv')

df = df.select(f.col('description_x').alias('text'))
df = df.withColumn('text_list', (f.split(df['text'], ' ')))

Необязательный параметр minDFтакже влияет на процесс подбора, указывая минимальное количество (или долю, если < 1,0) документов, в которых термин должен появиться, чтобы быть включенным в словарь.

Заполним столбцы на входе и на выходе:

cv = CountVectorizer(inputCol = 'text_list', outputCol = 'features')
model = cv.fit(df)
result = model.transform(df)

result.show(5, vertical = True)

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

result.take(1)[0]['features']

Отлично, получили две единички.

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

Не забудьте остановить спарк-сессию.

spark.stop()