NLP新闻文本分类学习赛 - Task4 基于fastText的文本分类
Datawhale零基础入门NLP赛事 - Task4 基于深度学习的文本分类1-fastText
Datawhale初级阶段NLP竞赛 - 第四项任务:基于深度学习进行文本分类(fastText)
- 学习FastText的使用和基础原理
- 学会使用验证集进行调参
文章目录
-
一、fastText核心思想:
-
-
1.1 n-gram
-
- (1) word-n-gram
- (2) char-n-gram
-
1.2 模型架构
-
1.3 hierarchical softmax
-
-
第2章 fastText实战应用
-
2.1 安装fastText库
- 使用Python代码:
pip install fasttext - 该模块提供了便捷的接口进行快速文本处理
- 使用Python代码:
-
2.2 fasttext.supervised()函数及其参数设置
- 常见参数包括:学习率、词向量大小、迭代次数等
- 示例:
model = fasttext.supervised('训练数据.txt', lr=0.05, size=300, epoch=5)
-
2.3 十折交叉验证机制
- 采用StratifiedKFold方法进行十折交叉验证划分
- 可视化结果有助于评估模型泛化性能
-
2.3.1 十折划分过程
- 每次取10%的数据作为验证集剩余作为训练集
- 确保数据分布均匀性以提高评估可靠性
-
2.3.2 调参优化策略
-
根据交叉验证结果动态调整超参数设置
-
目标是找到最佳模型性能与计算效率平衡点
-
三、参考文献
-
一、fastText核心思想:
将整篇文档的词及n-gram向量叠加平均得到文档向量,然后使用文档向量做softmax多分类。
1.1 n-gram
(1) word-n-gram
在文本特征提取过程中常见于n-gram这一概念。它属于语言模型范畴内的一个算法体系,在其运行机制中通过将文本信息按照字节排列方式进行N位连续的滑动窗口处理(其中N代表预设窗口宽度),从而生成一系列长度固定的字节片段序列。以下是一个具体的案例:
我来到达观数据参观
相应的bigram特征为:我来 来到 到达 达观 观数 数据 据参 参观
相应的trigram特征为:我抵达了抵达观察中数据量参与数据基于参观
请注意:在n-gram模型中,“gram”这一术语依据其应用层面的不同而具有不同的意义。它既可以表现为字粒度的形式也可以表现为词粒度的形式。如前所述的例子属于字粒度形式的n-gram,请参考下面举例说明相应的词粒度形式则请参考下面举例说明
我 来到 达观数据 参观
相应的bigram特征为:我/来到 来到/达观数据 达观数据/参观
相应的trigram特征为:我/来到/达观数据 来到/达观数据/参观
(2) char-n-gram
Word2Vec将语料库中的每个词汇视为一个基本单位,并通过向量化处理为每个词汇分配一个独特的数值表示。这种方法忽视了词语内部的形态特征,在传统Word2Vec模型中这些词语内部形态的信息因被编码为不同id而无法保留下来。例如:'apple'与'apples';'达观数据'与'达观'这两对词语都具有较多公共字符;即它们在结构上较为相似;但在上述提到的传统Word2Vec模型中却无法反映出这种深层的一致性特征。
为了克服这个问题, fastText采用了基于字符级别的n-grams模型来表示每个单词.对于单词'apple',当n等于3时,它的trigram数量为多少
“<ap”, “app”, “ppl”, “ple”, “le>”
其中我们使用符号<...>分别代表前缀和后缀。因此我们可以利用这些trigram序列来编码"apple"这个词的意义接着说在构建其词向量时我们将这5个trigram的向量进行叠加从而得到完整的"apple"词向量表达
这带来两点好处:
- 稀有词汇创建的词向量性能将得到显著提升。由于它们的n-gram能够与其他词汇共享资源。
- 即使在未包含在训练语料库中的单词依然能够构建其对应的词向量。通过叠加这些字符级别的n-gram向量能够进一步提升表示能力。
1.2 模型架构

