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

Nee*_*hah 15 matlab classification machine-learning gradient-descent logistic-regression

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

t = 1 ./ (1 + exp(-z));
Run Code Online (Sandbox Code Playgroud)

哪里

z = x*theta
Run Code Online (Sandbox Code Playgroud)

我使用以下成本函数来计算成本,以确定何时停止培训.

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

我在每一步的成本都是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
Run Code Online (Sandbox Code Playgroud)

ray*_*ica 22

您可能会遇到两种可能的原因.

数据未标准化

这是因为当您将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为特征的均值ks_k为特征的标准偏差k.这也称为标准化数据.您可以在此处给出的另一个答案中阅读有关此内容的更多详细信息:此标准化数据代码如何工作?

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

mX = mean(x,1); 
mX(1) = 0; 
sX = std(x,[],1); 
sX(1) = 1; 
xnew = bsxfun(@rdivide, bsxfun(@minus, x, mX), sX);
Run Code Online (Sandbox Code Playgroud)

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

假设您有一个存储在矩阵中的新数据点xx,您将进行规范化,然后执行预测:

xxnew = bsxfun(@rdivide, bsxfun(@minus, xx, mX), sX);
Run Code Online (Sandbox Code Playgroud)

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

pred = sigmoid(xxnew*theta) >= 0.5;
Run Code Online (Sandbox Code Playgroud)

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

学习率太高

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

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


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

  • @MatthewGunn弄明白为什么'NaN'正在发生.对于这个问题,`y`可以是0或1,并且`y*log(x*theta)`其中`x*theta`可以接近0将因此使'0*log(0)`因此产生` NaN`. (2认同)

Mat*_*unn 5

让我们假设您有一个观察结果:

  • 真实值是 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));
Run Code Online (Sandbox Code Playgroud)

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

y_logical = y == 1;
cost = sum(-log(htheta(y_logical))) + sum( - log(1 - htheta(~y_logical)));
Run Code Online (Sandbox Code Playgroud)

这个想法是如果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 的数值问题。