【机器学习】(17)—— 类别特征处理入门

类别特征处理入门:词表、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 汽车示例里的几列

取值举例应按什么处理
weight3.43数值
mpg16标签(或数值回归目标)
brandFord, Toyota类别
bodySedan, SUV类别
colorRed, Black类别
zip10002, 20004看似数字,实为类别

2.2 邮编为什么不能当数值

若把邮编当数值喂给模型,等于暗示「20004 大约是 10002 的两倍」。地理上这两个区域未必有「两倍」关系,模型却被逼着学虚假量级。

正确做法:把每个邮编当成独立类别,各自学一个权重——这正是 One-Hot 的动机。

品牌编号同理:若用 Ford=0, Toyota=1, BMW=2 当一个标量特征,等于强加「BMW 比 Toyota 大、比 Ford 更大」的顺序,通常毫无业务依据。

请添加图片描述


3. 词表:先固定「允许出现哪些值」

词表(Vocabulary) 就是某一类别特征在训练数据中约定好的取值列表,并给每个取值分配稳定下标。

假设 color 只出现四种:

颜色词表下标
Red0
Blue1
Black2
White3

请添加图片描述

工程上:

  1. 只在训练集上统计词表并 fit
  2. 推理时用同一映射 transform
  3. 训练没见过的颜色(如 Purple)要事先约定:丢弃、归入 UNK,或扩展词表后重训

词表解决的是「类别 ↔ 稳定身份」;但下标本身仍不宜直接当线性模型的单个特征,下一步要用 One-Hot 展开。


4. One-Hot 编码:一个位置为 1,其余为 0

One-Hot 编码(One-Hot Encoding) 把词表里的每个类别,变成长度 N N N 的浮点向量( N = N= N= 类别个数):恰好一个位置是 1.0,其余全是 0.0

对上面四种颜色:

原始值RedBlueBlackWhite
Red1.00.00.00.0
Blue0.01.00.00.0
Black0.00.01.00.0
White0.00.00.01.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}} wRed1+wBlue0+wBlack0+wWhite0=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 w2 对 Black 的贡献固定是 w ⋅ 1 w\cdot 1 w1 对 Blue 的两倍,这个「两倍」没有语义。One-Hot 把每个类别拆成独立开关,权重互不绑架。

树模型有时能消化整数编码,那是另一套归纳偏置;本专栏目前以线性 / 逻辑回归为主线,默认走 One-Hot。


5. 类别特征的常见坑

问题现象应对思路
高基数邮编、商品 ID 成千上万维哈希编码、Embedding、或先聚合稀有类
长尾稀有类某品牌只出现 1~2 次合并为 OTHER,避免过拟合
未见类别线上出现训练没有的颜色handle_unknown='ignore' 或统一 UNK
训练泄漏用全量数据建词表再划分测试集词表只在训练集 fit
维度爆炸One-Hot + 交叉后特征极多正则化、特征选择、或换树模型/神经网络

稀疏性也值得心里有数:One-Hot 向量大多是 0,只有少数 1。线性模型对稀疏特征很友好;若盲目交叉,维度会涨得很快。


6. 特征交叉:让线性模型学「组合」

单独看,brand=Toyotabody=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 OneHotEncoderfit / transform、未知类别处理
sklearn ColumnTransformer数值列与类别列分流预处理
sklearn Pipeline预处理与模型绑成一条链
专栏第 14 篇数值侧归一化、分桶
专栏第 15 篇向量如何拼进模型

12. 小结

类别特征不是「多打几个字」,而是另一套进入模型的路径:先建词表,再用 One-Hot 变成互不强迫顺序的浮点维;需要交互时,用特征交叉造组合特征,让线性模型也能刻画「品牌 × 车型」这类效应。

记住:

类别字符串 → 词表 → One-Hot(可选交叉)→ 拼进特征向量 → 训练

邮编、ID、品牌序号看起来像数字时,先问一句:它们之间有没有真实的大小关系?没有,就按类别处理。

下一篇将进入数据集、泛化与过拟合:如何划分训练/验证/测试,以及模型为什么在训练集上好看、在新数据上翻车。

系列导航


如果本篇对你有帮助,欢迎点赞、收藏、关注博主,机器学习专栏持续更新中,下次更新不迷路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值