朴素贝叶斯模型
朴素贝叶斯的应用
朴素贝叶斯模型是文本领域永恒的经典,广泛应用在各类文本分析的任务上。只要遇到了文本分类问题,第一个需要想到的方法就是朴素贝叶斯,它在文本分类任务上是一个非常靠谱的基准(baseline)。
比如对于垃圾邮件的分类,朴素贝叶斯是一个极其有效且简单的模型。
不要小看一个简单的模型。实际上,我们真正需要的是既简单同时又有效的模型,因为最终的目的是用最小成本来解决问题。一个简单的模型既有利于短时间内训练,也有助于后续的维护和管理。
朴素贝叶斯,这个名字里的“朴素”源自于概率统计里的“条件独立”,因为在构造模型过程中做了一层基于条件独立的简化操作。 朴素贝叶斯模型作为分类算法,非常适合用在各类文本分类任务上,如:
- 垃圾邮件分类: 自动判断一个邮件是否为垃圾邮件或者正常邮件;
- 文本主题分类: 把一个文本按照主题做分类如体育类、娱乐类;
- 情感分析: 把给定的文本分类成正面或者负面情感;
朴素贝叶斯核心思想
垃圾邮件示例:
您提交的#3152号工单:来自李先生的留言 有更新。
请点击以下链接查看工单处理进度:
https://tingyun.kf5.com/hc/request/view/3152/
要添加另外的工单评论,请回复此邮件
启发: 有些单词经常出现在垃圾邮件里;
朴素贝叶斯的核心思想极其简单: 其实就是统计出不同文本类别中出现的单词的词频。
对于垃圾邮件的分类任务,我们需要统计出哪些单词经常出现在垃圾邮件,哪些单词经常出现在正常邮件就可以了。
如果在邮件里看到了“广告”,“购买”,“链接”等关键词,可以认为这个很可能是垃圾邮件,因为这些单词经常出现在垃圾邮件中,其实很多邮件过滤系统是这样过滤垃圾邮件的。当然,我们也可以自行设定一些规
则来过滤垃圾邮件。在这种规则里,我们通常也是指定哪些单词跟垃圾邮件相关。
对于垃圾邮件的分类, 直接弄一份关键词词库, 然后一旦邮件里包含了这些词就认为是垃圾邮件,
如果按照词库来判断一个邮件是垃圾邮件还是正常邮件,这有点类似于通常说的“一刀切”的方法。其实更好的方案是加入一些概率的要素,把不确定性也加进来,比如虽然出现了“广告”,“推销”等关键词,但同时也出现
了大量的没有包含在词库里的单词,最终按照概率的角度可以判定为是正常邮件,这实际上就是朴素贝叶斯模型的核心思想。
如果以词库为依据来直接判定邮件的种类是存在一定的问题的。它的问题在于,即便出现了一些广告类单词,但并不一定是垃圾邮件; 没有出现任何广告类单词也不一定是正常邮件。所以每个单词虽然有倾向性,但不能以偏概全。
如何把这些不同的单词以概率统计的方式整合在一起呢? 答案就是朴素贝叶斯!
利用朴素贝叶斯识别垃圾邮件

