Java SDK
本文介绍Feature Generator工具Java SDK的使用。
使用限制
Java SDK 目前只支持在
linux-x86_64和macosx-arm64平台上运行。
使用Java SDK
以Maven项目为例,介绍如何使用Java SDK。
1. 下载FG SDK的jar包到本地位置,如 /path/to/feature_generator-${version}-${platform}.jar。
platform | FG SDK Jar package |
linux-x86_64 | |
macosx-arm64 |
注意事项:目前FG默认采用std::hash做hash分箱,不同的平台上C++标准库中std::hash的实现是不同的,因此最终的分箱结果是不一致的。
如果需要分箱结果的跨平台一致性,可以通过设置环境变量USE_FARM_HASH_TO_BUCKETIZE=true来实现,具体参考FG全局配置的文档。
2. 使用mvn install:install-file安装到本地仓库
mvn install:install-file \
-Dfile=/path/to/feature_generator-${version}-${platform}.jar \
-DgroupId=com.aliyun.pai \
-DartifactId=feature_generator \
-Dversion=${version} \
-Dclassifier=${platform} \
-Dpackaging=jar注意:需要替换上述命令中的${version}为实际的版本号;${platform}要替换为实际的平台名称。
3. 在你的pom.xml中添加依赖
<dependencies>
<dependency>
<groupId>com.aliyun.pai</groupId>
<artifactId>feature_generator</artifactId>
<version>${version}</version>
<classifier>linux-x86_64</classifier>
</dependency>
</dependencies>4. 创建fg.json文件
Demo示例:
{
"features": [
{
"feature_name": "query_word",
"feature_type": "id_feature",
"value_type": "String",
"expression": "user:query_word",
"default_value": "",
"combiner": "mean",
"need_prefix": false,
"is_multi": true
},
{
"feature_name": "query_match",
"feature_type": "lookup_feature",
"map": "user:query_token",
"key": "item:title",
"needDiscrete": false,
"needKey": false,
"default_value": "0",
"combiner": "sum",
"need_prefix": false,
"value_type": "double"
},
{
"feature_name": "goods_id",
"feature_type": "id_feature",
"value_type": "String",
"expression": "item:goods_id",
"default_value": "-1024",
"combiner": "mean",
"need_prefix": false,
"value_dimension": 1
},
{
"feature_name": "filter_type",
"feature_type": "id_feature",
"value_type": "String",
"expression": "item:filter_type",
"default_value": "-1024",
"combiner": "mean",
"need_prefix": false,
"value_dimension": 1
},
{
"feature_name": "day_h",
"feature_type": "id_feature",
"value_type": "int64",
"expression": "user:day_h",
"default_value": "0",
"combiner": "mean",
"need_prefix": false,
"value_dimension": 1
},
{
"feature_name": "week_day",
"feature_type": "id_feature",
"value_type": "int64",
"expression": "user:week_day",
"default_value": "0",
"combiner": "mean",
"need_prefix": false,
"value_dimension": 1
},
{
"feature_name": "city",
"feature_type": "id_feature",
"value_type": "String",
"expression": "user:city",
"default_value": "-1024",
"combiner": "mean",
"need_prefix": false,
"value_dimension": 1
},
{
"feature_name": "province",
"feature_type": "id_feature",
"value_type": "String",
"expression": "user:province",
"default_value": "-1024",
"combiner": "mean",
"need_prefix": false,
"value_dimension": 1
},
{
"feature_name": "country",
"feature_type": "id_feature",
"value_type": "String",
"expression": "user:country",
"default_value": "-1024",
"combiner": "mean",
"need_prefix": false,
"value_dimension": 1
},
{
"feature_name": "is_new_user",
"feature_type": "id_feature",
"value_type": "int64",
"expression": "user:is_new_user",
"default_value": "-1024",
"combiner": "mean",
"need_prefix": false,
"value_dimension": 1
},
{
"feature_name": "focus_author",
"feature_type": "id_feature",
"value_type": "String",
"expression": "user:focus_author",
"separator": ",",
"default_value": "-1024",
"combiner": "mean",
"need_prefix": false,
"is_multi": true
},
{
"feature_name": "title",
"feature_type": "id_feature",
"value_type": "String",
"expression": "item:title",
"default_value": "-1024",
"combiner": "mean",
"need_prefix": false,
"is_multi": true
}
],
"reserves": [
"request_id",
"user_id",
"is_click",
"is_pay"
]
}5. 参考如下代码使用Java API
package org.example;
import com.aliyun.pai.fg.*;
import java.net.URL;
import java.util.*;
public class Main {
public static void main(String[] args) {
String filePath = "/path/to/fg.json";
FgHandler handler = new FgHandler(filePath, 4, false);
List<String> outputs = new ArrayList<>();
outputs.add("goods_id");
outputs.add("is_new_user");
outputs.add("day_h");
outputs.add("query_match"); // set output feature_name
outputs.add("title");
outputs.add("filter_type");
outputs.add("city");
outputs.add("province");
outputs.add("country");
outputs.add("focus_author");
handler.setOutputs(outputs.toArray(new String[0]));
List<String> expectGoods = Arrays.asList("218687106", "1142068348", "1142068347");
VariantVectorMap inputs = new VariantVectorMap.Builder()
.putOptionalString("goods_id", expectGoods)
.putOptionalInt32("is_new_user", Arrays.asList(0, 1, 0))
.putOptionalInt32("day_h", Arrays.asList(6, 8, 11))
.putListString("title", Arrays.asList(
Arrays.asList("k2", "k3", "k5"),
Arrays.asList("k1", "k2", "k3"),
Arrays.asList("k2", "k4")))
.putMapStringFloat("query_token", Arrays.asList(
new HashMap<String, Float>() {{
put("k2", 0.8f);
put("k3", 0.5f);
}},
new HashMap<String, Float>() {{
put("k1", 0.9f);
put("k4", 0.2f);
}},
new HashMap<String, Float>() {{
put("k1", 0.7f);
put("k2", 0.3f);
put("k4", 0.6f);
}}))
.putOptionalString("filter_type", Arrays.asList(null, "f1", null))
.putOptionalString("city", Arrays.asList("hangzhou"))
.putOptionalString("province", Arrays.asList("zhejiang"))
.putOptionalString("country", Arrays.asList("china"))
.putOptionalString("focus_author", Arrays.asList("2255010511022,14164467", "10511022,24164467", "550105110,34164467"))
.build();
VariantVectorMap results = handler.Process(inputs);
if (results == null || results.isNull()) {
System.out.println("fg result is null");
return;
}
System.out.println("result size=" + results.size());
List<String> features = results.getKeys();
System.out.println("result features=" + features);
List<String> goodsIds = results.getList("goods_id");
System.out.println("goods_ids=" + String.join(", ", goodsIds));
List<List<String>> titles = results.getList("title");
System.out.println("titles=" + titles);
List<Long> dayHours = results.getList("day_h");
System.out.println("day_h=" + dayHours);
List<String> filters = results.getList("filter_type");
System.out.println("filter_type=" + String.join(", ", filters));
List<List<String>> focus = results.getList("focus_author");
System.out.println("focus_author=" + focus);
List<String> citys = results.getList("city");
System.out.println("city=" + String.join(", ", citys));
List<String> provinces = results.getList("province");
System.out.println("provinces=" + String.join(", ", provinces));
List<String> countrys = results.getList("country");
System.out.println("country=" + String.join(", ", countrys));
List<Long> isNewUsers = results.getList("is_new_user");
System.out.println("is_new_user=" + isNewUsers);
List<Double> queryMatch = results.getList("query_match");
System.out.println("query_match=" + queryMatch);
System.out.println("===========================================================");
Set<String> itemInputs = handler.GetItemInputNames();
System.out.println("item side inputs=" + itemInputs);
Set<String> userInputs = handler.GetUserInputNames();
System.out.println("user side inputs=" + userInputs);
Set<String> ctxInputs = handler.GetContextInputNames();
System.out.println("context side inputs=" + ctxInputs);
Set<String> reserved = handler.GetReserveColumns();
System.out.println("reserved columns =" + reserved);
Set<String> qminputs = handler.GetFeatureInputs("query_match");
System.out.println("inputs of query_match =" + qminputs);
String defaultVal = handler.DefaultFeatureValue("query_match");
System.out.println("default feature value of query_match =" + defaultVal);
List<String> allFeatures = handler.GetAllFeatureNames();
System.out.println("all feature names:" + allFeatures);
Map<String, String> schema = handler.GetTableSchema();
System.out.println("table schema:" + schema);
handler.close(); // 释放资源
}
}Java SDK 使用文档
1. 类概述
FgHandler 用于根据配置(config_json)对输入特征进行处理,并输出处理后的特征结果。
主要能力:
通过配置初始化特征处理器(可指定线程数、是否只做 bucketize 等)。
处理输入特征:Process(VariantVectorMap inputs) -> VariantVectorMap
设置输出特征集合:setOutputs(...)
查询配置中的输入/输出/特征元信息(feature names、schema、默认值、维度等)。
支持显式释放 native 资源:close()(非常重要)
2. 快速开始
2.1 初始化
提供四种构造方式(最终都会调用 native allocate):
// 1) 仅配置
FgHandler handler = new FgHandler(configJsonOrPath);
// 2) 配置 + 线程数
FgHandler handler = new FgHandler(configJsonOrPath, threadNum);
// 3) 配置 + 线程数 + 是否仅 bucketize
FgHandler handler = new FgHandler(configJsonOrPath, threadNum, bucketizeOnly);
// 4) 配置 + 线程数 + 是否仅 bucketize + config 是否为文件路径
FgHandler handler = new FgHandler(configJsonOrPath, threadNum, bucketizeOnly, isCfgPath);参数语义:
config_json:配置内容或配置路径(由 is_cfg_path 决定)
thread_num:native 内部处理线程数(size_t)
bucketize_only:是否只执行 bucketize(可通过 IsOnlyBucketize() 查询)
is_cfg_path:true 表示 config_json 参数是配置文件路径;false 表示配置 JSON 文本
2.2 设置输出特征
可通过两种方式设置输出:
handler.setOutputs("feature_a", "feature_b");
// 或者使用 String[] 数组作为参数
List<String> outputs = new ArrayList<>();
outputs.add("goods_id");
outputs.add("is_new_user");
outputs.add("title");
outputs.add("city");
outputs.add("province");
outputs.add("country");
handler.setOutputs(outputs.toArray(new String[0]));或使用底层接口:
StrVector outs = new StrVector();
outs.push_back("feature_a");
outs.push_back("feature_b");
handler.setOutputs(outs);注意:如果不设置输出,则默认输出配置的所有特征。
2.3 执行处理
VariantVectorMap inputs = new VariantVectorMap();
// TODO: 按你的 VariantVectorMap 约定填充 inputs
VariantVectorMap outputs = handler.Process(inputs);
// TODO: 从 outputs 读取结果FgHandler.Process(inputs) 一次处理一批样本(batch),其输入输出类型都是 VariantVectorMap,含义是:
VariantVectorMap:Map<String, VariantVector>(key 为字段/特征名)
VariantVector:一个“按列存储”的批数据容器(列向量),支持多种嵌套形态:
Optional 标量:List<T>(长度 = batch size;可包含 null)
List 序列:List<List<T>>(外层长度 = batch size)
Map 特征:List<Map<K,V>>(外层长度 = batch size)
Matrix 特征:List<List<List<T>>>(外层长度 = batch size,每个样本一个二维矩阵)
3. 输入与输出数据结构说明
3.1 VariantVectorMap(输入/输出容器)
3.1.1 基本 API
构造:
new VariantVectorMap()
VariantVectorMap.fromJavaMap(Map<String, VariantVector>)
new VariantVectorMap.Builder()...build()
写入:
put(String key, VariantVector value)
putAll(Map<String, VariantVector>)
读取:
VariantVector get(String key)
boolean contains(String key)
List<String> getKeys()
long size()
Map<String, VariantVector> toJavaMap()(把当前 map 中的键值转成 Java Map)
3.1.2 推荐:Builder 构造 inputs
VariantVectorMap.Builder 提供大量 putXXX 方法:
常见(标量 optional,对应一个batch):
VariantVectorMap inputs = new VariantVectorMap.Builder()
.putOptionalInt64("user_id", Arrays.asList(1001L, 1002L))
.putOptionalString("item_id", Arrays.asList("i1", "i2"))
.putOptionalDouble("score", Arrays.asList(0.1, null)) // 支持 null
.build();单值便捷(batch=1):
VariantVectorMap inputs = new VariantVectorMap.Builder()
.putSingleOptionalInt64("user_id", 1001L)
.putSingleOptionalString("item_id", "i1")
.build();序列(List 类型,batch 内每个样本一个 list):
VariantVectorMap inputs = new VariantVectorMap.Builder()
.putListInt64("clicked_item_ids",
Arrays.asList(
Arrays.asList(11L, 12L),
Arrays.asList(21L)
)
)
.build();Map 特征(batch 内每个样本一个 map):
VariantVectorMap inputs = new VariantVectorMap.Builder()
.putMapStringFloat("user_dense",
Arrays.asList(
Map.of("age", 18.0f, "lvl", 3.0f),
Map.of("age", 25.0f)
)
)
.build();Matrix(每个样本一个二维矩阵):
VariantVectorMap inputs = new VariantVectorMap.Builder()
.putMatrixFloat("image_emb",
Arrays.asList(
Arrays.asList( // sample1 matrix
Arrays.asList(0.1f, 0.2f),
Arrays.asList(0.3f, 0.4f)
),
Arrays.asList( // sample2 matrix
Arrays.asList(1.0f, 2.0f)
)
)
)
.build();3.1.3 getList:按类型安全方式读取(输出侧常用)
VariantVectorMap.getList(key) 会根据 VariantVector.getType() 自动选择注册的 TypeReference 做转换。
示例:读取输出 feature_x(假设是 OPTIONAL_FLOAT):
List<Float> xs = out.getList("feature_x"); // 实际返回 List<Float>或读取 LIST_INT64(返回 List<List>):
List<List<Long>> seq = out.getList("seq_feature");注意:
getList 返回类型是 <T> List<T>,编译期无法强约束;类型不匹配会在运行时抛 IllegalArgumentException("Type mismatch...")。
TYPE_REGISTRY 已预注册了所有你定义的类型常量到 TypeReference 的映射。
建议做法:先获取输出表的Schema信息,再根据schema决定getList函数的返回值类型。
Map<String, String> schema = handler.GetTableSchema();
System.out.println("table schema:" + schema);3.2 VariantVector(列向量与类型系统)
VariantVector 是“单列”数据,必须设置 type 并按该类型组织内部数据。
3.2.1 支持类型一览(按常量)
Optional(允许 null,外层即 batch):
OPTIONAL_STRING / INT32 / INT64 / FLOAT / DOUBLE
Java 表示:List<T>(长度 = batch;元素可为 null)
List(每条样本一段序列):
LIST_STRING / INT32 / INT64 / FLOAT / DOUBLE
Java 表示:List<List<T>>(外层长度 = batch)
Map(每条样本一个 map):
MAP_STRING_*:List<Map<String, V>>
MAP_INT32_*:List<Map<Integer, V>>
MAP_INT64_*:List<Map<Long, V>>
Matrix(每条样本一个 2D 矩阵):
MATRIX_FLOAT / MATRIX_INT64 / MATRIX_STRING
Java 表示:List<List<List<T>>>
3.2.2 推荐构造方式:静态方法 fromXXX
你通常不需要手写底层 Builder(type).withXXXData(),直接用:
VariantVector.fromOptionalInt64(List<Long> values)
VariantVector.fromListFloat(List<List<Float>> values)
VariantVector.fromMapStringInt32(List<Map<String,Integer>> maps)
VariantVector.fromMatrixFloat(List<List<List<Float>>> matrices) 等。
示例(手动构造再放入 map):
VariantVector userIds = VariantVector.fromOptionalInt64(Arrays.asList(1L, 2L, null));
VariantVectorMap inputs = new VariantVectorMap();
inputs.put("user_id", userIds);3.2.3 读取:toXXXList
拿到一个 VariantVector vv 后,可按类型调用转换方法:
VariantVector vv = out.get("feature_x");
if (vv.getType() == VariantVector.OPTIONAL_DOUBLE) {
List<Double> vals = vv.toOptionalDoubleList();
}也可用 VariantVectorMap.getList(key) 自动分发(见上文 3.1.3)。
3.2.4 辅助方法:size / isEmpty / totalElementCount
vv.size():
Optional:返回 batch size(nullFlags 长度)
其他:返回 sizes[0](外层元素数,通常=batch size)
vv.isEmpty():根据 type 检查底层数据是否为空
vv.totalElementCount():对 list/map/matrix 计算嵌套展开后的总元素量(主要用于调试/检查)
4. 元信息查询接口
这些接口主要用来了解配置内容与特征依赖。
4.1 查询输入字段集合
Set<String> itemInputs = handler.GetItemInputNames();
Set<String> userInputs = handler.GetUserInputNames();
Set<String> ctxInputs = handler.GetContextInputNames();4.2 查询所有特征名 / 保留列 / 特殊集合
List<String> allFeatures = handler.GetAllFeatureNames();
Set<String> reserveCols = handler.GetReserveColumns();
Set<String> userSide = handler.GetUserSideFeatures();
Set<String> seqFeatures = handler.GetSequenceFeatures();4.3 查询特征依赖与默认值/维度/bucketizer
Set<String> deps = handler.GetFeatureInputs("feature_x");
String defaultVal = handler.DefaultFeatureValue("feature_x");
long defaultBucket = handler.DefaultBucketizedFeatureValue("feature_x");
long dim = handler.GetFeatureValueDim("feature_x");
boolean hasBucketizer = handler.HasBucketizer("feature_x");
boolean onlyBucketize = handler.IsOnlyBucketize();适用场景:
生成输入准备逻辑(知道某个 feature 需要哪些 raw inputs)
输入缺失时进行补齐/校验(默认值)
下游模型需要固定维度时(dim)
决定是否需要额外 bucketize 相关处理(hasBucketizer/onlyBucketize)
4.4 查询MC输出表结构(Schema)
使用方式:
Map<String, String> schema = handler.GetTableSchema();
String type = schema.get("feature_x");5. 资源释放(必须阅读)
FgHandler 持有 native 侧对象指针,必须在不用时释放,否则可能造成内存泄露。
5.1 当FgHandler不再被使用时,必须调用close()函数释放资源。该函数有如下特性:
线程安全:synchronized
幂等:重复调用不会重复释放
重要:close() 后不要再调用任何 native 方法(包括 Process、查询接口等),否则可能崩溃(use-after-free)。
FgHandler handler = new FgHandler(cfg, 4);
try {
...
} finally {
handler.close();
}5.2 调试阶段可以用try-with-resources
try (FgHandler handler = new FgHandler(cfg, 4)) {
handler.setOutputs("f1", "f2");
VariantVectorMap out = handler.Process(in);
}5.3 正式生产环境不要频繁创建相同的FgHandler对象
重要:在一个服务内部使用FgHandler时,不要每次请求都创建FgHandler对象,因为该对象的初始化开销比较大,频繁创建会影响服务性能。
推荐在服务的初始化函数里创建全局对象,在服务的退出函数里调用close()函数;需要创建多个不同的FgHandler对象时,也要遵循同样的模式。
6. 常见用法示例(模板)
处理前先根据配置准备输入字段
FgHandler handler = new FgHandler(cfgJson, 8, false, false);
try {
// 1) 设置需要的输出
handler.setOutputs("feature_a", "feature_b");
// 2) 了解需要准备哪些输入
Set<String> itemInputs = handler.GetItemInputNames();
Set<String> userInputs = handler.GetUserInputNames();
Set<String> ctxInputs = handler.GetContextInputNames();
// 3) 构造 inputs(示意,具体取决于 VariantVectorMap 的 API)
VariantVectorMap inputs = new VariantVectorMap();
// inputs.put("user_id", ...)
// inputs.put("item_id", ...)
// inputs.put("ts", ...)
VariantVectorMap outputs = handler.Process(inputs);
// 读取 outputs...
} finally {
handler.close();
}针对只做 bucketize
FgHandler handler = new FgHandler(cfgJson, 4, true, false);
try {
if (!handler.IsOnlyBucketize()) {
// 配置/构造参数不匹配时可做提示
}
...
} finally {
handler.close();
}7. Demo项目
下载Java FG Demo,解压之后运行项目目录下的run.sh脚本(记得检查脚本中使用的jar包版本是否为最新版本)。