类别特征处理入门:词表、One-Hot 与特征交叉
文章目录
摘要:前几篇把数值特征从「看清 → 清洗 → 拼成向量」讲通了,但真实表格里还有大量类别特征(Categorical Feature):品牌、车身类型、颜色、邮编。它们不是大小可比的量,不能直接塞进 y ′ = b + w x y'=b+wx y′=b+wx。本文讲清如何用词表与 One-Hot 编码把类别变成浮点向量,以及用特征交叉让线性模型学到组合效应。适合读完第 15、16 篇的读者。
1. 为什么「Ford」不能直接乘权重
专栏贯穿示例里,汽车有重量、油耗,也有品牌、颜色这类文字字段。线性回归、逻辑回归只会做浮点运算:
y ′ = b + w 1 x 1 + w 2 x 2 + ⋯ y' = b + w_1 x_1 + w_2 x_2 + \cdots y′=b+w1x1+w2x2+⋯
字符串 "Ford" 没法参与乘法。于是必须先做编码(Encoding):把每个类别变成一组固定的浮点数,再拼进第 15 篇说的特征向量。

| 类型 | 例子 | 能不能直接当 x x x |
|---|---|---|
| 数值特征 | 重量 3.43、排量 2.0 | 可以,必要时再归一化 |
| 类别特征 | 品牌 Ford、颜色 Red | 不行,必须先编码 |
第 14 篇提过:邮编长得像数字,本质却是类别——本节会把这件事说透。
2. 什么是类别数据
类别数据(Categorical Data) 的取值来自一个有限集合:红 / 蓝 / 黑,或轿车 / SUV / 皮卡。集合里的元素是标签,不是刻度尺上的位置。
2.1 汽车示例里的几列
| 列 | 取值举例 | 应按什么处理 |
|---|---|---|
weight | 3.43 | 数值 |
mpg | 16 | 标签(或数值回归目标) |
brand | Ford, Toyota | 类别 |
body | Sedan, SUV | 类别 |
color | Red, Black | 类别 |
zip | 10002, 20004 | 看似数字,实为类别 |
2.2 邮编为什么不能当数值
若把邮编当数值喂给模型,等于暗示「20004 大约是 10002 的两倍」。地理上这两个区域未必有「两倍」关系,模型却被逼着学虚假量级。
正确做法:把每个邮编当成独立类别,各自学一个权重——这正是 One-Hot 的动机。
品牌编号同理:若用 Ford=0, Toyota=1, BMW=2 当一个标量特征,等于强加「BMW 比 Toyota 大、比 Ford 更大」的顺序,通常毫无业务依据。

3. 词表:先固定「允许出现哪些值」
词表(Vocabulary) 就是某一类别特征在训练数据中约定好的取值列表,并给每个取值分配稳定下标。
假设 color 只出现四种:
| 颜色 | 词表下标 |
|---|---|
| Red | 0 |
| Blue | 1 |
| Black | 2 |
| White | 3 |

工程上:
- 只在训练集上统计词表并
fit - 推理时用同一映射
transform - 训练没见过的颜色(如 Purple)要事先约定:丢弃、归入
UNK,或扩展词表后重训
词表解决的是「类别 ↔ 稳定身份」;但下标本身仍不宜直接当线性模型的单个特征,下一步要用 One-Hot 展开。
4. One-Hot 编码:一个位置为 1,其余为 0
One-Hot 编码(One-Hot Encoding) 把词表里的每个类别,变成长度 N N N 的浮点向量( N = N= N= 类别个数):恰好一个位置是 1.0,其余全是 0.0。
对上面四种颜色:
| 原始值 | Red | Blue | Black | White |
|---|---|---|---|---|
| Red | 1.0 | 0.0 | 0.0 | 0.0 |
| Blue | 0.0 | 1.0 | 0.0 | 0.0 |
| Black | 0.0 | 0.0 | 1.0 | 0.0 |
| White | 0.0 | 0.0 | 0.0 | 1.0 |

