前言

    上一篇《机器学习算法实践:决策树 (Decision Tree)》总结了决策树的实现,本文中我将一步步实现一个朴素贝叶斯分类器,并采用SMS垃圾短信语料库中的数据进行模型训练,对垃圾短信进行过滤,在最后对分类的错误率进行了计算。

    与决策树分类和k近邻分类算法不同,贝叶斯分类主要借助概率论的知识来通过比较提供的数据属于每个类型的条件概率, 将他们分别计算出来然后预测具有最大条件概率的那个类别是最后的类别。当然样本越多我们统计的不同类

    型的特征值分布就越准确,使用此分布进行预测则会更加准确。

  

  贝叶斯准则

    朴素贝叶斯分类器中最核心的便是贝叶斯准则,他用如下的公式表示:

  

    此公式表示两个互换的条件概率之间的关系,他们通过联合概率关联起来,这样使得我们知道p(B|A) 的情况下去计算p(A|B) 成为了可能,而我们的贝叶斯模型便是通过贝叶斯准则去计算某个样本在不同类别条件下

  的条件概率并取具有最大条件概率的那个类型作为分类的预测结果。

    使用条件概率来进行分类

    这里我通俗的介绍下如何通过条件概率来进行分类,假设我们看到了一个人的背影,想通过他背影的一些特征(数据)来判断这个人的性别(类别),假设其中涉及到的特征有: 是否是长发, 身高是否在170以上,腿细,

  是否穿裙子。当我们看到一个背影便会得到一个特征向量用来描述上述特征(1表示是,0表示否): ω=[0,1,1,0]

    贝叶斯分类便是比较如下两个条件概率:

    1、p(男生|ω) ,ω 等于 [0,1,1,0 的条件下此人是男生的概率

    2、p(女生|ω)) ,ω 等于 [0,1,1,0] 的条件下此人是女生的概率

    若p(男生|ω)>p(女生|ω) , 则判定此人为男生, 否则为女生

    那么p(男生|ω) 怎么求呢? 这就要上贝叶斯准则了

    根据贝叶斯准则,

  

    写成好理解些的便是:

  

    如果特征之间都是相互独立的(条件独立性假设),那么便可以将上述条件概率改写成:

    

    这样我们就能计算当前这个背影属于男生和属于女生的条件概率了。

    实现自己的贝叶斯分类器

    贝叶斯分类器实现起来非常的简单, 下面我以进行文本分类为目的使用Python实现一个朴素贝叶斯文本分类器.

    为了计算条件概率,我们需要计算各个特征的在不同类别下的条件概率以及类型的边际概率,这就需要我们通过大量的训练数据进行统计获取近似值了,这也就是我们训练我们朴素贝叶斯模型的过程.

    针对不同的文本,我们可以将所有出现的单词作为数据特征向量,统计每个文本中出现词条的数目(或者是否出现某个词条)作为数据向量。这样一个文本就可以处理成一个整数列表,并且长度是所有词条的数目,

  这个向量也许会很长,用于本文的数据集中的短信词条大概一共3000多个单词。

def get_doc_vector(words, vocabulary):

    ''' 根据词汇表将文档中的词条转换成文档向量

    :param words: 文档中的词条列表

    :type words: list of str

    :param vocabulary: 总的词汇列表

    :type vocabulary: list of str

    :return doc_vect: 用于贝叶斯分析的文档向量

    :type doc_vect: list of int

    '''

    doc_vect = [0]*len(vocabulary)

    for word in words:

        if word in vocabulary:

            idx = vocabulary.index(word)

            doc_vect[idx] = 1

    return doc_vect

    统计训练的过程的代码实现如下:

