机器学习实战-朴素贝叶斯

使用python进行文本分类

# -*- coding: utf-8 -*-
"""
Created on Mon Aug 21 17:17:19 2017

@author: rocky
"""

#机器学习实战第四章
#朴素bayes
#使用python进行文本分类

#从文本中构建词向量
from numpy import *
def loadDataSet():#创建一个实验样本
    postingList=[['my','dog','has','flea','problem','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 createVocalList(dataSet):
    vocabSet=set([])#set返回一个不含重复词的列表
    for document in dataSet:
        vocabSet=vocabSet|set(document)#创建两个集合的并集
    return list(vocabSet)

def setOfWord2Vec(vocabList,inputSet):#输入为经上个函数的结果以及
    returnVec=[0]*len(vocabList)#创建一个全零向量
    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
#以上的三个程序的作用:
#程序1返回几篇文档和文本类别,程序2对程序1进行非重复处理并拼接,返回一篇文档
#程序3的输入分别对应程序2的结果和程序1中的第i篇文档,返回的结果是2的结果的每一个词
#是否出现在第i篇文章中。出现则是1,没出现则是0.
#这样我们就把一篇文章(在第i篇文档环境下)转换成了一组数字

我们可以做这样的实验,在命令窗口输入以下:

listOPosts,listClasses=loadDataSet()
data=[['my','dog'],['is','rain']]

lis=createVocalList(data)
lis
Out[22]: ['rain', 'my', 'is', 'dog']
setOfWord2Vec(lis,listOPosts[5])
the word: quit is not in my Vocabulary!
the word: buying is not in my Vocabulary!
the word: worthless is not in my Vocabulary!
the word: food is not in my Vocabulary!
the word: stupid is not in my Vocabulary!
Out[23]: [0, 0, 0, 1]

我们可以判断第六行的每个词是否出现在data中,从而得到data的数字表示,为后面对data进行概率计算(好坏属性判断)准备。

训练算法:从词向量计算概率

#训练算法:从词向量计算概率(假设二类分布,即只有两个属性)
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs=len(trainMatrix)#统计矩阵行数,即文档数
    numWords=len(trainMatrix[0])#统计列数,即每个文档的字数
    pAbusive=sum(trainCategory)/float(numTrainDocs)#计算文档属性的概率(P1,另一个就是P0)
    p0Num=zeros(numWords)#建立零向量,长度为文档字数
    p1Num=zeros(numWords)
    p0Denom=0.0
    p1Denom=0.0#以上是对概率的初始化
    for i in range(numTrainDocs):#循环每个文档
        if trainCategory[i]==1:#如果这个文档的属性是1
            p1Num+=trainMatrix[i]#把第i行数据(或者第i个文档)赋给p1Num
            p1Denom+=sum(trainMatrix[i])#求该文档中1的和,赋给。。。
        else:
            p0Num+=trainMatrix[i]
            p0Denom+=sum(trainMatrix[i])
    p1Vect=p1Num/p1Denom
    p0Vect=p0Num/p0Denom
    return p0Vect,p1Vect,pAbusive

在命令行输入以下命令检验:

#建立自己的数据集和类别,不使用上面的。
data=[['to','my','worthless'],['dog','take','ate'],['stop','to','him']]

listclass=[1,1,0]

mylist=createVocalList(data)
#mylist就是待检验的文档
mylist
Out[17]: ['to', 'worthless', 'take', 'ate', 'my', 'him', 'dog', 'stop']

trainmat=[]
#生成一个文本矩阵,代表着这个待检验文本在每个样本下的存在与否
for postindoc in data:
    trainmat.append(setOfWord2Vec(mylist,postindoc))
  
trainmat
Out[21]: [[1, 1, 0, 0, 1, 0, 0, 0], [0, 0, 1, 1, 0, 0, 1, 0], [1, 0, 0, 0, 0, 1, 0, 1]]
#计算各个概率
p0V,p1V,pAb=trainNB0(trainmat,listclass)

pAb
Out[23]: 0.66666666666666663

p0V
Out[24]: 
array([ 0.33333333,  0.        ,  0.        ,  0.        ,  0.        ,
        0.33333333,  0.        ,  0.33333333])

p1V
Out[25]: 
array([ 0.16666667,  0.16666667,  0.16666667,  0.16666667,  0.16666667,
        0.        ,  0.16666667,  0.        ])
   

在trainNB0的循环中,循环参数是行数,也就是类别数,也就是3,而不是列数(8),这一点要注意。在上面自己的数据中,前两行类别都是1,那么经过i=0和i=1后,p1Num=(11111010)(两个向量相加),p1Denom=6(即3+3),经i=2后,p0Num=(10000101)(第三个向量),p0Denom=3。这样就可以计算概率了。
以上计算的是文本中每个词在各个类别下出现的概率。

测试算法,修改分类器

上面计算了各个词在每个类别下的概率,下面就要计算多个概率的乘积来获得文档属于某个类别的概率。那么如果一个词的概率为0,总结果就是0,这显然不行。所以进行简单处理,所有词的初始出现数为1,分母初始化2,修改上述程序。修改p0Num\p0Denom\p1Num\p1Denom的初值,以及最后的概率计算,改为对数运算。

def trainNB0(trainMatrix,trainCategory):
    numTrainDocs=len(trainMatrix)#统计矩阵行数,即文档数
    numWords=len(trainMatrix[0])#统计列数,即每个文档的字数
    pAbusive=sum(trainCategory)/float(numTrainDocs)#计算文档属性的概率(P1,另一个就是P0)
    p0Num=ones(numWords)#建立o向量,长度为文档字数
    p1Num=ones(numWords)
    p0Denom=2.0
    p1Denom=2.0#以上是对概率的初始化
    for i in range(numTrainDocs):#循环每个文档
        if trainCategory[i]==1:#如果这个文档的属性是1
            p1Num+=trainMatrix[i]#把第i行数据(或者第i个文档)赋给p1Num
            p1Denom+=sum(trainMatrix[i])#求该文档中1的和,赋给。。。
        else:
            p0Num+=trainMatrix[i]
            p0Denom+=sum(trainMatrix[i])
    p1Vect=log(p1Num/p1Denom)
    p0Vect=log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive
    

下面构建完整的分类器。

#创建朴素贝叶斯分类函数
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
    p1=sum(vec2Classify*p1Vec)+log(pClass1)
    p0=sum(vec2Classify*p0Vec)+log(1.0-pClass1)
    if p1>p0:
        return 1
    else:
        return 0

#以下的命令其实是将一些命令窗口中的操作封装为一个函数,叫便利函数。节省输入时间。    
def testingNB():
    listOPosts,listClasses=loadDataSet()
    myVocabList=createVocalList(listOPosts)
    trainMat=[]
    for postinDoc in listOPosts:
        trainMat.append(setOfWord2Vec(myVocabList,postinDoc))
    p0V,p1V,pAb=trainNB0(array(trainMat),array(listClasses))
    testEntry=['love','my','dalmation']
    thisDoc1=array(setOfWord2Vec(myVocabList,testEntry))
    #上面这一句的意思是检查myVocabList中的文字是否出现在testEntry中
    print(testEntry,'classified as: ',classifyNB(thisDoc1,p0V,p1V,pAb))
    
    testEntry=['stupid','garbage']
    thisDoc2=array(setOfWord2Vec(myVocabList,testEntry))
    print(testEntry,'classified as: ',classifyNB(thisDoc2,p0V,p1V,pAb))
    return thisDoc1,thisDoc2

    

最后的return thisDoc1,thisDoc2是自己加的,检查结果。后一个函数是封装了一些操作。命令窗口输入以下:

testingNB()
['love', 'my', 'dalmation'] classified as:  0
['stupid', 'garbage'] classified as:  1
Out[31]: (array([0, 0, 0, ..., 1, 0, 0]), array([0, 0, 0, ..., 0, 1, 0]))

我们可以看到,thisDoc1结果是([0, 0, 0, ..., 1, 0, 0]),中间有一些省略号,看不到中间是什么数据,这时我们可以在开头定义numpy的下面,输入一行'np.set_printoptions(threshold=np.inf)',可以保证数据全部显示,另外我也将myVocabList输出(return稍作修改即可):

testingNB()
['love', 'my', 'dalmation'] classified as:  0
['stupid', 'garbage'] classified as:  1
Out[42]: 
(array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
        0, 0, 0, 0, 0, 0, 1, 0, 0]),
 array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 1, 0, 0, 0, 0, 1, 0]),
['to',
  'worthless',
  'ate',
  'quit',
  'stop',
  'steak',
  'take',
  'has',
  'I',
  'problem',
  'park',
  'dog',
  'so',
  'love',
  'is',
  'posting',
  'flea',
  'not',
  'help',
  'maybe',
  'my',
  'licks',
  'food',
  'cute',
  'buying',
  'stupid',
  'how',
  'please',
  'him',
  'dalmation',
  'garbage',
  'mr'])

以上我们可以看到['love', 'my', 'dalmation']的出现,用1表示,而之前我们得到了一个概率分布,这样thisDoc1(thisDoc2)表示待测文本在当前环境下的表示(仍保持thisDoc1的意义),这样再利用函数classifyNB就可以计算thisDoc1是否属于侮辱性文档的概率。

注:处理垃圾邮件尚未学习,等回顾上述思路待后续。

posted @ 2018-03-23 23:30  歪胡子的日常  阅读(181)  评论(0编辑  收藏  举报