fastText原理及实践

    xiaoxiao2022-07-04  209

    文章目录

    fastText原理及实践预备知识Softmax回归分层Softmaxn-gram特征 fastText分类字符级别的n-gram模型架构核心思想关于分类效果 keras构建fastText 参考资料

    fastText原理及实践

    预备知识

    Softmax回归

    Softmax回归(Softmax Regression)又被称作多项逻辑回归(multinomial logistic regression),它是逻辑回归在处理多类别任务上的推广。

    分层Softmax

    标准的Softmax回归中,要计算 y = j y=j y=j 时的Softmax概率: P ( y = j ) P(y=j) P(y=j) ,我们需要对所有的 K K K 个概率做归一化,这在 ∣ y ∣ |y| y 很大时非常耗时。于是,分层Softmax诞生了,它的基本思想是使用树的层级结构替代扁平化的标准Softmax,使得在计算 P ( y = j ) P(y=j) P(y=j) 时,只需计算一条路径上的所有节点的概率值,无需在意其它的节点。 下图是一个分层Softmax示例: 树的结构是根据类标的频数构造的霍夫曼树。 K K K 个不同的类标组成所有的叶子节点, K − 1 K-1 K1 个内部节点作为内部参数,从根节点到某个叶子节点经过的节点和边形成一条路径,路径长度被表示为 L ( y j ) L(y_{j}) L(yj) 。于是, P ( y j ) P(y_{j}) P(yj) 就可以被写成: 其中: σ ( ⋅ ) \sigma(·) σ() 表示sigmoid函数; L C ( n ) LC(n) LC(n) 表示 n n n节点的左孩子; ⟦ x ⟧ ⟦x⟧ x 是一个特殊的函数,被定义为: θ n ( y j , l ) \theta_{n(y_{j},l)} θn(yj,l) 是中间节点 n ( y j , l ) n(y_{j},l) n(yj,l)的参数;

    X X X是Softmax层的输入 上图中,高亮的节点和边是从根节点到 y 2 y_{2} y2 的路径,路径长度 L ( y 2 ) = 4 , P ( y 2 ) L(y_{2})=4,P(y_{2}) L(y2)=4,P(y2)可以被表示为: 于是,从根节点走到叶子节点 y_{2} ,实际上是在做了3次二分类的逻辑回归。 通过分层的Softmax,计算复杂度一下从 ∣ K ∣ |K| K降低到 l o g ∣ K ∣ log|K| logK

    n-gram特征

    在文本特征提取中,常常能看到n-gram的身影。它是一种基于语言模型的算法,基本思想是将文本内容按照字节顺序进行大小为N的滑动窗口操作,最终形成长度为N的字节片段序列。

    例子如下: 我来到达观数据参观

    相应的bigram特征为:我来 来到 到达 达观 观数 数据 据参 参观

    相应的trigram特征为:我来到 来到达 到达观 达观数 观数据 数据参 据参观

    注意一点:n-gram中的gram根据粒度不同,有不同的含义。它可以是字粒度,也可以是词粒度的。上面所举的例子属于字粒度的n-gram,词粒度的n-gram看下面例子:

    我 来到 达观数据 参观

    相应的bigram特征为:我/来到 来到/达观数据 达观数据/参观

    相应的trigram特征为:我/来到/达观数据 来到/达观数据/参观

    n-gram产生的特征只是作为文本特征的候选集,后面可能会采用信息熵、卡方统计、IDF等文本特征选择方式筛选出比较重要特征。

    fastText分类

    一般情况下,使用fastText进行文本分类的同时也会产生词的embedding,即embeddin是fastText分类的产物。除非你决定使用预训练的embedding来训练fastText分类模型,这另当别论。

    字符级别的n-gram

    word2vec把语料库中的每个单词当成原子的,它会为每个单词生成一个向量。这忽略了单词内部的形态特征,比如:“apple” 和“apples”,“达观数据”和“达观”,这两个例子中,两个单词都有较多公共字符,即它们的内部形态类似,但是在传统的word2vec中,这种单词内部形态信息因为它们被转换成不同的id丢失了。 为了克服这个问题,fastText使用了字符级别的n-grams来表示一个单词。对于单词“apple”,假设n的取值为3,则它的trigram有

    “<ap”, “app”, “ppl”, “ple”, “le>”

    其中,<表示前缀,>表示后缀。于是,我们可以用这些trigram来表示“apple”这个单词,进一步,我们可以用这5个trigram的向量叠加来表示“apple”的词向量。

    这带来两点好处:

    对于低频词生成的词向量效果会更好。因为它们的n-gram可以和其它词共享。

    对于训练词库之外的单词,仍然可以构建它们的词向量。我们可以叠加它们的字符级n-gram向量。

    模型架构

    fastText模型架构图: 注意:此架构图没有展示词向量的训练过程。可以看到,和CBOW一样,fastText模型也只有三层:输入层、隐含层、输出层(Hierarchical Softmax),输入都是多个经向量表示的单词,输出都是一个特定的target,隐含层都是对多个词向量的叠加平均。不同的是,CBOW的输入是目标单词的上下文,fastText的输入是多个单词及其n-gram特征,这些特征用来表示单个文档;CBOW的输入单词被onehot编码过,fastText的输入特征是被embedding过;CBOW的输出是目标词汇,fastText的输出是文档对应的类标。 值得注意的是,fastText在输入时,将单词的字符级别的n-gram向量作为额外的特征;在输出时,fastText采用了分层Softmax,大大降低了模型训练时间。

    核心思想

    仔细观察模型的后半部分,即从隐含层输出到输出层输出,会发现它就是一个softmax线性多类别分类器,分类器的输入是一个用来表征当前文档的向量;模型的前半部分,即从输入层输入到隐含层输出部分,主要在做一件事情:生成用来表征文档的向量。那么它是如何做的呢?叠加构成这篇文档的所有词及n-gram的词向量,然后取平均。叠加词向量背后的思想就是传统的词袋法,即将文档看成一个由词构成的集合。 于是fastText的核心思想就是:将整篇文档的词及n-gram向量叠加平均得到文档向量,然后使用文档向量做softmax多分类。这中间涉及到两个技巧:字符级n-gram特征的引入以及分层Softmax分类。

    关于分类效果

    为何fastText的分类效果常常不输于传统的非线性分类器? 假设我们有两段文本:

    我 来到 达观数据 俺 去了 达而观信息科技

    这两段文本意思几乎一模一样,如果要分类,肯定要分到同一个类中去。但在传统的分类器中,用来表征这两段文本的向量可能差距非常大。传统的文本分类中,你需要计算出每个词的权重,比如tfidf值, “我”和“俺” 算出的tfidf值相差可能会比较大,其它词类似,于是,SVM(向量空间模型)中用来表征这两段文本的文本向量差别可能比较大。但是fastText就不一样了,它是用单词的embedding叠加获得的文档向量,词向量的重要特点就是向量的距离可以用来衡量单词间的语义相似程度,于是,在fastText模型中,这两段文本的向量应该是非常相似的,于是,它们很大概率会被分到同一个类中。

    使用词embedding而非词本身作为特征,这是fastText效果好的一个原因;另一个原因就是字符级n-gram特征的引入对分类效果会有一些提升 。

    keras构建fastText

    from keras.utils import np_utils from keras.models import Sequential from keras.layers import Embedding from keras.layers import GlobalAveragePooling1D from keras.layers import Dense VOCAV_SIZE = 2000 EMVEDDING_DIM = 200 MAX_WORDS = 300 CLASS_NUM = 3 def build_fastText(): model = Sequential() #通过embedding层,我们将词汇表映射成EMBEDDING_DIM维向量 model.add(Embedding(VOCAV_SIZE, EMVEDDING_DIM, input_length=MAX_WORDS)) #通过GlobalAveragePooling1D,我们平均了文档中所有词的embedding model.add(GlobalAveragePooling1D()) #通过输出层Softmax分类(真实的fastText这里是分层Softmax),得到类别概率分布 model.add(Dense(CLASS_NUM, activation='softmax')) #定义损失函数、优化器、分类度量指标 model.compile(loss='categorical_crossentropy', optimizer='SGD', metrics=['accuracy']) return model if __name__ == '__main__': model = build_fastText() print(model.summary())

    参考资料

    fastText原理及实践

    最新回复(0)