我正在通过重新创建常见的“预测垃圾邮件”项目来学习Python中的机器学习和自然语言处理。我完成了所有初步的清理和预处理步骤,直到得到一个包含2000个词项的TF-IDF文档-词项矩阵。然后,我执行了SVD,将其减少到300个词项(或成分),并对结果进行缩放,以便我可以快速运行一个逻辑分类器来为后续模型设定基准。
在项目后期,当构建随机森林时,我意识到我忘记注释掉下面的缩放器,并且使用缩放后的SVD来构建森林,这完全是多余的。然而,我没有意识到这会使随机森林的运行速度比未缩放的SVD慢,而且更糟糕的是,灵敏度也降低了大约10%。
有谁能帮我理解这是为什么吗?
以下是网格搜索中最佳(最高灵敏度)的未缩放SVD的结果:
Elapsed: 1348 sBest params: {'max_depth': 20, 'max_features': 250, 'min_samples_split': 10, 'n_estimators': 200}Confusion matrix on validation set: pred_neg pred_posneg 844 2pos 5 124Evaluation metrics:accuracy: 0.9928sensitivity: 0.9612specificity: 0.9976
以下是网格搜索中最佳(最高灵敏度)的缩放SVD的结果:
Elapsed: 5297 sBest params: {'max_depth': 5, 'max_features': 250, 'min_samples_split': 5, 'n_estimators': 200}Confusion matrix on validation set: pred_neg pred_posneg 838 8pos 18 111Evaluation metrics:accuracy: 0.9733sensitivity: 0.8605specificity: 0.9905
罪魁祸首在这里:
from scipy.sparse.linalg import svdsfrom sklearn.utils.extmath import svd_flipfrom sklearn.preprocessing import MaxAbsScalerdef perform_SVD(X, n_components=300): # transpose to a term-document matrix U, Sigma, VT = svds(X.asfptype().T, k=n_components) # reverse outputs Sigma = Sigma[::-1] U, VT = svd_flip(U[:, ::-1], VT[::-1]) # transpose to get V V = VT.T # scale for logistic classifier only # can't take log of negative numbers # ends up predicting ham base rate # comment out for random forests! scaler = MaxAbsScaler() X_scaled = scaler.fit_transform(V) return X_scaled
回答:
这并不令人惊讶。想象一下,你对分类感兴趣,你的数据集看起来像这样 –
如果你试图用决策树来拟合这个数据集,它很容易找到决策边界,因此你的分类准确率会相当高。
现在想象一下,如果你首先尝试缩放它。新的数据集会看起来像这样 –
如你所见,数据之间的重叠更多,因此模型更难找到决策边界。
当你缩放数据时,你是在使两个轴彼此靠近。这可能导致它们变得不那么易于区分。
在这一点上,你可能会想,如果真是这样,为什么我们还要费心进行这种重新缩放呢。毕竟,无论你使用什么模型,这种效果都会显现。虽然这是真的,进行缩放确实会使数据变得不那么易于区分,但在像神经网络这样的模型中,如果你不进行这种缩放操作,会有很多其他缺点出现。比如,一个特征的权重被人为地放大,或者梯度不能正常流动等等。在这种情况下,缩放的优点可能超过缺点,你仍然可以得到一个好的模型。
至于你关于为什么速度会有所不同的问题,同样的效果,随机森林在后一种情况下可能需要更长时间来搜索,以获得相同参数下的良好拟合。这并不令人惊讶。
这是用于生成图表的代码 –
import numpy as npimport matplotlib.pyplot as pltfrom sklearn.preprocessing import MaxAbsScalerstd = 7X = np.random.multivariate_normal([2, 2], [[std, 0], [0, std]], size=100)Y = np.random.multivariate_normal([10, 10], [[std, 0], [0, std]], size=100)plt.scatter(X[:, 0], X[:, 1])plt.scatter(Y[:, 0], Y[:, 1])plt.show()scaler = MaxAbsScaler()X_scaled = scaler.fit_transform(X)Y_scaled = scaler.fit_transform(Y)plt.scatter(X_scaled[:, 0], X_scaled[:, 1])plt.scatter(Y_scaled[:, 0], Y_scaled[:, 1])plt.show()