【问题标题】:Cost function in logistic regression gives NaN as a result逻辑回归中的成本函数结果为 NaN
【发布时间】:2016-05-26 23:36:46
【问题描述】:

我正在使用批量梯度下降实现逻辑回归。输入样本将分为两类。类是 1 和 0。在训练数据时,我使用了以下 sigmoid 函数:

t = 1 ./ (1 + exp(-z));

在哪里

z = x*theta

我正在使用以下成本函数来计算成本,以确定何时停止训练。

function cost = computeCost(x, y, theta)
    htheta = sigmoid(x*theta);
    cost = sum(-y .* log(htheta) - (1-y) .* log(1-htheta));
end

我将每一步的成本设为 NaN,因为在大多数情况下,htheta 的值要么为 1,要么为零。我应该怎么做才能确定每次迭代的成本值?

这是逻辑回归的梯度下降代码:

function [theta,cost_history] = batchGD(x,y,theta,alpha)

cost_history = zeros(1000,1);

for iter=1:1000
  htheta = sigmoid(x*theta);
  new_theta = zeros(size(theta,1),1);
  for feature=1:size(theta,1)
    new_theta(feature) = theta(feature) - alpha * sum((htheta - y) .*x(:,feature))                         
  end
  theta = new_theta;
  cost_history(iter) = computeCost(x,y,theta);
end
end

【问题讨论】:

  • 您使用什么语言进行编码?您能否提供一个最小的可重现示例以及数据?
  • 数据由 57 个特征组成,标签为 1 或 0,即 y 向量
  • 我可以提供更多详细信息吗?
  • 如果您能提供您的数据文件的链接,那就太好了。您是否通过 cost_history 变量验证 NaN 值?请注意,此变量的大小为 1000,但您正在运行 5000000 次迭代。所以cost_history(iter) = computeCost(x,y,theta); 可能定义了超出范围的值。
  • 这高度依赖于您忽略包含的输入数据。你的数据矩阵x是什么样的?

标签: matlab machine-learning classification logistic-regression gradient-descent


【解决方案1】:

发生这种情况的原因有两个。

数据未归一化

这是因为当您将 sigmoid / logit 函数应用于您的假设时,输出概率几乎全为 0 或全为 1,并且使用您的成本函数,log(1 - 1)log(0) 将产生 -Inf。您的成本函数中所有这些单独项的累积最终将导致NaN

具体来说,如果y = 0 用于训练示例,并且假设的输出是log(x),其中x 是一个非常小的数字,接近于0,则检查成本函数的第一部分会给我们0*log(x) 实际上会产生NaN。类似地,如果y = 1 用于训练示例,并且假设的输出也是log(x),其中x 是一个非常小的数字,这将再次给我们0*log(x) 并将产生NaN。简单地说,你的假设的输出要么非常接近 0,要么非常接近 1。

这很可能是因为每个特征的动态范围差异很大,因此您的假设的一部分,特别是您拥有的每个训练示例的 x*theta 的加权总和会给您带来非常大的负数或正值,如果您将 sigmoid 函数应用于这些值,您将非常接近 0 或 1。

解决此问题的一种方法是在使用梯度下降进行训练之前对矩阵中的数据进行标准化。一种典型的方法是使用零均值和单位方差进行归一化。给定输入特征 x_k 其中 k = 1, 2, ... n 具有 n 特征,新的归一化特征 x_k^{new} 可以通过以下方式找到:

m_k 是特征 k 的平均值,s_k 是特征 k 的标准差。这也称为标准化数据。您可以在我在这里给出的另一个答案中阅读有关此问题的更多详细信息:How does this code for standardizing data work?

因为您使用线性代数方法进行梯度下降,所以我假设您在数据矩阵前面添加了一列全为 1。知道了这一点,我们可以像这样规范化您的数据:

mX = mean(x,1); 
mX(1) = 0; 
sX = std(x,[],1); 
sX(1) = 1; 
xnew = bsxfun(@rdivide, bsxfun(@minus, x, mX), sX);

每个特征的均值和标准差分别存储在mXsX中。您可以通过阅读我上面链接到您的帖子来了解此代码的工作原理。我不会在这里重复这些内容,因为这不是本文的范围。为了确保正确的标准化,我将第一列的均值和标准差分别设为 0 和 1。 xnew 包含新的标准化数据矩阵。将xnew 与您的梯度下降算法一起使用。现在,一旦找到参数,要执行任何预测,您必须使用 训练集 的均值和标准差对任何新测试实例进行归一化。由于学习的参数与训练集的统计信息有关,因此您还必须对要提交给预测模型的任何测试数据应用相同的转换。

假设您将新数据点存储在名为xx 的矩阵中,您将进行归一化然后执行预测:

xxnew = bsxfun(@rdivide, bsxfun(@minus, xx, mX), sX);

现在你有了这个,你可以执行你的预测:

pred = sigmoid(xxnew*theta) >= 0.5;

您可以将阈值 0.5 更改为您认为最能确定示例属于正类还是负类的任何值。

学习率太大

