机器学习数学基础专题【左扬精讲】—— 稀疏特征与稠密特征:特征空间的两个世界
机器学习数学基础专题【左扬精讲】—— 稀疏特征与稠密特征:特征空间的两个世界
本文涉及的核心模块/API:
nn.Embedding ← 稀疏 ID → 稠密向量(核心转换层)
nn.Linear ← 全连接层(DNN 基础组件)
nn.ReLU ← 激活函数(引入非线性)
nn.Sequential ← 模型容器(顺序堆叠层)
torch.cat ← 张量拼接(多特征融合)
torch.tensor ← 张量创建(数据输入)
稀疏特征 稠密特征 One-Hot 编码 Embedding 特征工程 推荐系统
学习重点:本文从"为什么会有两种特征"出发,逐层拆解稀疏/稠密特征的定义、计算差异、Embedding 转换原理,以及不同模型对两种特征的处理策略。重点掌握:稀疏度的判定方法、Embedding 层的参数计算、为什么 DNN 无法直接处理 One-Hot。
- 一、核心定义
- ├──(一)稀疏特征(Sparse Feature)
- └──(二)稠密特征(Dense Feature)
- 二、关键对比
- 三、稀疏转稠密:Embedding 层
- 四、模型如何处理两种特征
- 五、常见问题 FAQ(20 组)
- Roadmap 预告
What — 稀疏特征和稠密特征分别是什么?
稀疏特征和稠密特征是对特征向量"值的分布形态"的两种分类,描述的是一个特征在向量空间中大部分值是 0 还是"几乎每个维度都有实际含义"。这不是模型的选择,而是特征本身的物理属性。
以下用一张学生成绩表说明:
学号 姓名 性别 数学 英语 物理 … 有没有选修"量子力学"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
001 张三 男 85 72 80 … 0
002 李四 女 90 88 85 … 1
003 王五 男 78 65 70 … 0
...
这个"有没有选修量子力学"就是典型的稀疏特征:1000 个学生中只有 5 人选修,向量中 995 个位置是 0,只有 5 个位置是 1。
而"数学成绩"是典型的稠密特征:每个学生都有一个具体分数(0~100),1000 个学生的 1000 个值,几乎每个维度都有实际含义。
一、核心定义
(一)稀疏特征(Sparse Feature)
What — 什么是稀疏特征?
稀疏特征是指在一个高维向量中,绝大多数维度的值为 0,只有极少数维度有非零值的特征。稀疏性的衡量标准通常用"非零元素占比"来表示。
Why — 为什么稀疏性如此普遍?
现实世界中有大量的"分类"信息。一个用户可能有 100 万个可能的兴趣标签,但每个人只对其中几十个感兴趣。一个物品可能有 10 亿个可能的 ID,但当前物品只对应其中一个。这种"从极大集合中选一个"的模式天然产生稀疏性。
没有稀疏性会发生什么?
- One-Hot 编码的高维空间(如 100 万维)无法直接用矩阵存储,内存爆炸
- 传统算法(如逻辑回归)在稀疏输入下效率极高,但如果强行 densify 反而丧失优势
- 稀疏性本身携带了"互斥"信息(同一位置只有一个 1),Densify 会丢失这一结构
稀疏特征最常见的三种来源:
1. One-Hot 编码的类别特征
将离散类别映射为高维向量,每个类别对应一个维度,取值为 1,其余全 0:
# 三个类别:北京、上海、广州 → 维度 3
# One-Hot 编码结果
北京 → [1, 0, 0] # 100% 稀疏(2/3 = 66.7% 为零)
上海 → [0, 1, 0] # 性别=上海的 One-Hot,第 2 维为 1
广州 → [0, 0, 1] # 性别=广州的 One-Hot,第 3 维为 1
# 现实场景:100 万用户ID → 维度 1,000,000
user_12345 → [0, ..., 1, ..., 0] # 999,999 个零,仅 1 个 1
# 稀疏度 = 99.9999%
2. 文本特征(Bag of Words / TF-IDF)
词汇表通常有数万到数十万个词,一篇文章只用到其中几百个:
# 词汇表大小 50,000,一篇文章用了 200 个词
text_vector = [0, 0, 1, 0, ..., 3, ..., 0, 0] # 49,800 个零
# 稀疏度 = 99.6%
3. 统计特征中的零值聚集
点击次数、购买次数等统计特征,对大多数用户都是 0,只有少数非零:
# 100 万用户中,只有 5,000 人点击过某广告
click_count = [0]*999_500 + [1, 2, 3, ...]*500 # 99.95% 是 0
关键认识:稀疏不是"坏"的属性,它反映了数据的天然结构。正确利用稀疏性可以大幅降低计算量和存储开销 【公认】。
(二)稠密特征(Dense Feature)
What — 什么是稠密特征?
稠密特征是指在一个向量中,几乎所有维度都有非零的、有实际含义的值。每个维度都携带信息,没有大量无意义的零值填充。
Why — 为什么稠密特征是深度学习的"母语"?
神经网络中的每一层都是矩阵乘法(y = Wx + b),矩阵乘法天然擅长处理稠密向量。当输入是稠密的时候,每个权重 W[i][j] 都在参与计算,信息流动完整。
没有稠密表示会发生什么?
- 原始 One-Hot 的高维稀疏向量直接送入 DNN → 第一层参数矩阵爆炸(100 万维输入 → 第一层权重 100 万 × 隐藏层维度)
- 稀疏特征的"互斥"语义在 DNN 中无法被有效利用(DNN 是连续空间中的函数拟合器)
- 无法进行有效的向量运算(如余弦相似度、欧氏距离)
1. 归一化后的数值特征
年龄、价格、评分等连续数值,经过标准化或归一化后,每个维度都有意义:
# 数值特征标准化(Z-Score)
age = 25 # 原始年龄
mean_age = 35.2 # 训练集的年龄均值
std_age = 10.5 # 训练集的年龄标准差
age_normalized = (age - mean_age) / std_age # -0.97,单个稠密值
# 多特征拼接成稠密向量
features = [age_norm, price_norm, rating_norm, ...] # 每个维度都有实际含义,无零值填充
# 每个维度都有实际含义,无零值填充
2. Embedding 层的输出
这是稀疏转稠密的最核心机制。Embedding 层把高维稀疏 ID 映射为低维稠密向量:
import torch # PyTorch 核心库
import torch.nn as nn # 神经网络模块
# 100万用户 → 每个映射为 64 维稠密向量
embedding = nn.Embedding(num_embeddings=1_000_000, embedding_dim=64) # 嵌入层:100万行 × 64 维
# 输入:用户 ID(稀疏的整数索引)
user_id = torch.tensor([12345]) # 单个整数,代表用户 12345
# 输出:64 维稠密向量(每个维度都有学习的语义信息)
user_vector = embedding(user_id) # shape: [1, 64]
# [0.12, -0.34, 0.87, ..., 0.45] ← 没有大量零值
3. Transformer 的输出向量
BERT 等模型的 [CLS] token 输出就是 768 维(BERT-base)稠密向量,每个维度编码了句子的某种语义:
# BERT 输出的 [CLS] 向量
cls_vector = outputs.last_hidden_state[:, 0, :] # shape: [batch, 768]
# 每个维度都是经过训练的语义编码,全部"有意义"
关键认识:稠密特征的优势在于"每个维度都承载信息",这使得向量运算(相似度、聚类、分类)高效且有意义 【公认】。
二、关键对比
What — 如何快速判断一个特征是稀疏还是稠密?
看一个简单的判据:取出 1000 个样本的特征向量,统计零值的比例。如果零值超过 50%,通常认为是稀疏特征;如果零值很少(比如不到 10%),就是稠密特征。
以下从五个维度系统对比:
维度 │ 稀疏特征 │ 稠密特征
━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
典型例子 │ One-Hot 类别、BoW、 │ 归一化数值、Embedding、
│ 用户/物品 ID │ 图像像素、BERT 向量
向量形态 │ 大部分为 0,少量非零 │ 几乎全为非零,每个维度有意义
存储方式 │ 稀疏矩阵(CSR/CSC/COO) │ 普通稠密矩阵(Dense Tensor)
计算效率 │ 只算非零元素,节省计算量 │ 全矩阵运算,但 GPU 高度优化
语义表达 │ 表示"是什么"(类别归属) │ 表示"有多少/像什么"(程度)
三、稀疏转稠密:Embedding 层
What — Embedding 层是什么?
Embedding 层是一个"查找表"(Lookup Table),本质上是一个可训练的矩阵。输入是离散的整数索引(稀疏的),输出是对应的低维向量(稠密的)。它是连接稀疏特征世界和稠密特征世界的桥梁。
Why — 为什么必须做稀疏转稠密?
问题一:维度灾难
如果直接用 One-Hot 编码,100 万个用户的 ID 就是 100 万维的向量。第一层 DNN 的权重矩阵大小是 100 万 × 256,仅这一层就需要 2.56 亿个参数。这不可训练、不可存储。
问题二:语义鸿沟
One-Hot 向量之间都是正交的(内积为 0),两个用户 ID 无论多相似,在原始空间中距离都一样远。模型无法学习"用户 A 和用户 B 有相似的购买偏好"。
解决方案:Embedding 层
用一个 64 维(或 128/256 维)的稠密向量来表示每个 ID。相似的 ID 在向量空间中距离更近。100 万个 ID × 64 维 = 6400 万个参数,可训练、可存储、可比较。
没有 Embedding 会发生什么?
- 维度爆炸:高维 One-Hot 直接送入 DNN,参数量不可接受
- 语义丢失:正交编码无法表达"相似性"
- 泛化失败:未见过的 ID 完全无法处理(Embedding 有泛化能力)
PyTorch 中 nn.Embedding 是稀疏转稠密的标准实现:
import torch # PyTorch 核心库
import torch.nn as nn # 神经网络模块
class Recommender(nn.Module): # 推荐模型基类
def __init__(self, num_users, num_items, embed_dim=64): # 初始化:用户数、物品数、嵌入维度
super().__init__() # 调用父类构造函数
# 用户 ID 嵌入:1,000,000 个用户 → 每个 64 维
self.user_embed = nn.Embedding(num_users, embed_dim, padding_idx=0) # 用户嵌入层,0 为 padding 索引
# 物品 ID 嵌入:100,000 个物品 → 每个 64 维
self.item_embed = nn.Embedding(num_items, embed_dim, padding_idx=0) # 物品嵌入层
# 稠密特征 MLP:user_vec + item_vec → 预测分数
self.mlp = nn.Sequential( # 多层感知机
nn.Linear(embed_dim * 2, 256), # 第一层:全连接,输出 256 维
nn.ReLU(), # ReLU 激活函数
nn.Linear(256, 1) # 第二层:输出 1 维(预测分数)
)
def forward(self, user_ids, item_ids, dense_features): # 前向传播
# 稀疏 → 稠密(Embedding 查找)
u_vec = self.user_embed(user_ids) # [batch, 64] # 用户嵌入向量
i_vec = self.item_embed(item_ids) # [batch, 64] # 物品嵌入向量
# 拼接稠密特征(如用户年龄、物品价格等)
x = torch.cat([u_vec, i_vec, dense_features], dim=-1) # 拼接用户向量、物品向量和稠密特征
# x 现在是全稠密向量,送入 MLP
return self.mlp(x) # MLP 输出预测分数
注意:nn.Embedding 的 padding_idx 参数会将指定索引的嵌入向量初始化为全零,常用于处理填充(padding)位置,避免噪声向量干扰训练 【公认】。
四、模型如何处理两种特征
不同模型对稀疏/稠密特征的处理方式有本质差异。
模型类型 │ 稀疏特征处理 │ 稠密特征处理
━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
逻辑回归 │ 直接输入(天然支持稀疏) │ 标准化后直接输入
FM / FFM │ 隐向量内积(每个特征 │ 搭配使用
│ 学习一个低维向量) │
Wide & Deep │ Wide 部分处理稀疏特征 │ Deep 部分处理稠密特征
│ (交叉特征) │(Embedding + MLP)
DNN │ 先 Embedding 转稠密 │ 直接输入
│ 再送入网络 │
Transformer │ Token Embedding 转稠密 │ 直接输入数值特征
│ (vocab lookup) │
一句话总结:稀疏特征是"从极大集合中选一个",向量中大部分是 0;稠密特征是"每个维度都有意义",向量中几乎没有无意义的零值。Embedding 层是两者之间的桥梁,将稀疏 ID 映射为低维稠密向量,使深度学习模型能够高效处理。
五、常见问题 FAQ(20 组)
FAQ 分组说明:本节覆盖稀疏/稠密特征的定义、计算、工程实现、Embedding 原理、模型选择五个维度。
Q1. 稀疏特征和稠密特征的根本区别是什么?
一句话结论:稀疏特征向量中大部分值是 0,稠密特征向量中几乎所有值都有实际含义。
判断标准是"零值的比例"。稀疏特征的零值占比通常超过 50%,而稠密特征的零值占比极低。这个区分维度是特征本身的物理属性,与模型无关。
Q2. One-Hot 编码为什么会产生稀疏向量?
一句话结论:One-Hot 为每个类别分配一个独立维度,只有当前类别位置为 1,其余全为 0。
如果有 100 万个可能的用户 ID,One-Hot 编码就是 100 万维向量,其中只有 1 个位置是 1。这种"1-of-N"的编码方式是稀疏性的根本来源,不是 bug,而是类别数据的自然表示。
Q3. 什么是稀疏度(Sparsity)?如何计算?
一句话结论:稀疏度 = 零值元素数 / 总元素数,范围 0(完全稠密)到 1(完全稀疏)。
计算公式:sparsity = count(zeros) / total_elements。一个 1000 维向量中有 990 个零,稀疏度就是 0.99(99% 稀疏)。
Q4. 为什么高维稀疏特征不能直接送入 DNN?
一句话结论:第一层权重矩阵的参数量等于"输入维度 × 隐藏层维度",高维输入导致参数量不可接受。
例如 100 万维输入、256 隐藏层 → 第一层参数 2.56 亿个。这不仅存储成本高,训练时前向/反向传播的计算量也极其昂贵。而且正交的 One-Hot 向量无法表达相似性。
Q5. Embedding 层是怎么解决维度问题的?
一句话结论:用低维稠密向量(如 64 维)替换高维 One-Hot,参数量从"输入维度 × 隐藏层"降为"类别数 × 嵌入维度"。
100 万个 ID × 64 维嵌入 = 6400 万参数,远少于 2.56 亿。而且 Embedding 向量可以表达语义相似性(相近的 ID 有相近的向量)。
Q6. Embedding 和 One-Hot 哪个存储更省?
一句话结论:Embedding 存储更省,但需要额外训练;One-Hot 无需训练但存储爆炸。
One-Hot 需要存储完整的维度信息(100 万维),而 Embedding 只需要存储"每个 ID 对应的 64 个浮点数"。对于 100 万 ID:One-Hot 需要 100 万个 float32 = 4MB(单条),Embedding 需要 6400 万个 float32 = 256MB(一次性,可复用)。
Q7. 文本特征为什么是稀疏的?
一句话结论:词汇表通常有数万到数十万词,一篇文章只用到其中一小部分。
BoW/TF-IDF 向量中,一篇文章用到的词可能只有几百个,其余几万个维度都是 0。这就是文本特征的天然稀疏性。
Q8. 图像像素是稀疏还是稠密的?
一句话结论:原始像素是稠密的(每个像素都有值),但二值化/阈值化后的图像可以视为稀疏的。
一张 224×224 的 RGB 图像有 150,528 个像素值,每个都有具体的颜色信息(0~255),所以原始图像是稠密的。经过边缘检测或二值化后,大量像素变为 0,才变成稀疏的。
Q9. 归一化(Normalization)会改变特征的稀疏/稠密性质吗?
一句话结论:不会。归一化只是对数值进行缩放,不改变零值的分布。
如果一个特征原始是稀疏的(大部分为 0),Z-Score 标准化后仍然大部分为 0。归一化改变的是数值范围,不改变稀疏度。
Q10. 为什么 Wide & Deep 模型同时处理两种特征?
一句话结论:Wide 部分擅长记忆(稀疏交叉特征),Deep 部分擅长泛化(稠密 Embedding),两者互补。
Wide 部分(LR)直接输入稀疏交叉特征,负责"记住"重要的规则组合(如"用户 A 且 地点 B 且 时间 C")。Deep 部分通过 Embedding 将稀疏特征转稠密后送入 DNN,负责"泛化"到未见过的组合。
Q11. FM(Factorization Machine)如何处理稀疏特征?
一句话结论:FM 为每个特征学习一个隐向量,通过隐向量的内积来建模特征之间的二阶交互。
FM 的核心创新是:即使某个特征组合在训练集中从未出现过(如用户 A × 物品 B),只要两个特征的隐向量都已学习,就可以通过内积估计交互强度。这解决了稀疏数据下的泛化问题。
Q12. 什么情况下应该保留稀疏表示而不是转稠密?
一句话结论:当特征维度极高(百万级以上)且非零元素极少时,稀疏矩阵运算比稠密矩阵更高效。
例如推荐系统中的百万级用户/物品 ID,如果直接 densify 成 64 维向量,存储和计算开销都远大于稀疏矩阵乘法。但在深度学习框架中,Embedding 层通常是最优选择。
Q13. 什么是特征哈希(Feature Hashing)?它和稀疏性有什么关系?
一句话结论:特征哈希用哈希函数将高维类别映射到固定大小的桶中,有意引入可控的哈希冲突,产生固定维度的稀疏向量。
特征哈希的好处是不需要维护词汇表(vocabulary),可以直接处理从未见过的类别。但哈希冲突会导致不同类别被映射到同一桶,损失部分信息。
Q14. 为什么推荐系统大量使用 Embedding?
一句话结论:推荐系统的核心输入(用户 ID、物品 ID)天然是极高维稀疏的,Embedding 是唯一可行的深度学习接入方式。
YouTube 推荐、Google 广告等系统都有数十亿用户和物品 ID,One-Hot 完全不现实。Embedding 将每个 ID 映射为 64~200 维稠密向量,既压缩了存储,又保留了语义相似性。
Q15. 稀疏特征训练时梯度怎么传播?
一句话结论:Embedding 层的反向传播只更新参与当前 batch 的那些 ID 对应的行,其他行保持不变。
PyTorch 的 nn.Embedding 在反向传播时,只会计算并更新被索引到的嵌入向量。这实际上是天然的稀疏梯度更新,大大节省了计算量。
Q16. 什么是特征交叉(Feature Crossing),它和稀疏性有什么关系?
一句话结论:特征交叉将两个特征组合成一个新特征,在稀疏场景下能捕捉单特征无法表达的交互关系。
在 Wide & Deep 的 Wide 部分,人工构造特征交叉(如"性别=女 AND 年龄<25"),这本质上是在稀疏空间中手动创建更有信息的特征。深度模型通过 Embedding + MLP 自动学习这种交互。
Q17. 离散特征和稀疏特征是一回事吗?
一句话结论:不完全一样。离散特征强调"取值有限",稀疏特征强调"零值占比高"。两者经常重叠,但不等价。
一个只有 3 个取值的离散特征(男/女/未知)经过 One-Hot 编码后维度只有 3,稀疏度只有 66.7%——它是离散的,但不是"高维稀疏"意义上的稀疏。真正的高维稀疏特征是"取值集合极大但每次只取一个"。
Q18. 连续特征一定是稠密的吗?
一句话结论:通常是的。连续特征的每个样本都有一个实际数值,不存在"大量零值"的情况,所以天然稠密。
但存在例外:如果某个连续特征在大多数样本中恰好是 0(比如"购买金额",大部分用户从未购买),那这个特征的实际分布也是稀疏的。判断依据是零值占比,不是数据类型。
Q19. Transformer 中的 Embedding 和推荐系统中的 Embedding 有什么区别?
一句话结论:功能相同(稀疏 ID → 稠密向量),但语义不同。推荐系统 Embedding 编码用户偏好,Transformer Embedding 编码词义。
推荐系统的 Embedding 训练目标是"相似用户有相似向量"(通过 CTR 预测的监督信号)。Transformer 的 Token Embedding 训练目标是"上下文中的词义"(通过 MLM 的监督信号)。两者数学形式相同,优化目标不同。
Q20. 如何判断模型更适合处理稀疏还是稠密特征?
一句话结论:看模型的第一层设计——Embedding 层说明输入是稀疏的,Dense 层说明输入是稠密的。
如果模型第一层是 nn.Embedding → 输入是稀疏 ID。如果第一层是 nn.Linear → 输入是稠密向量。这个判断方法可以直接从代码中确认。
全篇总结:
- 稀疏特征 = 大部分值为 0 的特征(One-Hot、BoW、ID 编码)
- 稠密特征 = 几乎所有值都有实际含义的特征(数值、Embedding、像素)
- Embedding 层是稀疏转稠密的核心桥梁,用低维向量编码语义
- DNN 天然处理稠密输入,稀疏特征必须先转稠密才能送入深度学习模型
- 判断一个特征是稀疏还是稠密,看零值占比,不看数据类型
Roadmap 预告
下一篇预告:Embedding 进阶专题——深入探讨 Word2Vec、GloVe、冷启动、多模态 Embedding 的原理与代码实现。敬请期待。

浙公网安备 33010602011771号