博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
机器学习之基于朴素贝叶斯文本分类算法
阅读量:6382 次
发布时间:2019-06-23

本文共 8578 字,大约阅读时间需要 28 分钟。

原理

在分类(classification)问题中,经常须要把一个事物分到某个类别。一个事物具有非常多属性。把它的众多属性看做一个向量,即x=(x1,x2,x3,,xn)。用x这个向量来代表这个事物。类别也是有非常多种,用集合Y=y1,y2,ym表示。假设x属于y1类别,就能够给x打上y1标签,意思是说x属于y1类别。

这就是所谓的分类(Classification)

x的集合记为X。称为属性集。一般X和Y的关系是不确定的,你仅仅能在某种程度上说x有多大可能性属于类y1,比方说x有80%的可能性属于类y1,这时能够把X和Y看做是随机变量,P(Y|X)称为Y的后验概率(posterior probability),与之相对的,P(Y)称为Y的先验概率(prior probability)

在训练阶段。我们要依据从训练数据中收集的信息,对X和Y的每一种组合学习后验概率P(Y|X)分类时,来了一个实例x,在刚才训练得到的一堆后验概率中找出全部的P(Y|x), 当中最大的那个y,即为x所属分类。依据贝叶斯公式,后验概率为P(Y|X)=P(X|Y)P(Y)P(X)

在比較不同Y值的后验概率时。分母P(X)总是常数,因此能够忽略。先验概率P(Y)能够通过计算训练集中属于每个类的训练样本所占的比例easy地预计。这里直接计算P(Y|X)比較麻烦,而朴素贝叶斯分类提出了一个独立性如果:x1,x2,...,xn相互独立,这也是被称之为“朴素”的原因之中的一个。于是P(X|Y) = P(x1|Y)P(x2|Y)...P(xn|Y)。而P(xi|Y)非常好求了。

对于二元分类,要比較P(Y=1|X)和P(Y=0|X)的大小,仅仅需比較分子P(X|Y)P(Y)部分。

因此仅仅需计算n个条件概率和先验概率。

文本分类

在文本分类中,如果我们有一个文档d∈X,X是文档向量空间(document space),和一个固定的类集合C={c1,c2,…,cj},类别又称为标签。显然,文档向量空间是一个高维度空间。我们把一堆打了标签的文档集合<d,c>作为训练样本,<d,c>∈X×C。

比如:

<d,c>={Beijing joins the World Trade Organization, China}

对于这个仅仅有一句话的文档,我们把它归类到 China,即打上china标签。

我们期望用某种训练算法。训练出一个函数γ,可以将文档映射到某一个类别:

γ:X→C

这样的类型的学习方法叫做有监督学习,由于事先有一个监督者(我们事先给出了一堆打好标签的文档)像个老师一样监督着整个学习过程。

朴素贝叶斯分类器是一种有监督学习,常见有两种模型,多项式模型(multinomial model)和伯努利模型(Bernoulli model)。

多项式模型

在多项式模型中。 设某文档d=(t1,t2,…,tk)。tk是该文档中出现过的单词。同意反复。则

先验概率P(c)= 类c下单词总数/整个训练样本的单词总数

类条件概率P(tk|c)=(类c下单词tk在各个文档中出现过的次数之和+1)/(类c下单词总数+|V|)

V是训练样本的单词表(即抽取单词集合。单词出现多次,仅仅算一个),|V|则表示训练样本包括多少种单词。在这里,m=|V|, p=1/|V|。

P(tk|c)能够看作是单词tk在证明d属于类c上提供了多大的证据。而P(c)则能够觉得是类别c在总体上占多大比例(有多大可能性)。

伯努利模型

P(c)= 类c下文件总数/整个训练样本的文档总数

P(tk|c)=(类c下包括单词tk的文件数+1)/(类c下文档总数+2)     在这里,m=2, p=1/2。

 #这里一定要注意:伯努利是以文档为粒度的,所以分母是文档总数,而不是网上以讹传讹类c下单词总数

这里贴几个以讹传讹的链接:

还有好多其它的就不一一列举了。

上面两个模型中的分子分母都加上了一些数,这是为了防止某个条件概率为0,从而整个P(X|Y) = P(x1|Y)P(x2|Y)...P(xn|Y)乘积为0。

实例演示