使用朴素贝叶斯模型一般需要两步骤:
首先,统计出每一个单词对一个邮件成为垃圾邮件或正常邮件的贡献。比如 p ( 广告 | 垃圾 ) 、p( 广告∣正常 )分别代表在 垃圾 / 正常邮件里出现 “广告”这个关键词的概率。
其次,用这些统计的结果对一个新的邮件做预测
朴素贝叶斯广泛地应用在文本分类任务中,其中最为经典的场景为垃圾文本分类(如垃圾邮件分类: 给定一个邮件,把它自动分类为垃圾或者正常邮件)。这个任务本身是属于文本分析任务,因为对应的数据均为文
本类型,所以对于此类任务我们首先需要把文本转换成向量的形式,然后再带入到模型当中。
import pandas as pd import numpy as np import matplotlib.mlab as mlab import matplotlib.pyplot as plt # 读取spam.csv文件 df = pd.read_csv("/home/anaconda/data/Z_NLP/spam.csv", encoding='latin') df.head() # 重命名数据中的v1和v2列,使得拥有更好的可读性 df.rename(columns={'v1':'Label', 'v2':'Text'}, inplace=True) df.head() # 把'ham'和'spam'标签重新命名为数字0和1 df['numLabel'] = df['Label'].map({'ham':0, 'spam':1}) df.head() # 统计有多少个ham,有多少个spam print ("# of ham : ", len(df[df.numLabel == 0]), " # of spam: ", len(df[df.numLabel == 1])) print ("# of total samples: ", len(df)) # 统计文本的长度信息,并画出一个histogram text_lengths = [len(df.loc[i,'Text']) for i in range(len(df))] plt.hist(text_lengths, 100, facecolor='blue', alpha=0.5) plt.xlim([0,200]) plt.show() # 导入英文的停用词库 from sklearn.feature_extraction.text import CountVectorizer # 构建文本的向量 (基于词频的表示) vectorizer = CountVectorizer() X = vectorizer.fit_transform(df.Text) y = df.numLabel # 把数据分成训练数据和测试数据 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=100) print ("训练数据中的样本个数: ", X_train.shape[0], "测试数据中的样本个数: ", X_test.shape[0]) # 利用朴素贝叶斯做训练 from sklearn.naive_bayes import MultinomialNB from sklearn.metrics import accuracy_score clf = MultinomialNB(alpha=1.0, fit_prior=True) clf.fit(X_train, y_train) y_pred = clf.predict(X_test) print("accuracy on test data: ", accuracy_score(y_test, y_pred)) # 打印混淆矩阵 from sklearn.metrics import confusion_matrix confusion_matrix(y_test, y_pred, labels=[0, 1]) ======>>> # of ham : 4825 # of spam: 747 # of total samples: 5572 <Figure size 640x480 with 1 Axes> 训练数据中的样本个数: 4457 测试数据中的样本个数: 1115 accuracy on test data: 0.97847533632287 array([[956, 14], [ 10, 135]])

朴素贝叶斯模型的训练
朴素贝叶斯的训练过程,也是词频统计的过程。
计算单词的概率
正如之前说过的一样,朴素贝叶斯训练的核心步骤就是统计各个单词在不同类别中的概率。 举个例子,当我们去分析一个文件是否为垃圾邮件或者正常邮件时,一个判断的依据是如果很多跟垃圾邮件相关的关键
词出现在了文本里,我们即可以把它归类为垃圾邮件,这些决策最终基于概率统计的方式来实现。
总体来讲,朴素贝叶斯分为两个阶段:
- 计算每个单词在不同分类中所出现的概率,这个概率是基于语料库(训练数据)来获得的。
- 利用已经计算好的概率,再结合贝叶斯定理就可以算出对于一个新的文本,它属于某一个类别的概率值,并通过这个结果做最后的分类决策。






利用计算好的概率来预测
目前为止我们已经算好了每一个单词在不同类别中的概率,以及垃圾邮件和正常邮件在整个数据中的占比,后者也称之为先验概率(prior)。统计完这些概率之后,如何使用它们来预测一个邮件是否为垃圾或者正常邮件呢?

Bayes_Theorem
为了表示两个条件概率值 p(垃圾|邮件内容)和 p(正常|邮件内容),需要用到一个著名的概率公式-贝叶斯定理。利用贝叶斯定理可以把上述条件概率做进一步的分解,最终可以计算出它们的值

计算预测概率
在预测过程中我们使用一个叫作条件独立(conditional independence)的假设。简单来讲,基于这个性质可以把条件概率p(x,y|z)p(x,y∣z)写成p(x,y|z)=p(x|z)p(y|z)p(x,y∣z)=p(x∣z)p(y∣z)的形式,
这时候可以说变量xx和变量yy是条件独立于变量zz的。
这也是为什么把朴素贝叶斯说成“朴素”的主要原因,因为做了一层计算上的简化。如果不使用条件独立的假设,我们是不能把概率p(x,y|z)p(x,y∣z)写成上述形式的,这样一来问题就变得格外地复杂。
条件独立的性质可以延展到更多的变量,如p(x_1,x_2,x_3|y)=p(x_1|y)p(x_2|y)p(x_3|y)p(x1,x2,x3∣y)=p(x1∣y)p(x2∣y)p(x3∣y)。当我们把这里的每个变量x_ixi看作是每一个单词的时候,就得到了朴素贝叶斯模型。

