我正在使用批量梯度下降实施逻辑回归。输入样本有两类要分类。类是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
答案 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)
我们假设您有一个观察点:
然后,您的费用函数的值为NaN
,因为您要添加0 * log(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或反之亦然,但是如果您的数据集已标准化并且权重已正确初始化,不会有问题。)