模型真正吃进去的是这一行浮点数,不是字符串,也不是下标整数。于是线性部分可以写成:
w Red ⋅ 1 + w Blue ⋅ 0 + w Black ⋅ 0 + w White ⋅ 0 = w Red w_{\text{Red}}\cdot 1 + w_{\text{Blue}}\cdot 0 + w_{\text{Black}}\cdot 0 + w_{\text{White}}\cdot 0 = w_{\text{Red}} wRed⋅1+wBlue⋅0+wBlack⋅0+wWhite⋅0=wRed
每种颜色各自一个权重,互不强迫顺序。
4.1 和特征向量怎么拼
一辆车:重量已标准化为 − 0.08 -0.08 −0.08,颜色为 Blue,则:
x = ( − 0.08 , 0 , 1 , 0 , 0 ) \mathbf{x} = (-0.08,\ 0,\ 1,\ 0,\ 0) x=(−0.08, 0, 1, 0, 0)
前一个分量是数值特征,后四个是颜色 One-Hot。顺序一旦在训练时定死,推理必须一致——第 15 篇强调过这一点。
4.2 Multi-Hot 是什么
若一个样本可同时属于多个标签(如一辆车挂两个标签「通勤」「越野」),向量里可以有多个 1.0,称为 Multi-Hot。本专栏汽车颜色是互斥的,用标准 One-Hot 即可。
4.3 为什么不用「一个整数下标」代替 One-Hot
有人会问:词表已经给了 Red=0、Blue=1,直接把这个整数喂给模型不行吗?
对线性模型和逻辑回归,不行。因为单个整数会把类别当成有序刻度: w ⋅ 2 w\cdot 2 w⋅2 对 Black 的贡献固定是 w ⋅ 1 w\cdot 1 w⋅1 对 Blue 的两倍,这个「两倍」没有语义。One-Hot 把每个类别拆成独立开关,权重互不绑架。
树模型有时能消化整数编码,那是另一套归纳偏置;本专栏目前以线性 / 逻辑回归为主线,默认走 One-Hot。
5. 类别特征的常见坑
| 问题 | 现象 | 应对思路 |
|---|---|---|
| 高基数 | 邮编、商品 ID 成千上万维 | 哈希编码、Embedding、或先聚合稀有类 |
| 长尾稀有类 | 某品牌只出现 1~2 次 | 合并为 OTHER,避免过拟合 |
| 未见类别 | 线上出现训练没有的颜色 | handle_unknown='ignore' 或统一 UNK |
| 训练泄漏 | 用全量数据建词表再划分测试集 | 词表只在训练集 fit |
| 维度爆炸 | One-Hot + 交叉后特征极多 | 正则化、特征选择、或换树模型/神经网络 |
稀疏性也值得心里有数:One-Hot 向量大多是 0,只有少数 1。线性模型对稀疏特征很友好;若盲目交叉,维度会涨得很快。
6. 特征交叉:让线性模型学「组合」
单独看,brand=Toyota 与 body=SUV 可能各有一点信号;真正重要的往往是组合:Toyota 的 SUV 与 Ford 的 SUV 油耗模式未必相同。
特征交叉(Feature Cross) 对两个(或多个)类别或分桶特征做笛卡尔积,得到新的二元特征。品牌 2 种 × 车身 2 种 → 4 个交叉项:
| 交叉特征 | 含义 |
|---|---|
| Ford_Sedan | 福特轿车 |
| Ford_SUV | 福特 SUV |
| Toyota_Sedan | 丰田轿车 |
| Toyota_SUV | 丰田 SUV |
若样本是丰田 SUV,则交叉向量为 ( 0 , 0 , 0 , 1 ) (0,0,0,1) (0,0,0,1)。

直觉上,交叉类似第 14 篇的多项式项:都是造出新特征,让线性模型逼近非线性关系。差别是:多项式多作用于数值;交叉多作用于类别或分桶后的数值。
6.1 什么时候交叉有用
- 业务上存在明显交互(品牌 × 车型、城市 × 时段)
- 基模型是线性 / 逻辑回归,表达能力有限
- 类别数不多,交叉后维度仍可接受
纬度 × 经度若直接用原始浮点相乘,量级含义混乱;更常见做法是先分桶再交叉——对应「街区 / 城市」级的组合效应。
6.2 什么时候要克制
交叉维数 ≈ 各特征基数之积。三个 100 类特征交叉,理论可达百万维。此时应配合 L2 正则、哈希技巧,或交给能自动学交互的神经网络 / 树模型。
6.3 和汽车油耗任务怎么结合
专栏前半段只用重量预测 MPG 或「是否高效」。加上类别后,可以问更细的问题:同样重量区间里,不同品牌的高效比例是否不同?若数据支持,brand × weight_bucket 这类交叉,往往比单独 One-Hot 品牌更能解释残差。
实操建议:先上数值 + 简单 One-Hot,看基线;再按业务假设加 1~2 个交叉,用验证集比较 Log Loss 或 AUC,避免一上来全量笛卡尔积。
7. 从原始类别到向量:一条流水线

