Fasttext - 负采样介绍
1 课程概览
本课讲解负采样(Negative Sampling)。负采样是 Fasttext 的第三个优势,用于降低计算量。原理:一次训练完,仅更新一小部分权重,而不是更新所有权重。类比复习错题本,而不是从第一个知识点开始复习。负样本:除了目标词以外的词汇;正样本:目标词。
2 核心概念与定义
- 负采样(Negative Sampling):随机选择一小部分负样本进行训练,降低计算量。
- 负样本(Negative Word):除了目标词以外的词汇。
- 正样本(Positive Word):目标词。
- Skip-Gram:基于中间预测两端,计算量比 CBOW 大。
3 模型与算法详解
为什么需要负采样?
词汇量上万个时,softmax 计算概率需要计算上万个概率,且每个值都需要反向传播更新参数。
- 词汇量上万个
- softmax 计算上万个概率
- 每个值都需要反向传播更新参数
- 非常消耗资源,训练慢
类比:复习错题本
学到现在知识点近 1000 个,复习时不会从第一个知识点开始,而是复习错题本。
- 不从第一个知识点开始复习
- 复习错题本或不熟的知识点
- 负采样同理:仅更新一小部分权重
负采样原理
一次训练完,仅更新一小部分权重,降低梯度下降的计算量。
- 训练样本:input word = "hello",output word = "man"
- 词汇表大小:10000
- 期望:man 对应神经元输出 1,其余 9999 个输出 0
- 负采样:从 9999 个负样本中随机选一小部分(如 5~20 个)
- 仅更新正样本和选中的负样本的权重
正样本和负样本
| 类型 | 说明 | 数量 |
|---|---|---|
| 正样本(Positive Word) | 目标词 | 1 个 |
| 负样本(Negative Word) | 非目标词 | 9999 个(选一小部分) |
负采样选择数量
| 数据规模 | 选择负样本数量 |
|---|---|
| 小规模数据集 | 5~20 个 |
| 大规模数据集 | 2~5 个 |
计算量对比
假设隐层输出 300×10000 个权重矩阵。
| 方法 | 更新权重数量 | 说明 |
|---|---|---|
| 不用负采样 | 300×10000 = 300万 | 更新所有权重 |
| 负采样(选 5 个) | 300×6 = 1800 | 仅更新正样本 + 5 个负样本 |
- 不用负采样:更新 300 万权重
- 负采样:更新 1800 权重(0.06%)
- 计算量大大降低
4 数学原理与推导
传统 softmax 更新
$$\Delta W = \frac{\partial L}{\partial W}$$
需要更新所有 $V$ 个词汇的权重($V$ 可能上万)。
负采样
仅更新正样本和 $K$ 个负样本的权重。
$$L = -\log \sigma(v_{w_O}^T h) - \sum_{i=1}^{K} \log \sigma(-v_{w_i}^T h)$$
其中:
- $\sigma$ 是 sigmoid 函数
- $v_{w_O}$ 是正样本的权重
- $v_{w_i}$ 是负样本的权重
- $h$ 是隐藏层输出
- $K$ 是负样本数量
权重更新
$$\Delta W = 300 \times (1 + K)$$
其中 $K$ 是负样本数量。
5 代码示例
import numpy as np
def negative_sampling(vocab_size, positive_word, num_negative=5):
"""
负采样
:param vocab_size: 词汇表大小
:param positive_word: 正样本索引
:param num_negative: 负样本数量
:return: 正样本和负样本的索引
"""
# 1. 创建所有词汇的索引
all_words = list(range(vocab_size))
# 2. 移除正样本
all_words.remove(positive_word)
# 3. 随机选择负样本
negative_words = np.random.choice(all_words, num_negative, replace=False)
return positive_word, negative_words
# 测试
if __name__ == "__main__":
vocab_size = 10000 # 词汇表大小
positive_word = 42 # 正样本索引(例如 "man")
num_negative = 5 # 负样本数量
# 负采样
positive, negatives = negative_sampling(vocab_size, positive_word, num_negative)
print(f"词汇表大小: {vocab_size}")
print(f"正样本索引: {positive}")
print(f"负样本索引: {negatives}")
print(f"更新权重数量: 300 × {1 + num_negative} = {300 * (1 + num_negative)}")
print(f"不用负采样更新权重数量: 300 × {vocab_size} = {300 * vocab_size}")
print(f"计算量降低比例: {300 * (1 + num_negative) / (300 * vocab_size) * 100:.4f}%")
代码说明
| 代码 | 说明 |
|---|---|
np.random.choice(all_words, num_negative, replace=False) | 随机选择负样本 |
positive_word | 正样本索引 |
num_negative | 负样本数量 |
6 重难点与易错提醒
- ❗重点:负采样降低计算量,仅更新一小部分权重。
- ❗重点:正样本是目标词,负样本是非目标词。
- ❗重点:小规模数据集选 5~20 个负样本,大规模选 2~5 个。
- ⚠️易错:负采样是随机选择负样本。
- 💡深入理解:类比复习错题本。
7 课堂问答精选
Q1:为什么需要负采样?
A:词汇量上万个时,softmax 计算概率需要计算上万个概率,且每个值都需要反向传播更新参数,非常消耗资源。负采样仅更新一小部分权重,降低计算量。
Q2:负采样的原理是什么?
A:一次训练完,仅更新正样本和随机选择的一小部分负样本的权重。例如词汇表 10000 个,正样本 1 个,负采样选 5 个,仅更新 6 个权重,而不是 10000 个。
Q3:正样本和负样本是什么?
A:正样本(Positive Word)是目标词,负样本(Negative Word)是除了目标词以外的词汇。
Q4:负采样选择多少个负样本?
A:小规模数据集选 5~20 个,大规模数据集选 2~5 个。
8 本课小结
- 负采样:随机选择一小部分负样本进行训练,降低计算量。
- 正样本:目标词;负样本:非目标词。
- 小规模数据集选 5~20 个负样本,大规模选 2~5 个。
- 计算量从 300 万降到 1800(0.06%)。
- 类比复习错题本。
9 延伸思考
- Fasttext 如何使用负采样进行训练?
- 如何直接训练 Fasttext 模型?