自然语言处理之朴素贝叶斯

一 、朴素贝叶斯基本原理

  • 基于朴素贝叶斯公式,比较出后验概率的最大值来进行分类,后验概率的计算是由先验概率与类条件概率的乘积得出,先验概率和类条件概率要通过训练数据集得出,即为朴素贝叶斯分类模型,将其保存为中间结果,测试文档进行分类时调用这个中间结果得出后验概率。

1、基本定义

  • 分类是把一个事物分到某个类别中。一个事物具有很多属性,把它的众多属性看作一个向量,即X=(x_{1},x_{2},x_{3},\cdot \cdot \cdot ,x_{n}),用x这个向量来代表这个事物,x的集合记为X,称为属性集。类别也有很多种,用集合C={c{_{1}},c{_{2}},\cdot \cdot \cdot ,c{_{m}}}表示。一般X和C的关系是不确定的,可以将X和C看作是随机变量,P(C|X)称为C的后验概率,与之相对的,P(C)称为C的先验概率。
  • 根据贝叶斯公式,后验概率P(C|X)=P(X|C)P(C)/P(X),但在比较不同C值的后验概率时,分母P(X)总是常数,忽略掉,后验概率P(C|X)=P(X|C)P(C),先验概率P(C)可以通过计算训练集中属于每一个类的训练样本所占的比例,对类条件概率P(X|C)的估计,我们只谈论朴素贝叶斯分类器方法,因为朴素贝叶斯假设事物属性之间相互条件独立,P(X|C)=\prod P(x_{i}|c_{i})

2、模型原理与训练

  • 朴素贝叶斯分类器是一种有监督学习,常见有两种模型,多项式模型(multinomial model)即为词频型和伯努利模型(Bernoulli model)即文档型,还有一种高斯模型。
  • 前二者的计算粒度不一样,多项式模型以单词为粒度,伯努利模型以文件为粒度,因此二者的先验概率和类条件概率的计算方法都不同。计算后验概率时,对于一个文档d,多项式模型中,只有在d中出现过的单词,才会参与后验概率计算,伯努利模型中,没有在d中出现,但是在全局单词表中出现的单词,也会参与计算,不过是作为“反方”参与的。
  • 这里暂不考虑特征抽取、为避免消除测试文档时类条件概率中有为0现象而做的取对数等问题。

(1)高斯模型

有些特征可能是连续型变量,比如说人的身高,物体的长度,这些特征可以转换成离散型的值,比如如果身高在160cm以下,特征值为1;在160cm和170cm之间,特征值为2;在170cm之上,特征值为3。也可以这样转换,将身高转换为3个特征,分别是f1、f2、f3,如果身高是160cm以下,这三个特征的值分别是1、0、0,若身高在170cm之上,这三个特征的值分别是0、0、1。不过这些方式都不够细腻,高斯模型可以解决这个问题。高斯模型假设这些一个特征的所有属于某个类别的观测值符合高斯分布,也就是:

from sklearn.naive_bayes import GaussianNB
#高斯贝叶斯
def train_model_GaussianNB():
    pass
    clf3 = GaussianNB()
    clf3.fit(X[499:], y[499:])#训练模型
    predict_labels = clf3.predict(X[0:499])
    # 预测对了几个?
    n = 0
    for i in range(len(predict_labels)):
        if (predict_labels[i] == y[i]):
            n = n + 1
    print("高斯贝叶斯:")
    # 正确率
    print n / 499.0
    # 混淆矩阵
    confusion_matrix(y[0:499], predict_labels)
    return

2)多项式模型


在多项式模型中,设某文档d=(t1,t2,…,tk),tk是该文档中出现过的单词,允许重复,则先验概率P(c)= 类c下单词总数/整个训练样本的单词总数。类条件概率P(tk|c)=(类c下单词tk在各个文档中出现过的次数之和+1)/(类c下单词总数+|V|)。
其中V是训练样本的单词表(即抽取单词,单词出现多次,只算一个),|V|则表示训练样本包含多少种单词。P(tk|c)可以看作是单词tk在证明d属于类c上提供了多大的证据,而P(c)则可以认为是类别c在整体上占多大比例(有多大可能性)。

 


from sklearn.naive_bayes import MultinomialNB
#多项式贝叶斯
def train_model_MultinomialNB():
    pass
    clf = MultinomialNB()
    #训练模型
    clf.fit(X[499:],y[499:])
    #预测训练集
    predict_labels = clf.predict(X[0:499])
    #预测对了几个?
    n = 0
    for i in range(len(predict_labels)):
        if(predict_labels[i] == y[i]):
            n = n + 1
    print("多项式贝叶斯:")
    #正确率
    print n/499.0
    #混淆矩阵
    confusion_matrix(y[0:499], predict_labels)
    return

(3)伯努利模型


P(c)= 类c下文件总数/整个训练样本的文件总数 
P(tk|c)=(类c下包含单词tk的文件数+1)/(类c下包含的文件+2)

from sklearn.naive_bayes import BernoulliNB
#伯努利贝叶斯
def train_model_BernoulliNB():
    pass
    clf2 = BernoulliNB()
    clf2.fit(X[499:], y[499:])
    predict_labels = clf2.predict(X[0:499])
    # 预测对了几个?
    n = 0
    for i in range(len(predict_labels)):
        if (predict_labels[i] == y[i]):
            n = n + 1
    print("伯努利贝叶斯:")
    # 正确率
    print n / 499.0
    # 混淆矩阵
    confusion_matrix(y[0:499], predict_labels)
    return

 

