《机器学习实战》第四章——朴素贝叶斯

被称之为“朴素”,是因为只进行最原始、最简单的假设–各个特征之间没有关联性

朴素贝叶斯两种实现方式

  1. 基于贝努利模型:只考虑词在文本中是否出现,而不考虑出现次数。因此需要假设词的出现是等权重的
  2. 基于多项式模型:考虑词在文本中出现次数
  • 优点:数据较少的情况下依旧有效;可以处理多类别问题;节约了内存消耗和计算时间
  • 缺点:数据输入方式较为敏感;较强的特征条件独立假设
  • 适用于:标称型数据
  • 重要应用于文本分类
贝叶斯决策理论

假设有统计参数,用 p1(x,y)表示数据点(x,y)属于类别1的概率, p2(x,y)表示数据点(x,y)属于类别2的概率。对于一个新的点(x’,y’),如果
p1(x’,y’) > p2(x’,y’) 那么该数据点属于类别1,反之属于类别2
贝叶斯决策理论的核心为:选择高概率对应的类别

条件概率

P ( A ∣ B ) = P ( A a n d B ) P ( B ) P(A|B) = \frac{P(A and B)}{P(B)} P(AB)=P(B)P(AandB)
P ( A ∣ B ) = P ( B ∣ A ) P ( A ) P ( B ) P(A|B) = \frac{P(B|A)P(A)}{P(B)} P(AB)=P(B)P(BA)P(A)
在实际中,B会是一个向量,公式变为
P ( c i ∣ w ⃗ ) = P ( w ⃗ ∣ c i ) P ( c i ) P ( w ⃗ ) P(c_{i}|\vec{w})= \frac{P(\vec{w}|c_{i})P(c_{i})}{P(\vec{w})} P(ciw )=P(w )P(w ci)P(ci)
P ( c i ∣ w ⃗ ) P(c_{i}|\vec{w}) P(ciw ) 表示为,在有 w ⃗ \vec{w} w 特征下,是某一类别的概率; P ( c i P(c_{i} P(ci : 每个类别的概率; P ( w ⃗ ∣ c i ) P(\vec{w}|c_{i}) P(w ci) : 给定类别下特征(被预测文档出现的词)的概率 ,计算方法: P ( w ⃗ ∣ c i ) = N i N P(\vec{w}|c_{i}) = \frac{N_{i}}{N} P(w ci)=NNi 其中 N i N_{i} Ni v e c w vec{w} vecw c i c_{i} ci 类别的文档中出现的次数 N 为所属类别 c i c_{i} ci下的所有文档中词出现的总数;

其中因为假设了特征独立,那么
P ( w ⃗ ∣ c i ) = P ( w 0 ∣ c i ) P ( w 1 ∣ c i ) P ( w 2 ∣ c i ) . . . P ( w N ∣ c i ) P(\vec{w}|c_{i}) = P(w_{0}|c_{i}) P(w_{1}|c_{i})P(w_{2}|c_{i})... P(w_{N}|c_{i}) P(w ci)=P(w0ci)P(w1ci)P(w2ci)...P(wNci)

计算例子

在这里插入图片描述

词表转换为向量
def createVocabList(dataSet):
    '''
    获取所有包含词的列表
    '''
    vocabSet = set([])   # 创建空集,用来存放所有的词
    for doument in dataSet:
        vocabSet = vocabSet | set(doument)
    return list(vocabSet)

def setOfWords2Vec(vocabList, inputSet):
    '''
    统计inputSet的词在vocabList文档是否出现
    '''
    returnVec = [0]*len(vocabList)  # 初始权为0.没有出现
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else :
            print("the word %s is not in my Vocabulary!" % word)
    return returnVec
朴素贝叶斯分类器
def trainNB0(trainMatrix, trainCategory):
    '''
    朴素贝叶斯分类器
    trainMatrix: 文档矩阵
    trainCategory: 文档类别标签
    '''
    numTrainDocs = len(trainMatrix)  # 样本数
    numWords = len(trainMatrix[0])  # 特征
    pAbusive = sum(trainCategory)/float(numTrainDocs)  # p(1)  # 这里是计算class= 1 的占比
#     p0Num = np.zeros(numWords)  # 放置所有class = 0 的样本中各个特征出现的次数
#     p1Num = np.zeros(numWords)   
#     p0Denom = 0.0               # class = 0 的样本中出现的词的总数量
#     p1Denom = 0.0 
#   修改后
    p0Num = np.ones(numWords)  # 放置所有class = 0 的样本中各个特征出现的次数
    p1Num = np.ones(numWords)   
    p0Denom = 2.0               # class = 0 的样本中出现的词的总数量
    p1Denom = 2.0 
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
            
    p1Vect = p1Num/p1Denom    # 是一个向量,每个元素为P(wi|c=1)
    p0Vect = p0Num/p0Denom    
        
    return p0Vect, p1Vect, pAbusive
    
修改分类器
  1. 在计算 P ( w ⃗ ∣ c i ) = P ( w 0 ∣ c i ) P ( w 1 ∣ c i ) P ( w 2 ∣ c i ) . . . P ( w N ∣ c i ) P(\vec{w}|c_{i}) = P(w_{0}|c_{i}) P(w_{1}|c_{i})P(w_{2}|c_{i})... P(w_{N}|c_{i}) P(w ci)=P(w0ci)P(w1ci)P(w2ci)...P(wNci)时可能会出现 P ( w 2 ∣ c i ) = 0 P(w_{2}|c_{i})=0 P(w2ci)=0 的情况,导致最终的计算结果也为零。为避免此情况:增加拉普拉斯平滑系数
    P ( w ⃗ ∣ c i ) = N i + α N + α × m P(\vec{w}|c_{i}) = \frac{N_{i} + \alpha }{N + \alpha × m} P(w ci)=N+α×mNi+α
    其中 α \alpha α 为指定的系数一般为1,m为训练文档中特征词个数。在代码中可以修改所有词的出现数初始化为1,分母初始化为2
  2. 另一问题为下溢出,这是由于太多很小的数相乘造成的。在代数中有 ln ⁡ ( a ∗ b ) = ln ⁡ ( a ) ∗ ln ⁡ ( b ) \ln (a*b) = \ln (a) * \ln (b) ln(ab)=ln(a)ln(b) ,于是通过求对数可以避免下溢出或者浮点数四舍五入导致的错误。
def classifyNB(vec2Classfy, p0Vec, p1Vec, pClass):
    '''
    求概率,比较大小,决定分类
    利用对数相乘,避免下溢出
    '''
    p1Vec = np.log(p1Vec)
    p0Vec = np.log(p0Vec)
    p1 = sum(vec2Classfy * p1Vec) + np.log(pClass)
    p0 = sum(vec2Classfy * p0Vec) + np.log(pClass)
    if p0 > p1:
        return 0
    else :
        return 1
准备数据、测试
def loadDataSet():
    '''
    准备数据
    '''
    postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
                 ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                 ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                 ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                 ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                 ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    classVec = [0,1,0,1,0,1]    #1代表侮辱性文字,0代表正常言论
    return postingList, classVec

def testingNB():
    listOPosts, listClasses = loadDataSet()   # 加载文档数据
    myVocabList = createVocabList(listOPosts)   # 获取所有单词的列表
    trainMat = []
    for postinDoc in listOPosts:   
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))   # 文字提取,得文字出现样本[0,1,1,0,0]-0为没有出现,1为出现
    p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses))     # 分类器  传入参数都为array类型
    testEntry = ['love', 'my', 'dalmation']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))         
    print(testEntry, 'classified as : ', classifyNB(thisDoc, p0V, p1V, pAb))   # 计算概率
    testEntry = ['syupid', 'garbage']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as : ', classifyNB(thisDoc, p0V, p1V, pAb))
