Оцените числовое значение с помощью Spark MLlib Regression - PullRequest
1 голос
/ 30 октября 2019

Я тренируюсь линейным регрессором Spark MLlib, но мне кажется, что я не понимаю часть практического использования библиотек.

У меня есть 1 функция (NameItem) и один выход (Accumulator). Первый - категориальный (скорость, температура и т. Д.), Второй - числовой в двойном типе.

Учебный набор состоит из нескольких миллионов записей, и они не имеют линейной корреляции (я проверил с помощью карты температур и индексов корреляции). ).

Issue : Я хотел бы оценить значение Accumulator для значения NameItem с помощью линейной регрессии, но я думаю, что это не то, чем я на самом деле занимаюсь.

Вопрос : Как я могу это сделать?

Сначала я разделил набор данных на training set и data set:

(trainDF, testDF) = df.randomSplit((0.80, 0.20), seed=42)

После этого я попробовал конвейерный подход, как показывают большинство уроков:

1) Я проиндексировал NameItem

indexer = StringIndexer(inputCol="NameItem", outputCol="CategorizedItem", handleInvalid = "keep")

2) Затем я закодировал его

encoderInput = [indexer.getOutputCol()]
encoderOutput = ["EncodedItem"]
encoder = OneHotEncoderEstimator(inputCols=encoderInput, outputCols=encoderOutput)

3) И также собрал его

assemblerInput = encoderOutput
assembler = VectorAssembler(inputCols=assemblerInput, outputCol="features")

После этого я продолжил эффективную тренировку :

lr = LinearRegression(labelCol="Accumulator")
pipeline = Pipeline(stages=[indexer, encoder, assembler, lr])
lrModel = pipeline.fit(trainDF)

Это то, что я получаю, когда применяюпрогноз на тестовом наборе :

predictions = lrModel.transform(testDF).show(5, False)
+--------------+-----------------+---------------+-----------------+-------------------------------+------------------+
|NameItem      |Accumulator      |CategorizedItem|EncodedItem      |features                       |prediction        |
+--------------+-----------------+---------------+-----------------+-------------------------------+------------------+
|Speed         |44000.00000000   |265.0          |(688,[265],[1.0])|(689,[265,688],[1.0,44000.0])  |44000.100892495786|
|Speed         |245000.00000000  |265.0          |(688,[265],[1.0])|(689,[265,688],[1.0,245000.0]) |245000.09963708033|
|Temp          |4473860.00000000 |66.0           |(688,[66],[1.0]) |(689,[66,688],[1.0,4473860.0]) |4473859.874261986 |
|Temp          |6065.00000000    |66.0           |(688,[66],[1.0]) |(689,[66,688],[1.0,6065.0])    |6065.097757082314 |
|Temp          |10140.00000000   |66.0           |(688,[66],[1.0]) |(689,[66,688],[1.0,10140.0])   |10140.097731630483|
+--------------+-----------------+---------------+-----------------+-------------------------------+------------------+
only showing top 5 rows

Как это возможно для той же категориальной функции (fили пример Temp) Я получаю 3 разных прогноза?

Даже если они очень близки к ожидаемому значению, я чувствую, что что-то не так.

1 Ответ

1 голос
/ 31 октября 2019

Как может получиться, что для одного и того же категориального признака (например, Temp) я получу 3 разных прогноза?

Это потому, что ваш вывод Accumulator нашел еговплоть до features (что, конечно, не должно иметь место), поэтому модель просто «предсказывает» (по сути, копирует) эту часть ввода;вот почему прогнозы , поэтому"точны" ...

Похоже, что VectorAssembler все испортило. Дело в том, что вам на самом деле не нужен VectorAssembler здесь, поскольку на самом деле у вас есть только «одиночная» функция (закодированный вектор с горячим кодированием в EncodedItem). Это может быть причиной того, что VectorAssembler ведет себя здесь так (его просят "собрать" одну особенность), но в любом случае это будет ошибка.

Так чтоЯ предлагаю избавиться от VectorAssembler и переименовать EncodedItem напрямую в features, то есть:

indexer = StringIndexer(inputCol="NameItem", outputCol="CategorizedItem", handleInvalid = "keep")

