4、类别列:categorical_column_with_vocabulary_list、词汇表构建、OOV处理
好,咱们接着聊特征列。前面几章我们把数值特征处理得差不多了,现在该聊聊「类别特征」了。
说实话,在真实项目中,类别特征比数值特征要麻烦得多。我最早做推荐系统时,光处理用户ID和商品ID就折腾了好几天。你想想看,用户ID是字符串,模型又不认识字符串,怎么办?
答案就是:把类别映射成整数ID。这就是 categorical_column_with_vocabulary_list 干的事。
4.1 什么是词汇表?
说白了,词汇表就是一个「字符串 → 整数」的映射字典。
比如你有一个特征叫「城市」,取值是 北京、上海、广州、深圳。那词汇表就是:
北京 → 0
上海 → 1
广州 → 2
深圳 → 3
模型拿到「北京」,就知道它对应ID 0。就这么简单。
但这里有个坑——词汇表必须提前定义好。你不能训练时突然冒出一个「杭州」,模型会懵掉的。
核心要点:词汇表是静态的,训练前就要确定。所有可能的取值,都得列进去。
4.2 实战:用 vocabulary_list 构建类别列
咱们直接上代码。假设你有一个数据集,里面有个 color 特征:
import tensorflow as tf
# 定义词汇表
vocab_list = ['red', 'green', 'blue']
# 构建类别列
color_column = tf.feature_column.categorical_column_with_vocabulary_list(
key='color',
vocabulary_list=vocab_list
)
# 看看效果
feature_dict = {'color': ['red', 'green', 'blue', 'yellow']}
input_layer = tf.feature_column.input_layer(feature_dict, [color_column])
print(input_layer)
运行一下,你会发现 red 变成了 [1. 0. 0.],green 变成了 [0. 1. 0.],blue 变成了 [0. 0. 1.]。
等等,yellow 呢?
嗯,这里要注意——yellow 不在词汇表里,默认会被映射成 [0. 0. 0.],也就是全零向量。这在很多场景下是有问题的。
警告:默认情况下,OOV(Out-Of-Vocabulary)的取值会被忽略,变成全零向量。这可能导致模型学到错误的信息。
4.3 OOV 处理:别让未知值搞崩你的模型
我在项目中遇到过这么一件事:有个电商推荐模型,上线后CTR突然暴跌。查了半天,发现是双十一期间出现了大量新商品类别,这些类别不在词汇表里,全被映射成了全零向量。模型直接懵了。
从那以后,我养成了一个习惯——永远给 OOV 留一个坑位。
怎么做?加一个 default_value 参数:
color_column = tf.feature_column.categorical_column_with_vocabulary_list(
key='color',
vocabulary_list=vocab_list,
default_value=len(vocab_list) # 把 OOV 映射到最后一个ID
)
这样,yellow 就会被映射成 ID 3,而不是被忽略。模型至少能知道「这是个没见过的东西」,而不是「这是个不存在的东西」。
我的建议:把 default_value 设成词汇表长度,也就是新开一个ID给OOV。这样词汇表大小就是 len(vocab_list) + 1。
4.4 词汇表构建的三种方式
实际项目中,词汇表不会只有三个值。我见过最大的词汇表有上百万个商品ID。那怎么构建?
三种常见方式:
| 方式 | 适用场景 | 示例 |
|---|---|---|
| 手动列表 | 取值少且固定 | ['男', '女'] |
| 从数据中提取 | 取值多但可控 | df['city'].unique() |
| 从文件加载 | 取值非常多 | pd.read_csv('vocab.csv') |
我个人最常用的是第二种——从训练数据中提取。但要注意,一定要用训练集提取,不能用测试集。否则就是数据泄露。
# 从训练数据中提取词汇表
train_colors = train_df['color'].unique().tolist()
# 构建类别列
color_column = tf.feature_column.categorical_column_with_vocabulary_list(
key='color',
vocabulary_list=train_colors,
default_value=len(train_colors) # 给OOV留位置
)
4.5 词汇表大小与性能
这里有个权衡:词汇表越大,模型参数越多,训练越慢。
我曾经处理过一个用户ID特征,词汇表有500万。结果模型训练速度慢得像蜗牛。后来我用了哈希技巧(后面会讲),才把问题解决。
所以我的经验是:
- 词汇表 < 1000:直接用
vocabulary_list,简单粗暴 - 词汇表 1000 ~ 10000:可以用,但注意内存
- 词汇表 > 10000:建议考虑
categorical_column_with_hash_bucket
一句话总结:词汇表是类别特征的基础,OOV处理是避坑的关键。永远给未知值留一个位置,别让模型在推理时手足无措。
好,这一章就到这里。下一章我们聊聊哈希列——当词汇表大到离谱时,怎么用哈希技巧来救场。