文本分类是作为离散型数据的。朴素贝叶斯用于很多方面,数据就会有连续和离散的,连续型时可用正态分布,还可用区间,将数据的各属性分成几个区间段进行概率计算,测试时看其属性的值在哪个区间就用哪个条件概率。再有TF、TDIDF,这些只是描述事物属性时的不同计算方法,例如文本分类时,可以用单词在本文档中出现的次数描述一个文档,可以用出现还是没出现即0和1来描述,还可以用单词在本类文档中出现的次数与这个单词在剩余类出现的次数(降低此属性对某类的重要性)相结合来表述。
以上参见原文:https://blog.csdn.net/u013710265/article/details/72780520 
 

4)文本分类实践

搜狗新闻数据源:http://www.sogou.com/labs/resource/ca.php

从搜狗下载的数据是类似XML的带标签对的数据,因此需要使用正则表达式或者BeautifulSoup等工具处理为dataframe格式

import pandas as pd
import jieba
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
#从本地读取数据集,并切分训练、测试集
data = pd.read_table('news_data/news_data.txt')
x_train, x_test, y_train, y_test = train_test_split(data['新闻内容'], data['类别'], random_state=1)
#分词
def fenci(train_data):
    words_df = train_data.apply(lambda x:' '.join(jieba.cut(x)))
    return words_df

x_train_fenci = fenci(x_train)
x_train_fenci[:5]
#引入停用词
infile = open("stopwords.txt",encoding='utf-8')
stopwords_lst = infile.readlines()
stopwords = [x.strip() for x in stopwords_lst]
#文本特征提取(词库表示法)
vectorizer = CountVectorizer(stop_words=stopwords, max_features=5000)
vectorizer.fit(x_train_fenci)
#建模

classifier = MultinomialNB()
#模型训练
classifier.fit(vectorizer.transform(x_train_fenci), y_train)
#使用训练好的模型进行预测
classifier.score(vectorizer.transform(fenci(x_test)), y_test)

PS:(在文本特征提取中CountVectorizer旨在通过计数来将一个文档转换为向量。当不存在先验字典时,Countvectorizer作为Estimator提取词汇进行训练,并生成一个CountVectorizerModel用于存储相应的词汇向量空间。该模型产生文档关于词语的稀疏表示。)下面举个例子:

#用于转词向量的语料
yuliao = ['dog cat fish dog dog dog','cat eat fish','i like eat fish']

#sklearn库CountVectorizer转词向量
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer()
vector = cv.fit_transform(yuliao)
vector.todense()

接下来我们看下提取到的特征分别是

{'cat': 0, 'dog': 1, 'eat': 2, 'fish': 3, 'like': 4}

从上面的例子可以看出,语料中每个词作为一个特征,词频数作为特征的值,如第一句中dog出现了4次,因此特征值为4。

 

Tips1:

  • 加入文本特征提取(TF-IDF):TF-IDF(term frequency–inverse document frequency),词频-逆文件频率。ps:(ta是一种用于资讯检索与资讯探勘的常用加权技术。TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降)

一个词语在一篇文章中出现次数越多, 同时在所有文档中出现次数越少, 越能够代表该文章.

下面直接给出一个词x的IDF的基本公式如下:

IDF(x)=lg(N/N(x)) 其中,N代表语料库中文本的总数,而N(x)代表语料库中包含词x的文本总数

TF−IDF(x)=TF(x)∗IDF(x) 其中TF(x)指词x在当前文本中的词频

词库表示法的缺点:一些普遍出现的词,词频较高,看起来似乎是更重要的特征,但因为这个词普遍出现,这个词可能不是非常的重要。如果我们的向量化特征仅仅用词频表示就无法反应这一点。因此我们需要进一步的预处理来反应文本的这个特征,而这个预处理就是TF-IDF。
用scikit-learn进行TF-IDF预处理

from sklearn.feature_extraction.text import TfidfVectorizer
#使用tf-idf把文本转为向量
tv = TfidfVectorizer(stop_words=stopwords, max_features=5000, lowercase = False)
tv.fit(x_train_fenci)
#模型训练
classifier.fit(tv.transform(fenci(x_train)), y_train)
#利用训练好的模型测试
classifier.score(tv.transform(fenci(x_test)), y_test)

Tips2:

N-gram模型

在朴素贝叶斯算法中,为了避免维度灾难,有一个大胆的假设,即X的n个维度之间相互独立:

这个假设下,条件分布大大的简化了,但是这也可能带来预测的不准确性。n个维度相互独立,就是完全没有考虑上下文语境,把每个词拆开单独看,这么看的话,猫吃鱼、鱼吃猫得到的特征向量是完全一样的。

N-gram模型就是假设X{_{n}}与附近n个词是相关的,比如当n=(1,2)时,猫吃鱼提取到的特征就是[‘猫’,‘吃’,‘鱼’,‘猫吃’,‘吃鱼’],为了平衡计算量和上下文关系,N一般取2或者3。朴素贝叶斯n个维度之间完全相互独立的假设,就是N-gram的n=1时的情况

#转词向量
tv_2gram = TfidfVectorizer(stop_words=stopwords, max_features=5000, ngram_range=(1,2),lowercase = False)
tv_2gram.fit(x_train_fenci)
#训练模型
clf_2gram = MultinomialNB()
clf_2gram.fit(tv_2gram.transform(fenci(x_train)), y_train)
#预测
clf_2gram.score(tv_2gram.transform(fenci(x_test)), y_test)

 


更多精彩内容