我目前正在尝试实现一种机器学习算法,该算法涉及 MATLAB 中的逻辑损失函数。不幸的是,由于数字溢出,我遇到了一些麻烦。
一般来说,对于给定的输入s
,逻辑函数的值为:
log(1 + exp(s))
逻辑损失函数的斜率为:
exp(s)./(1 + exp(s)) = 1./(1 + exp(-s))
在我的算法中,s = X*beta
的值.这里X
是一个矩阵,每个数据点具有N
个数据点和P
特征(即 size(X)=[N,P]
(,beta
是每个特征的 P
系数的向量,使得 size(beta)=[P 1]
.
我对计算给定值 beta
的 Logistic 函数的平均值和梯度特别感兴趣。
逻辑函数 w.r.t 的平均值为 beta
:
L = 1/N * sum(log(1+exp(X*beta)),1)
逻辑函数 w.r.t. 的斜率与 b
值的平均值为:
dL = 1/N * sum((exp(X*beta)./(1+exp(X*beta))' X, 1)'
请注意,size(dL) = [P 1].
我的问题是这些表达式不断产生数字溢出。问题实际上来自这样一个事实,即s>1000
时exp(s)=Inf
,exp(s)=0
s<-1000.
我正在寻找一种解决方案,使s
可以在浮点运算中取任何值。理想情况下,我也非常感谢一个解决方案,它允许我以矢量化/有效的方式评估值和梯度。
以下近似值怎么样:
– 对于计算L
,如果s
很大,那么exp(s)
将远大于 1:
1 + exp(s) ≅ exp(s)
因此
log(1 + exp(s)) ≅ log(exp(s)) = s.
如果s
很小,则使用 exp(( 的泰勒级数
exp(s) ≅ 1 + s
并使用 log(( 的泰勒级数
log(1 + exp(s)) ≅ log(2 + s) ≅ log(2) + s / 2.
– 用于计算dL
,用于大型s
exp(s) ./ (1 + exp(s)) ≅ 1
和小s
exp(s) ./ (1 + exp(s)) ≅ 1/2 + s / 4.
– 计算L
的代码可能如下所示:
s = X*beta;
l = log(1+exp(s));
ind = isinf(l);
l(ind) = s(ind);
ind = (l == 0);
l(ind) = log(2) + s(ind) / 2;
L = 1/N * sum(l,1)
我找到了一篇关于这个问题的好文章。
切入很多单词,我们可以将论证简化为声明原始表达式
log(1 + exp(s))
可以改写为
log(exp(s)*(exp(-s) + 1))
= log(exp(s)) + log(exp(-s) + 1)
= s + log(exp(-s) + 1)
这可以阻止溢出的发生 - 它不会防止溢出,但是当溢出发生时,您就有了答案(即s
(。你不能只使用它代替原来的,因为它仍然会给你带来问题。但是,我们现在有一个可以编写的函数的基础,该函数将是准确的,并且不会产生溢出/下溢:
function LL = logistic(s)
if s<0
LL = log(1 + exp(s));
else
LL = s + logistic(-s);
我认为这保持了相当好的准确性。
现在编辑您的问题的实质 - 将其矢量化,并允许计算斜率。让我们一次拿一个:
function LL = logisticVec(s)
LL = zeros(size(s));
LL(s<0) = log(1 + exp(s(s<0)));
LL(s>=0) = s(s>=0) + log(1 + exp(-s(s>=0)));
要获得所需的平均值,请执行以下操作:
L = logisticVec(X*beta) / N;
斜率有点棘手;注意,我相信你的表达式中可能有错别字(缺少乘法符号(。
dL/dbeta = sum(X * exp(X*beta) ./ (1 + exp(X*beta))) / N;
如果我们将顶部和底部除以exp(X*beta)
我们得到
dL = sum(X ./ (exp(-X*beta) + 1)) / N;
再一次,溢出已经消失,我们只剩下溢出 - 但由于下溢值1
添加到其中,因此产生的错误是微不足道的。