【问题标题】:SGDRegressor() constantly not increasing validation performanceSGDRegressor() 不断提高验证性能
【发布时间】:2021-03-20 18:59:38
【问题描述】:

我的SGDRegressor 的模型拟合在大约 20,000 条训练记录后不会提高或降低其在验证集 (test) 上的性能。即使我尝试将penaltyearly_stopping (True/False)alphaeta0 切换到极高或极低级别,“卡住”验证分数test 的行为也没有变化。

我之前使用了StandardScaler 并打乱了训练集和测试集的数据。

train_test_split(X,y, test_size = 0.3, random_state=85, shuffle=True)

print(X_train.shape, X_test.shape)
print(y_train.shape, y_test.shape)
>>>(336144, 10) (144063, 10)
>>>(336144,) (144063,)

我的验证代码有什么问题吗?或者由于SGDRegressor 对训练数据的处理存在限制,这种行为是否可以解释?

from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error
import pandas
import matplotlib.pyplot as plt


scores_test = []
scores_train = []
my_rng = range(10,len(X_train),30000)
for m in my_rng:
    print(m)
    modelSGD = SGDRegressor(alpha=0.00001, penalty='l1')
    modelSGD.fit(X_train[:m], y_train[:m])
    
    ypred_train = modelSGD.predict(X_train[:m])
    ypred_test = modelSGD.predict(X_test)
    mse_train = mean_squared_error(y_train[:m], ypred_train)
    mse_test = mean_squared_error(y_test, ypred_test)
    scores_train.append(mse_train)
    scores_test.append(mse_test)

我如何“强制”SGDRegressor 尊重大量的训练数据并改变其在 test 数据上的性能?

编辑: 我试图想象模型在接受 30'000 或 300'000 条记录的训练后不会改变其在test 上的分数。这就是我在循环中初始化 SGDRegressor 的原因,因此它在每次迭代中都经过全新训练。

按照@Nikaido的要求,这些是拟合后的模型coef_intercept_

trainsize: 10, coef:  [ 0.81815135  2.2966633   1.61231584 -0.00339933 -3.03094922  0.12757874  -2.60874563  1.52383531  0.3250487  -0.61251297], intercept:  [50.77553038]
trainsize: 30010, coef:  [ 0.19097587 -0.35854903 -0.16142221  0.11281925 -0.66771756  0.55912533   0.90462141 -1.417289    0.50487032 -1.42423654], intercept:  [83.28458307]
trainsize: 60010, coef:  [ 0.09848169 -0.1362008  -0.15825232 -0.4401373   0.31664536  0.04960247  -0.37299047  0.6641436   0.02782047 -1.15355052], intercept:  [80.87163096]
trainsize: 90010, coef:  [-0.00923631  0.5845441   0.28485334 -0.29528061 -0.30643056  1.20320208   1.9723999  -0.47707621  1.25355186 -2.04990825], intercept:  [85.17812028]
trainsize: 120010, coef:  [-0.04959943 -0.15744169 -0.17071373 -0.20829149 -1.38683906  2.18572481   1.43380752 -1.48133799  2.18962484 -3.41135224], intercept:  [86.40188522]
trainsize: 150010, coef:  [ 0.56190926  0.05052168  0.22624504  0.55751301 -0.50829818  1.27571154   1.49847285 -0.15134682  1.30017967 -0.88259823], intercept:  [83.69264344]
trainsize: 180010, coef:  [ 0.17765624  0.1137466   0.15081498 -0.51520765 -1.00811419 -0.13203398   1.28565565 -0.03594421 -0.08053252 -2.31793746], intercept:  [85.21824705]
trainsize: 210010, coef:  [-0.53937513 -0.33872786 -0.44854466  0.70039384 -0.77073389  0.4361326   0.88175392 -0.32460908  0.5141777  -1.5123801 ], intercept:  [82.75353293]
trainsize: 240010, coef:  [ 0.70748011 -0.08992019  0.25365326  0.61999278 -0.29374005  0.25833863  -0.00485613 -0.21211637  0.19286126 -1.09503691], intercept:  [85.76414815]
trainsize: 270010, coef:  [ 0.73787648  0.30155102  0.44013832 -0.2355825   0.26255699  1.55410066   0.4733571   0.85352683  1.4399516  -1.73360843], intercept:  [84.19473044]
trainsize: 300010, coef:  [ 0.04861321 -0.35446415 -0.17774692 -0.1060901  -0.5864299   1.03429399   0.57160049 -0.13900199  1.09189946 -1.26298814], intercept:  [83.14797646]
trainsize: 330010, coef:  [ 0.20214825  0.22605839  0.17022397  0.28191112 -1.05982574  0.74025932   0.04981973 -0.27232538  0.72094765 -0.94875017], intercept:  [81.97656309]

