问题与思考:
1. fasttext是如何将文本转换为向量的?
子词向量—>词向量—>文本向量
1)子词向量:通过无监督的神经网络模型在训练过程中自动学习得到的,是模型在预测上下文(Skip-gram)或预测中心词(CBOW)时,通过反向传播算法不断调整优化出来的。
子词向量的生成和更新过程如下:
①初始化与存储:构建一个包含所有词汇及其子词片段的字典,例如 “<pla”、“lay”、“ing>”)都会被随机初始化为一个低维的实数向量。
② 参与前向传播(预测任务):
当模型处理一个目标单词(例如 “playing”)时
- 提取该单词的所有子词片段。("<pla","play","layi","ayin","ying","ing>")
- 将这些子词对应的向量从查找表中取出,并进行相加(或求平均),组合成该单词的当前词向量表示。
- 将这个组合后的词向量输入到神经网络的隐藏层,去预测它的上下文单词(Skip-gram 模式)或者由上下文单词预测它(CBOW 模式)。
③计算损失与反向传播:模型会将预测结果与真实的上下文进行对比,计算损失函数(Loss)。然后,通过反向传播算法(Backpropagation),将误差从输出层逐层传回。
④ 向量更新(核心机制):当误差传回时,fastText 会更新参与计算的向量
- 误差不仅会更新中心词的向量,还会按比例分配并更新该词包含的所有子词向量。
- 这意味着,如果单词 “playing” 和 “played” 经常出现在相似的上下文中,它们在反向传播时产生的梯度更新方向就会非常相似。
- 由于它们共享了相同的子词片段(如 “play”),这个共同的子词向量就会在训练中被反复推向一个能够同时适应 “playing” 和 “played” 语义的向量空间位置。
为什么不直接转换、而是要进行训练获得?
得到子词之间的相似性,经过海量文本的训练,那些经常出现在相似语境中的子词片段,其向量在空间中的距离就会越来越近。最终,这些子词向量就隐式地学到了词根、词缀等形态学信息(例如 “ing” 向量可能带有“进行时”的语义特征,“un” 向量可能带有“否定”的语义特征),从而让组合出的词向量具备极强的泛化能力。
2)词向量:一个单词的最终词向量,是通过将其包含的所有子词向量进行相加或求平均得到的
未登录词(OOV)处理:由于这种基于子词的机制,即使遇到训练集中从未出现过的新词,fastText 也能通过组合其包含的子词向量来推断并生成大致的词向量表示,具备极强的泛化能力
3)文本向量
- 分词:首先将输入的句子分割成单个单词(Token),例如将 “I love fastText” 分词为 [“I”, “love”, “fastText”]。
- 检索:根据上述的子词机制,获取句子中每个单词对应的 fastText 词向量。
- 求平均:将句子中所有单词的向量进行相加,然后除以单词的总数量,得出平均值。这个平均向量即代表了整段文本(句子)的嵌入向量。
2. fasttext是如何实现文本分类的?
① 文本向量化(输入层):
fastText 首先将输入的文本(句子或段落)转化为一个固定维度的稠密向量
- 机制:提取文本中所有的词,利用子词(Subword)机制获取每个词的向量,然后对这些词向量进行平均池化(Averaging)。
- 结果:无论原始文本有多长,最终都会被压缩成一个固定大小的文本表示向量(通常维度为 100~300)。
② 隐藏层特征提取(线性映射):
获得文本向量后,将其输入到一个单层的隐藏层(Hidden Layer)中
- 机制:通过一个全连接层(矩阵乘法),将文本向量映射到一个新的特征空间。
- 作用:这一步相当于提取了文本的深层语义特征。在这个阶段,fastText 还可以选择性地加入N-gram 特征(如双词组合 bigram),以捕获局部的词序和短语搭配信息,弥补纯平均池化丢失词序的缺陷。
③ 分类预测(输出层):
隐藏层输出的特征向量接着被送入输出层
- 机制:输出层同样是一个全连接层,其神经元数量等于分类标签(Categories)的数量。
- 激活函数:
- 对于多分类任务,通常使用Softmax函数,输出每个类别的概率分布。
- 对于多标签任务(一篇文章属于多个类别),则使用多个独立的Sigmoid函数。
④ 损失计算与训练(优化过程)
- 损失函数:模型使用交叉熵损失(Cross-Entropy Loss)来衡量预测概率与真实标签之间的差距。
- 优化器:采用随机梯度下降(SGD)及其变种(如 ASGD)来更新网络参数。
- 层次 Softmax(Hierarchical Softmax):为了解决类别数量极大时 Softmax 计算缓慢的问题,fastText 在底层实现中通常使用层次 Softmax 技术,将多分类问题转化为一系列的二分类问题,从而大幅降低计算复杂度。
3.层次softmax是如何减少复杂度,提升效率的?
在传统的softmax中,计算目标词的概率需要计算整个词汇表中所有词的概率,这在词汇表非常大的情况下计算开销非常大。层次softmax通过构建一个二叉树,将出现频次高的词放置到接近根节点,使得每次预测时只需要计算沿路径的几个节点,从而显著降低了计算量。
4.什么是负采样
负采样(Negative Sampling)是在word2id训练时使用,选取正样本(目标值真实的上下文数据) 和负样本(从词表中随机取k个数据),每次反向传播更新时只更新k+1个数据,从而大大降低了计算复杂度、提升了计算效率。
一、FastText工具介绍
1. 什么是FastText?
FastText是由Facebook AI Research (FAIR) 开发的一款高效文本表示和分类工具。它基于Word2Vec的思想,但进行了扩展,能够处理子词信息,适用于多种自然语言处理任务,如文本分类、词向量训练等
2. FastText作用
文本分类:它通过将文本表示为词向量的平均值,结合线性分类器(如 softmax)进行训练和预测。该方法非常快速,能够在短时间内处理大量文本数据,并生成分类模型。实现快速而准确的文本分类,包括多分类和二分类
词嵌入:在word2vec训练时,采用负采样、霍夫曼树,每次更新时只设计少量样本,可以大大调高训练的效率
3. FastText特点
子词信息: FastText不仅考虑整个词,还考虑词的子结构,这使得它能够更好地处理未登录词(OOV, Out-of-Vocabulary)和形态丰富的语言
高效训练:FastText使用了层次化softmax和负采样等技术,使得训练速度非常快,尤其适合大规模数据集。
多任务支持:FastText支持词向量训练和文本分类两种主要任务
4. FastText核心概念
字符级别的N-gram:FastText的核心思想是将单词分解成字符级别的n-grams,例如,对于单词 "apple",当 n=3 时,其3-gram包括:<ap, app, ppl, ple, le>,单词的前后加<和>是为了区分前缀和后缀。
子词信息(Subword Representation):将每个n-gram视为一个独立的单元,学习它们的向量表示。单词的向量则由组成该单词的所有n-gram向量的平均值得到。
基于负采样的skipgram(Skipgram with Negative Sampling(SGNS)):选取正样本(目标值真实的上下文数据) 和负样本(从词表中随机取k个数据),每次反向传播更新时只更新k+1个数据,从而大大降低了计算复杂度、提升了计算效率。
分层softmax(Softmax Classifier):在传统的softmax中,计算目标词的概率需要计算整个词汇表中所有词的概率,这在词汇表非常大的情况下计算开销非常大。层次softmax通过构建一个二叉树,将出现频次高的词放置到接近根节点,使得每次预测时只需要计算沿路径的几个节点,从而显著降低了计算量。
5. FastText优缺点
在保持较高精度的情况下, 快速的进行训练和预测是FastText的最大优势。
优点:
- FastText工具包中内含的FastText模型具有十分简单的网络结构,相比其他NLP模型,FastText在处理大规模文本数据时的速度更快。
- 使用FastText模型训练词向量时使用层次softmax结构, 来提升超多类别下的模型性能。
- 由于FastText模型过于简单无法捕捉词序特征, 因此会进行n-gram特征提取以弥补模型缺陷提升精度
缺点:
- 对于非常短的文本,效果可能不如深度学习的其他模型。
- 需要大量数据才能训练出高质量的模型。
6. FastText安装
# 在python解析器中安装FastText包
pip install fasttext
- 验证
二、FastText架构介绍
1. FastText模型架构
Skip-gram模型、CBOW(Continuous Bag of Words)模型
- 将这些子词对应的向量从查找表中取出,并进行相加(或求平均),组合成该单词的当前词向量表示。
- 将这个组合后的词向量输入到神经网络的隐藏层,去预测它的上下文单词(Skip-gram 模式)或者由上下文单词预测它(CBOW 模式)。
模型会将预测结果与真实的上下文进行对比,计算损失函数(Loss)。然后,通过反向传播算法(Backpropagation),将误差从输出层逐层传回。
当误差传回时,fastText 会更新参与计算的向量
2. 层次softmax
层次softmax是如何减少复杂度,提升效率的?
在传统的softmax中,计算目标词的概率需要计算整个词汇表中所有词的概率,这在词汇表非常大的情况下计算开销非常大。层次softmax通过构建一个二叉树,将出现频次高的词放置到接近根节点,使得每次预测时只需要计算沿路径的几个节点,从而显著降低了计算量。
2.1 霍夫曼树介绍
概念:
霍夫曼树是一种带权路径长度最短的二叉树,频率高的词在树中的路径较短,频率低的词路径较长
每个词通过霍夫曼编码映射到一条唯一的路径。路径是从根节点到叶子节点的节点序列,路径的长度与词的出现频率成反比:频率高的词路径短,频率低的词路径长。
作用:
作用在输出层,层次化 Softmax(Hierarchical Softmax),其核心作用是大幅降低计算复杂度,显著提升模型的训练与预测速度
将多分类转化为二分类路径选择,模型在计算概率时只需遍历从根节点到目标节点的路径,并将路径上所有二分类的概率相乘。每次都在做二分类任务,且频次越高的词越靠近根节点,使用的频率越高,从而大大提升效率
2.2 霍夫曼树相关概念
- 二叉树:每个节点最多有2个子树的有序树, 两个子树分别称为左子树、右子树。有序的意思是: 树有左右之分, 不能颠倒。
- 叶子节点:一棵树当中没有子节点的节点称为叶子节点。
- 路径和路径长度:在一棵树中, 从一个节点往下可以到达孩子或孙子节点之间的通路, 称为路径。通路中分支的数目称为路径长度。
- 节点的权及带权路径长度:若将树中节点赋予一个有某种含义的数值, 则这个数值称为该节点的权, 节点的带权路径长度为: 从根节点到该节点之间的路径长度与该节点的权的乘积。
- 树的带权路径长度:树的带权路径长度规定为所有叶子节点的带权路径长度之和, 记为WPL(weighted path length)。WPL最小的二叉树就是霍夫曼树。
2.3 构建霍夫曼树
2.3.1 构建步骤
初始化
- 为每个词汇或符号创建一个叶子节点,节点的权重为词频或概率。
- 将所有节点放入一个优先队列,按权重从小到大排序。
合并节点
- 从优先队列中取出权重最小的两个节点。
- 创建一个新的内部节点,其权重为这两个节点权重之和,左子节点为权重较小的节点,右子节点为权重较大的节点。
- 删除从优先队列中取出权重最小的两个节点,将新节点放回优先队列。
重复合并
- 重复步骤2合并节点,直到优先队列中只剩一个节点。这个节点就是霍夫曼树的根节点。
生成编码
- 从根节点开始,向左子树走记为
0,向右子树走记为1。 - 每个叶子节点的路径即为该符号的霍夫曼编码。
2.3.2 示例
假设有以下词汇及其频率:
| 词汇 | 频率 |
|---|---|
| A | 5 |
| B | 9 |
| C | 12 |
| D | 13 |
| E | 16 |
| F | 45 |
步骤1:初始化
- 创建叶子节点:
A(5),B(9),C(12),D(13),E(16),F(45)。 - 将所有节点放入优先队列(按频率排序):
[A(5), B(9), C(12), D(13), E(16), F(45)]。
步骤2:第一次合并
- 取出频率最小的两个节点:
A(5)和B(9)。 - 创建新节点
N1(14),左子节点为A(5),右子节点为B(9)。 - 将
N1(14)放回优先队列:[C(12), D(13), N1(14), E(16), F(45)]。
步骤3:第二次合并
- 取出频率最小的两个节点:
C(12)和D(13)。 - 创建新节点
N2(25),左子节点为C(12),右子节点为D(13)。 - 将
N2(25)放回优先队列:[N1(14), E(16), N2(25), F(45)]。
步骤4:第三次合并
- 取出频率最小的两个节点:
N1(14)和E(16)。 - 创建新节点
N3(30),左子节点为N1(14),右子节点为E(16)。 - 将
N3(30)放回优先队列:[N2(25), N3(30), F(45)]。
步骤5:第四次合并
- 取出频率最小的两个节点:
N2(25)和N3(30)。 - 创建新节点
N4(55),左子节点为N2(25),右子节点为N3(30)。 - 将
N4(55)放回优先队列:[F(45), N4(55)]。
步骤6:第五次合并
- 取出频率最小的两个节点:
F(45)和N4(55)。 - 创建新节点
N5(100),左子节点为F(45),右子节点为N4(55)。 - 优先队列中只剩一个节点
N5(100),构建完成。
最终霍夫曼树结构:
霍夫曼编码:
从根节点到每个叶子节点的路径即为该词汇的霍夫曼编码:
| 词汇 | 路径 | 编码 |
|---|---|---|
| F | 左 | 0 |
| C | 右 -> 左 | 100 |
| D | 右 -> 右 | 101 |
| A | 右 -> 左 | 1100 |
| B | 右 -> 右 | 1101 |
| E | 右 -> 右 | 111 |
2.4 【了解】霍夫曼编码转化为梯度计算
步骤 1:定义霍夫曼树
- 每个内部节点 v 有一个参数向量 θv。
- 每个叶子节点对应一个词汇。
步骤 2:计算目标词的概率
对于目标词 ww,其概率 P(w∣context)$是通过霍夫曼树路径上的二分类器的概率乘积得到的:
其中:
- Path(w) 是从根节点到目标词 ww 的路径。
- directionv是在节点 vv 的决策方向(左或右)。
- P(directionv∣context,θv)是节点 v 的二分类器概率。
步骤 3:定义二分类器概率
在节点 v,二分类器的概率通常使用逻辑回归:
其中:
- hh 是上下文向量(例如,Word2Vec 中的隐藏层输出)。
- σ 是 Sigmoid 函数:
。
步骤 4:计算损失函数
损失函数为负对数似然:
将 P(w∣context)代入:
步骤 5:计算梯度
对每个节点 v 的参数 θv 和上下文向量 h 计算梯度:
- 对 θv的梯度:
其中 I(⋅)是指示函数,如果方向为左则为1,否则为0。
- 对 hh的梯度:
步骤 6:更新参数
使用梯度下降法更新参数:
其中 ηη是学习率。
2.5 层次softmax工作原理
霍夫曼树:每个叶子节点代表一个词,每个非叶子节点代表一个二元分类问题。 词频高的词在树的较高层,词频低的词在树的较低层。 这使得高频词的路径更短,从而减少计算。
路径编码:从根节点到叶子节点的路径可以被编码为一个二元码 (0 或 1),其中0代表左子树,1代表右子树。
概率计算:每个非叶子节点代表一个二元分类问题,使用sigmoid函数计算概率。 从根节点到叶子节点的概率是路径上所有节点概率的乘积。
2.6 层次softmax优势
计算效率高:层次Softmax将计算复杂度从O(V)降低到O(logV),其中V是词汇量的大小。
内存效率高:只需要存储树的结构和节点的参数,不需要存储整个词汇表的参数。
3. 负采样
3.1 什么是负采样
负采样(Negative Sampling)是在word2id训练时使用,选取正样本(目标值真实的上下文数据) 和负样本(从词表中随机取k个数据),每次反向传播更新时只更新k+1个数据,从而大大降低了计算复杂度、提升了计算效率。
3.2 负采样原理
1. 解决的核心问题:归一化常数计算极其耗时
在传统的语言模型或 Skip-gram 模型中,为了计算某个词作为上下文词的概率,需要使用 Softmax 函数。Softmax 的分母需要对整个词汇表(V)中所有词的得分进行求和(即计算归一化常数)。
当词汇表极大(例如百万级别)时,每次更新参数都需要遍历整个词汇表,导致计算复杂度高达 O(V),这在工程上是不可接受的。
2. 负采样的作用与原理:将全局问题转化为局部二分类
负采样彻底抛弃了全局 Softmax 的计算,将模型的目标函数从“在 V 个词中选出正确的词(多分类)”转化为了“区分正确的词(预测词的真实的上下文)和几个错误的词(随机取k个)(二分类)”。
- 正样本:当前中心词及其真实出现的上下文词。
- 负样本:从词汇表中按照特定分布(通常是词频的 3/4 次方分布)随机采样出 K 个没有在当前上下文出现的词。
模型只需计算这 1 个正样本和 K 个负样本(通常 K 取 5~20)的 Sigmoid 概率,并更新这些词的向量参数。
3.3 负采样优势
- 计算与更新更高效:每次训练更新只涉及 1+K 个词,时间复杂度从 O(V) 降到了 O(K)。K 是一个极小的常数,随机取的负样本的个数,因此训练速度非常快。
- 更适合并行计算:霍夫曼树由于节点间存在依赖关系,难以并行化;而负采样每次采样的正负样本是相互独立的,非常适合在 GPU 或多核 CPU 上进行大规模并行训练。
- 对低频词更友好:霍夫曼树将高频词放在浅层,对高频词计算快,但低频词路径长,更新慢。负采样通过调整采样分布(降低高频词被选为负样本的概率),能够保证低频词也有足够的机会被采样到并得到充分训练。
三、FastText文本分类
1. 文本分类介绍
文本分类是什么?
文本分类是给文档(例如电子邮件,帖子,文本消息,产品评论等)分配一个或多个类别。
当今文本分类的实现多是使用机器学习方法从训练数据中提取分类规则,进行分类, 因此构建文本分类都是有监督的学习。
核心思想
- 词袋模型(Bag of Words):将文本表示为词向量的平均值。
- 子词信息(n-grams):通过引入子词信息(如字符级别的 n-grams),FastText能够捕捉到词的内部结构,从而更好地处理未登录词(OOV, Out-of-Vocabulary)和形态丰富的语言。
- 层次Softmax或负采样:为了加速训练,FastText使用层次Softmax或负采样来优化损失函数。
种类
- 二分类:文本被分类是否两个类别中, 比如:判断一句评论是好评还是差评。
- 单标签多分类:文本被分入到多个类别中, 且每条文本只能属于某一个类别(即被打上某一个标签), 比如: 输入一个人名, 判断它是来自哪个国家的人名。
- 多标签多分类:文本被分人到多个类别中, 但每条文本可以属于多个类别(即被打上多个标签), 比如: 输入一段描述, 判断可能是和哪些兴趣爱好有关, 一段描述中可能即讨论了美食, 又讨论了游戏爱好。
2. 文本分类实现
第一步: 获取数据
第二步: 训练集与验证集的划分
第三步: 训练模型
第四步: 使用模型进行预测并评估
第五步: 模型调优
第六步: 模型保存与重加载
1. 原始数据处理
数据要求:每一条数据(评论、邮件等) __label__类别名 text文本(词或字之间用空格隔开)
原因:fasttext 设计是用来处理英文的,所以中文使用时 词或字之间应用空格隔开
示例:
# 字
__label__game 体 验 2 D 巅 峰 倚 天 屠 龙 记 十 大 创 新 概 览
# 词
__label__game 体验 2D 巅峰 倚天 屠龙记 十大 创新 概览
2. 对处理好的数据进行训练、预测、评估
import fasttext # 模型训练 model = fasttext.train_supervised(input="data/cooking.pre.train", epoch=30, loss="hs", wordNgrams=2, autotuneValidationFile='data/cooking.pre.valid', autotuneDuration=300, verbose=3 # 设置日志输出的详细程度,方便观察自动参数调优的进度 ) # 保存模型 model.save_model(r'./model/model_cooking.bin') # 加载模型 model = fasttext.load_model(r'./model/model_cooking.bin') # 模型预测 print(model.predict('Are egg whites generally available at the store?')) # 模型评估 print(model.test(r'./data/cooking.valid'))输出:
# 预测结果
(('__label__egg-whites',), array([0.43338978]))
# 元组中的每项分别代表, 验证集样本数量, 精度以及召回率
(3000, 0.567, 0.24520686175580222)
3. 模型调优
例如上边代码,可以:
更改、增加轮次:epoch=30
loss="hs",
增加ngram特征:wordNgrams=2(一般为2或3)调整学习率:lr=0.2
自动参数调优:
① 指定验证数据集所在路径:autotuneValidationFile='data/cooking.pre.valid',
② 参数可以控制随机搜索的时间:autotuneDuration=300()