Apache spark 火花&x27;s LinearRegressionWithGD对特征缩放非常敏感

Apache spark 火花&x27;s LinearRegressionWithGD对特征缩放非常敏感,apache-spark,linear-regression,apache-spark-mllib,Apache Spark,Linear Regression,Apache Spark Mllib,我在Spark的MLlib中使用LinearRegressionWithGd拟合时遇到问题。我使用他们的示例从这里进行拟合(使用Python接口) 在他们的示例中,所有特征几乎都以平均值约为0,标准偏差约为1进行缩放。现在,如果我将其中一个按10的因子进行缩放,回归将中断(给出NaN或非常大的系数): 所以,我想我需要做特征缩放。如果我做了预缩放,它会起作用(因为我回到了缩放特征)。但是现在我不知道如何在原始空间中得到系数 from pyspark.mllib.regression import

我在Spark的MLlib中使用LinearRegressionWithGd拟合时遇到问题。我使用他们的示例从这里进行拟合(使用Python接口)

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

所以,我想我需要做特征缩放。如果我做了预缩放,它会起作用(因为我回到了缩放特征)。但是现在我不知道如何在原始空间中得到系数

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_模型,它是标准scalermodel对象。但我无法从中得到均值或方差。该类唯一的公共方法是transform,它可以将点从原始空间转换为transform。但是我不能把它颠倒过来。

为了重新表述你的问题,你需要找到截距
I
和系数
C_1
C_2
来解方程:
Y=I+C_1*x_1+C_2*x_2
(其中
x_1
x_2
是无标度的)

i
作为mllib返回的截距。同样,让
c_1
c_2
为mllib返回的系数(或权重)

m_1
x_1
的无标度平均值,
m_2
x_2
的无标度平均值

s_1
x_1
的无标度标准差,
s_2
x_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
我刚刚遇到了这个问题。如果训练数据中
x
较高(>3),则模型甚至无法学习
f(x)=x
。太可怕了

我认为另一个选择是改变步长,而不是缩放数据。这将在中讨论。从这里转述:

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

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


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

正如您指出的,pyspark中的StandardScalerModel对象不公开std和mean属性。有一个问题

你可以自己轻松地计算它们

import numpy as np
from pyspark.mllib.stat import Statistics

summary = Statistics.colStats(features)
mean = summary.mean()
std = np.sqrt(features.variance())
这些是您的定标器使用的相同的平均值和标准。您可以使用python magicdict

print scaler_model.__dict__.get('_java_model').std()
print scaler_model.__dict__.get('_java_model').mean()
print scaler_model.__dict__.get('_java_model').std()
print scaler_model.__dict__.get('_java_model').mean()