正如您在 cmets 中提到的,一旦您对数据进行标准化,成本似乎是有限的,但在几次迭代后突然变为 NaN。规范化只能让你到目前为止。如果您的学习率或alpha 太大,则每次迭代都会朝最小值方向超调,从而使每次迭代的成本出现波动甚至发散,这似乎正在发生。在您的情况下,每次迭代的成本都会发散或增加,以至于无法使用浮点精度表示。

因此,另一种选择是降低您的学习率alpha,直到您看到成本函数在每次迭代时都在降低。确定最佳学习率的一种流行方法是对alpha 的一系列对数间隔值执行梯度下降,并查看最终成本函数值是多少,然后选择导致最小成本的学习率。


假设成本函数是凸的,结合使用上述两个事实应该可以让梯度下降很好地收敛。在这种情况下,对于逻辑回归,它肯定是。

【讨论】:

  • 是的,我想通了。非常感谢。
  • 我得到的值很少,但大多数值仍然是 NaN。有什么办法可以克服吗?
  • 是的,如果发生这种情况,一种方法是对较大的负值和正值实施上限。在计算总和之前的成本函数文件中,您可以执行类似htheta(htheta >= 100) = 100; htheta(htheta <= -100) = -100; 这样的操作,这将确保当您将log 应用于假设向量时,您将获得浮点友好的结果。如果你得到一个大于 100 或小于 -100 的假设,那么我们可以安全地假设我们可以将输入分别分类为 1 或 0 类,因此将这个上限放在你的结果上应该是可以的。
  • 我仍然无法获得适当的准确性。这是我正在处理的数据集:archive.ics.uci.edu/ml/datasets/Spambase
  • @MatthewGunn 弄清楚了NaNs 发生的原因。 y 在这个问题上可以是 0 或 1,并且在 x*theta 可以接近于 0 的情况下执行 y*log(x*theta) 会导致 0*log(0) 并因此产生 NaN
【解决方案2】:

假设您有一个观察结果:

  • 真正的值是 y_i = 1
  • 您的模型非常极端并说 P(y_i = 1) = 1

那么您的成本函数将获得 NaN 的值,因为您要添加未定义的 0 * log(0)。因此:

您的成本函数公式有问题(有一个微妙的 0,无穷大问题)!

正如@rayryeng 指出的那样,0 * log(0) 会产生NaN,因为0 * Inf 不是犹太洁食。这实际上是一个巨大的问题:如果您的算法认为它可以完美地预测一个值,那么它错误地分配了 NaN 的成本。

代替:

cost = sum(-y .* log(htheta) - (1-y) .* log(1-htheta));

您可以通过在 Matlab 中编写成本函数来避免将 0 乘以无穷大:

y_logical = y == 1;
cost = sum(-log(htheta(y_logical))) + sum( - log(1 - htheta(~y_logical)));

这个想法是如果y_i 为1,我们将-log(htheta_i) 添加到成本中,但如果y_i 为0,我们将-log(1 - htheta_i) 添加到成本中。这在数学上等价于 -y_i * log(htheta_i) - (1 - y_i) * log(1- htheta_i),但不会遇到本质上源于 htheta_i 在双精度浮点范围内等于 0 或 1 的数值问题。

【讨论】:

  • 你能详细说明一下吗?我不明白这将如何避免 NaN 或 Inf 情况。谢谢。
  • 顺便说一句,马修,您可能会得到尺寸不匹配,因为使用 y_logical~y_logical 进行索引可能会产生不同大小的向量。我建议将sum 拆分为两个单独的操作...y == 1y == 0 然后将两个结果相加。
  • @NeelShah 类似:cost = sum(-log(htheta(y == 1))) + sum(-log(1 - htheta(y == 0))); 应该做得很好。
  • @rayryeng 哎呀!你说得对。现在应该更正了。
  • @codewarrior 在 Matlab 中,假设您有一个向量 x = [1, 2, 3, 4, 5, 6]';,您可以使用 y = x([1,0,1,1,0,1]'),然后 y 将等于 [1, 3, 4, 6]。它有点像 .selectSubsetBasedOnLogicalMask 函数。转到此页面上的逻辑索引:mathworks.com/company/newsletters/articles/…
【解决方案3】:

它发生在我身上是因为类型不确定:

0*log(0)

当其中一个预测值 Y 等于 01 时,可能会发生这种情况。 在我的情况下,解决方案是在 python 代码中添加一个 if 语句,如下所示:

y * np.log (Y)  + (1-y) * np.log (1-Y) if ( Y != 1 and Y != 0 ) else 0

这样,当实际值 (y) 和预测值 (Y) 相等时,不需要计算成本,这是预期的行为。

(请注意,当给定 Y 收敛到 0 时,左加数被取消(因为 y=0),右加数趋向 0 . 当 Y 收敛到 1 时也会发生同样的情况,但加数相反。)

(还有一种非常罕见的情况,您可能不需要担心,其中 y=0 和 Y=1 或反之亦然,但如果您的数据集已标准化并且权重已正确初始化,则不会是问题。)

【讨论】:

  • 仅供参考,此处显示的语法是 Python 中的。这是一个 MATLAB 问题。
猜你喜欢
  • 2021-02-16
  • 2020-05-08
  • 1970-01-01
  • 2015-09-28
  • 2018-03-01
  • 2020-08-08
  • 1970-01-01
  • 2020-08-27
相关资源
最近更新 更多