PMML模型概率值转换分数

数据组给出的PMML模型文件通过Java的jpmml运行后获取的值是一个(0,1)的概率值,指的是概率为1的值(二分类模型)。项目上需要将其均匀转换成一个分数,但是不同模型所属场景不同,需要转换的目标也不同。大致有两种情况,一种是模型阈值为0.5,一种是模型阈值不为0.5,这两种的Java转换思路如下。

  • 0-100标准分数且模型阈值不为0.5。

    这种情况一般模型会给出一个模型阈值,即区分好坏样本的中间值,一般的二分类模型可能是0.5,即大于0.5则为1,小于0.5则为0。但是实际生成中这个值会根据模型情况变动,不一定为0.5,这时候就需要结合该值进行转换,转换后0-100的分数中间值为50。

    /**
         * @param p          特征重要性
         * @param modelScore 模型概率
         * @return 模型分数
         */
        private String exchange(Double p, String modelScore) {
    
            //如果是科学计算,需要转换下
            if (modelScore.contains("E")) {
                modelScore = new BigDecimal(modelScore).toPlainString();
            }
    
            double x = Double.parseDouble(modelScore);
            double k;
            if (x > p) {
                if (p > 1 - 1E-6) {
                    k = 1 / (x - p);
                } else {
                    k = 1 / (1 - p);
                }
            } else {
                if (p < 1E-6) {
                    k = 1;
                } else {
                    k = 1 / p;
                }
            }
            //50+50*转化斜率*(概率值与模型阈值的差),String expression = "50+50*k*(x-p)";
            double score = Math.abs(50 + 50 * k * (x - p));
    
            return String.format("%.2f", score);
        }

    例如,当模型阈值为0.45,模型概率值为0.5,映射到0-100分数为:

    50 + 50 * 1 / (1 - 0.45) * (0.5 - 0.45) = 54.55

大致推导过程可以将映射过程分为两段即[0, k)映射到[0-50), [k,1]映射到[50,100]。并合并成通项公式,转换到其他区间也是如此。

  • 模型阈值为0.5,并转换为300-900

    /**
     * 模型分输出分值, 0-1概率,转换为300-900
     * 1-高风险, 0-低风险
     * @param targetValue 目标值
     * @return
     */
    private String exchange(Double targetValue){
      String modelScore = "";
      if(targetValue == null){
        return modelScore;
      }
      BigDecimal original = BigDecimal.valueOf(targetValue);
      int score = new BigDecimal(300).add(new BigDecimal(600).multiply(original)).intValue();
      //极值处理
      if(score == 300){
        score = score + RandomUtils.nextInt(0,10);
      }
      if(score == 900){
        score = score - RandomUtils.nextInt(0,10);
      }
      modelScore = String.valueOf(score);
      return modelScore;
    }

    公式如下

    newScore = 300 + ((oldScore - 0) * (900 - 300) / (1 - 0))
                     = 300 + 600 * oldScore

推导如下:

假设我们有一个原始区间 [a, b],我们想要将其均匀放大到目标区间 [c, d]。为了完成这个映射,我们可以使用以下的线性变换公式:

x_new = c + ((x_old - a) * (d - c) / (b - a))

其中:

  • x_old 是原始区间 [a, b] 内的任意数。
  • x_newx_old 映射到目标区间 [c, d] 后的对应数。
  • ab 是原始区间的下限和上限。
  • cd 是目标区间的下限和上限。

这个公式的原理是,首先计算 x_old 在原始区间内的相对位置(通过 (x_old - a) / (b - a) 得到,其值在 0 到 1 之间),然后将这个相对位置乘以目标区间的长度 (d - c),最后加上目标区间的下限 c,从而得到 x_new。但该公式仅用于模型阈值为0.5的情况,也就是均匀分布,均匀放大。

最后修改:2025 年 05 月 11 日
如果觉得我的文章对你有用,请随意赞赏