Java SDK

更新时间:
复制为 MD 格式

本文介绍Feature Generator工具Java SDK的使用。

使用限制

  • Java SDK 目前只支持在linux-x86_64macosx-arm64平台上运行

使用Java SDK

Maven项目为例,介绍如何使用Java SDK

1. 下载FG SDKjar包到本地位置,如 /path/to/feature_generator-${version}-${platform}.jar

platform

FG SDK Jar package

linux-x86_64

fg-linux-x86_64.jar

macosx-arm64

fg-macosx-arm64.jar

注意事项:目前FG默认采用std::hashhash分箱,不同的平台上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包版本是否为最新版本)。