python训练XGB模型,使用Java调用踩坑
记录jpmml转xgb模型踩坑、为什么及解决方法。
工作中需要使用Python进行模型训练,导出为pmml文件进而用Java调用,部署在服务器上。调研了一波后,使用jpmml包可以将xgb原生接口保存的模型文件转为pmml文件,之后可以在Java中调用。
xgb原生接口save_model后得到一个xgb_model.json文件,使用jpmml包将json文件转换为pmml文件,这个pmml是个xml格式的文件,里面存的是树的结构。此时,再使用jpmml-evaluation去调用模型,进行预测。
这时问题出现了,使用Python调用和Java调用两者的值差别很大,检查后发现。json文件中保存了50个特征,而转换之后的pmml文件中只有45个特征,少了5个特征,查看json文件中特征值重要性排序:
import xgboost as xgb
model = xgb.Booster({'nthread':1})
model.load_model("xgb_model.json")
#查看模型重要性
sorted([(i,v) for i,v in model.get_fscore().items()], key=lambda x:x[1])
发现列出来的只有45个特征,缺少的5个特征值由于系数为0,没有显示出来。这里对xgb模型中系数为0的特征值又进行下一步调研,得出:虽然特征重要性为0,但是不一定代表没有作用,该特征与其他特征关联起来也会起到一定作用,因此不能随意删除。
转而再继续调研jpmml,得出:jpmml系列的代码(包括Java包jpmml和Python包sklearn2pmml)都会将系数为0的特征去除,不是bug而是jpmml的特性,想要保留那些被去除的特征,解决方法是直接修改pmml文件,将删除的特征重新填进去。 这时又会出现另一个问题,pmml文件中,保存的都是数的结构,有很多棵树,我们不知道缺失的5个特征存在于哪棵树上,因此修改pmml文件也有很大困难。
最终解决方法是,1、重新训练xgb,重新审视重要性为0的特征是否真的对模型有作用;2、不使用Java调用,依旧Python调用,这样就不会丢失特征值。
查资料查了5个小时,发现有遇见这个问题的博文不多,以下是参考过的链接:
1、https://www.cnblogs.com/lockyluo/p/17160839.html
2、http://ponder.work/2020/04/10/convent-xgboost-model-to-pmml-guide/
3、https://github.com/jpmml/sklearn2pmml/issues/242
4、https://www.jianshu.com/p/225f260af1e6
5、https://www.jianshu.com/p/386fa2eb218a/
6、https://www.jianshu.com/p/fcb7256fcbd5