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

来源:互联网 发布:大漠驼铃 php 编辑:程序博客网 时间:2024/05/16 12:20

使用Python进行文本分类

要从文本中获取特征,需要先拆分文本,具体如何做呢?这里的特征是来自文本的词条,一个词条是字符的任意组合。可以把词条想象为单词,也可以使用非单词词条,如URL、IP地址或者任意其他字符串。然后将每一个文本片段表示为一个词条向量,其中值为1表示词条出现在文档中,0表示词条未出现。

此处以社区留言为例,为了过滤侮辱性的言论,我们使用1 和 0 来代表是侮辱类和非侮辱类。

首先会给出将文本转换为数字向量的过程,然后介绍如何基于这些向量来计算条件概率,并在此基础上构建分类器,最后介绍利用Python实现朴素贝叶斯过程中需要考虑的问题。

在进行朴素贝叶斯实战前,建议先理解朴素贝叶斯的原理,我在自己的博客中介绍过 机器学习初涉–贝叶斯分类

1.准备数据,从文本中构建词向量

我们将文本看成词向量或者词条向量,也就是说将橘子转换为向量。考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说所要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。接下来我们正式开始。

from numpy import *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 is 侮辱性文字, 0 not    return postingList, classVecdef createVocabList(dataSet):    vocabSet = set([])  # create empty set    for document in dataSet:        vocabSet = vocabSet | set(document)  # 并集    return list(vocabSet)def setOfWords2Vec(vocabList, inputSet):    """    :param vocabList:  词汇表    :param inputSet:  某个文档    :return:  文档向量,向量的元素是0/1,分别表示词汇表中的单词在输入文档中是否出现。    """    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

已经完成了将一组单词转换为一组数字,那么如何使用这组数字来计算概率。现在已经知道了一个字是否出现在一篇文档中,也知道文档所属的类别。那么一个留言属于侮辱性的还是非侮辱性的这两个概率如何计算?

首先,通过类别i(侮辱性和非侮辱性留言)中文档数除以总的文档数来计算概率P(Ci),接下来计算P(W|Ci),这里需要用到朴素贝叶斯假设,如果将W展开为一个个独立的特征,那么就可以将上述概率写作:P(W0,W1,W2..Wn|Ci)。这里假设所有词都相互独立,也称为条件独立性假设,它意味着可以使用P(W0|Ci)P(W1|Ci)P(W2|Ci)..P(Wn|Ci)来计算上述概率。

函数伪代码:

计算每个类别中的文档数目对每篇训练文档:    对每个类别:        如果词条出现在文档中——>增加该词条的计数值        增加所有词条的计数值    对每个类别:        对每个词条:            将该词条的数目除以总词条数得到条件概率(也就是求P(Wn|Ci))    返回每个类别的条件概率

说明:这里是将每个词条视为独立的一个特征,因此,需要计算出每一个词条在Ci中出现的概率。

这部分的代码如下:

def trainNB0(trainMatrix, trainCategory):    """    :param trainMatrix:  文档矩阵    :param trainCategory:   由每篇文档类别所构成的向量    :return:    """    numTrainDocs = len(trainMatrix)    numWords = len(trainMatrix[0])    pAbusive = sum(trainCategory) / float(numTrainDocs) # P(Ci)    # 初始化概率.因为计算P(W0|Ci)*P(W1|Ci)...P(W2|Ci)时会出现其中一个概率为0,导致最后的结果也为0,    # 因此,将初始化的出现次数设置为1,并将默认的词条总数设置为2    p0Num = ones(numWords)    p1Num = ones(numWords)    p0Denom = 2.0    p1Denom = 2.0    # 对于每一篇文章    for i in range(numTrainDocs):        if trainCategory[i] == 1:            p1Num += trainMatrix[i] # 将出现的词条对应计数加1            p1Denom += sum(trainMatrix[i])  # 词条出现的总数加1        else:            p0Num += trainMatrix[i]            p0Denom += sum(trainMatrix[i])    # 计算出现概率时,由于大部分因子非常小,会导致程序下溢或者得到不正确的答案,因此,解决办法是通过求对数    p1Vect = log(p1Num / p1Denom)    # 计算第一个类别中的P(W/Ci),即在Ci中每个词条的出现概率    p0Vect = log(p0Num / p0Denom)    # 另一个类别    return p0Vect, p1Vect, pAbusive
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):    """    :param vec2Classify: 待分类文档的词向量数组    :param p0Vec: 第一个类别的各个特征出现的概率    :param p1Vec: 第二个类别的各个特征出现的概率    :param pClass1: P(Ci)    :return:    """    p1 = sum(vec2Classify * p1Vec) + log(pClass1)  # element-wise mult    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)    if p1 > p0:        return 1    else:        return 0def testingNB():    # 加载数据    listOPosts, listClasses = loadDataSet()    # 去重    myVocabList = createVocabList(listOPosts)    trainMat = []    # 创建文档矩阵    for postinDoc in listOPosts:        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))    # 得到每个特征在每个类别中出现的概率数组,以及P(Ci)    p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses))    # 测试数据    testEntry = ['love', 'my', 'dalmation']    # 计算测试数据的词向量    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))    print testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb)    # 第二组测试数据    testEntry = ['stupid', 'garbage']    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))    print testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb)

运行的结果如下:

['love', 'my', 'dalmation'] classified as:  0['stupid', 'garbage'] classified as:  1

截止到目前为止,我们将每个词的出现与否视为一个特征,这可以描述为词集模型。如果一个词在文档中出现不止一次,这可能意味着仅仅通过词出现与否表述为特征值是不够的,需要几记录下词的出现次数,这种方法被称为词袋模型。若词袋模型,可将setOfWordsVec()进行如下修改:

def bagOfWords2VecMN(vocabList, inputSet):    returnVec = [0] * len(vocabList)    for word in inputSet:        if word in vocabList:            returnVec[vocabList.index(word)] += 1    return returnVec
原创粉丝点击