Java实现逻辑回归时对数似然计算返回NaN问题排查
解决Java逻辑回归中对数似然计算返回NaN的问题
你碰到的NaN问题根源很明确:当sigmoid输出无限接近0或1时,Math.log(0)会返回负无穷,而0乘以负无穷在Java中会得到NaN。举个具体场景:
- 如果模型预测的概率
prob几乎为0,且样本类别cat=0,那么cat * Math.log(prob)就是0 * (-∞),结果就是NaN; - 如果
prob几乎为1,且cat=1,那么(1 - cat) * Math.log(1 - prob)就是0 * (-∞),同样会得到NaN。
结合你的代码,这里有两个简洁的解决方案:
方案1:限制概率值范围(推荐)
在findProbability方法中,给sigmoid的结果加一个极小的偏移量(epsilon),把概率牢牢限制在[epsilon, 1-epsilon]区间内,从根源上避免log(0)的情况:
public double findProbability(double[] x){ double s = 0; for(int i = 0; i < this.weights.length; i++){ if(i >= x.length) break; s += this.weights[i] * x[i]; } double prob = sigmoid(s); // 用1e-10作为epsilon,既不影响模型精度,又能避免边界值 double epsilon = 1e-10; return Math.max(epsilon, Math.min(1 - epsilon, prob)); }
方案2:在对数计算时做边界判断
如果不想修改原始概率值,也可以在计算对数时单独处理极端情况:
private double getLogLikelihood(double cat, double[] x) { double prob = findProbability(x); // 处理log(prob)的边界情况 double logProb = prob < 1e-10 ? -23.02585093 : Math.log(prob); // 处理log(1-prob)的边界情况 double log1MinusProb = (1 - prob) < 1e-10 ? -23.02585093 : Math.log(1 - prob); return cat * logProb + (1 - cat) * log1MinusProb; }
注:-23.02585093是log(1e-10)的近似值,也可以用-1e20这类极小值代替。
额外说明
你初始化的权重里有不少绝对值较大的值(比如-2.732、-2.062),这会导致s = sum(w*x)的绝对值非常大,进而让sigmoid输出无限接近0或1——这也是为什么换随机权重后问题依然存在的原因。上面的两种方案都能有效解决这个问题,其中方案1更简洁易维护。
内容的提问来源于stack exchange,提问作者user8928579
相关产品推荐
相关产品推荐

