vector_trans udf 函数的开发及优化
一、背景
团体数据挖掘我的项目下,用户画像工程须要开发一些根底标签,如年龄、性别、婚姻状况、有车、有房、资产状况等。这些标签不能简略的应用团体下的各个数据作为最终评估后果,因为不同数据的置信度状况不一样。
因而咱们须要应用决策树 +XGBoost 等模型来综合预测用户标签。比方以下几种标签的决策树如图所示:
年龄:
年龄段:
性别:
婚姻状况:
在我本人曾经实现的决策树 + 算法模型预测用户标签的我的项目下,须要保障 XGBoost 模型输出的数据为一个 不同特色数据转换而来的向量数组。下边贴一段 scala 代码:
// 次要做的是将 app_list 特色数据转换成 XGBoost 须要的向量数组
data.map(row => {val appList = row.getAs[String]("app_list")
if(null == appList || appList.length == 0) {(row.getString(0), row.getString(1), row.getString(2),
row.getString(3), row.getString(4), null)
} else {val arr = appList.split("|")
val map = new mutable.LinkedHashMap[String, Double]() ++= appMap
arr.foreach(i => if (map.contains(i)) map(i) = 1d)
(row.getString(0), row.getString(1), row.getString(2),
row.getString(3), row.getString(4), map.values.toArray)
}
}).toDF("id", "finance_age", "xinan_age", "resume_age", "umc_age", "model_data")
为了缩小业务逻辑对形象工程的影响,我倡议把这段代码放到前一段 ETL 去做,而我这边的决策树我的项目自身只做数据集的分类计算的工作,这样能够让工程自身的扩展性大大提高。于是接下来便是怎么解决几百上千各特色在分布式环境下高效遍历的问题。我想到了自定义 UDF。
二、实现
代码实现其实不难,下边举个粒子:
str1:"王者光荣 | i 漫游服务 |qq 音乐"
str2:"王者光荣 | 爱奇艺 | i 漫游服务 |qq 音乐 |seetong| 天猫精灵 |qq 邮箱"
str1 是咱们某个用户的 app 软件列表,str2 是咱们模型须要的特色列表。
咱们想要失去的后果为:
out:Array[1.0,0.0,1.0,1.0,,0.0,0.0,0.0]
意思是在 str2 中某个地位如果存在,那这个地位的编码为 1,否则为 0。这样就把特色转换成了向量数组。
udf 代码如下:
package com.bj58.fbuudf.trans;
import org.apache.hadoop.hive.ql.exec.UDF;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 遍历传入数组,将其转为指标向量数组(XGBoost 模型)* 入参:【String, String, String】:待转换数组字符串,指标数组字符串,分隔符
* 例:select vector_trans("1,6", "1,2,3,4,5,6", ",")
* 返回值:【Array<Double>】: 指标向量数组
* 例:[1.0,0.0,0.0,0.0,0.0,1.0]
*
* @author yangfan
* @since 2021/3/18
* @version 1.0.0 init
*/
public class ModelVectorTransUdf extends UDF {
private Map<String, Double> targetMap;
public List<Double> evaluate(String str1,
@Nonnull String str2,
@Nonnull String regex) {if (null == str1 || str1.length() == 0) {return null;}
if (null == targetMap) {targetMap = new LinkedHashMap<>();
for (String s : str2.split(regex)) {targetMap.put(s, 0d);
}
}
Map<String, Double> map = new LinkedHashMap<String, Double>() {{putAll(targetMap);
}};
for (String s : str1.split(regex)) {if (map.containsKey(s)) {map.put(s, 1d);
}
}
return new ArrayList<>(map.values());
}
public static void main(String[] args) {List<Double> result = new ModelVectorTransUdf()
.evaluate("1,6", "1,2,3,4,5,6", ",");
System.out.println(result);
}
}
三、优化
其实优化曾经提当初 udf 代码中了,这里提一下。依据理论状况,参数 str2 其实是模型数组,同一个模型下,这个字符串是不变的。因而能够在第一次传入后将其转换成 LinkedHashMap<String, Double> 缓存下来,这样下次再计算时,就不必每次都去解析字符串了。至于为什么是 LinkedHashMap,而不是 HashMap、List 构造,是因为模型向量数组中值的程序是不能扭转的,否则预测后果将不精确,而所以用 LinkedHashMap 而不是 HashMap;前边说过咱们的特色有几百上千个,那就须要在遍历 str1 数组时,效率越高越好,而 Map 的查找时间复杂度为 O(1),所以不必 List 构造。那就说这么多吧~