fastText是facebook在2016年提出的一个文本分类算法,是一个有监督模型,其简单高效,速度快,在工业界被广泛的使用。在学术界,可以作为baseline的一个文本分类模型。
FastText结构同CBOW的结构很像,但是FastText的输出是对一个文本或句子的分类,而不再是中间词根。FastText对于输入的每个x,经过一个转换矩阵A(本质是一个lookup表,关于这一点可以看我关于skip-gram和cbow的blog),得到对应的词向量v,所有N个向量求和取平均,平均值经过矩阵B映射到预先设定好的n个类别,经过softmax就得到了概率,公式表示和示意图如下:
s o f t m a x ( B N ∑ n = 1 N ( A x n ) ) softmax\Big(\frac{B}{N}\sum_{n=1}^{N} (Ax_n)\Big) softmax(NBn=1∑N(Axn))
x n x_n xn表示文档的输入特征,A,B是权值矩阵。
FastText有两个重要优化,分别是Hierarchical Softmax、N-gram,但这两个优化方法并不是FastText独有的,Hierarchical Softmax可以看我的另一篇blog,N-gram就是连续考虑n个词(或字符),如果只有一个词(或字符)就是unigram(1-gram),考虑两个连续词(或字符)就是bigram(2-gram),考虑三个连续词(或字符)就是3-gram,对于一个连续的句子,对词级别和字符级别的bigram实例如下:
原始句子:我来到达观数据参观
分词后:我 来到 达观数据 参观
字符级别bigram:我来 来到 到达 达观 观数 数据 据参 参观
词级别bigram:我/来到 来到/达观数据 达观数据/参观
在上图中我用黄字着重标出来了,FastText的输入的每个 x x x都是ngram,这样做能够保证更加准确的获取局部信息,同时论文指出,这样做的效果同考虑所有词的相互顺序的效果类似。
Bag of words is invariant to word order but taking explicitly this order into account is often computationally very expensive. Instead, we use a bag of n-grams as additional features to capture some partial information about the local word order. This is very efficient in practice while achieving comparable results to methods that explicitly use the order
同时由于FastText在平均前的工作相互独立,所以可以利用多线程进行操作,在pypi的FastText库中,默认利用的是12线程。
这里直接利用python的FastText库,可以直接利用pip安装:
pip install FastText
以真假新闻分辨为例,给定一段新闻摘要,利用FastText进行分类,真新闻标记为0,假新闻标记为1,训练集train_data.csv实例如下,前缀" _ _ l a b e l _ _ \_\_label\_\_ __label__“是一个标识,让FastText知道这是这一特征是标签,那么问题来了,为什么不规定第一个空格之前的是标签呢?这是因为标签可能不止一个,每个标签都要加上” _ _ l a b e l _ _ \_\_label\_\_ __label__"前缀。标签后的数据经过了分词处理,分词之间通过空格分隔。这里展示的是对原始数据直接分词的结果,我并没用进行类似删掉标点符号,去停用词的处理。
__label__1 【 传 深圳 房企 老板 在 澳门 赌输 价值 百亿 大楼 】 据 媒体报道 , 网友 爆料 称 深圳 房企京基 集团 董事长 陈华 在 澳门 赌博 输掉 了 估值 百亿 的 深圳 地标 — — 京基 100 大厦 的 控股权 , 现该 大厦 正办 过户 手续 。 目前 , 京基 方面 未 对 传闻 作出 回应 。 ...
__label__1 杨澜 : 虽然 我入 了 美国 籍 , 但 我 出身 于 中国 , 所以 从 原产地 角度 而言 , 我 不 出席 美国 两会 而 出席 中国 的 两会 是 “ 天经地义 ” 。 杨澜 的 荒唐 逻辑 , 实在 匪夷所思 。
__label__1 【 《 背影 》 因 违反 交规 被 逐出 教材 ? 】 南京大学中文系 教授 问 学生 , 从 小学 到 高中 , 难道 没 一篇 课文 感动 你 ? 如 朱自清 《 背影 》 。 一 学生 答 , 《 背影 》 早 从 课本上 撤掉 了 。 为什么 , 学生 答 : “ 违反 交通规则 ” 。 文中 父亲 跳 下 站台 、 穿过 铁道 到 对面 给 儿子 买 橘子 , 是 违反 交规 的 ...
__label__0 【 她们 登上 警车 尬 舞 后来 被 警察 """" 团灭 """" 了 】 7 月 10 日 凌晨 , 贵州 都匀市 4 名 女孩 在 酒吧 喝完 酒 , 爬 上 路边 警车 尬 舞 。 当天 下午 , 4 名 女孩 被 警方 抓获 。 4 人 因涉嫌 寻衅滋事 被行 拘 8 日 , 由于 不满 18 周岁 且 是 初次 违反 治安管理 处罚法 , 不 执行 处罚 , 警方 对 其 进行 了 批评 , 令 监护人 带回家 严加管教
测试集数据test_data.csv同train_data.csv格式相同。如果模型训练好后,我们想直接调用predict方法判断一个句子的类别,这个句子首先应该经过了分词,然后把该句子存成字符串或者字符串数组,格式如下,这是一个一维数组。
['帮转 请 叫 我 小 B 龟 : Mico 是 中美 混血 男孩 , 3 周岁 , 身高 1 米左右 , 偏瘦 , 寸头 , 离开 时 身穿 绿色 棉布 大衣 、 深蓝色 裤子 、 咖色 皮鞋 , 能 说 简单 的 中文 及 英文 。 于 1 月 7 日 16 : 33 分 左右 , 在 厦门 爱绿 双语 幼儿园 门口 育秀 路段 ( 大润发 ) 被 抢走 , 至今 下落 未 明 , 妈妈 已近 崩溃 , 望 知情者 提供线索 , 我 是 他 妈妈 的 朋友 , 求 扩散 !',
'【 海航 允许 携带 宠物 进入 客舱 统一 收费 800 元 】 近日 , 海南 航空 试行 “ 客舱 运输 宠物 ” 产品 服务 , 要求 为 一般 家庭 驯养 的 猫 、 狗 , 需 6 个 月 以上 , 健康 , 未 怀孕 , 未 在 48 小时 内 分娩 ; 旅客 需为 宠物 佩戴 口套 、 穿戴 纸尿裤 , 并 全程 将 其 置于 宠物 箱中 , 不得 喂食 。 每 只 收费 800 元 。 其他 乘客 如 不想 挨着 宠物 , 可 申请 调换 座位 。 | 海航 : 宠物 ...',
'【 城管 给 卖菜 老太 打伞 老太 临别时 道谢 】 “ 这样 的 城管 多 几个 还 行 。 ” 9 月 4 日 开始 , 一张 “ 城管 给 小贩 撑伞 ” 的 照片 在 朋友圈 疯传 。 照片 中 , 一位 城管 模样 的 男子 左手 提 着 一个 水杯 , 右手 举着 一把 红花 雨伞 。 伞下 , 一个 身体 佝偻 的 老太太 正在 收拾 摆在 地上 的 蔬菜 。 via 重庆晚报 | 重庆 城管 给 ...']
具体代码如下:
import fasttext
# 训练模型,具体参数可以看blog最后引用文献部分,列出的官网网址
model = fasttext.train_supervised(input="./train_data.csv", epoch=25, wordNgrams=2)
# 测试,k表示考虑top k,由于是二分类,所以k=1
print(model.test("/home/mec/liangdongtian/fake_news_detection/data/test_data.csv", k=1))
# 输出:(7695, 0.9815464587394412, 0.9815464587394412),表示共有7695条数据,准确率召回率均是0.982
# 推断,用例是上面给的一维测试数组
print(model.predict(data))
# 输出:([['__label__1'], ['__label__0'], ['__label__0']], array([[0.99721408],[0.99995029], [0.99978548]]))
# 输出解释:对于三个新闻摘要,模型认为第一个是假的,后两个是真的,实际也确实如此;后面给的数组表示判断的概率,我们可以通过下面代码输出看出,和另一个label是互补的。
print(model.predict(tmp, k=2))
# 输出:([['__label__1', '__label__0'], ['__label__0', '__label__1'], ['__label__0', '__label__1']],
array([[9.97214079e-01, 2.80591869e-03],
[9.99950290e-01, 6.97360520e-05],
[9.99785483e-01, 2.34513165e-04]]))
# 保存模型
model.save_model("fake_news_detected_model.bin")
# 读取模型
model2 = fasttext.load_model("fake_news_detected_model.bin")
FastText除了可以对文档进行分类,还可以执行CBOW和Skip-gram来学习词向量表示:
import fasttext
# 词向量表示
model = fasttext.train_unsupervised(input="./train_data.csv", epoch=1, lr=0.5, minn=1, maxn=3, model='cbow')
# 获取词向量:
model.get_word_vector("奶奶")
# 输出:
"""
array([ 102059.4 , -68441.32 , 304470.2 , 311754.75 ,
-230531.8 , 25913.648 , -182974.78 , -190766.12 ,
-644414.1 , -132576.38 , 108882.25 , -332925.12 ,
12349.384 , -463407.5 , -232429.98 , -185747.69 ,
80663.14 , 53180.64 , -18819.648 , -410663.25 ,
-398879.12 , 96190.484 , 341425.25 , -256364.27 ,
62487.023 , 41527.76 , 375745.1 , -79907.46 ,
-112595.57 , -504839.22 , 115009.78 , 213641.34 ,
-193202.36 , 89021.52 , -162374.52 , -533262.1 ,
33116.242 , -39390.29 , 416710.5 , 163098.66 ,
-410393.94 , 625811.7 , 17629.508 , -312635.75 ,
-259025.39 , -113679.305 , -347809.4 , -37010.855 ,
5223.478 , 249829.23 , -453246.5 , 65942.984 ,
180671.67 , -257055.95 , 209196.62 , 158813.66 ,
-6870.5166, -177275.7 , 81687.23 , -440451.38 ,
9726.117 , -367380.03 , 136241.1 , -275263.84 ,
-116055.7 , 199297.5 , 277341.53 , -3254.2344,
-357637.62 , 167770.25 , 49575.13 , -199634.33 ,
-27568.742 , 210491.83 , 287567.53 , -174878.77 ,
-117145.234 , -493248.25 , -128043.22 , 258058.11 ,
-50932.105 , -398822.56 , -164988.56 , -444483.72 ,
63238.523 , 215993.94 , -288719.78 , 75995.94 ,
-139015.2 , -56318.47 , -212303.69 , -176017.08 ,
-239202.4 , 118557.68 , 205427.66 , -133535.67 ,
61893.58 , -22306.602 , -90023.414 , -298827.75 ],
dtype=float32)
"""
# facebook的实现是有get_nearest_neighbors()这一方法的,获取输入词语意相近的词,但是可惜pypi里包含的FastText没有实现这个,不然可以看看FastText模型学习到的语意相关词
参考文献:
论文地址:https://arxiv.org/pdf/1607.01759.pdf
FastText官方文档:https://fasttext.cc/docs/en/supervised-tutorial.html
FastText在pypi的介绍:https://pypi.org/project/fasttext/
fastText原理和文本分类实战,看这一篇就够了:https://blog.csdn.net/feilong_csdn/article/details/88655927
fastText原理及实践:https://blog.csdn.net/John_xyz/article/details/79421618