编辑2: @Nikaido 要求:这是数据的分布。非常相似的分布式训练/测试数据功能来自于类别(范围 1-9)或解构时间戳的原始值,如 NumberOfMonth、DayOfWeek、Hours、Minutes。 labels 图显示 100 左右缺乏正态分布。原因是:缺失值已被每个类别的全球平均值取代,该平均值介于 80 到 95 之间。

进一步,我创建了一个图,显示由上面的代码 sn-p 通过更改生成的验证缩放:

my_rng = range(1000,len(X_train)-200000,2000)

SGD 典型的围绕最佳值的跳跃是可见的。但无论如何,随着训练集记录的增加,测试分数的趋势并没有发生任何显着变化。

【问题讨论】:

  • 您在for loo 中的用法很奇怪——我们通常不以这种方式使用 SGD;另外,您的 alpha 非常小 - 始终先尝试使用默认值。
  • 谢谢。 @desertnaut 有什么建议可以在循环中使用 SGD 来更好地可视化行为吗?如上所述,当我将 alpha 更改为极高或极低值时,它不会改变行为,或者只是将其保留为默认值。

标签: python machine-learning scikit-learn linear-regression gradient-descent


【解决方案1】:

编辑:关于您的输出,我的猜测是您的结果与验证集非常接近,因为像 SGDregressor 这样的线性模型往往无法适应复杂数据

要查看这一点,您可以检查模型在每次迭代时输出的权重。您会发现它们是相同的或非常接近的

为了提高输出的可变性,您需要引入非线性和复杂性

您正在获得机器学习中所谓的“偏差”(与“方差”相反)

我想我现在明白了。

SamAmani 最后我认为问题是欠拟合。以及您正在使用数据集的增量大小的事实。模型欠拟合很快(这意味着模型一开始就卡在一个或多或少固定的模型上)

只有第一个训练为测试集输出不同的结果,因为它或多或少没有达到最终模型

潜在的可变性在于增量训练集。 简单地说,测试结果是对欠拟合模型性能的更准确估计。并且添加训练样本最终会导致测试和训练之间的结果接近,而不会改善太多。

您可以检查训练的增量数据集与测试集不同的事实。你做错的是检查所有训练集的统计数据


首先,您为什么要在增量训练集大小上进行训练?奇怪的结果是由于您正在以增量方式训练数据集。

当你这样做时:

for m in my_rng:
    modelSGD = SGDRegressor(alpha=0.00001, penalty='l1')
    modelSGD.fit(X_train[:m], y_train[:m])
    [...]

你基本上是以增量方式训练你的模型,使用这个增量大小:

for m in range(10, 180001, 30000):
    print(m)


10
30010
60010
90010
120010
150010

如果您尝试进行小批量梯度下降,您应该将数据集拆分为独立的批次,而不是进行增量批次。像这样的:

previous = 0
for m in range(30000, 180001, 30000):
    modelSGD.partial_fit(X_train[previous:m], y_train[previous:m])
    previous = m

# training set ranges
0 30000
30000 60000
60000 90000
90000 120000
120000 150000
150000 180000

另外请注意,我使用的是partial_fit 方法,而不是fit(因为我没有从零开始重新训练模型,我只做了一步,即梯度下降的迭代),我不会每次都初始化一个新模型(我的 sgd 初始化不在 for 循环中)。完整的代码应该是这样的:

my_rng = range(0 ,len(X_train), 30000)
previous = 0
modelSGD = SGDRegressor(alpha=0.00001, penalty='l1')
for m in my_rng:
    modelSGD.partial_fit(X_train[previous:m], y_train[previous:m])
    ypred_train = modelSGD.predict(X_train[previous:m])
    ypred_test = modelSGD.predict(X_test)
    mse_train = mean_squared_error(y_train[previous:m], ypred_train)
    mse_test = mean_squared_error(y_test, ypred_test)
    scores_train.append(mse_train)
    scores_test.append(mse_test)

通过这种方式,您正在模拟 一个时期 小批量随机梯度。为了创造更多的时代,需要一个外循环

来自 sklearn:

SGD 允许通过 partial_fit 进行小批量(在线/核外)学习 方法。为了使用默认学习率计划获得最佳结果, 数据应具有零均值和单位方差。

详情here

【讨论】:

  • 谢谢!我知道 SGD 中有一个批处理功能,这就是我使用这个模型的原因。但这不是重点。我的目的是向您展示,无论我使用 30'000 或 300'000 个训练记录训练模型,验证数据的性能都不会改变。这就是让我感到困惑的地方。
  • 真的很感谢您对@Nikaido 的支持!我已经重新编辑了帖子thx。
  • @SamaAmani 我能想到的另一件事是您的实例的 y 值。它们是如何分布的?无论如何,洗牌会防止这种有偏见的数据集分裂。我有点疑惑。无论如何,培训也以某种方式“卡在”特定范围内,但范围更广。在 167 -174 之间。相反,测试在 176-177 之间
  • @Samamani 我认为这不值得您花时间。我认为您得到的只是模型的欠拟合。事实上,如果你更具体地检查测试,测试结果存在变异性,但总体结果比训练差。这是意料之中的,因为训练倾向于“过度拟合”训练数据,因此表现更好。这是很常见的。我认为您可以尝试的其他事情是:更改您切割训练和设置的分割,并尝试更复杂的算法
  • 例如 MLPRegressor
【解决方案2】:

我认为您要做的是查看数据集大小的增加对模型性能的影响。如果是这种情况,如您所见,随着数据大小的增加,错误会增加,并且它会达到有足够数据开始学习的阶段(训练错误略有减少)并最终稳定,因为数据集的增加现在并不重要。此外,它不会过度拟合,因为默认情况下 SGDRegressor 仅运行 1000 个 epoch。

我建议您在拟合之前对数据进行归一化(0 均值和单位方差)。

测试代码:

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error
import pandas
import matplotlib.pyplot as plt

X = np.random.randn(336144+144063, 10)
y = np.sum(X, axis=1) + np.random.randn(336144+144063)
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size = 0.3, random_state=85, shuffle=True)

print (X_train.shape, X_test.shape)

scores_test = []
scores_train = []

my_rng = range(5000,len(X_train),10000)
for m in my_rng:
    modelSGD = SGDRegressor(alpha=0.00001, penalty='l1')
    modelSGD.fit(X_train[:m], y_train[:m])
    
    ypred_train = modelSGD.predict(X_train[:m])
    ypred_test = modelSGD.predict(X_test)
    mse_train = mean_squared_error(y_train[:m], ypred_train)
    mse_test = mean_squared_error(y_test, ypred_test)
    scores_train.append(mse_train)
    scores_test.append(mse_test)

plt.plot(scores_train)
plt.plot(scores_test)

如果您尝试批量渐变,请关注@Nikaido 的回答

【讨论】:

  • 你完全正确。我想证明我的模型在少量训练数据后不会提高验证性能。所以您是在暗示我的数据是问题所在,除了在我的数据上使用StandardScaler 之外,还有更多步骤要做吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-03
  • 2020-10-02
  • 1970-01-01
  • 1970-01-01
  • 2020-06-15
  • 1970-01-01
相关资源
最近更新 更多