Ви завантажили 500 ГБ даних, запустили .fit() і через годину отримали OutOfMemoryError. Pandas та scikit-learn упираються в RAM однієї машини. Рішення — розподілене навчання на Spark MLlib. Ми займаємося розподіленим ML на Spark більше 5 років, виконали 20+ проєктів з датасетами до 10 ТБ. Нижче — практичний досвід, який допоможе уникнути типових помилок і швидко запустити навчання на кластері.
Проблеми масштабування та feature engineering
Spark MLlib розподіляє обчислення на кластер, обробляючи дані будь-якого обсягу. Типовий поріг — датасети >100 GB або >100 млн рядків, де pipeline на sklearn перестає працювати. Сотні ознак, категоріальні змінні з мільйонами унікальних значень — StringIndexer і OneHotEncoder в Spark справляються без вивантаження в пам'ять. CrossValidator на Spark запускає фолди паралельно, прискорюючи підбір гіперпараметрів у 3–5 разів. Вбудовані інструменти для роботи з пропусками (Imputer) та масштабування (StandardScaler) дозволяють будувати пайплайни без перемикання контексту.
Порівняння Spark MLlib з альтернативами
Порівняємо з підходами на pandas+sklearn та Dask. Spark виграє за рахунок нативної підтримки розподілених DataFrames, оптимізованих під shuffle, вбудованих алгоритмів (GBT, RandomForest, KMeans) та інтеграції з MLflow для трекінгу експериментів.
| Інструмент | Час навчання GBT (10 млн записів) | Споживання пам'яті | Масштабування |
|---|---|---|---|
| scikit-learn | ~45 хв | 32 GB+ (OOM) | Ні |
| Dask+sklearn | ~20 хв | 16 GB | Обмежене |
| Spark MLlib | ~8 хв | 8 GB на executor | Горизонтальне |
Spark MLlib на 80% швидший при вдвічі меншому споживанні ресурсів на executor.
Як налаштувати Spark MLlib для оптимальної продуктивності?
Ключові прийоми налаштування
- Репартиціонування: `df.repartition(200)` перед fit — рівномірне навантаження на executor. - Кешування: `train_df.cache()` прискорює CV у 3–5 разів. - Налаштування shuffle partitions: `spark.sql.shuffle.partitions = 2 * total_cores`. - Паралелізм CV: параметр `parallelism=4` у CrossValidator запускає фолди паралельно.Ці налаштування скорочують час CV з 6 до 1.5 годин на кластері з 10 executor.
| Параметр | Default | Рекомендоване | Ефект |
|---|---|---|---|
| spark.sql.shuffle.partitions | 200 | 2x cores | Уникнути skew |
| executor.memory | 1g | 4-8g | Кеш датасету |
| spark.ml.param.maxParallelism | 1 | 4-8 | CV паралелізм |
| repartition перед fit | ні | 200-400 | Рівномірне навантаження |
| caching train_df | ні | так | 3-5x прискорення CV |
Чому Spark MLlib швидший за sklearn на великих даних?
Spark MLlib використовує розподілені обчислення та оптимізовані алгоритми для роботи з даними, що не поміщаються в пам'ять. На відміну від sklearn, який завантажує все в RAM, Spark обробляє дані частинами на кластері. Це дозволяє досягти лінійної масштабованості при додаванні вузлів.
Реалізація пайплайну: від конфігурації до деплою
Конфігурація кластера та підготовка даних
Використовуємо PySpark 3.4+, MLflow 2.x, ONNX для інференсу. Конфігурація кластера підбирається під задачу: executor memory від 4 до 16 GB, кількість executor кратна числу партицій. Нижче — робочий пайплайн для бінарної класифікації з градієнтним бустингом.
from pyspark.sql import SparkSession
from pyspark.ml import Pipeline
from pyspark.ml.feature import (VectorAssembler, StringIndexer,
StandardScaler, Imputer)
from pyspark.ml.classification import GBTClassifier, RandomForestClassifier
from pyspark.ml.evaluation import BinaryClassificationEvaluator
from pyspark.ml.tuning import CrossValidator, ParamGridBuilder
spark = SparkSession.builder \
.appName("ML Pipeline") \
.config("spark.executor.memory", "8g") \
.config("spark.executor.core", "4") \
.config("spark.executor.instances", "10") \
.config("spark.sql.adaptive.enabled", "true") \
.config("spark.ml.param.maxParallelism", "4") \
.getOrCreate()
# Загрузка данных
df = spark.read.parquet("s3://data/training/*.parquet")
df = df.repartition(200) # Оптимальное число партиций
# Feature engineering
numeric_cols = ['amount', 'age', 'days_since_last_tx', 'tx_count_30d']
categorical_cols = ['category', 'country', 'device_type']
# Imputer для числовых
imputer = Imputer(
inputCols=numeric_cols,
outputCols=[f"{c}_imputed" for c in numeric_cols],
strategy="median"
)
# Кодирование категориальных
indexers = [
StringIndexer(inputCol=col, outputCol=f"{col}_idx",
handleInvalid="keep")
for col in categorical_cols
]
# Сборка вектора признаков
all_feature_cols = (
[f"{c}_imputed" for c in numeric_cols] +
[f"{c}_idx" for c in categorical_cols]
)
assembler = VectorAssembler(
inputCols=all_feature_cols,
outputCol="features_raw",
handleInvalid="keep"
)
scaler = StandardScaler(
inputCol="features_raw",
outputCol="features",
withMean=True,
withStd=True
)
# Модель
gbt = GBTClassifier(
labelCol="label",
featuresCol="features",
maxIter=100,
maxDepth=5,
stepSize=0.05,
subsamplingRate=0.8,
seed=42
)
# Pipeline
pipeline = Pipeline(stages=[
imputer,
*indexers,
assembler,
scaler,
gbt
])
# Train/test split
train_df, test_df = df.randomSplit([0.8, 0.2], seed=42)
# Обучение
model = pipeline.fit(train_df)
predictions = model.transform(test_df)
# Оценка
evaluator = BinaryClassificationEvaluator(
labelCol="label",
rawPredictionCol="rawPrediction",
metricName="areaUnderROC"
)
auc = evaluator.evaluate(predictions)
print(f"Test AUC: {auc:.4f}")
Гіперпараметрична оптимізація
# Cross-validation на кластере
param_grid = ParamGridBuilder() \
.addGrid(gbt.maxDepth, [4, 6, 8]) \
.addGrid(gbt.maxIter, [50, 100]) \
.addGrid(gbt.stepSize, [0.05, 0.1]) \
.build()
cv = CrossValidator(
estimator=pipeline,
estimatorParamMaps=param_grid,
evaluator=evaluator,
numFolds=3,
parallelism=4, # Параллельный запуск фолдов
seed=42
)
cv_model = cv.fit(train_df)
best_model = cv_model.bestModel
print(f"Best params: {cv_model.bestModel.stages[-1].extractParamMap()}")
Feature Importance та інтерпретація
# Извлечение feature importance
gbt_model = best_model.stages[-1]
importance = gbt_model.featureImportances
# Маппинг на имена признаков
feature_names = all_feature_cols
importance_df = spark.createDataFrame(
[(name, float(imp)) for name, imp in zip(feature_names, importance.toArray())],
["feature", "importance"]
).orderBy("importance", ascending=False)
importance_df.show(20)
# SHAP через pandas на выборке
sample_pandas = predictions.sample(fraction=0.01).toPandas()
# ... далее стандартный TreeExplainer
Збереження та деплой моделі
import mlflow
import mlflow.spark
# Логирование в MLflow
with mlflow.start_run():
mlflow.log_param("max_depth", gbt.getMaxDepth())
mlflow.log_param("max_iter", gbt.getMaxIter())
mlflow.log_metric("auc", auc)
# Сохранение Spark модели
mlflow.spark.log_model(best_model, "spark_model")
# Экспорт в ONNX для быстрого инференса
from onnxmltools import convert_sparkml
onnx_model = convert_sparkml(best_model, "GBT Model", test_df.limit(5))
mlflow.onnx.log_model(onnx_model, "onnx_model")
# Загрузка для предсказаний
loaded_model = mlflow.spark.load_model("runs:/RUN_ID/spark_model")
batch_predictions = loaded_model.transform(new_data_df)
Що входить в роботу
При замовленні інтеграції Spark MLlib ви отримуєте:
- Документація пайплайну та конфігурацій (включаючи обґрунтування вибору алгоритмів та параметрів).
- Налаштований MLflow tracking server для відтворюваності експериментів.
- Docker-образ для інференсу з ONNX Runtime.
- Навчання команди замовника (1 день) — робота з PySpark, MLflow, оптимізація.
- Підтримка 2 тижні після деплою — виправлення інцидентів, консультації.
Процес роботи, строки та гарантії
- Аналітика: вивчаємо дані, визначаємо ознаки, цільову змінну, метрики.
- Проектування: вибираємо алгоритм, конфігурацію кластера, пайплайн.
- Реалізація: пишемо код, налаштовуємо MLflow, запускаємо пробне навчання.
- Тестування: валідація на відкладеній вибірці, A/B тест у стейджингу.
- Деплой: упаковка в MLflow Model, сервінг через REST API або ONNX.
Строки під ключ — від 2 до 4 тижнів залежно від складності даних та алгоритму. Вартість розраховується індивідуально після аудиту вашого датасету та інфраструктури. Отримайте консультацію — оцінимо проєкт безкоштовно.
Багаторічний досвід у промисловому ML на Spark, десятки завершених проєктів з датасетами від 100 GB до 10 TB. Сертифіковані спеціалісти з Spark та MLflow. Гарантуємо відтворюваність результатів.
Замовте інтеграцію Spark MLlib — ми налаштуємо пайплайн під ваш датасет. Зв'яжіться для консультації — допоможемо вибрати оптимальну архітектуру та запустити перший пайплайн за тиждень.







