特征注册核心概念:特征ID、特征名称、特征类型、特征维度、特征版本

好,咱们正式开始聊特征注册。上一章我大概提了一下特征注册在整个特征平台里的位置,这一章咱们把最核心的几个概念掰开揉碎讲清楚。

你想想看,一个特征平台里可能管理着成千上万个特征。如果没有一套清晰的标识和分类体系,那基本就是灾难。我在项目中就见过,同一个特征在不同团队叫法不一样,最后上线模型的时候才发现特征冲突——嗯,那场面挺尴尬的。

特征ID:特征的唯一身份证

特征ID,说白了就是每个特征的全局唯一标识。我习惯用64位整数或者UUID来生成。为什么不用字符串?因为整数做索引和哈希的效率高得多,尤其在实时特征计算场景下。

核心原则:特征ID一旦分配,终身不变。哪怕特征下线了,ID也不能复用。

我曾经踩过一个坑:早期设计时觉得特征ID用自增主键就行,结果后来做特征回溯和版本对比时,发现ID被重新分配给了新特征,历史数据全乱了。从那以后,我强制要求所有特征ID必须用雪花算法或者UUID生成,绝不依赖数据库自增。

特征ID的生成策略,我建议这样选:

  • 雪花算法:适合分布式场景,64位长整型,按时间戳+机器ID+序列号生成,性能高、不重复
  • UUID:适合小规模团队,128位字符串,生成简单,但存储和索引效率低
  • 业务前缀+自增:适合特征数量少的场景,比如"feat_0001",但扩展性差

我个人更倾向雪花算法。你想想看,在微服务架构下,多个服务同时注册特征,自增ID很容易冲突。雪花算法天然支持分布式,而且还能从ID里反推出注册时间,排查问题的时候特别有用。

特征名称:让人看懂的名字

特征ID是给机器看的,特征名称是给人看的。一个好的特征名称,应该让人一眼就知道这个特征是什么、从哪里来。

我见过最离谱的特征名称叫"f_123",代码里全是这种名字,三个月后连写代码的人自己都忘了是什么意思。所以,命名规范一定要定死。

我推荐这种命名风格:

# 推荐格式:{数据源}_{实体类型}_{业务含义}_{聚合方式}
user_30d_avg_order_amount
item_7d_click_count
device_1d_active_flag

# 不推荐格式
f_123
aaa_bbb
test_feature_v2_final

这里有个小技巧:特征名称里不要带版本号。版本号是单独管理的,写在名称里会让后续的版本管理变得非常痛苦。我曾经接手过一个项目,特征名称叫"user_age_v3_final_final",你猜后来有没有v4?

命名规范建议:

  • 全小写,用下划线分隔
  • 长度不超过64个字符
  • 禁止使用特殊字符(除了下划线)
  • 禁止以数字开头
  • 禁止包含版本号、日期等可变信息

特征类型:连续、离散、ID类

特征类型决定了这个特征怎么存储、怎么计算、怎么参与模型训练。我一般把特征类型分成三大类:

类型 说明 示例 存储建议
连续型 数值范围连续,可比较大小 年龄、收入、点击率 Float/Double
离散型 有限个取值,无序 性别、城市、设备类型 String/Enum
ID类 标识性ID,取值空间大 用户ID、商品ID、设备ID String/Long

你可能会问,为什么要把ID类单独拎出来?

嗯,这里有个坑。ID类特征虽然看起来像数值,但它不能直接参与数值计算。比如用户ID是1001和1002,你不能说1002比1001大。ID类特征通常要做embedding或者one-hot编码,存储和计算方式跟连续型完全不一样。

我记得有一次,新来的同事把一个用户ID特征标成了连续型,结果模型训练时直接拿ID当数值算,模型效果一塌糊涂。排查了半天才发现是类型标错了。

