NBSVM

NBSVM作为一个经常被用到的分类模型,却很难找到一篇讲解它的博客,索性自己读一下论文,写一写自己的理解。
论文地址Baselines and Bigrams: Simple, Good Sentiment and Topic Classification
作者代码:github nbsvm
摘要:
各种朴素贝叶斯和支持向量机的变种总是被用作文本分类的基线模型,但是性能却很大地依赖于所使用的变种、特征和数据集。我们发现:(i)word bigram特征在情感分析任务中总是有小;(ii)对于短文本情感任务,朴素贝叶斯总是比支持向量机的效果好(对于更长的文本则相反);(iii)一个简单的但是新颖的使用朴素贝叶斯对数计数比率作为特征值的SVM变种在不同的任务和数据集上都表现得很好。基于这些事实,我们提出一个简单的NB和SVM的变种,它在大多数情感分类数据集上都表现优异,有时甚至达到了state-of-the-art 的水平。

一、理论讲解

简单来说NBSVM就是使用朴素贝叶斯对数计数比率作为特征值的SVM。

1、方法:

模型变种主要还是一个线性分类器(不考虑核函数时SVM就是一个线性分类器,这就是SVM的线性分界面嘛)

image.png

f^{(i)}∈R^{|V|}
是训练实例i的特征计数向量,标签为
f^{(i)}∈{-1,1}
。V是 特征的集合,
f_{j}^{(i)}
表示特征
V_{j}
在实例i中出现的次数(共现次数)。定义计数向量为
p=\alpha +\sum _{i:y^{(i)}=1}f^{(i)}
q=\alpha +\sum _{i:y^{(i)}=-1}f^{(i)}
, 其中
\alpha
为平滑系数。对数计数比率为:
image.png

2、多项式NB

在MNB中,x^{(k)}=f^{(k)},w=r,b=log(N_{+}/N_{-})N_{+},N_{-}是训练集正例和负例的数量。作者发现二值化的f^{(k)}效果更好。

3、支持向量机:

对于支持 向量机

image.png

,就是以特征作为输入。
w,b通过最小化
优化函数1

得到。
看到这里不免让人疑问,支持向量机的优化公式不应该时这样的吗支持向量机通俗导论(理解SVM的三层境界)

优化函数2

(i)、先说这个max, 第一个优化函数里面的max 道理同

参考 支持向量机中的 “4、支持向量机通俗导论(理解SVM的三层境界)中有如下内容,从公式(18)到公式(20)如何理解?”
简单说就是为了满足所有的约束条件。
(ii)、二次幂,下文有提到说这时L2正则化的SVM。这样一说不带二次幂的就是L1正则了。

4、使用NB特征的SVM

NBSVM中

image.png

就是用对数比率和特征的内积作为SVM的输入。
另外,还可以在MNB和SVM之间做个均衡,让
{w}'=(1-\beta )\overline{w}+\beta w
,其中
\overline{w}=\left \| w \right \|_{_{1}}/\left | V \right |
是w的均值,且
\beta \in [0,1]
是平衡参数。这种平衡可以看作一种正则化:相信NB除非SVM可信度非常高。

二、代码实现

代码来自NB-SVM strong linear baseline
这个实现准确地说是NBLR,就是把NBSVM中的SVM换成LR。
现在我们只关注如何计算NB log-count value
预处理部分

import re, string
re_tok = re.compile(f'([{string.punctuation}“”¨«»®´·º½¾¿¡§£₤‘’])')
def tokenize(s): return re_tok.sub(r' \1 ', s).split()

n = train.shape[0]
vec = TfidfVectorizer(ngram_range=(1,2), tokenizer=tokenize,
               min_df=3, max_df=0.9, strip_accents='unicode', use_idf=1,
               smooth_idf=1, sublinear_tf=1 )
trn_term_doc = vec.fit_transform(train[COMMENT])
test_term_doc = vec.transform(test[COMMENT])

核心代码:

def pr(y_i, y):
    p = x[y==y_i].sum(0)
    return (p+1) / ((y==y_i).sum()+1)

x = trn_term_doc
test_x = test_term_doc

def get_mdl(y):
    y = y.values
    r = np.log(pr(1,y) / pr(0,y))
    m = LogisticRegression(C=4, dual=True)
    x_nb = x.multiply(r)
    return m.fit(x_nb, y), r

preds = np.zeros((len(test), len(label_cols)))

for i, j in enumerate(label_cols):
    print('fit', j)
    m,r = get_mdl(train[j])
    preds[:,i] = m.predict_proba(test_x.multiply(r))[:,1]

函数pr(y_i, y)就是用来计算p/\left \| p \right \|_{1}q/\left \| q \right \|_{1}
1就是平滑系数α。
x就对应f^{(i)}, 上面已经知道f^{(i)}是实例i的特征计数向量。那x是什么呢?x=trn_term_doc,trn_term_doc是TFIDF向量(TFIDF)。
f_{j}^{(i)}表示特征V_{j}在实例 i 中出现的次数,词频TF刚好能表示这个含义,这里用了TFIDF,可能是效果更好吧。
这里的分类器用的是LR,也可以换成SVM,哪个效果好就要看具体情况了。
还有一个细节就是在预测的时候也需要乘上对数比率r。
相关链接:简单、强壮的情感、主题分类工具——牛逼 SVM

推荐阅读更多精彩内容