逻辑回归中的成本函数给出NaN作为结果

时间:2016-02-15 21:58:58

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

我正在使用批量梯度下降实施逻辑回归。输入样本有两类要分类。类是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或0。我该怎么做才能确定每次迭代的成本价值?

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

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

3 个答案:

答案 0 :(得分:23)

为什么会发生这种情况有两种可能的原因。

数据未标准化

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

具体来说,如果NaN用于训练示例,并且假设的输出是y = 0,其中log(x)是一个非常小的数字,接近0,检查第一部分费用函数会给我们x,实际上会产生0*log(x)。同样,如果NaN用于培训示例,并且假设的输出也是y = 1,其中log(x)是一个非常小的数字,这又会给我们x并且会产生0*log(x)。简而言之,您的假设的输出要么非常接近0,要么非常接近1.

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

解决此问题的一种方法是在使用渐变下降进行训练之前,规范化矩阵中的数据。典型的方法是使用零均值和单位方差进行归一化。给定输入要素x*theta,其中x_k具有k = 1, 2, ... n个功能,可以通过以下方式找到新的规范化要素n

x_k^{new}是功能m_k的平均值,k是功能s_k的标准偏差。这也称为标准化数据。您可以在此处提供的另一个答案中了解有关此内容的更多详细信息:How does this code for standardizing data work?

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

k

每个要素的均值和标准差分别存储在mX = mean(x,1); mX(1) = 0; sX = std(x,[],1); sX(1) = 1; xnew = bsxfun(@rdivide, bsxfun(@minus, x, mX), sX); mX中。您可以通过阅读我上面链接到您的帖子来了解此代码的工作原理。我不会在这里重复这些内容,因为这不是这篇文章的范围。为确保正确归一化,我将第一列的均值和标准差分别设为0和1。 sX包含新的规范化数据矩阵。请使用xnew代替渐变下降算法。现在,一旦找到参数,要执行任何预测,您必须使用训练集的均值和标准差来规范化任何新测试实例。由于学习的参数与训练集的统计数据有关,因此您还必须对要提交给预测模型的任何测试数据应用相同的变换。

假设您将新数据点存储在名为xnew的矩阵中,您可以进行规范化,然后执行预测:

xx

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

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

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

学习率太大

正如您在评论中提到的,一旦您对数据进行标准化,成本似乎是有限的,但在几次迭代后突然转到NaN。规范化只能让你到目前为止。如果你的学习率或pred = sigmoid(xxnew*theta) >= 0.5; 太大,每次迭代都会朝着最小化的方向超调,从而使每次迭代的成本发生振荡甚至发散,这正是看起来正在发生的事情。在您的情况下,成本在每次迭代时会发散或增加,直到它太大而无法使用浮点精度表示。

因此,另一个选择是降低学习率alpha,直到您看到成本函数在每次迭代时减少。确定最佳学习率的常用方法是在alpha的对数间隔值范围内执行梯度下降,并查看最终成本函数值是什么,并选择导致最小成本的学习率。

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

答案 1 :(得分:4)

我们假设您有一个观察点:

  • 真值是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));

您可以避免将0乘以无穷大,而不是将您的成本函数写在Matlab中:

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。

答案 2 :(得分:1)

发生在我身上的原因是类型不确定:

0*log(0)

当预测值Y之一等于 0 1 时,可能会发生这种情况。 在我的情况下,解决方案是向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或反之亦然,但是如果您的数据集已标准化并且权重已正确初始化,不会有问题。)