Недавно я прочитал статью под названием Обучайте sklearn в 100 раз быстрее, в которой рассказывается о модуле Python с открытым исходным кодом под названием sk-dist. Модуль реализует распределенный« scikit-learn », расширяя его встроенное распараллеливание мета-оценки, например, pipeline.Pipeline
, model_selection.GridSearchCV
, feature_selection.SelectFromModel
и ensemble.BaggingClassifier,
и т. Д., Используя spark.
Был час ночи. Мудрецы и женщины посоветовали мне не ложиться спать допоздна и пользоваться компьютерами. Однако у меня слишком малоподвижный образ жизни, чтобы рано спать, мне слишком скучно netflix and chill, и я слишком трезв, чтобы мечтать о следующем большом событии после tiktok. Итак, я сделал следующее лучшее. Чтение статей о программировании и машинном обучении.
В статье был представлен образец кода, который в основном реализует уменьшенную задачу распознавания цифр MNIST (классификации изображений) с использованием sk-dist. Никаких упоминаний о требованиях к библиотеке не было, но импорт выглядел следующим образом:
from sklearn import datasets, svm from skdist.distribute.search import DistGridSearchCV from pyspark.sql import SparkSession
Я работаю как с Spark, так и с Scikit - довольно много учусь. У меня был Dockerfile, который создает образ контейнера с помощью Spark 3.0.1, Python 3.7.9 и scikit-learn 0.24.0. Я расширил файл требований с помощью sk-dist 0.1.9 и построил образ. Затем я записал образец кода и запустил команду:
docker run -it -v ${PWD}:/opt/app spark3-dev python digit.py
После некоторых отладочных сообщений программа резко провалилась. Я был немного в зоне. Очевидно, призрак Агаты Кристи сказал мне:
Все надо учитывать. Если факт не укладывается в теорию - отпустите теорию .
Итак, я начал свое расследование. Могу ли я действительно ускорить обучение scikit-learn, как предлагает статья?
Работает ли код?
Статья написана осенью 2019 года. Должно быть, тогда она сработала. Посмотрел репо. Последний коммит с успешной сборкой был сделан где-то в ноябре 2020 года. В нем упоминаются как Spark 2.4, так и Spark 3.0. Итак, я подумал, что если я создам образ докера с немного более старой версией Spark, Python и scikit-learn, это может сработать. После небольшого количества проб и ошибок код работал со следующим Dockerfile.
FROM python:3.7.4-stretch RUN apt-get update && apt-get install -yq openjdk-8-jdk && apt-get clean RUN apt-get update && apt-get install ca-certificates-java && apt-get clean && update-ca-certificates -f ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/ RUN export JAVA_HOME ENV HADOOP_VERSION 2.7 ENV APACHE_SPARK_VERSION 2.4.4 ENV APACHE_SPARK_HASH 2E3A5C853B9F28C7D4525C0ADCB0D971B73AD47D5CCE138C85335B9F53A6519540D3923CB0B5CEE41E386E49AE8A409A51AB7194BA11A254E037A848D0C4A9E5 ENV SPARK_HOME /usr/local/spark-${APACHE_SPARK_VERSION}-bin-hadoop${HADOOP_VERSION} RUN cd /tmp && \ wget -q --show-progress https://archive.apache.org/dist/spark/spark-${APACHE_SPARK_VERSION}/spark-${APACHE_SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz && \ echo "${APACHE_SPARK_HASH} *spark-${APACHE_SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz" | sha512sum -c - && \ tar xzf spark-${APACHE_SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz -C /usr/local && \ rm spark-${APACHE_SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz RUN python -m pip install --upgrade pip RUN pip install --no-cache-dir --upgrade setuptools COPY requirements.txt /tmp/requirements.txt RUN pip install -r /tmp/requirements.txt WORKDIR /opt/app
Файл requirements.txt
включает следующие модули:
pyspark==2.4.4 sk-dist==0.1.9 scikit-learn==0.23.0
На этот раз контейнер успешно выполнил код. Мне удалось создать аналогичную модель исполнения, используя следующее:
sklearn.model_selection.GridSearchCV
skdist.distribute.search.DistGridSearchCV
Конечно, поскольку я выполнил код на одной машине, я не заметил реальной разницы в скорости, когда GridSearchCV
вызывается с использованием 10 заданий.
Может ли sk-dist обучать модели быстрее для небольших объемов данных?
В статье Обучите sklearn в 100 раз быстрее предполагается, что sk-dist применимо к данным малого и среднего размера (менее 1 миллиона записей), и утверждает, что обеспечивает лучшую производительность, чем параллельные scikit-learn и spark.ml. Я решил сравнить разницу во времени выполнения между scikit-learn, sk-dist и spark.ml при классификации изображений MNIST. Однако я выбрал больший набор данных и другой алгоритм, чтобы сравнение было более справедливым.
Я решил использовать Databricks для запуска теста, который является моим переходом на платформу, чтобы опробовать правильные искровые коды. Схема эксперимента описана ниже:
Кластер
Кластер содержит 1 драйвер с памятью 14,0 ГБ, 4 ядрами ЦП и 1–8 рабочих с памятью 14,0–112,0 ГБ, 4–32 ядра. Кластер оснащен Spark 2.4.5, Python 3.7.4, scikit-learn 0.23.0 и sk-dist 0.1.9.
Данные
Я использовал полный набор MNIST, который включает 60000 представлений изображений для обучения и 10000 для тестирования. Его можно легко загрузить из файловой системы databricks, используя следующий код:
def get_mnist_data_databricks(is_train_data, cache): data_path="/databricks-datasets/mnist-digits/data-001/" if is_train_data: filename = "mnist-digits-train.txt" else: filename = "mnist-digits-test.txt" data = spark.read.format("libsvm")\ .option("numFeatures", "784")\ .load(os.path.join(data_path, filename)) if cache: data.cache() return data
Этот код возвращает фрейм данных PySpark с двумя столбцами: label и feature, где label указывает конкретный код изображения, а компонент представляет 784-мерное представление изображения в кодировке SparseVector
. Чтобы преобразовать эти данные в массив numpy, я создал следующую вспомогательную функцию.
def get_numpy_array_from_sparse_vector(data): dataset = data\ .apply(lambda _ : np.array(_.toArray()))\ .values.reshape(-1,1) ser_data = np.apply_along_axis(lambda _ : _[0], 1, dataset) return ser_data
Модель обучения scikit-learn
Я обучил модель scikit-learn, используя следующий код.
sk_estimator = sklearn.tree.DecisionTreeClassifier(random_state=0) sklearn_model = GridSearchCV( estimator=sk_estimator, param_grid={"max_depth": [2, 3, 4, 5, 6, 7, 8, 9, 10]}, cv=10, scoring="f1_weighted", n_jobs=10 )
Эта модель дала результат f1 0,8511 по наилучшей кратности обучения и 0,8655 по тестовым данным.
Тренировочная модель sk-dist
Я обучил модель scikit-learn, используя следующий код.
sd_estimator = sklearn.tree.DecisionTreeClassifier(random_state=0) skdist_model = DistGridSearchCV( estimator=sd_estimator, param_grid={"max_depth": [2, 3, 4, 5, 6, 7, 8, 9, 10]}, cv=10, scoring="f1_weighted", sc=spark.sparkContext )
Модель дала ту же оценку, что и sklearn_model
.
Обучающая модель spark.ml
Для обучения модели spark.ml я использовал следующий код. Модель дала оценку f1 0,8554 на лучшем тренировочном сгибе и 0,8677 на тестовых данных. Это означает, что модель дала такую же точность, как и две другие модели.
from pyspark.ml.classification import DecisionTreeClassifier from pyspark.ml.evaluation import MulticlassClassificationEvaluator def get_pyspark_validator(): idxr = StringIndexer(inputCol="label", outputCol="indexedLabel") clfr = DecisionTreeClassifier(labelCol="indexedLabel") estimator = Pipeline(stages=[idxr, clfr]) mce = MulticlassClassificationEvaluator(labelCol="indexedLabel") grid = ParamGridBuilder()\ .addGrid(classifier.maxDepth, [2, 3, 4, 5, 6, 7, 8, 9, 10])\ .build() validator = CrossValidator(estimator=estimator, evaluator=mce, estimatorParamMaps=grid, numFolds=10) return validator
Сравнение времени выполнения
Чтобы сравнить время выполнения, я сгенерировал все три модели, используя приведенные выше коды, и 50 раз применил их к набору обучающих данных. Я использовал модуль Python timeit для расчета времени выполнения.
Модель scikit-learn заняла около 261 секунды на испытание, модель spark.ml - 391 секунду на испытание, а модель sk-dist - 78 секунд. Ясно, что sk-dist дал лучшую производительность. Однако это точно не в 100 раз быстрее.
Кроме того, Databricks позволяет автоматически отслеживать параметры модели и метрики в mlflow при использовании CrossValidator
. Я не нашел способа отключить эту функцию, что увеличивает время выполнения. Непросто подсчитать, какой вклад в исполнение дает автоматическое отслеживание.
После мыслей
- Значительно ли ускоряет sk-dist обучение scikit-learn? да.
- Обеспечивает ли sk-dist ускорение, как говорится в статье Обучайте sklearn в 100 раз быстрее. Очень сложно сказать. Автор не предоставил достаточно подробностей своих результатов, чтобы повторить тот же эксперимент. В следующий раз расскажите подробнее!
- Интересно ли заглянуть в ск-дист? Определенно.
- Пригоден ли такой несовершенный эксперимент? да. В реальном мире проводить тщательные испытания сложно, а зачастую и нецелесообразно. Достаточно хороший тест намного лучше, чем отсутствие тестов. Однако к результатам такого теста следует относиться с недоверием. Они должны открывать больше возможностей для дальнейших испытаний, а не давать однозначных выводов.
- Было ли это исследование поздней ночью / ранним утром, подкрепленное апельсиновым соком и сэндвичем с балони, на фоне того, что происходило во 2 сезоне Марко Поло Netflix, стоящим? Черт возьми. Я увеличил свои знания в Spark и scikit-learn частично. Какой выброс адреналина!
Заявление об ограничении ответственности
Полный исходный код можно получить здесь. Возможно, я ошибался. Никакой непроверенный код не гарантирует абсолютной точности. Не стесняйтесь ковырять в работе дыры. С удовольствием исправлю свои ошибки.