我在使用批量梯度下降法实现逻辑回归。输入样本需要被分类为两个类别,分别是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
在每一步的成本计算中,由于htheta
的值在大多数情况下为1或0,我得到的结果是NaN。我应该如何在每次迭代中确定成本值?
这是逻辑回归的梯度下降代码:
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);endend
回答:
出现这种情况可能有两个原因。
数据未归一化
这是因为当你对假设应用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的小数,这同样会得到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
的标准差。这也被称为标准化数据。你可以在这里阅读我给出的另一个答案,了解更多关于此的详细信息:这个标准化数据的代码是如何工作的?
因为你使用的是线性代数方法进行梯度下降,我假设你已经在数据矩阵前添加了一列全为1的值。知道这一点,我们可以像这样归一化你的数据:
mX = mean(x,1); mX(1) = 0; sX = std(x,[],1); sX(1) = 1; xnew = bsxfun(@rdivide, bsxfun(@minus, x, mX), sX);
每个特征的均值和标准差分别存储在mX
和sX
中。你可以通过阅读我上面链接的帖子来了解这段代码是如何工作的。我不会在这里重复那些内容,因为这不是本文的范围。为了确保正确的归一化,我已经将第一列的均值和标准差分别设为0和1。xnew
包含新的归一化数据矩阵。用xnew
代替你的梯度下降算法。现在一旦你找到参数,要进行任何预测,你必须使用来自训练集的均值和标准差对任何新的测试实例进行归一化。因为学习的参数是相对于训练集的统计数据的,你也必须对你想要提交给预测模型的任何测试数据应用相同的变换。
假设你有新的数据点存储在一个名为xx
的矩阵中,你将进行归一化然后进行预测:
xxnew = bsxfun(@rdivide, bsxfun(@minus, xx, mX), sX);
现在你有了这个,你可以进行预测:
pred = sigmoid(xxnew*theta) >= 0.5;
你可以将0.5的阈值更改为你认为最适合确定示例属于正类还是负类的任何值。
学习率过大
正如你在评论中提到的,一旦你归一化数据,成本似乎是有限的,但几次迭代后突然变成NaN。归一化只能帮你到此为止。如果你的学习率或alpha
过大,每次迭代都会朝向最小值的方向过度移动,从而使每次迭代的成本振荡甚至发散,这就是似乎正在发生的情况。在你的情况下,成本在每次迭代中发散或增加到如此大的程度,以至于无法用浮点精度表示。
因此,另一个选择是降低你的学习率alpha
,直到你看到每次迭代的成本函数都在减少。确定最佳学习率的一个流行方法是在alpha
的一系列对数间隔值上进行梯度下降,并查看最终的成本函数值是多少,然后选择导致最小成本的学习率。
结合上述两个事实,应该可以让梯度下降很好地收敛,假设成本函数是凸的。在逻辑回归的情况下,确实如此。