【问题标题】:Spark's LinearRegressionWithSGD is very sensitive to feature scalingSpark Linear Regression With SGD 对特征缩放非常敏感
【发布时间】:2015-09-07 23:51:50
【问题描述】:

我在使用 Spark 的 MLlib 中的 LinearRegressionWithSGD 时遇到问题。我使用他们的示例从这里进行拟合 https://spark.apache.org/docs/latest/mllib-linear-methods.html(使用 Python 接口)。

在他们的示例中,所有特征几乎都以 0 左右的平均值和 1 左右的标准差进行缩放。现在,如果我将其中一个缩放 10 倍,则回归中断(给出 nan 或非常大的系数):

from pyspark.mllib.regression import LabeledPoint, LinearRegressionWithSGD
from numpy import array

# Load and parse the data
def parsePoint(line):
    values = [float(x) for x in line.replace(',', ' ').split(' ')]
    # UN-SCALE one of the features by a factor of 10
    values[3] *= 10

    return LabeledPoint(values[0], values[1:])

data = sc.textFile(spark_home+"data/mllib/ridge-data/lpsa.data")
parsedData = data.map(parsePoint)

# Build the model
model = LinearRegressionWithSGD.train(parsedData)

# Evaluate the model on training data
valuesAndPreds = parsedData.map(lambda p: (p.label,     model.predict(p.features)))
MSE = valuesAndPreds.map(lambda (v, p): (v - p)**2).reduce(lambda x, y: x + y) / valuesAndPreds.count()
print("Mean Squared Error = " + str(MSE))
print "Model coefficients:", str(model)

所以,我想我需要进行特征缩放。如果我进行预缩放它可以工作(因为我回到了缩放功能)。但是现在我不知道如何在原始空间中获取系数。

from pyspark.mllib.regression import LabeledPoint, LinearRegressionWithSGD
from numpy import array
from pyspark.mllib.feature import StandardScaler
from pyspark.mllib.feature import StandardScalerModel

# Load and parse the data
def parseToDenseVector(line):
    values = [float(x) for x in line.replace(',', ' ').split(' ')]
    # UN-SCALE one of the features by a factor of 10
    values[3] *= 10
    return Vectors.dense(values[0:])

# Load and parse the data
def parseToLabel(values):
    return LabeledPoint(values[0], values[1:])

data = sc.textFile(spark_home+"data/mllib/ridge-data/lpsa.data")

parsedData = data.map(parseToDenseVector)
scaler = StandardScaler(True, True)
scaler_model = scaler.fit(parsedData)
parsedData_scaled = scaler_model.transform(parsedData)

parsedData_scaled_transformed = parsedData_scaled.map(parseToLabel)

# Build the model
model = LinearRegressionWithSGD.train(parsedData_scaled_transformed)

# Evaluate the model on training data
valuesAndPreds = parsedData_scaled_transformed.map(lambda p: (p.label, model.predict(p.features)))
MSE = valuesAndPreds.map(lambda (v, p): (v - p)**2).reduce(lambda x, y: x + y) / valuesAndPreds.count()
print("Mean Squared Error = " + str(MSE))
print "Model coefficients:", str(model)

所以,这里我有转换空间中的所有系数。现在我怎么去原来的空间?我也有 scaler_model 这是 StandardScalerModel 对象。但我无法从中获得任何手段或差异。该类唯一的公共方法是transform,它可以将点从原始空间转换为transform。但我不能让它反转。

【问题讨论】:

    标签: apache-spark linear-regression apache-spark-mllib


    【解决方案1】:

    我刚刚遇到了这个问题。如果训练数据中的x 很高(>3),模型甚至无法学习f(x) = x。太可怕了。

    我认为,除了缩放数据之外,另一种选择是更改步长。这在SPARK-1859 中进行了讨论。从那里解释:

    步长应小于 Lipschitz 常数 L 的 1。 对于二次损失和 GD,最佳收敛发生在 stepSize = 1/(2L)。 Spark 在损失函数上有一个(1/n) 乘数。

    假设您有n = 5 数据点,最大特征值为1500。所以L = 1500 * 1500 / 5。最佳收敛发生在stepSize = 1/(2L) = 10 / (1500 ^ 2)

    最后一个等式甚至没有意义(我们如何在分子中得到 2?)但我以前从未听说过 Lipschitz 常数,所以我没有资格修复它。无论如何,我认为我们可以尝试不同的步长,直到它开始起作用。

    【讨论】:

      【解决方案2】:

      为了重新表述您的问题,您希望找到解方程的截距 I 和系数 C_1C_2Y = I + C_1 * x_1 + C_2 * x_2(其中 x_1x_2 未缩放)。

      i 为mllib 返回的截距。同样让c_1c_2 成为mllib 返回的系数(或权重)。

      m_1x_1未缩放平均值,m_2x_2 的未缩放平均值。

      s_1x_1未缩放标准差,s_2x_2 的未缩放标准差。

      然后C_1 = (c_1 / s_1)C_2 = (c_2 / s_2)

      I = i - c_1 * m_1 / s_1 - c_2 * m_2 / s_2

      这可以很容易地扩展到 3 个输入变量:

      C_3 = (c_3 / s_3)I = i - c_1 * m_1 / s_1 - c_2 * m_2 / s_2 - c_3 * m_3 / s_3

      【讨论】:

        【解决方案3】:

        正如您指出的那样,pyspark 中的 StandardScalerModel 对象不会公开 std 和 mean 属性。有问题https://issues.apache.org/jira/browse/SPARK-6523

        您可以自己轻松计算

        import numpy as np
        from pyspark.mllib.stat import Statistics
        
        summary = Statistics.colStats(features)
        mean = summary.mean()
        std = np.sqrt(features.variance())
        

        这些与您的 Scaler 使用的均值和标准相同。您可以使用 python magic dict

        验证这一点
        print scaler_model.__dict__.get('_java_model').std()
        print scaler_model.__dict__.get('_java_model').mean()
        

        【讨论】:

          猜你喜欢
          • 2013-06-09
          • 2021-03-09
          • 1970-01-01
          • 2020-05-31
          • 2021-12-22
          • 2016-09-25
          • 2021-06-22
          • 2012-12-27
          • 2023-03-30
          相关资源
          最近更新 更多