Apache Spark стал одним из наиболее часто используемых и поддерживаемых инструментов с открытым исходным кодом для машинного обучения и анализа данных.
В этом посте я помогу вам начать использовать Apache Spark’s spark.ml Linear Regression для прогнозирования цен на жилье в Бостоне. Наши данные взяты из Конкурса Kaggle: стоимость жилья в пригородах Бостона. Для каждого наблюдения за домом у нас есть следующая информация:
ПРЕСТУПНОСТЬ - уровень преступности на душу населения по городам.
ZN - доля земли под жилую застройку, зонированная на участки площадью более 25 000 кв. футов.
INDUS - доля акров, не относящихся к розничной торговле, на город.
CHAS - фиктивная переменная реки Чарльз (= 1, если участок ограничивает реку; 0 в противном случае).
NOX - концентрация оксидов азота (частей на 10 миллионов).
RM - среднее количество комнат в доме.
ВОЗРАСТ - доля занимаемых владельцами единиц жилья, построенных до 1940 года.
DIS - средневзвешенное расстояние до пяти бостонских центров занятости.
RAD - индекс доступности радиальных магистралей.
НАЛОГ - полная ставка налога на имущество из расчета 10 000 долларов США.
PTRATIO - соотношение учеников и учителей по городам.
ЧЕРНЫЙ - 1000 (Bk - 0,63) ², где Bk - доля черных по городам.
LSTAT - более низкий статус населения (в процентах).
MV - средняя стоимость домов, занимаемых владельцами, в 1000 долларов США. Это целевая переменная.
Набор исходных данных содержит данные о деталях различных домов. На основе предоставленной информации цель состоит в том, чтобы разработать модель для прогнозирования средней стоимости данного дома в этом районе.
Загрузите данные
from pyspark import SparkConf, SparkContext from pyspark.sql import SQLContext sc= SparkContext() sqlContext = SQLContext(sc) house_df = sqlContext.read.format('com.databricks.spark.csv').options(header='true', inferschema='true').load('boston.csv') house_df.take(1)
[Строка (CRIM = 0,00632, ZN = 18,0, INDUS = 2,309999943, CHAS = 0, NOX = 0,537999988, RM = 6,574999809, ВОЗРАСТ = 65,19999695, DIS = 4,090000153, RAD = 1, TAX = 296, PT = 15.30000019, B = 396.8999939, LSTAT = 4.980000019, MV = 24.0)]
Исследование данных
Распечатать схему в виде дерева.
house_df.cache() house_df.printSchema() root |-- CRIM: double (nullable = true) |-- ZN: double (nullable = true) |-- INDUS: double (nullable = true) |-- CHAS: integer (nullable = true) |-- NOX: double (nullable = true) |-- RM: double (nullable = true) |-- AGE: double (nullable = true) |-- DIS: double (nullable = true) |-- RAD: integer (nullable = true) |-- TAX: integer (nullable = true) |-- PT: double (nullable = true) |-- B: double (nullable = true) |-- LSTAT: double (nullable = true) |-- MV: double (nullable = true)
Проведите описательную аналитику
house_df.describe().toPandas().transpose()
Матрица разброса - отличный способ примерно определить, есть ли у нас линейная корреляция между несколькими независимыми переменными.
import pandas as pd numeric_features = [t[0] for t in house_df.dtypes if t[1] == 'int' or t[1] == 'double'] sampled_data = house_df.select(numeric_features).sample(False, 0.8).toPandas() axs = pd.scatter_matrix(sampled_data, figsize=(10, 10)) n = len(sampled_data.columns) for i in range(n): v = axs[i, 0] v.yaxis.label.set_rotation(0) v.yaxis.label.set_ha('right') v.set_yticks(()) h = axs[n-1, i] h.xaxis.label.set_rotation(90) h.set_xticks(())
Трудно увидеть. Найдем корреляцию между независимыми переменными и целевой переменной.
import six for i in house_df.columns: if not( isinstance(house_df.select(i).take(1)[0][0], six.string_types)): print( "Correlation to MV for ", i, house_df.stat.corr('MV',i)) Correlation to MV for CRIM -0.3883046116575088 Correlation to MV for ZN 0.36044534463752903 Correlation to MV for INDUS -0.48372517128143383 Correlation to MV for CHAS 0.17526017775291847 Correlation to MV for NOX -0.4273207763683772 Correlation to MV for RM 0.695359937127267 Correlation to MV for AGE -0.37695456714288667 Correlation to MV for DIS 0.24992873873512172 Correlation to MV for RAD -0.3816262315669168 Correlation to MV for TAX -0.46853593528654536 Correlation to MV for PT -0.5077867038116085 Correlation to MV for B 0.3334608226834164 Correlation to MV for LSTAT -0.7376627294671615 Correlation to MV for MV 1.0
Коэффициент корреляции колеблется от –1 до 1. Когда он близок к 1, это означает, что существует сильная положительная корреляция; например, среднее значение имеет тенденцию повышаться при увеличении количества комнат. Когда коэффициент близок к –1, это означает, что имеется сильная отрицательная корреляция; среднее значение имеет тенденцию снижаться, когда увеличивается процент населения с более низким статусом. Наконец, коэффициенты, близкие к нулю, означают отсутствие линейной корреляции.
Пока мы сохраним все переменные.
Подготовьте данные для машинного обучения. А нам нужно всего два столбца - характеристики и метка («MV»):
from pyspark.ml.feature import VectorAssembler vectorAssembler = VectorAssembler(inputCols = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PT', 'B', 'LSTAT'], outputCol = 'features') vhouse_df = vectorAssembler.transform(house_df) vhouse_df = vhouse_df.select(['features', 'MV']) vhouse_df.show(3)
splits = vhouse_df.randomSplit([0.7, 0.3]) train_df = splits[0] test_df = splits[1]
Линейная регрессия
from pyspark.ml.regression import LinearRegression lr = LinearRegression(featuresCol = 'features', labelCol='MV', maxIter=10, regParam=0.3, elasticNetParam=0.8) lr_model = lr.fit(train_df) print("Coefficients: " + str(lr_model.coefficients)) print("Intercept: " + str(lr_model.intercept))
Коэффициенты: [0.0,0.007302310571175137, -0.03286303124593804,1.4134773328268, -7.91932366863737,5.341921692409693,0.0, -0.5791187396097941,0.0, -0,0010503197747184644, -0.7748333592630333,0.01126108224671488, -0,3932170620689197]
Intercept: +11,327590788070061
Обобщите модель по обучающей выборке и распечатайте некоторые показатели:
trainingSummary = lr_model.summary print("RMSE: %f" % trainingSummary.rootMeanSquaredError) print("r2: %f" % trainingSummary.r2)
RMSE: 4,675914
r2: 0,743627
RMSE измеряет различия между значениями, прогнозируемыми моделью, и фактическими значениями. Однако само по себе среднеквадратичное значение не имеет смысла, пока мы не сравним его с фактическим значением «MV», таким как среднее, минимальное и максимальное. После такого сравнения наша RMSE выглядит неплохо.
train_df.describe().show()
R в квадрате 0,74 указывает на то, что в нашей модели около 74% изменчивости MV можно объяснить с помощью модели. Это согласуется с результатом Scikit-Learn. Это не плохо. Однако мы должны быть осторожны, так как производительность на обучающем наборе не может быть хорошим приближением к производительности на тестовом наборе.
lr_predictions = lr_model.transform(test_df) lr_predictions.select("prediction","MV","features").show(5) from pyspark.ml.evaluation import RegressionEvaluator lr_evaluator = RegressionEvaluator(predictionCol="prediction", \ labelCol="MV",metricName="r2") print("R Squared (R2) on test data = %g" % lr_evaluator.evaluate(lr_predictions))
test_result = lr_model.evaluate(test_df) print("Root Mean Squared Error (RMSE) on test data = %g" % test_result.rootMeanSquaredError)
Среднеквадратичная ошибка (RMSE) для тестовых данных = 5,52048
Конечно, на тестовой выборке мы достигли худших значений RMSE и R в квадрате.
print("numIterations: %d" % trainingSummary.totalIterations) print("objectiveHistory: %s" % str(trainingSummary.objectiveHistory)) trainingSummary.residuals.show()
numIterations: 11
objectiveHistory: [0,49999999999999956, +0,4281126976069304, +0,22539203628598917, +0,20185326295592582, +0,1686847843494657, +0,16588096079648484, +0,16543041085178495, +0,16508485781434112, 0,16484472289473545, 0,16454785266359198, +0,16447743850144508]
Используя нашу модель линейной регрессии, чтобы сделать некоторые прогнозы:
predictions = lr_model.transform(test_df) predictions.select("prediction","MV","features").show()
Регрессия дерева решений
from pyspark.ml.regression import DecisionTreeRegressor dt = DecisionTreeRegressor(featuresCol ='features', labelCol = 'MV') dt_model = dt.fit(train_df) dt_predictions = dt_model.transform(test_df) dt_evaluator = RegressionEvaluator( labelCol="MV", predictionCol="prediction", metricName="rmse") rmse = dt_evaluator.evaluate(dt_predictions) print("Root Mean Squared Error (RMSE) on test data = %g" % rmse)
Среднеквадратичная ошибка (RMSE) для тестовых данных = 4,39053
Важность функции
dt_model.featureImportances
SparseVector (13, {0: 0,0496, 1: 0,0, 4: 0,0118, 5: 0,624, 6: 0,0005, 7: 0,1167, 8: 0,0044, 10: 0,013, 12: 0,1799})
house_df.take(1)
[Строка (CRIM = 0,00632, ZN = 18,0, INDUS = 2,309999943, CHAS = 0, NOX = 0,537999988, RM = 6,574999809, ВОЗРАСТ = 65,19999695, DIS = 4,090000153, RAD = 1, TAX = 296, PT = 15.30000019, B = 396.8999939, LSTAT = 4.980000019, MV = 24.0)]
По-видимому, количество комнат является наиболее важной характеристикой для прогнозирования средней цены дома в наших данных.
Регрессия дерева с усилением градиента
from pyspark.ml.regression import GBTRegressor gbt = GBTRegressor(featuresCol = 'features', labelCol = 'MV', maxIter=10) gbt_model = gbt.fit(train_df) gbt_predictions = gbt_model.transform(test_df) gbt_predictions.select('prediction', 'MV', 'features').show(5)
gbt_evaluator = RegressionEvaluator( labelCol="MV", predictionCol="prediction", metricName="rmse") rmse = gbt_evaluator.evaluate(gbt_predictions) print("Root Mean Squared Error (RMSE) on test data = %g" % rmse)
Среднеквадратичная ошибка (RMSE) для тестовых данных = 4,19795
Древовидная регрессия с градиентным усилением показала наилучшие результаты на наших данных.
Исходный код можно найти на Github. Буду рад услышать любые отзывы или вопросы.