基于fastText的文本分类
在上一章节,我们使用传统机器学习算法来解决了文本分类问题,从本章开始我们将尝试使用深度学习方法。
与传统机器学习不同,深度学习既提供特征提取功能,也可以完成分类的功能。从本章开始我们将学习如何使用深度学习来完成文本表示。
学习目标
- 学习FastText的使用和基础原理
- 学会使用验证集进行调参
文本表示方法 Part2
现有文本表示方法的缺陷
在上一章节,我们介绍几种文本表示方法:
- One-hot
- Bag of Words
- N-gram
- TF-IDF
也通过sklean进行了相应的实践,相信你也有了初步的认知。但上述方法都或多或少存在一定的问题:转换得到的向量维度很高,需要较长的训练实践;没有考虑单词与单词之间的关系,只是进行了统计。
与这些表示方法不同,深度学习也可以用于文本表示,还可以将其映射到一个低纬空间。其中比较典型的例子有:FastText、Word2Vec和Bert。在本章我们将介绍FastText,将在后面的内容介绍Word2Vec和Bert。
FastText
FastText是一种典型的深度学习词向量的表示方法,它非常简单通过Embedding层将单词映射到稠密空间,然后将句子中所有的单词在Embedding空间中进行平均,进而完成分类操作。
所以FastText是一个三层的神经网络,输入层、隐含层和输出层。
下图是使用keras实现的FastText网络结构:
FastText在文本分类任务上,是优于TF-IDF的:
- FastText用单词的Embedding叠加获得的文档向量,将相似的句子分为一类
- FastText学习到的Embedding空间维度比较低,可以快速进行训练
如果想深度学习,可以参考论文:
Bag of Tricks for Efficient Text Classification, https://arxiv.org/abs/1607.01759
安装
FastText可以快速的在CPU上进行训练,最好的实践方法就是官方开源的版本: https://github.com/facebookresearch/fastText/tree/master/python
- pip安装
pip install fasttext - 源码安装
git clone https://github.com/facebookresearch/fastText.git
cd fastText
sudo pip install .
两种安装方法都可以安装,如果你是初学者可以优先考虑使用pip安装。
https://blog.csdn.net/IOT_victor/article/details/90449989 win10 pip install fasttxet 报错问题
import pandas as pd
from sklearn.metrics import f1_score
# 转换为FastText需要的格式
train_df = pd.read_csv('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('train.csv', index=None, header=None, sep='\t')
import fasttext
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']]
print(f1_score(train_df['label'].values[-5000:].astype(str), val_pred, average='macro'))
# 0.82
0.820615425052941
fasttext.supervised() 参数
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)
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
返回的是一个分类器,可以通过下面这些方法或者属性来进行查看你需要的东西:
model.words
model.labels
model[‘king’]
model.predict ()
如何使用验证集调参
在使用TF-IDF和FastText中,有一些模型的参数需要选择,这些参数会在一定程度上影响模型的精度,那么如何选择这些参数呢?
- 通过阅读文档,要弄清楚这些参数的大致含义,那些参数会增加模型的复杂度
- 通过在验证集上进行验证模型精度,找到模型在是否过拟合还是欠拟合
这里我们使用10折交叉验证,每折使用9/10的数据进行训练,剩余1/10作为验证集检验模型的效果。这里需要注意每折的划分必须保证标签的分布与整个数据集的分布一致。
label2id = {}
for i in range(total):
label = str(all_labels[i])
if label not in label2id:
label2id[label] = [i]
else:
label2id[label].append(i)
通过10折划分,我们一共得到了10份分布一致的数据,索引分别为0到9,每次通过将一份数据作为验证集,剩余数据作为训练集,获得了所有数据的10种分割。不失一般性,我们选择最后一份完成剩余的实验,即索引为9的一份做为验证集,索引为1-8的作为训练集,然后基于验证集的结果调整超参数,使得模型性能更优。
本章小结
本章介绍了FastText的原理和基础使用,并进行相应的实践。然后介绍了通过10折交叉验证划分数据集。
本章作业
- 阅读FastText的文档,尝试修改参数,得到更好的分数
- 基于验证集的结果调整超参数,使得模型性能更优
修改参数
关于train_supervised中的参数可以参考官方文档的说明 https://pypi.org/project/fasttext/#text-classification-model
尝试修改参数:
import pandas as pd
from sklearn.metrics import f1_score
# 转换为FastText需要的格式
train_df = pd.read_csv('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('train.csv', index=None, header=None, sep='\t')
import fasttext
model = fasttext.train_supervised('train.csv', lr=1.0, wordNgrams=2,
verbose=2, minCount=1, epoch=25, loss="softmax",)
val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[-5000:]['text']]
print(f1_score(train_df['label'].values[-5000:].astype(str), val_pred, average='macro'))
0.8762459333854703
import fasttext
model = fasttext.train_supervised('train.csv', lr=1.0, wordNgrams=2,
verbose=2, minCount=1, epoch=25, loss="hs", dim=300)
val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[-5000:]['text']]
print(f1_score(train_df['label'].values[-5000:].astype(str), val_pred, average='macro'))
0.8255512653164967
import fasttext
model = fasttext.train_supervised('train.csv', lr=2, wordNgrams=2,
verbose=2, minCount=1, epoch=25, loss="ova", dim=300 ,thread=16)
val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[-5000:]['text']]
print(f1_score(train_df['label'].values[-5000:].astype(str), val_pred, average='macro'))
0.8794743156275387
import fasttext
model = fasttext.train_supervised('train.csv', lr=3.0, wordNgrams=2,
verbose=2, minCount=1, epoch=25, loss="softmax", dim=300)
val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[-5000:]['text']]
print(f1_score(train_df['label'].values[-5000:].astype(str), val_pred, average='macro'))
0.882009703370854
改变了训练次数和损失函数,增加次数效果不明显,改为损失函数"softmax",上升0.05左右。
十折交叉验证
-
(1) 留出法 Hold-out method
将原始数据随机分为两组,一组做为训练集,一组做为验证集,利用训练集训练分类器,然后利用验证集验证模型,记录最后的分类准确率 -
(2)十折交叉验证 10-fold cross-validation
将数据集分成十份,轮流将其中9份作为训练数据,1份作为测试数据,进行试验。每次试验都会得出相应的正确率(或差错率)。10次的结果的正确率(或差错率)的平均值作为对算法精度的估计,一般还需要进行多次10折交叉验证(例如10次10折交叉验证),再求其均值,作为对算法准确性的估计。
-
(3) 留一验证 leave-one-out cross-validation
留一验证(LOOCV)意指只使用原本样本中的一项来当做验证资料, 而剩余的则留下来当做训练资料。 这个步骤一直持续到每个样本都被当做一次验证资料。 事实上,这等同于 K-fold 交叉验证是一样的,其中K为原本样本个数。 在某些情况下是存在有效率的演算法,如使用kernel regression 和Tikhonov regularization。
用StratifiedKFold实现十折交叉划分
将原始数据进行十折交叉划分,分成十份训练集和测试集并保存。
import pandas as pd
from sklearn.model_selection import StratifiedKFold
print('starting K10 cross-validation data split:')
train_df = pd.read_csv('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')
starting K10 cross-validation data split:
the 0 data split ...
the 1 data split ...
the 2 data split ...
the 3 data split ...
the 4 data split ...
the 5 data split ...
the 6 data split ...
the 7 data split ...
the 8 data split ...
the 9 data split ...
利用十折交叉验证调参
import fasttext
import pandas as pd
from sklearn.metrics import f1_score
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))
starting K10 cross-validation training:
the f1_score of 0 training is: 0.9294013969094791
the f1_score of 1 training is: 0.9316360856767221
the f1_score of 2 training is: 0.9300947381298338
the f1_score of 3 training is: 0.9308906409140556
the f1_score of 4 training is: 0.9297180951129093
时间太长了,果断放弃了。
参考文献
NLP新闻文本分类学习赛 - Task4 基于fastText的文本分类
win10 pip install fasttxet 报错问题
机器学习-CrossValidation交叉验证Python实现