如何从JPMML模型的InputField获取实际字段名?解决x{#}映射问题
解决JPMML调用scikit-learn模型时特征名变为x{#}的映射问题
我明白你遇到的困扰——明明训练时用了有意义的列名,结果在Java端调用时拿到的却是x0、x1这种混淆后的名称,没法和原始特征对应上。这个问题的核心是训练数据丢失了列名信息,导致sklearn2pmml生成PMML时只能用默认的占位符名称。下面给你分步解决:
一、问题根源
你在Python代码里用了as_matrix()把DataFrame转成了numpy数组,而numpy数组是没有列名元数据的。哪怕你后来设置了pipe.active_fields,但因为模型训练时用的是无列名的数组,sklearn2pmml没法把原始列名正确写入PMML文件,只能生成x0、x1这类默认名称。
二、最优解决方案:重新生成带原始列名的PMML
最直接的方法是修改Python代码,保留训练数据的列名信息,让sklearn2pmml自动把原始列名写入PMML:
修改后的Python代码
from sklearn2pmml import PMMLPipeline import pandas as pd from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier # 读取数据 data = pd.read_csv('/pydata/training.csv') # 直接使用DataFrame保存特征列,不要转成numpy数组(保留列名) X = data[data.columns[:-1]] y = data['classname'] # 拆分数据集,X_train依然是带列名的DataFrame X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) # 构建PMMLPipeline,直接传入带列名的特征数据 pipe = PMMLPipeline([ ("classifier", RandomForestClassifier(n_jobs=5, n_estimators=200, max_features='auto')) ]) # 用带列名的DataFrame训练模型 pipe.fit(X_train, y_train) # 生成PMML,此时原始列名会被自动写入模型文件 sklearn2pmml(pipe, "/pydata/model.pmml", with_repr=True)
对应的Java代码调整
重新生成PMML后,Java端就能直接获取原始特征名了,不需要额外映射:
Map<FieldName, FieldValue> arguments = new LinkedHashMap<>(); for (InputField inField : patternEvaluator.getInputFields()) { // 这里直接拿到原始列名 String originalFeatureName = inField.getName().getValue(); int value = activeFeatures.contains(originalFeatureName) ? 1 : 0; FieldValue inputFieldValue = inField.prepare(value); arguments.put(inField.getName(), inputFieldValue); } Map<FieldName, ?> results = patternEvaluator.evaluate(arguments);
三、补救方案:已生成PMML的映射处理
如果没法重新生成PMML,你可以在Python端提前保存特征名和x{#}的映射关系,Java端加载这个映射表来转换:
1. Python端保存映射文件
在生成PMML时,添加代码保存特征名顺序(因为x0对应第一列,x1对应第二列,以此类推):
# 在sklearn2pmml之前执行 feature_names = data.columns[:-1].tolist() # 保存映射关系为JSON文件 import json with open('/pydata/feature_mapping.json', 'w') as f: json.dump({f"x{i}": name for i, name in enumerate(feature_names)}, f)
2. Java端加载映射表
读取JSON文件,把x{#}转换成原始特征名:
// 先加载映射表(这里用Jackson示例,你可以用自己熟悉的JSON库) ObjectMapper mapper = new ObjectMapper(); Map<String, String> featureMapping = mapper.readValue( new File("/pydata/feature_mapping.json"), new TypeReference<Map<String, String>>() {} ); // 处理InputField Map<FieldName, FieldValue> arguments = new LinkedHashMap<>(); for (InputField inField : patternEvaluator.getInputFields()) { String rawName = inField.getName().getValue(); // 映射回原始特征名 String originalFeatureName = featureMapping.get(rawName); int value = activeFeatures.contains(originalFeatureName) ? 1 : 0; FieldValue inputFieldValue = inField.prepare(value); arguments.put(inField.getName(), inputFieldValue); } Map<FieldName, ?> results = patternEvaluator.evaluate(arguments);
内容的提问来源于stack exchange,提问作者webber
相关产品推荐
相关产品推荐

