Apache Spark содержит в себе множество различных библиотек, среди которых есть библиотека MLlib, предназначенная для машинного обучения. В ее состав входят различные алгоритмы машинного обучения и она может использоваться во всех языках программирования, поддерживаемых фреймворком Spark.
В этой статье мы покажем вам, как использовать эту библиотеку в своих программах, и дадим некоторые рекомендации по ее применению.
Архитектура MLlib достаточно проста: она позволяет применять разные алгоритмы к распределенным массивам данных, представленным в виде наборов RDD. Напомним, что RDD (Resilient Distributed Dataset) — это базовая единица данных в Apache Spark.
Основные шаги
Для того, чтобы задействовать библиотеку MLlib в задачах классификации текста (например, для выявления спама среди электронных писем), достаточно выполнить следующие шаrи:
создать набор RDD строк, представляющих содержимое электронных писем;
выполнить один из алгоритмов извлечения признаков (feature extraction) в библиотеке MLlib, чтобы преобразовать текст в наборы чисел (пригодные для использования в алгоритмах обучения). В результате у нас будет набор RDD векторов;
вызвать алгоритм классификации (например, алгоритм логистической регрессии (logistic regression)) для обработки RDD векторов. В результате мы получим объектную модель, которую затем можно использовать для классификации новых точек;
применить модель к тестовому набору данных с использованием одной из функций библиотеки MLlib.
Важно отметить, что библиотека MLlib содержит только параллельные алгоритмы, пригодные для использования в кластерных системах. Именно по этой причине некоторые классические алгоритмы машинного обучения не вошли в библиотеку так как они не предназначены для работы на параллельных платформах. Но, при этом, в MLlib имеется реализация нескольких алгоритмов для кластеров, таких как распределенные случайные леса (distributed random forests ), метод К‑средних (K‑means) и метод чередующихся наименьших квадратов (alternating least squares).
Аналогично для некоторых процессов машинного обучения характерно требовать применения одного и того же алгоритма к маленькому набору данных со множеством параметров настройки, чтобы выбрать лучший вариант. В Spark сделать это можно с помощью parallelize() и списка параметров для проведения обучения на разных узлах, опять же с применением библиотеки, предназначенной для выполнения на одном узле.
Системные требования
Для работы MLlib требует наличия в системе некоторых библиотек линейной алгебры. Прежде всего, необходима библиотека времени выполнения gfortran. Если MLlib предупредит об отсутствии gfortran, выполните следующие действия для установки:
$ sudo apt update
$ sudo apt -y install gfortran-multilib-sparc64-linux-gnu
Также, для работы с MLlib в Python, нужно установить пакет NumPy2. Здесь все достаточно просто — устанавливаем пакет с помощью pip
$ pip install numpy
Типы данных
В MLlib имеется несколько собственных типов данных, которые определяются в пакетах pyspark.mllib. Одним из наиболее распространенных является математический вектор. При этом MLlib поддерживает обе разновидности векторов: плотные, хранящие все элементы, и разреженные, хранящие только ненулевые значения для экономии памяти. Векторы могут конструироваться с помощью класса mllib.linalg.Vectors.
Плотные векторы хранят в массиве вещественных чисел все элементы Например, вектор с размером 100 будет хранить 100 значений типа double. Разреженные векторы, напротив, хранят только ненулевые значения и их индексы. Обычно разреженные векторы предпочтительнее (и с точки зрения использования памяти, и с точки зрения скорости), если не более 10% элементов имеют ненулевые значения. Большинство алгоритмов определения характеристических признаков возвращают очень разреженные векторы, поэтому применение данной разновидности векторов часто оказывается важной оптимизацией.
Ниже представлены примеры создания обоих типов векторов.
from numpy import array
from pyspark.mllib.linalg import Vectors
# Создать плотный вектор <1.0, 2.0, 3.0>
denseVecl = array([1.0, 2.0, 3.0]) # в MLlib можно передавать
# непосредственно массивы NumPy
denseVec2 = Vectors.dense([1.0, 2.0, 3.0]) # .. или использовать класс Vectors
# Создать разреженный вектор <1.0, О.О, 2.0, О.О>; соответствующие
# методы принимают только размер вектора (4) и
# индексы с ненулевыми элементами. Исходные данные можно передать
# в виде словаря или как два риска - индексов и значений.
sparseVec1 = Vectors.sparse (4, {0: 1.0, 2: 2.0})
sparseVec2 = Vectors.sparse(4, [0, 2], [1.0, 2.0])
Другой тип LabeledPoint — маркированная точка в пространстве данных для использования в алгоритмах обучения, таких как классификация и регрессия. Включает вектор признаков и маркер (являющийся вещественным числом). Определение находится в пакете mllib.regression package;
Тип Rating это оценка продукта пользователем, используемая в пакете mllib.recommendation для определения рекомендаций;
И семейство классов Model — все модели типа Model являются результатом работы алгоритма обучения и обычно имеют метод predict() для применения модели к новой точке или к набору RDD новых точек данных.
Стоит отметить, что большинство алгоритмов оперируют непосредственно наборами RDD объектов Vector, LabeledPoint или Rating. Наборы этих объектов можно создавать вручную, но обычно они создаются в ходе преобразований внешних данных — например, путем загрузки текстовых файлов или выполнением команд Spark SQL — с последующим применением map() для превращения данных в объекты MLlib.
Классификация спама
В рамках данной статьи мы не будем рассматривать основы машинного обучения, так как предполагаем, что читатель имеет представление о том, как работают алгоритмы машинного обучения.
Поэтому давайте сразу рассмотрим пример программы, генерирующей классификатор спама. В этой программе мы будем использовать два алгоритма из MLlib: HashingTF, создающий вектор частот встречаемости терминов (term frequency) в тексте, и LogisticRegressionWithSGD, реализующий процедуру логистической регрессии (logistic regression) методом стохастического градиентного спуска (Stochastic Gradient Descent, SGD).
Предполагается, что имеются два файла, spam.txt и normal.txt, каждый из которых содержит примеры электронных писем со спамом и без спама, по одному в строке. Каждое электронное письмо в этих файлах преобразуется в вектор признаков с частотами терминов, и производится обучение модели логистической регрессии для разделения сообщений двух типов.
from pyspark.mllib.regression import LabeledPoint
from pyspark.mllib.feature import HashingTF
from pyspark.mllib.classification import LogisticRegressionWithSGD
spam = sc.textFile("spam.txt")
normal = sc.textFile("normal.txt")
# Создать экземпляр HashingTF для отображения текста электронных
# писем в векторы с 10 ООО признаков.
tf = HashingTF(numFeatures = 10000)
# Разбить каждое электронное письмо на слова и каждое слово
# отобразить в один признак.
spamFeatures = spam.map(
lambda email: tf.transform(email.split(" ")))
normalFeatures = normal.map(
lambda email: tf.transform(email.split(" ")))
# Создать наборы данных LabeledPoint для примеров, дающих
# положительную реакцию (спам) и отрицательную (обычные письма).
positiveExamples = spamFeatures.map(
lambda features: LabeledPoint(l, features))
negativeExamples = normalFeatures.map(
lambda features: LabeledPoint(O, features))
trainingData = positiveExamples.union(negativeExamples)
trainingData.cache() # Кэшировать, потому что алгоритм
# Logistic Regression является циклическим.
# Выполнить логистическую регрессию методом SGD.
model = LogisticRegressionWithSGD.train(trainingData)
# Проверить положительный экземпляр (спам) и отрицательный (неспам).
# Сначала применить то же преобразование HashingTF, чтобы получить
# векторы признаков, затем применить модель.
posTest = tf.transform(
"О М G GET cheap stuff Ьу sending money to ... ".split (" "))
negTest = tf.transform(
"Hi Dad, I started studying Spark the other ... ".split(" "))
print "Prediction for positive test example: %g" % model.predict(posTestExample)
print "Prediction for negative test example: %g" % model.predict(negTestExample)
Регрессия и классификация
Рассмотрим регрессию и классификацию — две типичные формы управляемого обучения (или обучения с учителем — supemised leaming), когда алгоритмы пытаются классифицировать переменную по признакам объектов, использовавшихся для обучения (то есть объектов, классификация которых известна заранее).
Различия между этими двумя формами — тип возвращаемого значения: в классификации результат получается дискретный (то есть принадлежит конечному множеству значений, называемых классами); например, для электронных писем классами могут быть spam и nonspam или название языка, на котором написан текст. В регрессии результат принадлежит непрерывному ряду (например, результат предсказания роста человека по его возрасту и весу).
В обоих случаях, и в классификации, и в регрессии, используется класс LabeledPoint из MLlib, находящийся в пакете mllib.regression, о котором мы уже говорили ранее, когда рассматривали типы данных. Экземпляр LabeledPoint состоит из маркера label (всегда являющегося вещественным числом типа Double, но в задачах классификации могущего получать дискретные целые значения) и вектора признаков features.
Линейная регрессия — один из наиболее часто используемых методов регрессии для предсказания выходного значения по линейной комбинации признаков. MLlib поддерживает также регуляризованные регрессии L 1 и L2, которые часто называют регрессией Лассо
и гребневой регрессией.

