【问题标题】:Sigmoid function return NaN in JavaJava中的Sigmoid函数返回NaN
【发布时间】:2018-06-19 10:40:02
【问题描述】:

我正在尝试在 java 中创建逻辑回归算法,但是当我计算似然的对数时,它总是返回 NaN。我计算对数的方法如下所示:

//Calculate log likelihood on given data
private double getLogLikelihood(double cat, double[] x) {
    return cat * Math.log(findProbability(x))
         + (1 - cat) * Math.log(1 - findProbability(x));
}

findProbability 方法只是从数据集中获取一个实例并返回 0 到 1 之间的 sigmoid 函数结果。

//Calculate the sum of w * x for each weight and attribute
//call the sigmoid function with that s
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];        
    }
    return sigmoid(s);
}

private double sigmoid(double s){
    return 1 / (1 + Math.exp(-s));
}

此外,我的起始体重是:

[-0.2982955509135178, -0.4984900460081106, -1.816880187922516, -2.7325608512266073, 0.12542715714800834, 0.1516078084483485, 0.27631147403449774, 0.1371611094778011, 0.16029832096058613, 0.3117065974657231, 0.04262385176091778, 0.1948263133838624, 0.10788353525185314, 0.770608588466501, 0.2697281907888033, 0.09920694325563077, 0.003224073601703939, 0.021573742410541247, 0.21528348692817675, 0.3275511757298476, -0.1500597314893408, -0.7221692528386277, -2.062544912370121, 1.4315146889363015, 0.2522133355419722, 0.23919315019065995, 0.3200037377021523, 0.059466770771758076, 0.04012493980772944, 0.2553236501265919]

最后,我的数据集中的一个实例是:[M,17.99,10.38,122.8,1001,0.1184,0.2776,0.3001,0.1471,0.2419,0.07871,1.095,0.9053,8.589,153.4,0.006399,0.04904,0.05373,0.01587,0.03003,0.006193,25.38,17.33,184.6,2019,0.1622,0.6656,0.7119,0.2654,0.4601,0.1189]

我尝试用不同的随机数初始化起始权重,但这并没有解决问题。

【问题讨论】:

  • 你检查s的值了吗?
  • 是的,它在 2358.7420164286 和 3522.2404828342 之间
  • 可能是这个函数Math.log(1 - findProbability(x) 给你负数。这意味着您的findProbability(x) 返回的值大于一。检查那里
  • e^(-3522) 非常接近 0,所以您的 sigmoid 函数返回 1。因此您要求 log(0)?

标签: java nan logistic-regression


【解决方案1】:

我找到了解决问题的方法,所以我在这里发布: 我添加了溢出检查:

private double sigmoid(double s){
    if(s>20){
        s=20;
    }else if(s<-20){
        s=-20;
    }
    double exp = Math.exp(s);
    return exp/(1+exp);
}

同样将1/(1+Math.exp(s) 更改为exp/(1+exp) 证明在输入的小扰动下更稳定。

【讨论】:

    【解决方案2】:

    NaN 是除以零或在非正数上调用 Math.log 的结果,因此您应该尝试找出发生这种情况的确切位置。我建议调试或添加代码来打印你取对数/除数的值。

    编辑:这似乎是一个舍入错误:exp(-s) 将返回一个很小的结果,加上 1 它仍将保持 1。这导致对数返回 -Inf。我建议你尝试通过尝试近似指数的对数来找到一种数学方法来解决这个问题。

    【讨论】:

    • 我认为 Math.log 不能被称为负数,因为它从 sigmoid 函数中获取结果,该函数返回 0 到 1 之间的值。
    • 我刚刚测试了您的代码,在您的数据集实例中省略了 M(因为我不知道它是如何计算的)。这在调用 getloglikelihood(0.5,dataset) 时给了我一个不错的结果,所以也许你的数据集中的 M 在计算之前已经是 NaN 了?
    • 我将 M 转换为 1,所以它不会影响任何东西
    【解决方案3】:

    算术导致舍入错误,留下 1。

    double b = 1 + Math.exp(-3522);
    

    b 将等于 1,否则您将需要太多 sig figs。您必须近似该值以保持精度。 1/(1+s) ~= 1 - s;这意味着您需要计算 log(1) 和 log(s)。

    编辑:抱歉,我犯了一个错误,似乎 Math.exp(-3522) 在四舍五入后被评估为 0。我会留下这个答案,因为 Math.exp(-x) 可能太小而不能加到 1,或者它可能只是零。

    【讨论】:

      猜你喜欢
      • 2021-09-18
      • 2012-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-06
      • 2021-05-10
      相关资源
      最近更新 更多