encoderInput = [indexer.getOutputCol()]
encoderOutput = ["features"]  # 1st change
encoder = OneHotEncoderEstimator(inputCols=encoderInput, outputCols=encoderOutput)

lr = LinearRegression(labelCol="Accumulator")
pipeline = Pipeline(stages=[indexer, encoder, lr])  # 2nd change
lrModel = pipeline.fit(trainDF)

ОБНОВЛЕНИЕ (после обратной связи в комментариях)

Моя версия Spark - 1.4.4

К сожалению, я не могу воспроизвести проблему просто потому, что у меня нет доступа к Spark 1.4.4, который вы используете. Но я подтвердил, что он работает нормально в самой последней версии Spark 2.4.4 , что еще больше склоняет меня к мысли, что в версии 1.4 действительно была какая-то ошибка, которая впоследствии была исправлена. .

Вот воспроизведение в Spark 2.4.4 с использованием некоторых фиктивных данных, похожих на ваши:

spark.version
# '2.4.4'

from pyspark.ml.feature import VectorAssembler, OneHotEncoderEstimator, StringIndexer
from pyspark.ml.regression import LinearRegression
from pyspark.ml import Pipeline

# dummy data resembling yours:

df = spark.createDataFrame([['Speed', 44000], 
                            ['Temp', 23000], 
                            ['Temp', 5000], 
                            ['Speed', 75000], 
                            ['Weight', 5300], 
                            ['Height', 34500], 
                            ['Weight', 6500]], 
                            ['NameItem', 'Accumulator'])

df.show()
# result:
+--------+-----------+
|NameItem|Accumulator|
+--------+-----------+
|   Speed|      44000|
|    Temp|      23000|
|    Temp|       5000|
|   Speed|      75000|
|  Weight|       5300|
|  Height|      34500|
|  Weight|       6500|
+--------+-----------+

indexer = StringIndexer(inputCol="NameItem", outputCol="CategorizedItem", handleInvalid = "keep")

encoderInput = [indexer.getOutputCol()]
encoderOutput = ["EncodedItem"]
encoder = OneHotEncoderEstimator(inputCols=encoderInput, outputCols=encoderOutput)

assemblerInput = encoderOutput
assembler = VectorAssembler(inputCols=assemblerInput, outputCol="features")

lr = LinearRegression(labelCol="Accumulator")

pipeline = Pipeline(stages=[indexer, encoder, assembler, lr])
lrModel = pipeline.fit(df) 
lrModel.transform(df).show() # predicting on the same df, for simplicity

Результат последнего transform равен

+--------+-----------+---------------+-------------+-------------+------------------+
|NameItem|Accumulator|CategorizedItem|  EncodedItem|     features|        prediction|
+--------+-----------+---------------+-------------+-------------+------------------+
|   Speed|      44000|            2.0|(4,[2],[1.0])|(4,[2],[1.0])|           59500.0|
|    Temp|      23000|            1.0|(4,[1],[1.0])|(4,[1],[1.0])|14000.000000000004|
|    Temp|       5000|            1.0|(4,[1],[1.0])|(4,[1],[1.0])|14000.000000000004|
|   Speed|      75000|            2.0|(4,[2],[1.0])|(4,[2],[1.0])|           59500.0|
|  Weight|       5300|            0.0|(4,[0],[1.0])|(4,[0],[1.0])| 5900.000000000004|
|  Height|      34500|            3.0|(4,[3],[1.0])|(4,[3],[1.0])|           34500.0|
|  Weight|       6500|            0.0|(4,[0],[1.0])|(4,[0],[1.0])| 5900.000000000004|   
+--------+-----------+---------------+-------------+-------------+------------------+

откуда видно, что:

  1. features теперь не включает значения выходной переменной Accumulator, как и должно быть;на самом деле, как я уже говорил выше, features теперь идентично EncodedItem, что делает VectorAssembler избыточным, как и следовало ожидать, поскольку у нас есть только одна особенность.
  2. predictionзначения теперь идентичны для тех же значений NameItem, опять же, как мы и ожидали, плюс они менее точны и, следовательно, более реалистичны.

Так что, безусловно, ваша проблема имеетделать с значительно устаревшей Spark версии 1.4.4, которую вы используете. Spark сделал скачки с версии 1.4, и вы должны серьезно подумать об обновлении ...

...