该架构图未能呈现word2vec具体训练流程;从图中可以看出,在fastText模型中存在三层结构:分别是输入层、隐藏层以及输出层;其中输入部分由多组单词及其n-gram序列构成,并各自拥有对应的word embedding;各隐藏单元通过计算多组word embedding均值来构建特征空间;最终输出结果对应于所分析文档所属的具体类别标签
1.3 hierarchical softmax
hierarchical softmax属于该损失函数家族,在分类问题中被广泛应用于处理多标签场景。当标签类别数量极大时,在计算过程中引入层次结构优化以显著提升整体计算效率。
该想法通过构建一棵二叉树实现。其叶节点对应于标签。每个中间节点采用一个二元决策激活函数(如sigmoid)。预测结果将基于左或右分支的方向确定。输出单元的概率计算基于从根节点到该单元路径上各中间节点概率的乘积。其中每个中间节点的概率贡献将影响最终结果的可能性大小。
采用该方法中的哈夫曼树结构优化流程,在高频输出的情况下其搜索效率更高。由此可得,在这种情况下平均搜索时间表现最佳。
二、 fastText实战
2.1 安装fastText
pip install fasttext
2.2 fasttext.supervised() 参数
(1)用法:
import fasttext
model = fasttext.train_supervised(input_fine, lr=1.0,
dim=100, ws=5, epoch=25, minCount=1,
wordNgrams=2, loss='ns', bucket=2000000,
thread=12, lrUpdateRate=100, t=1e-4,
label='__label__', verbose=2)
(2)参数说明:
input_file 训练文件路径(必须)
output 输出文件路径
label_prefix 标签前缀 default __label__
lr 学习率 default 0.1
lr_update_rate 学习率更新速率 default 100
dim 词向量维度 default 100
ws 上下文窗口大小 default 5
epoch epochs 数量 default 5
min_count 最低词频,过滤词频低的词 default 5
word_ngrams n-gram 设置 default 1
loss 损失函数 {ns,hs,softmax,ova}
minn 最小字符长度 default 3,用于设定word-n-gram
maxn 最大字符长度 default 6, 用于设定word-g-gram
thread 线程数量 default 12
t 采样阈值 default 0.0001
silent 禁用 c++ 扩展日志输出 default 1
encoding 指定 input_file 编码 default utf-8
verbose 日志显示,0不显示,1显示进度条,2显示详细信息
pretrained_vectors 指定使用已有的词向量 .vec 文件 default None
loss:
- ns: negative sampling (NS), a common technique in machine learning for addressing challenges in large-scale datasets.
- hs: hierarchical softmax (HS), an optimization method for multi-class classification problems that structures the output space hierarchically.
- ova: one-against-all (OVA), a multi-label classification approach where each category is trained independently to distinguish its instances from all others.
import pandas as pd
from sklearn.metrics import f1_score
import fasttext
# 转换为FastText需要的格式
train_df = pd.read_csv('TRAIN_DATA/train_set.csv', sep='\t', nrows=15000)
train_df['label_ft'] = '__label__' + train_df['label'].astype(str)
train_df[['text','label_ft']].iloc[:-5000].to_csv('fasttext_train.csv', index=None, header=None, sep='\t')
model = fasttext.train_supervised('fasttext_train.csv', lr=1.0, wordNgrams=2,
verbose=2, minCount=1, epoch=25, loss='ova')
x=train_df.iloc[-5000:]['text']
y=train_df['label']
val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[-5000:]['text']]
val_y0 = train_df['label'].values[-5000:]
val_y1 = val_y0.astype(str)
print(f1_score(train_df['label'].values[-5000:].astype(str), val_pred, average='macro'))
// 0.8845029863261721
2.3 十折交叉验证
将原始数据随机划分为两组,其中一部分作为训练集,另一部分作为验证集.通过训练集构建分类器模型,随后通过验证集评估模型性能,并记录最终的分类准确率值.
(2) 10-fold cross-validation
将数据集划分为十个子集,并依次选取九个子集构成训练集合剩余的一个子集合成为测试集合进行评估
每一次实验都能得到相应的准确率(或误判率)。用于评估算法精度的标准是通过十次实验计算所得结果(包括准确率和误判率)的平均值。为了提高评估结果的可靠性通常会采用多次十折交叉验证的方法(如十次十折交叉验证),然后计算这些结果的平均值以评估算法性能。
该方法意指从原始样本中选取一项作为验证数据集,并将其余数据用于训练模型。该过程会持续到每个样本都被用作一次验证数据。实际上这是与K折交叉验证相同的原理,在这种情况下K等于原始数据集中的样本数量。
在某些情况下是存在有效率的演算法, 如使用kernel regression 和Tikhonov regularization.
2.3.1 用StratifiedKFold实现十折交叉划分
将原始数据进行十折交叉划分,分成十份训练集和测试集并保存。
import pandas as pd
from sklearn.model_selection import StratifiedKFold
print('starting K10 cross-validation data split:')
train_df = pd.read_csv('TRAIN_DATA/train_set.csv', sep='\t')
skf = StratifiedKFold(n_splits=10) //分层采样,确保分出来的训练集和测试集中各类别样本的比例与原始数据集中相同。
for n_fold, (tr_idx, val_idx) in enumerate(skf.split(train_df['text'],train_df['label'])):
print(f'the {n_fold} data split ...')
tr_x, tr_y, val_x, val_y = train_df['text'].iloc[tr_idx], train_df['label'][tr_idx], train_df['text'].iloc[val_idx], train_df['label'][val_idx]
tr_y = '__label__' + tr_y.astype(str)
traindata = pd.DataFrame(list(zip(tr_x.values, tr_y.values)))
traindata.to_csv(f'fasttext_skf10_datasplit/train_split{n_fold}.csv', index=None, header=['text', 'label_ft'], sep='\t')
testdata = pd.DataFrame(list(zip(val_x.values, val_y.values)))
testdata.to_csv(f'fasttext_skf10_datasplit/test_split{n_fold}.csv', index=None, header=['text', 'label'], sep='\t')
2.3.2 利用十折交叉验证调参
print('starting K10 cross-validation training:')
val_f1=[]
for n_fold in range(10):
model = fasttext.train_supervised(f'fasttext_skf10_datasplit/train_split{n_fold}.csv', lr=1.0, wordNgrams=2,
verbose=2, minCount=1, epoch=25, loss='softmax')
val_df = pd.read_csv(f'fasttext_skf10_datasplit/test_split{n_fold}.csv', sep='\t')
val_pred = [model.predict(x)[0][0].split('__')[-1] for x in val_df['text']]
val_f1.append(f1_score(val_df['label'].values.astype(str), val_pred, average='macro'))
print(f'the f1_score of {n_fold} training is:', val_f1[n_fold])
print()
print('The average f1_score is', sum(val_f1)/len(val_f1))
结果:
The average f1_score is 0.9303210475140224