Алгоритмы линейной регрессии доступны в виде классов mllib. regression.LinearRegressionWithSGD, LassoWithSGD и RidgeRegressionWithSGD. Имена этих классов следуют соглашениям, принятым повсюду в библиотеке MLlib, в соответствии с которыми множественные реализации решения одной и той же задачи включают часть With, указывающую на используемый метод решения. Здесь под SGD подразумевается
Stochastic Gradient Descent (стохастический метод градиентного спуска).
Данные классы имеют несколько параметров настройки алгоритма:
numIterations
— число итераций (по умолчанию: 100);stepSize
— величина шага градиентного спуска (по умолчанию: 1.0);intercept
— определяет необходимость добавления систематического
признака в данные — то есть еще одного признака, значение которого всегда равно 1 (по умолчанию false);regParam
— параметр регуляризации для регрессии Лассо и гребневой регрессии (по умолчанию 1.0).
В разных языках вызов алгоритма выполняется по‑разному. В Python достаточно вызвать метод класса LinearRegressionWithSGD.train(), передав ему именованные параметры, как показано в примере ниже:
from pyspark.mllib.regression import LabeledPoint
from pyspark.mllib.regression import LinearRegressionWithSGD
points = ... # (создать набор объектов LabeledPoint)
model = LinearRegressionWithSGD.train(points, iterations=200, intercept=True)
print "weights: %s, intercept: %s" % (model.weights, model.intercept)
Заключение
В этой статье мы рассмотрели некоторые примеры работы с библиотекой MLlib из состава Apache Spark. Стоит отметить, что в статье представлена лишь небольшая часть тех возможностей, которые предлагает разработчикам библиотека MLlib. Также с помощью этой библиотеки можно работать с деревьями решений, байесовскими классификаторами, методом К‑средних и другими популярными алгоритмами машинного обучения. При этом, мы также можем использовать всю мощь распределенных вычислений Spark для реализации своих задач.
Если вы работаете с большими данными или машинным обучением, эти открытые уроки станут важным шагом к улучшению вашего мастерства и повышению эффективности проектов:
26 мая в 20:00 — Spark в Kubernetes
На уроке вы научитесь развертывать Spark в Kubernetes, управлять ресурсами и оптимизировать производительность. Узнаете, как запускать Spark-приложения в облаке и локальных кластерах, а также как избежать типичных ошибок при развертывании и масштабировании. Записаться11 июня в 20:00 — Spark ML
Познакомитесь с Spark ML — инструментом для масштабируемого машинного обучения. Обсудим создание и тестирование моделей на больших данных, подготовку данных и валидацию моделей, а также развертывание их в промышленной среде для реальных ML-проектов. Записаться
Хотите понять, насколько вы разбираетесь в Spark? Пройдите вступительное тестирование — оно поможет оценить уровень знаний.