平滑操作
另外,在上述过程中可以看到分子的计算过程涉及到了很多概率的乘积,一旦遇到这种情形,就要知道可能会有潜在的风险。比如其中一个概率值等于0,那不管其他概率值是多少,最后的结果一定为0,有点类
似于“功亏一篑“的情况,明明出现了很多垃圾邮件相关的单词,就是因为其中的一个概率0,最后判定为属于垃圾邮件的概率为0,这显然是不合理的。为了处理这种情况,有一个关键性操作叫作平滑
(smoothing),其中最为常见的平滑方法为加一平滑(add-one smoothing)。


朴素贝叶斯的最大似然估计
从最大似然估计的角度来理解朴素贝叶斯模型。换句话说,首先根据最大似然估计来构造朴素贝叶斯的目标函数,接下来再通过优化手段来求解朴素贝叶斯的最优解。
朴素贝叶斯模型以及如何估算每个单词的概率,并用这个概率来预测一个文本的分类。
但有没有思考过: 为什么要计算这些单词的概率? 它的依据是什么? 这个问题看似有点愚蠢,但实际上需要认真地思考才能发现其背后的奥妙之处。
计算语料库中每个单词的条件概率,这个过程实际上是模型训练的过程。另外,模型的训练过程其实就是寻找最优解的过程。
从另外一个更严谨的角度来学习朴素贝叶斯。首先,构造朴素贝叶斯的目标函数,之后再试着去寻找朴素贝叶斯的最优解。最终会发现,以这种方式得出来的最优解恰恰就是计算出的结果;
为了推导出朴素贝叶斯的目标函数,仍需要从最大似然开始入手,并通过最大似然估计来获得朴素贝叶斯的目标函数。


投硬币问题




有了朴素贝叶斯的目标函数之后,剩下的环节无非就是寻找最优解的过程了。从上述目标函数中可以发现它具有一个限制条件。那这样的目标函数我们应该如何去优化呢?
这种优化问题也称之为带限制条件的优化(constrained optimization)。
带限制条件的优化
带限制条件的优化相比无限制条件的优化稍微复杂一些。对于无限制条件的优化问题,在逻辑回归中已经涉及到了。 回顾无限制条件的优化,同时重点来学习带限制条件的优化问题如何解决。


朴素贝叶斯目标函数的优化
当理解了如何求解带限制条件的优化问题之后,就可以试着来求解朴素贝叶斯目标函数的最优解了。 整个过程有一些繁琐,但用到的基本知识都是上面提过的内容,只要细心去体会,应该都能看得懂。


通过这种方式得出来的结果恰好跟上节的结果是吻合的,所以从这个例子可以看出,任何一个计算结果实际上都是有背后的依据的。为了更好地理解每一个知识点,我们需要尝试着去深挖底层的细节,这样才能
达到打通知识体系的目的。
生成模型与判别模型
两种模型直观上的区别
生成模型和判别模型在机器学习领域是一个非常重要的概念。任意的机器学习模型我们均可以归类为判别模型或者生成模型。那具体什么是生成模型,什么是判别模型呢?
从字面上来看, 生成模型模型可以生成数据;(生成模型的一大功能是,训练好之后可以用来生成新的数据。)
让机器学习写程序?让机器学习学习画画?让机器编一个曲子?这些事情生成模型都可以做,当然效果好不好是另一回事情了。
训练一个生成模型通常不仅可以用来完成识别或者分类任务,也可以用来生成一些新的数据,包括图片、文章、代码、视频、音频等等。 虽然生成这些数据本身具有很大挑战,但至少从理论的角度来讲是一条可
行的道路。
从字面上来理解, 判别模型主要用来判别样本的类别;(不同于生成模型,判别模型是没有办法用来生成新数据的。虽然生成即可以用来解决判别问题,也可以生成数据,但这不代表生成模型由于判别模型。实际上,
很多判别任务(如分类任务)上, 判别模型的表现要好于生成模型。)
判别模型的初衷是用来解决判别问题,而且只做一件事情(不像生成模型即可以解决分类问题也可以解决生成数据的问题),所以在分类问题上它的效果通常要优于生成模型的。接下来试着从另外一个角度来理解
它俩之间的区别。


从数据的角度理解它俩的区别

逻辑回归的最大似然是基于条件概率来构造的,但朴素贝叶斯的最大似然是基于x和y的联合概率来构造的;所以逻辑回归是判别模型,朴素贝叶斯是生成模型;

