第七章:One-hot 编码与词袋模型
文本表示是将自然语言转化为计算机能够理解和处理的数值形式的过程。本章介绍最基础的两种文本表示方法:One-hot 编码和词袋模型。
One-hot 编码
什么是 One-hot?
One-hot 编码是最简单的词向量表示方式:将词汇表中的每个词映射为一个稀疏向量,向量的长度等于词表大小,该词对应位置为 1,其余位置为 0。
示例
假设词表为:["我", "爱", "自然", "语言", "处理"],词表大小为 5。
"我" → [1, 0, 0, 0, 0]
"爱" → [0, 1, 0, 0, 0]
"自然" → [0, 0, 1, 0, 0]
"语言" → [0, 0, 0, 1, 0]
"处理" → [0, 0, 0, 0, 1]更直观的表示:
我 爱 自然 语言 处理
"我" 1 0 0 0 0
"爱" 0 1 0 0 0
"自然" 0 0 1 0 0
"语言" 0 0 0 1 0
"处理" 0 0 0 0 1One-hot 的数学定义
对于词表
其中
One-hot 的优缺点
| 优点 | 缺点 |
|---|---|
| 实现简单,直观易懂 | 向量维度等于词表大小,非常稀疏 |
| 无需训练 | 无法表达词与词之间的语义关系 |
| 正交性:任意两个词的点积都为 0 | |
| 维度灾难:词表增大时向量维度爆炸 |
核心问题
One-hot 编码中,任意两个不同词的向量都是正交的(点积为 0),这意味着:
模型无法知道"国王"和"女王"比"国王"和"香蕉"更相似。
词袋模型(Bag of Words, BoW)
什么是词袋模型?
词袋模型将一段文本表示为一个向量,向量的每个维度对应词表中的一个词,值为该词在文本中出现的次数(或频率)。
"词袋"这个名字来源于一个直觉:把文本中的所有词扔进一个袋子里,忽略词序,只关心每个词出现了几次。
示例
假设词表为:["我", "爱", "自然", "语言", "处理", "深度", "学习"]
文本1:"我 爱 自然 语言 处理"
→ [1, 1, 1, 1, 1, 0, 0]
文本2:"我 爱 深度 学习"
→ [1, 1, 0, 0, 0, 1, 1]
文本3:"自然 语言 处理 是 深度 学习 的 基础"
→ [0, 0, 1, 1, 1, 1, 1] ("是"和"的"不在词表中,被忽略)词袋模型的问题
1. 忽略词序
文本A:"服务很好但味道差劲"
文本B:"味道很好但服务差劲"
分词后:
A: ["服务", "很", "好", "但", "味道", "差劲"]
B: ["味道", "很", "好", "但", "服务", "差劲"]
词袋表示:
A: [1, 1, 1, 1, 1, 1]
B: [1, 1, 1, 1, 1, 1] ← 完全相同!这两条评论表达的情感完全相反,但词袋模型无法区分。
2. 无法捕捉语义
"这部电影太好了" → [1, 1, 1, 1]
"这部电影太差了" → [1, 1, 1, 1] ← "好"和"差"被当作不同的维度词袋模型无法理解"好"和"差"是反义词。
CountVectorizer:词袋模型的实现
Scikit-learn 提供了 CountVectorizer 来实现词袋模型:
python
from sklearn.feature_extraction.text import CountVectorizer
# 创建词袋模型
vectorizer = CountVectorizer()
# 训练语料
corpus = [
"我爱自然语言处理",
"我爱深度学习",
"自然语言处理是深度学习的基础"
]
# 构建词表并转换
X = vectorizer.fit_transform(corpus)
# 查看词表
print("词表:", vectorizer.get_feature_names_out())
# ['基础', '学习', '我', '是', '爱', '深度', '自然语言处理']
# 查看词袋向量
print("词袋矩阵:\n", X.toarray())
# [[0, 0, 1, 0, 1, 0, 1],
# [0, 1, 1, 0, 1, 1, 0],
# [1, 1, 0, 1, 0, 1, 1]]二值词袋(Binary BoW)
除了使用词频,还可以使用二值表示(0 或 1),只关心词是否出现:
python
vectorizer = CountVectorizer(binary=True)
X = vectorizer.fit_transform(corpus)词袋模型的应用:文本分类
词袋模型常与传统机器学习算法结合使用:
python
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
# 示例数据
texts = [
"这家餐厅太好吃了,推荐!",
"服务态度很差,不推荐",
"环境优美,菜品精致",
"味道一般,价格太贵",
"非常满意,下次还来",
"等了一个小时才上菜,太慢了"
]
labels = [1, 0, 1, 0, 1, 0] # 1=正面, 0=负面
# 词袋表示
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(texts)
# 训练模型
model = LogisticRegression()
model.fit(X, labels)
# 预测
test = ["这家餐厅很好吃"]
test_X = vectorizer.transform(test)
print(model.predict(test_X)) # [1] (正面)词袋模型的改进方向
词袋模型的主要局限是忽略词序和无法捕捉语义。后续的方法在不同方向上进行了改进:
| 方法 | 改进方向 | 核心思想 |
|---|---|---|
| N-gram | 保留局部词序 | 将相邻的 n 个词作为一个特征 |
| TF-IDF | 考虑词的重要性 | 降低常见词的权重,提高罕见词的权重 |
| Word2Vec | 语义表示 | 学习词的语义向量,相似词距离更近 |
| Transformer | 上下文语义 | 同一个词在不同上下文中有不同的表示 |
这些方法将在后续章节中详细讲解。
小结
| 方法 | 表示方式 | 优点 | 缺点 |
|---|---|---|---|
| One-hot | 稀疏二值向量 | 简单直观 | 无法表达语义,维度灾难 |
| 词袋模型 | 词频向量 | 简单高效 | 忽略词序,无法捕捉语义 |
虽然 One-hot 和词袋模型已很少直接用于深度学习,但它们是理解后续更复杂方法的基础。