def train(self, dataset, classes):

    ''' 训练朴素贝叶斯模型

    :param dataset: 所有的文档数据向量

    :type dataset: MxN matrix containing all doc vectors.

    :param classes: 所有文档的类型

    :type classes: 1xN list

    :return cond_probs: 训练得到的条件概率矩阵

    :type cond_probs: dict

    :return cls_probs: 各种类型的概率

    :type cls_probs: dict

    '''

    # 按照不同类型记性分类

    sub_datasets = defaultdict(lambda: [])

    cls_cnt = defaultdict(lambda: 0)

    for doc_vect, cls in zip(dataset, classes):

        sub_datasets[cls].append(doc_vect)

        cls_cnt[cls] += 1

    # 计算类型概率

    cls_probs = {k: v/len(classes) for k, v in cls_cnt.items()}

    # 计算不同类型的条件概率

    cond_probs = {}

    dataset = np.array(dataset)

    for cls, sub_dataset in sub_datasets.items():

        sub_dataset = np.array(sub_dataset)

        # Improve the classifier.

        cond_prob_vect = np.log((np.sum(sub_dataset, axis=0) + 1)/(np.sum(dataset) + 2))

        cond_probs[cls] = cond_prob_vect

    return cond_probs, cls_probs

    注意这里对于基本的条件概率直接相乘有两处改进:

    1、各个特征的概率初始值为1,分母上统计的某一类型的样本总数的初始值是1,这是为了避免如果有一个特征统计的概率为0,则联合概率也为零那自然没有什么意义了, 如果训练样本足够大时,并不会对比较结果产生影响.

    2、由于各个独立特征的概率都是小于1的数,累积起来必然会是个更小的书,这会遇到浮点数下溢的问题,因此在这里我们对所有的概率都取了对数处理,这样在保证不会有损失的情况下避免了下溢的问题。

    获取了统计概率信息后,我们便可以通过贝叶斯准则预测我们数据的类型了,这里我并没有直接计算每种情况的概率,而是通过统计得到的向量与数据向量进行内积获取条件概率的相对值并进行相对比较做出决策的。

def classify(self, doc_vect, cond_probs, cls_probs):

    ''' 使用朴素贝叶斯将doc_vect进行分类.

    '''

    pred_probs = {}

    for cls, cls_prob in cls_probs.items():

        cond_prob_vect = cond_probs[cls]

        pred_probs[cls] = np.sum(cond_prob_vect*doc_vect) + np.log(cls_prob)

    return max(pred_probs, key=pred_probs.get)

    进行短信分类

    已经构建好了朴素贝叶斯模型,我们就可以使用此模型来统计数据并用来预测了。这里我使用了SMS垃圾短信语料库中的垃圾短信数据, 并随机抽取90%的数据作为训练数据,剩下10%的数据作为测试数据来测试我们的贝叶斯模型预测的准确性。

    当然在建立模型前我们需要将数据处理成模型能够处理的格式:

ENCODING = 'ISO-8859-1'

TRAIN_PERCENTAGE = 0.9

def get_doc_vector(words, vocabulary):

    ''' 根据词汇表将文档中的词条转换成文档向量

    :param words: 文档中的词条列表

    :type words: list of str

    :param vocabulary: 总的词汇列表

    :type vocabulary: list of str

    :return doc_vect: 用于贝叶斯分析的文档向量

    :type doc_vect: list of int

    '''

    doc_vect = [0]*len(vocabulary)

    for word in words:

        if word in vocabulary:

            idx = vocabulary.index(word)

            doc_vect[idx] = 1

    return doc_vect

def parse_line(line):

    ''' 解析数据集中的每一行返回词条向量和短信类型.

    '''

    cls = line.split(',')[-1].strip()

    content = ','.join(line.split(',')[: -1])

    word_vect = [word.lower() for word in re.split(r'\W+', content) if word]

    return word_vect, cls

def parse_file(filename):

    ''' 解析文件中的数据

    '''

    vocabulary, word_vects, classes = [], [], []

    with open(filename, 'r', encoding=ENCODING) as f:

        for line in f:

            if line:

                word_vect, cls = parse_line(line)

                vocabulary.extend(word_vect)

                word_vects.append(word_vect)

                classes.append(cls)

    vocabulary = list(set(vocabulary))

    return vocabulary, word_vects, classes

    有了上面三个函数我们就可以直接将我们的文本转换成模型需要的数据向量,之后我们就可以划分数据集并将训练数据集给贝叶斯模型进行统计。

# 训练数据 & 测试数据

ntest = int(len(classes)*(1-TRAIN_PERCENTAGE))

test_word_vects = []

test_classes = []

for i in range(ntest):

    idx = random.randint(0, len(word_vects)-1)

    test_word_vects.append(word_vects.pop(idx))

    test_classes.append(classes.pop(idx))

train_word_vects = word_vects

train_classes = classes

train_dataset = [get_doc_vector(words, vocabulary) for words in train_word_vects]

    训练模型:

cond_probs, cls_probs = clf.train(train_dataset, train_classes)

    剩下我们用测试数据来测试我们贝叶斯模型的预测准确度:

