NLP入门之新闻文本分类竞赛——FastText
1、FastText简介:
该算法是Facebook AI Research 开发出的一种简单的方法。具体而言,在文本处理过程中,系统会利用嵌入层将每个单词转换为高维向量表示(即嵌入),这些向量位于密集的空间中。随后系统会对整个句子中的所有单词向量进行平均运算以得到一个整体表示(即全局表示)。经过实验对比,在大多数情况下FastText算法能够与深度学习方法达到相近的准确率水平,并且其计算效率显著优于传统的深度学习方法。该方法可作为文本分类的基础基准模型。
2、FastText优点
作为一种高效的文本分类方法, fastText相较于基于神经网络的传统分类方法而言,展现出显著的技术优势
3、FastText模型简介
3.1 模型架构
fastText 的模型架构与 Word2Vec 中 CBOW 模型在结构上有高度相似性

3.2 层次SoftMax
在神经网络中,
Softmax函数主要负责将输出层的数值标准化至0到1之间,
构建一个概率分布。
这种方法的核心作用在于对神经元输出进行标准化处理。
在标准Softmax计算中,
为了得到某个类别的概率,
需要对所有类别的概率值进行归一化处理。
然而,
当某一类别的数值特别大的时候,
这种归一化计算会变得非常耗时。
因此,
提出了分层Softmax(Hierarchical Softmax)的思想:
通过根据类别频率构建霍夫曼树来替代标准Softmax方法。
霍夫曼树是一种以权重大小决定节点位置的结构,
其特点是从根节点开始逐步查找目标节点。
由于在霍夫曼树中权重较大的节点位于靠近根节点的位置,
因此搜索速度更快。
采用分层Softmax方法能够有效降低分类任务的时间复杂度,
将从线性级数 N 优化至对数级别 logN。
3.3 N-gram特征
传统词袋模型无法有效捕捉语义信息中的上下文关系。例如,在传统的词袋模型中,并不能区分"我爱你"与"你爱我"这两个短语的意义。然而通过N-grams模式可以更好地保持语义信息中的上下文结构。n-gram方法是一种基于语言建模技术的信息处理方法其核心思想在于将文本划分为长度固定的滑动窗口片段从而生成一系列长度固定为N的小字节序列块。此外在这个过程中采用了Hash编码技巧来实现特征向量的空间降维。该技术的核心思路在于利用哈希函数将原始高维特征空间映射至低维表示空间从而实现了更高效的语义表示与信息提取过程
3.4 FastText代码实现
3.4.1 参数调节
from fastText import train_unsupervised
model = train_unsupervised(input, model='skipgram', lr=0.05,
dim=100, ws=5, epoch=5, minCount=5,
wordNgrams=1, loss='ns', bucket=2000000,
thread=12, lrUpdateRate=100, t=0.0001,
label='__label__', verbose=2,
pretrainedVectors='')
首先是对模型架构的设定,在Word2Vec中主要涉及Skip-Gram与CBOW两种方式。其中Skip-Gram算法通过当前词预测其周围的上下文词(window),而CBOW(连续词袋模型)则基于上下文信息推测当前词(window)。通常建议采用Skip-Gram算法因其计算效率较高但运算速度较慢但预测精度略高于CBOW方法。需要注意的是默认情况下,默认使用1-gram模式即单独提取每个单词作为单位进行建模。如果语料量较大可以考虑将维度设置在300附近具体大小则需要根据实验结果逐步调整步长为50测试不同维度对结果的影响。关于窗口大小参数ws它表示当前词与其目标词之间的最大距离在实际应用中通常取值范围在5到11之间具体取决于源码实现细节需结合实验数据进行验证。对于wordNgrams参数默认情况下,默认使用1-gram模式即单独提取每个单词作为单位进行建模。如果英文语料由于其单个单词就是最小单位因此可以直接设置为1即可无需拆分词语而中文语料则需要先做好分词工作后再设置为1以简化计算过程无需考虑更高阶的情况如bi-gram等。
import pandas as pd
from sklearn.metrics import f1_score
import fasttext
from sklearn.model_selection import StratifiedKFold
from sklearn.externals import joblib
def tunningFasttext():
# 调参
train_df = pd.read_csv('../dataset/train_set.csv', sep='\t', nrows=50000)
train_df['label_ft'] = '__label__' + train_df['label'].astype(str)
train_df[['text','label_ft']].iloc[:-5000].to_csv('train.csv', index=None, header=None, sep='\t')
#lr_list = [0.8, 0.9, 1, 1.1, 1.2, 1.3]
#minCount_list = [1,2,3,4,5]
#epoch_list = [20, 25, 30, 35]
# lr:学习率
# wordNgrams:n-gram设置
# minCount:最低出现的词频
# loss:损失函数(ns, hs, softmax)
model = fasttext.train_supervised('train.csv', lr=1.0, wordNgrams=2,
verbose=2, minCount=1, epoch=25, loss="hs")
val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[-5000:]['text']]
fscore = f1_score(train_df['label'].values[-5000:].astype(str), val_pred, average='macro')
print("the f1_score of training is: " + str(fscore))
"""
# 调整部分参数f1_score的变化
minCount is 1 which the f1_score of training is: 0.8237674862710784
minCount is 2 which the f1_score of training is: 0.8249374300159736
minCount is 3 which the f1_score of training is: 0.8240444453594258
minCount is 4 which the f1_score of training is: 0.823250595891796
minCount is 5 which the f1_score of training is: 0.8239732463432038
lr is 0.9 which the f1_score of training is: 0.8251838864793
lr is 1.0 which the f1_score of training is: 0.8286428017584191
lr is 1.1 which the f1_score of training is: 0.8272700396899009
lr is 1.2 which the f1_score of training is: 0.8262960227484231
"""
"""
# 取15000条数据
the f1_score of training is: 0.8244907044064147
# 取50000条数据
the f1_score of training is: 0.8904843116417097
"""
3.4.2 10折交叉验证
构建10折交叉验证训练和测试集数据
def creatData():
# 构建10折交叉验证训练和测试数据集
train_df = pd.read_csv('../dataset/train_set.csv', sep='\t', nrows=200000) # 取200000条数据
sfolder = StratifiedKFold(n_splits = 10)
X = train_df['text']
y = train_df['label']
for index, (train_index, val_index) in enumerate(sfolder.split(X,y)):
train_x, train_y= train_df['text'].iloc[train_index], train_df['label'].iloc[train_index]
val_x, val_y = train_df['text'].iloc[val_index], train_df['label'].iloc[val_index]
train_y = '__label__' + train_y.astype(str)
traindata = pd.DataFrame(list(zip(train_x.values, train_y.values)))
traindata.to_csv(f'./data/sfalltrain{index}.csv', index=None, header=['text', 'label_ft'], sep='\t')
testdata = pd.DataFrame(list(zip(val_x.values, val_y.values)))
testdata.to_csv(f'./data/sfalltest{index}.csv', index=None, header=['text', 'label'], sep='\t')
进行10折交叉验证
############进行10折交叉验证############
def trainFasttext():
#进行10折交叉验证
fscore_list = []
for i in range(10):
train_path = './data/sfalltrain' + str(i) +'.csv'
test_path = './data/sfalltest' + str(i) +'.csv'
test_df = pd.read_csv(test_path, sep='\t')
#model = fasttext.train_supervised(train_path, lr=1.0, wordNgrams=2, verbose=2, minCount=1, epoch=25, loss="hs")
model = fasttext.train_supervised(train_path, lr=1.0, wordNgrams=2, verbose=2, minCount=1, epoch=25, loss='softmax')
test_pred = [model.predict(x)[0][0].split('__')[-1] for x in test_df['text']]
fscore = f1_score(test_df['label'].values.astype(str), test_pred, average='macro')
fscore_list.append(fscore)
print(f"the f1_score of training{i+1} is: " , fscore)
if i == 1:
break;
print('The average f1_score is', sum(fscore_list)/len(fscore_list))
"""
# 参数:loss='hs'
the f1_score of training1 is: 0.911607846104518
the f1_score of training2 is: 0.9164789318984321
the f1_score of training3 is: 0.9152974954237019
the f1_score of training4 is: 0.911595813010921
the f1_score of training5 is: 0.9172344278973873
the f1_score of training6 is: 0.9140821540157751
the f1_score of training7 is: 0.9156893038604335
the f1_score of training8 is: 0.9101087268398959
the f1_score of training9 is: 0.912208373997352
the f1_score of training10 is: 0.9158255016919351
The average f1_score is 0.9140128574740352
# 参数:loss='softmax'
the f1_score of training1 is: 0.9288426310944516
the f1_score of training2 is: 0.9314477168650777
the f1_score of training3 is: 0.9304964586973928
the f1_score of training4 is: 0.9306809809860263
the f1_score of training5 is: 0.9299938215659551
the f1_score of training6 is: 0.9295759220278372
the f1_score of training7 is: 0.9306571061874928
the f1_score of training8 is: 0.9276190929199206
the f1_score of training9 is: 0.9283404333208727
the f1_score of training10 is: 0.9324812547890577
The average f1_score is 0.9300135418454085
"""
# 可发现设置loss='softmax'精确度更高
官方技术文档中提供了详细的fasttext参数设置指导
具体而言, fasttext算法的基本流程包括以下几个步骤:首先, 从未标注的数据中学习词向量表示;其次, 利用这些词向量构建特征矩阵;最后, 使用线性分类器对特征矩阵进行分类预测