对一个邮件进行垃圾分类。数据集来自加州大学欧文分校的 spambase
数据格式说明例如以下:
英文的。我就不解释了(主要是解释起来费劲,-_-。

sorry。)

为了判别分类模型的好坏,能够计算AUC值。

本次实验採用sklearn包中的metrics计算ROC曲线和AUC值(ROC和AUC详见);而这个是须要每一个样本的后验概率的。

因此分子P(X)的值也要求出来,将整个式子转换下:

P(Y|X)=P(X|Y)P(Y)P(X) = P(x1|Y=1)P(x2|Y=1)...P(xn|Y=1)P(Y=1) (P(x1|Y=1)P(x2|Y=1)...P(xn|Y=1) + P(x1|Y=0)P(x2|Y=0)...P(xn|Y=0))
分子分母上下同一时候除以P(x1|Y=0)P(x2|Y=0)...P(xn|Y=0)得
公式不好打啊,过几天用手写。然后传图片吧,-_-。

sorry!

好了。废话不多说,上代码:
NaiveBayes.py
#!/usr/bin/python#  NaiveBayes classification#  lming_08 2014.07.06import mathimport numpy as npfrom sklearn import metricsclass NaiveBayes:    def __init__(self, trainFile):        self.trainingFile = trainFile        self.trainingData = self.read_data(trainFile)    # Read training or testing data    def read_data(self, file):        data = []        fd = open(file)        for line in fd:            arr = line.rstrip().split(',')            # Turn an instance's last column(y column) as integer            arr[-1] = int(arr[-1])            # Append the instance to trainingData            data.append(tuple(arr))        fd.close()        return data    def train_model_with_Bernoulli(self):        self.sumPosInstance = 0.        self.sumNegInstance = 0.        self.termfreq = {}        for instance in self.trainingData:                if int(instance[-1]) == 1:                    self.sumPosInstance += 1                else:                    self.sumNegInstance += 1                for i in range(len(instance) - 1):                    key = str()                    if i < 55:                                                if float(instance[i]) > 0:                            key = "freq" + "|" + str(i + 1) + "|" + "1"                        else:                            key = "freq" + "|" + str(i + 1) + "|" + "0"                    else:                        key = "length" + "|" + str(i + 1) + "|" + instance[i]                    if key not in self.termfreq:                        self.termfreq[key] = [0, 0]                                        if int(instance[-1]) == 1:                        self.termfreq[key][1] += 1                    else:                        self.termfreq[key][0] += 1        # prior_prob = p(y = 1)        self.prior_prob = self.sumPosInstance / (self.sumPosInstance + self.sumNegInstance)        # prior_ratio = p(y=1) / p(y=0)        self.prior_ratio = self.sumPosInstance / self.sumNegInstance    # the function should be called before predict()    def set_testfile(self, testFile):        self.testing_data = self.read_data(testFile)    def predict(self):        self.testingY = []        self.predict_result = []        for instance in self.testing_data:            self.predict_result.append(self.predict_instance_with_Bernoulli(instance))            self.testingY.append(instance[-1])        def get_statistics(self):        true_classify_count = 0.        false_classify_count = 0.        for instance in self.testing_data:            post_prob = self.predict_instance_with_Bernoulli(instance)            if post_prob >= 0.5 and instance[-1] == 1:                true_classify_count += 1            elif post_prob >= 0.5 and instance[-1] == 0:                false_classify_count += 1            elif post_prob < 0.5 and instance[-1] == 1:                false_classify_count += 1            elif post_prob < 0.5 and instance[-1] == 0:                true_classify_count += 1        return true_classify_count, false_classify_count	    def predict_instance_with_Bernoulli(self, instance):        f = 0.        for i in range(len(instance) - 1):            key = str()            if i < 55:                                        if float(instance[i]) > 0:                    key = "freq" + "|" + str(i + 1) + "|" + "1"                else:                    key = "freq" + "|" + str(i + 1) + "|" + "0"            else:                key = "length" + "|" + str(i + 1) + "|" + instance[i]            if key in self.termfreq:                f += math.log( (self.termfreq[key][1] + 1) / ((self.termfreq[key][0] + 2) * self.prior_ratio) )                posterior_ratio = self.prior_ratio * math.exp(f)        # posterior probability        prob = posterior_ratio / (1. + posterior_ratio)        return prob    def getAUC(self):        y = np.array(self.testingY)        pred = np.array(self.predict_result)        fpr, tpr, thresholds = metrics.roc_curve(y, pred) # metrics.roc_curve(y, pred, pos_label=1)        auc = metrics.auc(fpr, tpr)        return aucdef main(trainFile, testFile):    nb = NaiveBayes(trainFile)    nb.train_model_with_Bernoulli()    nb.set_testfile(testFile)    nb.predict()    print("the value of AUC: " % nb.getAUC())    print("the value of priori probability:" % nb.priorProb)if __name__ == "__main__":    if len(argv) != 3:        print "Usage: python %s trainFile(in) testFile(out)" % __file__        sys.exit(0)    main(argv[1], argv[2])
PartitionFile.py    用于将数据文件切割为训练文件(80%)和測试文件(20%)
#!/usr/bin/python#  Partitioning a file into training(80%) and testing file(20%)#  lming_08 2014.07.06from random import randintdef partition_file(file, train_file, test_file):    ltest = []    ltrain = []    fd = open(file, "r")    train_fd = open(train_file, "w")    test_fd = open(test_file, "w")    test_index = 0    train_index = 0    for line in fd:        rnum = randint(1, 10)        if rnum == 5 or rnum == 6:            test_index += 1            ltest.extend(line)            if test_index == 100:                test_fd.writelines(ltest)                ltest = []                test_index = 0        else:            train_index += 1            ltrain.extend(line)            if train_index == 100:                train_fd.writelines(ltrain)                ltrain = []                train_index = 0    if len(ltest) > 0:        test_fd.writelines(ltest)    if len(ltrain) > 0:        train_fd.writelines(ltrain)    train_fd.close()    test_fd.close()    fd.close()
Classify.py    运行文件
#!/usr/bin/python%#  main execute file#  lming_08 2014.07.06import sysfrom sys import argvfrom NaiveBayes import NaiveBayesfrom PartitionFile import partition_filedef main(srcFile, trainFile, testFile):    partition_file(srcFile, trainFile, testFile)    nb = NaiveBayes(trainFile)    nb.train_model_with_Bernoulli()    nb.set_testfile(testFile)    nb.predict()    print("the value of AUC: %f" % nb.getAUC())    print("the value of priori probability: %f" % nb.prior_prob)    true_classify_count, false_classify_count = nb.get_statistics()    error_rate = false_classify_count/(true_classify_count + false_classify_count)    print("the error rate : %f" % error_rate)	if __name__ == "__main__":    if len(argv) != 4:        print "Usage: python %s srcFile(in) trainFile(out) testFile(out)" % __file__        sys.exit(0)    main(argv[1], argv[2], argv[3])
运行结果为:
能够看到AUC值还是比較高的。分类错误率为9.87%,还是能够接受的。
发火最后再吐槽下。上文中指出的以讹传讹的算法居然在《机器学习实战》这本书中出现了,而网上好多同学也学习过书中的代码。但是并没有人怀疑并指出这个。