# 调用
testingNB()  

运行结果

['love', 'my', 'dalmation'] classified as :  0
the word syupid is not in my Vocabulary!
['syupid', 'garbage'] classified as :  1

上述模型为词集模型(set-of-words model),将每个词是否出现作为一个特征,这种方法对于一个词出现很多次的文档,特别是大段文章就不明显了;另一种方式叫做词袋模型(bag-of-words model),每个单词可以出现多次。
因此需要对setOfWords2Vec函数做修改

def bagOfWords2VecMN(vocabList, inputSet):
    '''
    统计inputSet的词在vocabList文档出现频数
    '''
    returnVec = [0]*len(vocabList)  # 初始权为0.没有出现
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
        else :
            print("the word %s is not in my Vocabulary!" % word)
    return returnVec

案例-垃圾邮件测试

利用了留存交叉验证:随机选择数据的一部分作为训练集,留一下一部分作为测试集

def textParse(bigString):
    '''
    以空格点等符号切割文本,返回长度大于2的文本集列表
    '''
    import re
    listOfTokens = re.split(r'\w*', bigString)   # 利用正则切分文本
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]

def spamTest():
    '''
    贝叶斯垃圾邮件分类器
    '''
    docList=[]
    classList = []
    fullList = []
    # 按不同文件夹导入并解析文件
    for i in range(1,26):
        wordList = textParse(open('spam/%d.txt' % i).read())
        docList.appded(wordList)
        fullList.extend(wordList)
        classList.append(1)
        wordList = textParse(open('ham/%d.txt' % i).read())
        docList.appded(wordList)
        fullList.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList)
    trainingSet = range(50)   # 构建训练集
    testSet = []      # 存放测试集
    # 随机一个数,将此位置的数从训练集挪到测试集
    for i in range(10):
        randIndex = int(np.random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])
    trainMat = []
    trainClasses = []
    for docIndex in trainingSet:
        trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])
    p0V, p1v, pSpam = trainNB0(array(trainMat), array(trainClasses))
    errorCount = 0
    for docIndex in testSet:
        wordVector = setOfWords2Vec(vocabList, docList[docIndex])
        if calssifyNB(array(wordVector), p0V, p1v, pSpam) != calssList[docIndex]:
            errorCount += 1
    print('the error rate is :', float(errorCount)/len(testSet))