# 测试模型

error = 0

for test_word_vect, test_cls in zip(test_word_vects, test_classes):

    test_data = get_doc_vector(test_word_vect, vocabulary)

    pred_cls = clf.classify(test_data, cond_probs, cls_probs)

    if test_cls != pred_cls:

        print('Predict: {} -- Actual: {}'.format(pred_cls, test_cls))

        error += 1

print('Error Rate: {}'.format(error/len(test_classes)))

    随机测了四组,错误率分别为:0, 0.037, 0.015, 0. 平均错误率为1.3%

    测完了我们尝试下看看不同类型短信各个词条的概率分布是怎样的吧:

# 绘制不同类型的概率分布曲线

fig = plt.figure()

ax = fig.add_subplot(111)

for cls, probs in cond_probs.items():

    ax.scatter(np.arange(0, len(probs)),

               probs*cls_probs[cls],

               label=cls,

               alpha=0.3)

    ax.legend()

plt.show()

  

    试试决策树

    上一篇我们基于ID3算法实现了决策树,同样是分类问题,我们同样可以使用我们的文本数据来构建用于分类短信的决策树,当然唯一比较麻烦的地方在于如果按照与贝叶斯相同的向量作为数据,则属性可能会非常多,

  我们在构建决策树的时候每层树结构都是递归通过遍历属性根据信息增益来选取最佳属性进行树分裂的,这样很多的属性可能会对构建决策树这一过程来说会比较耗时.那我们就试试吧…

# 生成决策树

if not os.path.exists('sms_tree.pkl'):

    clf.create_tree(train_dataset, train_classes, vocabulary)

    clf.dump_tree('sms_tree.pkl')

else:

    clf.load_tree('sms_tree.pkl')

# 测试模型

error = 0

for test_word_vect, test_cls in zip(test_word_vects, test_classes):

    test_data = get_doc_vector(test_word_vect, vocabulary)

    pred_cls = clf.classify(test_data, feat_names=vocabulary)

    if test_cls != pred_cls:

        print('Predict: {} -- Actual: {}'.format(pred_cls, test_cls))

        error += 1

print('Error Rate: {}'.format(error/len(test_classes)))

    随机测了两次,错误率分别为:0.09, 0.0

    效果还算不错

    我们还是用Graphviz可视化看一下决策树都选取了那些词条作为判别标准(这时候决策树的好处就体现出来了)。

    可见决策树的深度并不是很深,如果分类类型一多,估计深度增加上去决策树可能会有些麻烦。

  总结

    本文我们使用Python一步步实现了朴素贝叶斯分类器,并对短信进行了垃圾短信过滤,同样的数据我们同决策树的分类效果进行了简单的比较。本文相关代码实现:https://github.com/PytLab/MLBox/tree/master/naive_bayes 。

  决策树过滤垃圾短信的脚本在https://github.com/PytLab/MLBox/tree/master/decision_tree

  参考

  • 《Machine Learning in Action》

  • 实例详解贝叶斯推理的原理

  • 大道至简:朴素贝叶斯分类器

