机器学习之贝叶斯(五)
1 朴素贝叶斯概述
朴素贝叶斯是一种简单但是非常强大的线性分类器。它在垃圾邮件分类,疾病诊断中都取得了很大的成功。它只所以称为朴素,是因为它假设特征之间是相互独立的,但是在现实生活中,这种假设基本上是不成立的。那么即使是在假设不成立的条件下,它依然表现的很好,尤其是在小规模样本的情况下。但是,如果每个特征之间有很强的关联性和非线性的分类问题会导致朴素贝叶斯模型有很差的分类效果。
朴素贝叶斯的思想基础是这样的:对于给出的待分类样本特征x,求解在此样本出现的条件下各个类别出现的概率,哪个最大,就认为此待分类样本属于哪个类别。
优点:
- 朴素贝叶斯模型发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率;
- 对小规模的数据表现很好;
- 能处理多分类任务,适合增量式训练;
- 对缺失数据不太敏感,算法也比较简单,常用于文本分类
缺点:
- 只能用于分类问题
- 需要计算先验概率;
- 分类决策存在错误率;
- 对输入数据的表达形式很敏感
2 朴素贝叶斯的数学原理
了解贝叶斯法则是很必要的。它可以被简单地描述成下面的公式:
一般公式的表达是:
举个例子
某男生统计与心爱的女生在一个自习室自习的概率,
这里n=4,x(1)表示主课,x(2)表示天气,x(3)表示星期几,x(4)表示气氛,Y仍然是{去,不去},现在主课有8门,天气有晴、雨、阴三种、气氛有A+,A,B+,B,C五种,那么总共需要估计的参数有8×3×7×5×2=1680个,每天只能收集到一条数据,那么等凑齐1680条数据,大学都毕业了,男生大呼不妙
于是做了一个独立性假设,假设这些影响她去自习室的原因是独立互不相关的,
有了这个独立假设后,需要估计的参数就变为,(8+3+7+5)×2 = 46个了,而且每天收集的一条数据,可以提供4个参数,这样该男生就预测越来越准了,例如
条件概率
朴素贝叶斯模型中,特征之间不仅仅是独立的,而且是加条件的独立的。比如说:我的特征向量x中,有n个特征x中,有n个特征,那么我可以把概率写成下面的形式:
P(x|ωj)的概率我们可以理解成:在给定属于某个类别的条件下,观察到出现现象x的概率。在特征向量中的每个特点的概率我们都可以通过极大似然估计(maximum-likelihood estimate)来求得,也就是简单地求某个特征在某个类别中的频率,公式如下:
- Nxi|ωj:在所有属于类别ωj的训练样本中,特征xi出现的次数Nxi|ωj:在所有属于类别ωj的训练样本中,特征xi出现的次数
- Nωj:在所有属于类别ωj的训练样本中,所有特征出现的次数
先验概率
- Nωj:属于类ωj的样本数Nωj:属于类ωj的样本数
- Nc:所有的样本数
通过上面后验概率的公式,我们可知:如果先验概率服从均匀分布,那么后验概率将完全取决于条件概率和现象概率,然而现象概率是常量,所以后验概率就完全取决于条件概率了。
3 朴素贝叶斯模型
贝叶斯模型 >>>--------->>> 朴素贝叶斯模型 假设条件成立
垃圾邮件过滤实例:
P(d1|h+) * P(d2|d1, h+) * P(d3|d2,d1, h+) * ..
假设 di 与 di-1 是完全条件无关的(朴素贝叶斯假设特征之间是独立,互不影响)
简化为 P(d1|h+) * P(d2|h+) * P(d3|h+) * ..
对于P(d1|h+) * P(d2|h+) * P(d3|h+) * ..只要统计 di 这个单词在垃圾邮件中出现的频率即可
-
高斯分布朴素贝叶斯
高斯分布就是正态分布 【用途】用于一般分类问题 适合特征值为连续型变量
高斯模型假设某一特征属于某一类别的观测值符合高斯分布,比如身高小于160,160~170等
from sklearn import datasets iris = datasets.load_iris() X = iris.data y = iris.target from sklearn.naive_bayes import GaussianNB gnb = GaussianNB() gnb.fit(X,y).score(X,y) #输出 0.96
(1)使用鸢尾花自带的数据
import numpy as np import pandas as pd from pandas import Series,DataFrame import matplotlib.pyplot as plt %matplotlib inline
(2)加载数据
from sklearn.datasets import load_iris iris = load_iris() train = iris.data[:,:2] #取data数据前两列 target = iris.target plt.scatter(train[:,0],train[:,1],c=target)
(3)网格化转二维数组
x = np.linspace(train[:,0].min(),train[:,0].max(),300) #一维300个 y = np.linspace(train[:,1].min(),train[:,1].max(),300) #一维300个 #将一维数据x y网格化为二维数组 300x300 xx,yy = np.meshgrid(x,y) # ravel扁平化90000 # np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等,类似于pandas中的merge() X_test = np.c_[xx.ravel(),yy.ravel()] #输出 X_test array([[4.3 , 2. ], [4.31204013, 2. ], [4.32408027, 2. ], ..., [7.87591973, 4.4 ], [7.88795987, 4.4 ], [7.9 , 4.4 ]])
(4)采用高斯朴素贝叶斯
gaussion = GaussianNB() gaussion.fit(train,target) y_ = gaussion.predict(X_test) #取出一维 二维数据 绘制图形 plt.scatter(X_test[:,0],X_test[:,1],c=y_) #测试点的颜色设置 cmap = ListedColormap(['r','g','b']) plt.scatter(train[:,0],train[:,1],c=target,cmap=cmap)
-
多项式分布朴素贝叶斯
适用于文本数据 特征表示的是次数,例如某个词语的出现次数
多项式分布:
from sklearn.naive_bayes import MultinomialNB mnb = MultinomialNB() mnb.fit(X,y).score(X,y) # 0.9533333333333334
采用多项式分布
from sklearn.naive_bayes import MultinomialNB multinomial = MultinomialNB() multinomial.fit(train,target) y_ = multinomial.predict(X_test) plt.scatter(X_test[:,0],X_test[:,1],c=y_) cmap = ListedColormap(['r','g','b']) plt.scatter(train[:,0],train[:,1],c=target,cmap=cmap)
-
伯努利分布朴素贝叶斯
特征值取bool类型,文本分类中表示一个值(单词)有没有出现过
适用于伯努利分布,也用于文本数据(此时特征表示是否出现,例如某个词语的出现为1,不出现为0)
绝大多数情况下表现不如多项式分布,但有的时候伯努利分布表现得要比多项式分布要好,尤其是对于小数量级的文本数据from sklearn.naive_bayes import BernoulliNB bnb = BernoulliNB() bnb.fit(X,y).score(X,y) # 0.3333333333333333 得分要比多项式分布朴素贝叶斯差
案例:
文本分类,对短信进行二分类--->使用多项式分布朴素贝叶斯
import pandas as pd
from sklearn.naive_bayes import MultinomialNB
#转换数据,String串分析起来不方便,所以需要将字符串转进行转换
from sklearn.feature_extraction.text import TfidfVectorizer
data = pd.read_table('../data/SMSSpamCollection',header = None)
#处理数据
tf = TfidfVectorizer()
display(data[1].shape,type(data[1]))
X = tf.fit_transform(data[1])
y = data[0]
mnb = MultinomialNB()
#训练数据
mnb.fit(X,y)
# 使用tf.transform把文本变成能处理的数字,生成一个测试数据
X_test = tf.transform(['07732584351 - Rodger Burns - MSG = We tried to call you re your reply to our sms for a free nokia mobile + free camcorder. Please call now 08000930705 for delivery tomorrow'])
#预测数据
mnb.predict(X_test)
#输出结果显示这是一条垃圾短信
贝叶斯编写单词拼写器
需求:
argmaxc P(c|w) -> argmaxc P(w|c) P(c) / P(w)
- P(c), 文章中出现一个正确拼写词 c 的概率, 也就是说, 在英语文章中, c 出现的概率有多大
- P(w|c), 在用户想键入 c 的情况下敲成 w 的概率. 因为这个是代表用户会以多大的概率把 c 敲错成 w
- argmaxc, 用来枚举所有可能的 c 并且选取概率最大的
#贝叶斯构造单词拼写器
import re,collections
#将text内容转成小写,并且用a-z匹配去掉特殊字符
def words(text):
return re.findall('[a-z]+',text.lower())
#当出现新词时,设置其先验概率默认为1,因为为0的话则概率值都为0
def train(features):
model = collections.defaultdict(lambda:1)
for f in features:
model[f] += 1
return model
NWORDS = train(words(open('../Desktop/skilearn/big.txt').read()))
print(NWORDS)
alphabet = 'abcdefghijklmnopqrstuvwxyz'
'''
编辑距离:
两个词之间的编辑距离定义为使用了几次插入(在词中插入一个单字母), 删除(删除一个单字母), 交换(交换相邻两个字母), 替换(把一个字母换成另一个)的操作从一个词变到另一个词.
'''
#编辑距离为1 的集合
def edits1(word):
n = len(word)
return set([word[0:i]+word[i+1:] for i in range(n)]+ #删除
[word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)]+ #交换
[word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet]+ #替换
[word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet]) #插入
#编辑距离等于2的集合
def known_edits2(word):
return set(e2 for e1 in edits1(word) for e2 in edits1(e1) if e2 in NWORDS)
#
def known(words): return set(w for w in words if w in NWORDS)
#指定优先级的计算 :0-1-2
def correct(word):
candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word]
return max(candidates, key=lambda w: NWORDS[w])
correct('morw') #more
correct('goof') #good
correct('appl') #apple