LinearRegression 预测因输入类型而异

Szy*_*orz 3 python numpy machine-learning linear-regression scikit-learn

我正在研究一个通过属性预测汽车价格的模型。我注意到LinearRegression模型的预测因输入类型 ( numpy.ndarray, scipy.sparse.csr.csr_matrix)而异。

我的数据由一些数字和分类属性组成,没有 NaN。

这是一个简单的数据准备代码(对于我后面描述的每种情况都很常见):

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LinearRegression

# Splitting to test and train
X = data_orig.drop("price", axis=1)
y = data_orig[["price"]]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Numerical attributes pipeline
num_pipeline = Pipeline([ ("scaler", StandardScaler()) ])

# Categorical attributes pipeline
cat_pipeline = Pipeline([ ("encoder", OneHotEncoder(handle_unknown="ignore")) ])

# Complete pipeline
full_pipeline = ColumnTransformer([
    ("cat", cat_pipeline, ["model", "transmission", "fuelType"]),
    ("num", num_pipeline, ["year", "mileage", "tax", "mpg", "engineSize"]),
])
Run Code Online (Sandbox Code Playgroud)

让我们建立一个LinearRegression模型(X_trainX_test会的情况下scipy.sparse.csr.csr_matrix):

...   
X_train = full_pipeline.fit_transform(X_train)
X_test = full_pipeline.transform(X_test)

from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression().fit(X_train, y_train)
pred = lin_reg.predict(X_test)

r2_score(y_test, pred) # 0.896044623680753 OK
Run Code Online (Sandbox Code Playgroud)

如果我将X_test和转换X_trainnumpy.ndarray,则模型的预测完全不正确:

...   
X_train = full_pipeline.fit_transform(X_train).toarray() # Here
X_test = full_pipeline.transform(X_test).toarray() # And here

from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression().fit(X_train, y_train)
pred = lin_reg.predict(X_test)
    
r2_score(y_test, pred) # -7.919935999010152e+19 Something is wrong
Run Code Online (Sandbox Code Playgroud)

我还测试DecisionTreeRegressorRandomForestRegressorSVR但只出现问题LinearRegression

Stu*_*olf 5

源代码中,您可以查看您的输入是否为稀疏矩阵,它会进行一些居中,然后调用线性最小二乘的稀疏版本。如果数组是密集的,它会调用线性最小二乘法numpy 版本

然而,这个例子更大的问题是,在你执行 onehot 编码之前,你应该检查是否有任何分类值只有 1 个条目:

data_orig.select_dtypes(['object']).apply(lambda x:min(pd.Series.value_counts(x)))

model              1
transmission    2708
fuelType          28
Run Code Online (Sandbox Code Playgroud)

如果我们检查模型:

data_orig['model'].value_counts().tail()

 SQ7    8
 S8     4
 S5     3
 RS7    1
 A2     1
Run Code Online (Sandbox Code Playgroud)

因此,如果 RS7 和 A2 在您的测试中但不在您的训练中,那么这个系数将完全是垃圾,因为它的所有值都是零。如果我们尝试使用另一个种子来分割数据,您可以看到两个拟合是相似的:

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=22)
Run Code Online (Sandbox Code Playgroud)

以不同方式使用稀疏/密集数据的函数:

import matplotlib.pyplot as plt

def fit(X_tr,X_ts,y_tr,y_ts,is_sparse=True):
    data_train = full_pipeline.fit_transform(X_tr)
    data_test = full_pipeline.transform(X_ts)
    
    if not is_sparse:
        data_train = data_train.toarray()
        data_test = data_test.toarray()
    
    lin_reg = LinearRegression().fit(data_train, y_tr)
    pred = lin_reg.predict(data_test)
    
    plt.scatter(y_ts,pred)
    return {'r2_train':r2_score(y_tr, lin_reg.predict(data_train)),
            'r2_test':r2_score(y_ts, pred),
            'pred':pred}
Run Code Online (Sandbox Code Playgroud)

我们可以看到用于训练和测试的 r2:

sparse_pred = fit(X_train,X_test,y_train,y_test,is_sparse=True)
[sparse_pred['r2_train'],sparse_pred['r2_test']]

[0.8896333645670668, 0.898030271986993]
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

sparse_pred = fit(X_train,X_test,y_train,y_test,is_sparse=False)
[sparse_pred['r2_train'],sparse_pred['r2_test']]

[0.8896302753422759, 0.8980115229388697]
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

您可以在示例中使用种子 (42) 尝试上述操作,您将看到用于训练的 r^2 是相似的。是预测失控了。

因此,如果您使用稀疏矩阵,则最小二乘法很可能会为全零列返回更少的无意义系数(很可能是@piterbarg 所指的)。

尽管如此,我认为有意义的是在投入管道之前检查测试和训练之间这些缺失因素的数据。对于这个数据集,它很可能没有过度确定,因此稀疏与密集不应该是区别。