机器学习算法实践:朴素贝叶斯 (Naive Bayes)(转载)的更多相关文章

  1. 朴素贝叶斯 Naive Bayes

    2017-12-15 19:08:50 朴素贝叶斯分类器是一种典型的监督学习的算法,其英文是Naive Bayes.所谓Naive,就是天真的意思,当然这里翻译为朴素显得更学术化. 其核心思想就是利用 ...

  2. Python实现机器学习算法:朴素贝叶斯算法

    ''' 数据集:Mnist 训练集数量:60000 测试集数量:10000 ''' import numpy as np import time def loadData(fileName): ''' ...

  3. Python机器学习笔记:朴素贝叶斯算法

    朴素贝叶斯是经典的机器学习算法之一,也是为数不多的基于概率论的分类算法.对于大多数的分类算法,在所有的机器学习分类算法中,朴素贝叶斯和其他绝大多数的分类算法都不同.比如决策树,KNN,逻辑回归,支持向 ...

  4. 朴素贝叶斯(Naive Bayesian)

    简介 Naive Bayesian算法 也叫朴素贝叶斯算法(或者称为傻瓜式贝叶斯分类) 朴素(傻瓜):特征条件独立假设 贝叶斯:基于贝叶斯定理 这个算法确实十分朴素(傻瓜),属于监督学习,它是一个常用 ...

  5. 腾讯公司数据分析岗位的hadoop工作 线性回归 k-means算法 朴素贝叶斯算法 SpringMVC组件 某公司的广告投放系统 KNN算法 社交网络模型 SpringMVC注解方式

    腾讯公司数据分析岗位的hadoop工作 线性回归 k-means算法 朴素贝叶斯算法 SpringMVC组件 某公司的广告投放系统 KNN算法 社交网络模型 SpringMVC注解方式 某移动公司实时 ...

  6. 【分类算法】朴素贝叶斯(Naive Bayes)

    0 - 算法 给定如下数据集 $$T=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\},$$ 假设$X$有$J$维特征,且各维特征是独立分布的,$Y$有$K$种取值.则 ...

  7. 数据挖掘十大经典算法(9) 朴素贝叶斯分类器 Naive Bayes

    贝叶斯分类器 贝叶斯分类器的分类原理是通过某对象的先验概率,利用贝叶斯公式计算出其后验概率,即该对象属于某一类的概率,选择具有最大后验概率的类作为该对象所属的类.眼下研究较多的贝叶斯分类器主要有四种, ...

  8. 十大经典数据挖掘算法(9) 朴素贝叶斯分类器 Naive Bayes

    贝叶斯分类器 贝叶斯分类分类原则是一个对象的通过先验概率.贝叶斯后验概率公式后计算,也就是说,该对象属于一类的概率.选择具有最大后验概率的类作为对象的类属.现在更多的研究贝叶斯分类器,有四个,每间:N ...

  9. 04机器学习实战之朴素贝叶斯scikit-learn实现

    In [8]: import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl from sklearn.pre ...

随机推荐

  1. POJ 3907 Build Your Home | 计算多边形面积

    给个多边形 计算面积 输出要四舍五入 直接用向量叉乘就好 四舍五入可以+0.5向下取整 #include<cstdio> #include<algorithm> #includ ...

  2. [解决方案]IIS7.5 报错:无法启动计算机“."上的服务W3SVC

    报错场景: 在云服务器上,正常使用着,突然今天一打开网站就都用不了了,上去服务器一看,IIS中站点被停止了,我还怀疑是回收的问题,结果一直启动无果,我打算重启来解决这个问题,重启后发现所有站点都变成停 ...

  3. Tomcat学习笔记(十三)

    服务器组件和服务组件 服务器组件 org.apache.catalina.Server接口的实例表示Catalina的整个servlet引擎,包含了所有的组件.使用一种优雅的方法来启动/关闭整个系统, ...

  4. Heine-Borel定理

    前:开始学数学..来写写理解和补充吧.. 书:M.A.Armstrong著<Basic Topology> Heine-Borel定理:实轴上闭区间是紧集. 证法(1)延伸法: 思想 闭区 ...

  5. 在ESXi使用esxcli命令強制关闭VM

    最近學到一個在VMware ESXi 下面強制關閉一個沒有反應的VM的方法, 一般正常都是使用vSphere Client 去控制VM電源, 但是有時會發生即使用裡面的Power Off 按鈕但是還是 ...

  6. js得到时间戳(10位数)

    //从1970年开始的毫秒数然后截取10位变成 从1970年开始的秒数 function timest() { var tmp = Date.parse( new Date() ).toString( ...

  7. C++ MFC std::string转为 std::wstring

    std::string转为 std::wstring std::wstring UTF8_To_UTF16(const std::string& source) { unsigned long ...

  8. 关闭vs警告

    禁用所有编译器警告 当“解决方案资源管理器”中有项目选中时,在“项目”菜单上单击“属性”. 单击“编译”选项卡. 选中“禁用所有警告”复选框. 禁用单个编译器警告 在“解决方案资源管理器”中选定一个项 ...

  9. Fiddler抓包2-只抓APP的请求【转载】

    本篇转自博客:上海-悠悠 原文地址:http://www.cnblogs.com/yoyoketang/p/6582437.html 前言 fiddler抓手机app的请求,估计大部分都会,但是如何只 ...

  10. 【原创】SSAS-引用维度与多数据源、多数据源视图引发分区错误

    背景: 最近有个项目,有32家分公司,集团总部需要取这个32家分公司数据做分析,由于每个分公司的数据都比较庞大,所以最终方案是每个分公司一个DW,在cube搭建过程中将每个公司数据作为一个分区数据的来 ...