问题

离线特征是python加工出来的,在python中默认也是float类型,java中xgboost相关的api也都是float类型,为啥进行特征比对的时候结果就不一致呢?

这就涉及到底层数据结构的实现了,也就是本篇的由来,下面我们一探究竟。

在进行xgboost模型线上特征加工的时候,数据是采用Java float存储,为啥采用float呢?


因为如下的Booster api


public float[][] predict(DMatrix data, boolean outputMargin, int treeLimit) throws XGBoostError

Java预测时候,入参为DMatrix,数据是用的float类型

# https://xgboost.readthedocs.io/en/release_1.5.0/jvm/javadocs/index.html
public DMatrix(float[] data,
               int nrow,
               int ncol,
               float missing)
        throws XGBoostError
create DMatrix from dense matrix

Parameters:

data - data values

nrow - number of rows

ncol - number of columns

missing - the specified value to represent the missing value

在Java中 float占用4个字节,包括:

  • 1bit(符号位):0表示正数,1表示负数;
  • 8bits(指数位):float的偏移量为2^8 - 1,double的偏移量为2^11 - 1;
  • 23bits(尾数位):实际尾数部分中的小数点后的数值,规约浮点数使用标准的二进制科学计数法表示,其尾数范围在 [1,2),非规约浮点数的尾数部分范围在(0,1)

 

python中float类型是如何实现的?

python中的数值类型

共有三种不同的数值类型:整数浮点数复数。此外,布尔值是整数的一个子类型。

整数具有无限精度。

浮点数通常在 C 中使用double实现;有关运行程序的机器的浮点数精度和内部表示的信息可在sys.float_info. (这个就是Java float和python float数据存在差异的原因)

所以float是8个字节是真正的数字,但是float对象是24字节的。

复数具有实部和虚部,每个都是浮点数。要从复数z中提取这些部分,请使用z.realz.imag。(标准库包括额外的数字类型fractions.Fraction,用于有理数,和decimal.Decimal,用于具有用户可定义精度的浮点数。)

参见:Built-in Types — Python 3.11.0 documentation

案例重现

python具体示例:

>>> float(0.63693581908)

0.63693581908

>>> np.float16(0.63693581908)

0.6367

>>> np.float32(0.63693581908)

0.63693583

>>> np.float64(0.63693581908)

0.63693581908

Java具体示例


double a = -636935.81908; float b = (float) -636935.81908; float c = (float) -0.63693581908; float d = (float) 0.63693581908; System.out.println(a); System.out.println(b); System.out.println(c); System.out.println(d);


-636935.81908
-636935.8
-0.63693583
0.63693583

结论:

1、python中的float是和Java中的double是精度一样的

2、numpy中的float32是和Java中的float是等价的

所以在进行特征&模型结果比对的时候,最好按照这种结论中的形式进行数据精度对齐,保持结果一致。