參考文献

你可能感兴趣的文章
客户端在使用citrix应用如何开启本地输入法
查看>>
C# 一个字符串是否在另外一个字符串数组里 Array.Exists 的用法 Array.IndexOf 用法...
查看>>
delphi实现计算器
查看>>
CentOS7 网卡命名
查看>>
Django进阶之缓存和信号
查看>>
DataGridView 设定单元格只读:
查看>>
缺陷跟踪工具jira和团队协作与项目管理工具conflunce
查看>>
shell特性及变量设置
查看>>
RHEL6入门系列之十五,管理用户和组
查看>>
特斯拉悄悄搞出无人车AI芯片,已经投产测试,而且没带英伟达
查看>>
LVS、Nginx和HAProxy负载均衡器对比总结
查看>>
Samsung_tiny4412(驱动笔记01)----linux 3.5,U-Boot,Busybox,SD卡启动环境搭建
查看>>
爬虫攻略(一)
查看>>
正则表达式语法
查看>>
零元学Expression Blend 4 - Chapter 45 ListBox里的物件不能换行吗?
查看>>
Elasticsearch上手——几个基本概念
查看>>
WebView.简单使用_资料
查看>>
CAN协议栈总体架构
查看>>
python下正则表达式的随笔记录
查看>>
Wp8程序加载运行顺序(菜鸟篇)
查看>>