sklearn.naive_bayes.MultinomialNB

class sklearn.naive_bayes.MultinomialNB(alpha=1.0, fit_prior=True, class_prior=None)
  """
  :param alpha:float,optional(default = 1.0)加法(拉普拉斯/ Lidstone)平滑参数(0为无平滑)
  """

在这里插入图片描述

案例-互联网新闻分类

from sklearn.datasets import fetch_20newsgroups
from sklearn.cross_validation import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB

# 读取20类新闻文本的数据细节
news = fetch_20newsgroups(subset='all')
# 20类新闻文本数据分割
X_train,X_test,y_train,y_test = train_test_split(news.data,news.target,test_size=0.25,random_state=42)
# 文本转换为特征向量进行TF特征抽取
vec = CountVectorizer()
# 训练数据输入,并转换为特征向量
X_train = vec.fit_transform(X_train)
# 测试数据转换
X_test = vec.transform(X_test)

# 使用平滑处理初始化的朴素贝叶斯模型
mnb = MultinomialNB(alpha=1.0)

# 利用训练数据对模型参数进行估计
mnb.fit(X_train,y_train)

# 对测试验本进行类别预测。结果存储在变量y_predict中
y_predict = mnb.predict(X_test)


# 查看分类错误率
count = 0
for i in range(len(y_predict)):
    if y_predict[i] != y_test[i]:
        count += 1
print('分类错误个数:',count, '总个数:',len(y_predict), '错误率:', float(count)/len(y_predict))
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值