类型选择避坑指南:

  • 不要把所有数值都标成连续型——比如邮编虽然是数字,但本质是离散的
  • ID类特征不要参与数值运算——它只做匹配和映射
  • 离散型特征取值超过1000个时,建议考虑转为ID类做embedding

特征维度:特征的空间归属

特征维度,说白了就是这个特征属于谁。是用户的特征?还是商品的特征?还是某个时间窗口的特征?

我习惯把维度分成三层:

  • 实体维度:特征属于哪个实体,比如用户、商品、设备
  • 时间维度:特征的时间窗口,比如近7天、近30天、实时
  • 场景维度:特征在哪个场景下使用,比如推荐、搜索、广告

维度设计得好,特征复用率会大幅提升。我见过一个团队,同样的"用户近7天购买金额"特征,在推荐场景和搜索场景各注册了一次,维度字段一个写"推荐",一个写"搜索"。其实特征计算逻辑完全一样,只是使用场景不同。

我的建议是:维度只描述特征本身的属性,不描述使用场景。使用场景应该通过特征权限和标签来管理,而不是写在维度里。

特征版本:让特征可追溯

特征版本可能是最容易被忽视的概念。很多人觉得特征定义好了就不会变,但现实是——特征的计算逻辑、数据源、聚合方式都可能需要调整。

我经历过最惨的一次:线上模型用的特征,计算逻辑被悄悄改了,没有走版本管理。结果模型效果突然下降,排查了三天才发现是特征变了。从那以后,我强制要求所有特征必须有版本号,每次变更必须生成新版本。

版本管理我建议这样设计:

# 版本号格式:主版本.次版本.修订号
# 主版本:特征计算逻辑或数据源变更
# 次版本:特征参数或配置调整
# 修订号:bug修复或性能优化

# 示例
user_30d_avg_order_amount v1.0.0  # 初始版本
user_30d_avg_order_amount v2.0.0  # 数据源从A表切到B表
user_30d_avg_order_amount v2.1.0  # 增加了异常值过滤
user_30d_avg_order_amount v2.1.1  # 修复了空值处理bug

版本管理核心原则:

  • 版本一旦发布,不可修改
  • 新版本必须保留旧版本的历史数据
  • 线上模型必须指定使用哪个版本
  • 版本回滚要支持一键操作

你想想看,如果没有版本管理,你怎么知道线上模型用的是哪个版本的特征?出了问题怎么回滚?

核心概念关系图

下面这张图把五个核心概念的关系梳理了一下,你可以对照着理解:

特征注册核心概念关系图 特征 (Feature) 特征ID 特征名称 特征类型 特征维度 特征版本 说明: 特征ID和特征名称是特征的标识属性,特征类型决定存储和计算方式,特征维度描述空间归属,特征版本管理变更历史。 五个概念共同构成特征注册的核心元数据,缺一不可。

实际注册时的元数据模板

最后,我分享一下在实际项目中用的特征注册元数据模板。每次注册新特征,都按这个模板填写,基本不会漏东西:

{
  "feature_id": 100001,
  "feature_name": "user_30d_avg_order_amount",
  "feature_type": "continuous",
  "dimension": {
    "entity": "user",
    "time_window": "30d",
    "aggregation": "avg"
  },
  "version": "1.0.0",
  "description": "用户近30天平均订单金额",
  "data_source": "dwd_order_di",
  "owner": "recommend_team",
  "status": "online"
}

这个模板看起来简单,但每个字段背后都有讲究。比如feature_type填"continuous"还是"float",我建议统一用枚举值,避免不同团队写的不一样。再比如dimension里的time_window,一定要标准化成"7d"、"30d"这种格式,不要出现"7天"、"近一周"这种五花八门的写法。

嗯,这一章的内容就到这里。五个核心概念——特征ID、特征名称、特征类型、特征维度、特征版本——是特征注册的基石。你把这些搞清楚了,后面的注册流程、版本管理、元数据治理就都好办了。