原始字符串 → 建词表 → One-Hot →(可选)特征交叉 → 与数值特征拼接 → 模型
和数值侧(第 14、16 篇)拼在一起时,完整样本可能是:
x = [ x weight scaled ⏟ 数值 , e color ⏟ One-Hot , c brand × body ⏟ 交叉 ] \mathbf{x} = [\underbrace{x_{\text{weight}}^{\text{scaled}}}_{\text{数值}},\ \underbrace{\mathbf{e}_{\text{color}}}_{\text{One-Hot}},\ \underbrace{\mathbf{c}_{\text{brand}\times\text{body}}}_{\text{交叉}}] x=[数值 xweightscaled, One-Hot ecolor, 交叉 cbrand×body]
8. 动手:sklearn 编码汽车类别
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
df = pd.DataFrame({
"weight": [3.50, 3.69, 2.37, 4.34, 3.43, 4.42, 3.44],
"brand": ["Ford", "Toyota", "Toyota", "Ford", "BMW", "Ford", "Toyota"],
"body": ["Sedan", "SUV", "Sedan", "SUV", "Sedan", "SUV", "Sedan"],
"efficient": [0, 0, 1, 0, 0, 0, 0], # 是否高效,示例标签
})
X = df[["weight", "brand", "body"]]
y = df["efficient"]
pre = ColumnTransformer([
("num", StandardScaler(), ["weight"]),
("cat", OneHotEncoder(handle_unknown="ignore"), ["brand", "body"]),
])
# 简单交叉可用 PolynomialFeatures 对 One-Hot 后再处理,
# 或手工构造 brand_body 列再 One-Hot:
df["brand_body"] = df["brand"] + "_" + df["body"]
pre2 = ColumnTransformer([
("num", StandardScaler(), ["weight"]),
("cat", OneHotEncoder(handle_unknown="ignore"), ["brand", "body", "brand_body"]),
])
clf = Pipeline([
("prep", pre2),
("model", LogisticRegression(max_iter=1000)),
])
clf.fit(df[["weight", "brand", "body", "brand_body"]], y)
print("classes in encoder:", clf.named_steps["prep"].named_transformers_["cat"].categories_)
要点:
handle_unknown="ignore":线上出现未见类别时对应 One-Hot 全 0,而不是直接报错- 交叉列
brand_body先拼字符串再 One-Hot,等价于笛卡尔积里的非零项 Pipeline保证训练与推理走同一套变换——和第 15、16 篇一致
9. 能力边界与常见误区
9.1 适用边界
- One-Hot 适合低~中基数类别;百万级 ID 更常见 Embedding 或哈希
- 树模型(随机森林、GBDT)对类别有时可用序号或内置处理,但线性模型几乎总要编码
- 本篇不展开 Embedding;那是神经网络与推荐系统里的下一层工具
9.2 常见误区
| 误区 | 正解 |
|---|---|
| 把品牌编成 0,1,2 当一个数值特征 | 强加虚假顺序,线性模型会误解 |
| 把邮编当连续数值 | 量级无意义,应按类别编码 |
| 用全量数据建词表再切测试集 | 泄漏;只在训练集 fit |
| 交叉越多越好 | 维度与稀疏爆炸,需正则或筛选 |
| One-Hot 后还对同一列做标准化 | 0/1 列通常不必再 StandardScaler |
| 推理时换了一套颜色顺序 | 向量错位,权重全乱 |
| 「类别少就不用建词表」 | 哪怕只有 3 种颜色,也要固定顺序与未知值策略 |
「少」不等于「不用管流程」:词表的价值不在类别多,而在训练和线上用同一张对照表。
10. 关键术语速查
| 术语 | 一句话解释 |
|---|---|
| 类别特征 | 取值来自离散标签集合的特征 |
| 编码 | 把非数值变成模型可吃的浮点表示 |
| 词表 | 类别取值的固定列表与下标映射 |
| One-Hot | 长度 N N N 的向量,恰有一个 1.0 |
| Multi-Hot | 允许多个位置为 1.0 |
| 稀疏特征 | 大部分分量为 0 的特征表示 |
| 特征交叉 | 类别/分桶特征的笛卡尔积组合 |
| 高基数 | 可能取值极多的类别列 |
11. 延伸阅读
| 资源 | 适合看什么 |
|---|---|
| sklearn OneHotEncoder | fit / transform、未知类别处理 |
| sklearn ColumnTransformer | 数值列与类别列分流预处理 |
| sklearn Pipeline | 预处理与模型绑成一条链 |
| 专栏第 14 篇 | 数值侧归一化、分桶 |
| 专栏第 15 篇 | 向量如何拼进模型 |
12. 小结
类别特征不是「多打几个字」,而是另一套进入模型的路径:先建词表,再用 One-Hot 变成互不强迫顺序的浮点维;需要交互时,用特征交叉造组合特征,让线性模型也能刻画「品牌 × 车型」这类效应。
记住:
类别字符串 → 词表 → One-Hot(可选交叉)→ 拼进特征向量 → 训练
邮编、ID、品牌序号看起来像数字时,先问一句:它们之间有没有真实的大小关系?没有,就按类别处理。
下一篇将进入数据集、泛化与过拟合:如何划分训练/验证/测试,以及模型为什么在训练集上好看、在新数据上翻车。
系列导航:
- 上一篇:【机器学习】(16)—— 数值数据
- 下一篇(预告):数据集划分、泛化与过拟合入门
如果本篇对你有帮助,欢迎点赞、收藏、关注博主,机器学习专栏持续更新中,下次更新不迷路。
—— 类别特征处理入门&spm=1001.2101.3001.5002&articleId=162842777&d=1&t=3&u=1d4052cf2d0845b9a833a90e11a11e1f)
1744

被折叠的 条评论
为什么被折叠?



