【机器学习实战-4章】贝叶斯

贝叶斯算法用于邮件过滤的过程
独立性假设是指一个词的出现概率并不依赖于文档中的其他词,这也是称为朴素贝叶斯的原因
1.创建一个包含所有文档的不重复词汇的词汇表createVocabList()
2.将每个文档转换成特征向量[[data,label],…,[data,label]],如果词语在词汇表中出现,就在词汇表相应位置置1,最后形成包含0,1的特征向量setOfWord2Vec()
3.计算贝叶斯概率中的p(W|Ci),p(w1|c1),…p(wn|c1);p(w1|c0),…p(wn|c0)trainNB0()
4.计算输入向量的贝叶斯概率p(w|Ci)p(Ci),进行比较,输出可能性最大的分类
5.保存训练的结果
6.测试数据
基于概率论的分类方法:朴素贝叶斯
分类器有时会产生错误结果,这时可以要求分类器给出一个最优的类别猜测结果,同时给出这个猜测的概率估计值

4.1 基于贝叶斯决策理论的分类方法

  1. 朴素贝叶斯
    优点:在数据较少的情况下仍然有效,可以处理多类别问题
    缺点:对于输入数据的准备方式较为敏感
    使用数据类型:标称型数据

  2. 贝叶斯决策理论
    1)贝叶斯概率引入先验知识和逻辑推理来处理不确定命题
    2)一个数据集,由两类数据组成。两个参数已知的概率分布,参数决定了分布的形状。用p1(x,y)标识数据点(x,y)属于类别1的概率,p2(x,y)表示属于类别2的概率。
    3)对于一个新数据点(x,y),可以判定它的类别:
    如果p1(x,y)>p2(x,y),那么类别为1
    如果p1(x,y)<p2(x,y),那么类别为2
    贝叶斯决策理论的核心思想:选择具有最高概率的决策
    计算数据点属于每个类别的概率,并进行比较

4.2 条件概率

贝叶斯准则告诉我们如何交换条件概率中的条件与结果,即结果已知p(x|c),要求p(c|x).
p(c|x)=p(x|c)*p©/p(x)

4.3 使用条件概率来分类

p(c1|x,y) 给定某个由x,y表示的数据点,那么该数据点来自类别c1的概率是多少?
p(x,y|Ci) 给定类别,(x,y)属于类别c的概率是多少?
p(Ci|x,y)=p(x,y|Ci)*p(Ci)/p(x,y)

4.4 使用朴素贝叶斯进行文档分类(独立特征)

在文档分类中,整个文档(如一封电子邮件)是实例,而电子邮件中的某些元素则构成特征。

  1. 朴素贝叶斯的一般过程
    1).收集数据
    2).准备数据:需要数值型或者布尔型数据
    3).分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果更好
    4).训练算法:计算不同的独立特征的条件概率
    5).测试算法:计算错误率
    6).使用算法:一个常见的朴素贝叶斯应用是文档分类。可以在任意的分类场景中使用朴素贝叶分类器,不一定非要是文本

  2. 朴素贝叶斯的假设
    每个特征需要N个样本,10个特征需要的样本数为N10,所需要的样本数会随着特征数目增大而迅速增长。
    1)朴素贝叶斯假设每个特征之间
    相互独立**,N10就降到10*N个。 就邮件来说,每个词我们认为他没有上下文含义,也就是说是互相独立的。这种假设正是“朴素”的含义。
    2)每个特征
    同等重要**。也就是说忽略特征出现的次数。

4.5 使用python进行文本分类

4.5.1 准备数据:从文本中构建词向量

把文本看成单词向量或词条向量,也就是说将句子转换为向量。考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说索要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。
朴素贝叶斯通常有两种实现方式:一种是基于伯努利模型实现,一种基于多项式模型实现。这里采用伯努利实现。该实现方式并不考虑词在文档中出现的次数,只考虑初步出现,因此这个意义上相当于假设词是等权重的。

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

重写贝叶斯准则
p(Ci|W)=p(W|Ci)*p(Ci)/p(W)
W是一个向量,由多个数值组成。

  1. 首先可以通过类别i(侮辱性留言或非侮辱性留言)中文档数除以总的文档数来计算概率p(ci)

  2. 计算p(W|ci).使用贝叶斯假设,将W展开为一个个独立特征,那么有p(W|ci)=p(w1,w2,w3,…,wn|ci)=p(w1|ci)p(w2|ci)…p(wn|ci)

  3. 伪代码如下:
    计算每个类别中的文档数目
    对每篇训练文档:
    对每个类别:
    如果词条出现在文档中->增加该词条的计数值
    增加所有词条的计数值
    对每个类别:
    对每个词条:
    将该词条的数目除以总词条数目得到条件概率
    返回每个类别的条件概率

4.5.3 测试算法:根据现实情况修改分类器

  1. 利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算p(w1|ci)p(w2|ci)…p(wn|ci)。如果其中一个概率值为0,那么最后乘积为0,为消除这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2
  2. 另一个问题是下溢出,这是由于太多很小的数相乘造成的(太多很小的数相乘,最后python四舍五入得到0).一般解决办法是对乘积取自然对数。
def loadDataSet():
    """创建实验样本
    return postingList:对词条切分后的文档集合
    return classVec:类别标签的集合。这些标注信息人工标注,用于训练程序以便自动检测侮辱性留言
    """
    postingList = [
        ['my','dog','has','flea','problems','help','please'],
        ['maybe','not','take','him','to','dog','park','stupid'],
        ['maybe','not','take','him','to','dog','park','stupid']
    ]
    classVec = [0,1,1]    #1代表侮辱性文字,0代表正常言论
    return postingList,classVec
def createVocabList(dataSet):
    """创建一个包含在所有文档中出现的不重复词的列表
    dataset:文档的列表,遍历列表构建词汇表"""
    vocabSet = set([])
    #每篇文档返回新词集合加入到词汇列表中
    for document in dataSet:
        vocabSet = vocabSet|set(document)   #并集
    return sorted(list(vocabSet))
def setOfWord2Vec(vocabList,inputSet):
    """词集模型:输入参数为词汇表vocablist及某个文档inputset,输出的是文档特征向量,向量的每一个元素为1或0,分别表示词汇表中的单词在输入文档中是否出现。"""
    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

def trainNB0(trainMatrix,trainCategory):
    """朴素贝叶斯分类器训练函数p(wi|c1)\n
    因为后续的计算使用的numpy,故传入的两个数组需为numpy数组\n
    trainMatrix:文档矩阵,由一个一个的文档特征向量组成\n
    trainCategory:由每篇文档标签(是否是侮辱性文档)所构成的向量\n
    return p0Vect,p1Vect,pAbusive:两个类别的概率向量([p(w1|ci),p(w2|ci),...,p(wn|ci)])以及属于侮辱性文档的概率"""
    numTrainDocs = len(trainMatrix)
    print(trainMatrix)
    numWords = len(trainMatrix[0]) 
    pAbusive = sum(trainCategory)/float(numTrainDocs)   #属于侮辱性文档的概率p(c1)
    #为计算p(wi|c1)和p(wi|c0),需要初始化程序中的分子和分母变量。由于W中的元素很多,所以使用numpy进行计算
    p0Num = np.ones(numWords)
    p1Num = np.ones(numWords)
    p0Denom = 2.0
    p1Denom = 2.0
    for i in range(numTrainDocs):
        #如果是侮辱性文档,则该词加一(trainMatrix的每个向量的元素都是只含1或0的)
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])  #计算该类别的总词数
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    #p1Vect = log(p1Num/p1Denom) #每个元素除以该类别中的总词数p(wi|c1)
    """TYPEERROR:only size-1 arrays can be converted to Python scalars
    对于numpy矩阵,需要调用numpy中的log方法
    """
    p1Vect = np.log(p1Num/p1Denom)
    print(p1Vect)
    p0Vect = np.log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive
def classifyNB(vec2Classify:np.array,p0Vec,p1Vec,pClass1):
    """朴素贝叶斯分类函数
    vec2Classify:文档转化而来的特征向量\n
    p0Vec:[p(w1|c0),p(w2|c0),...,p(wn|c0)]\n
    p1Vec:[p(w1|c1),p(w2|c1),...,p(wn|c1)]\n
    pClass1:训练集中侮辱性文档的概率"""
    #是侮辱性文档的概率为p,则是正常文档的概率为1-p
    #[p(w1|c0),p(w2|c0),...,p(wn|c0)]+log[p(c1)]=p(W|c1)*p(c1).因为对于p0和p1,分母的值是一样的,所以不进行计算
    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 = createVocabList(listOPosts)
    trainMat = []
    for postinDoc in listOPosts:
        trainMat.append(setOfWord2Vec(myVocabList,postinDoc))
    p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses))
    testEntry = ['love','my','garbage']
    thisDoc = np.array(setOfWord2Vec(myVocabList,testEntry))
    print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))

4.5.4 准备数据:词袋模型

目前,我们将每个词的出现与否作为一个特征,这可以被描述为词集模型(set-of-words-model).如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法称为词袋模型(bag-of-words model)

def bagOfWord2VecMN(vocablist,inputSet):
    """词袋模型,允许一个词出现多次"""
    retrunVec = [0]*len(vocablist)
    for word in inputSet:
        if word in vocablist:
            returnVec[vocablist.index(word)] +=1
    return returnVec

4.6 示例:使用朴素贝叶斯过滤垃圾邮件

使用朴素贝叶斯解决问题时,需要先从文本内容得到字符串列表,然后生成词向量。
1.收集数据
2.准备数据:将文本文件解析成词条向量
3.分析数据:检查词条确保解析的正确性
4.训练算法
5.测试算法:使用classifyNB(),并构建一个新的测试函数来计算文档集的错误率
6.使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上

4.6.1 准备数据:切分文本

对于一个文本字符串,可以使用python的string.split()方法将其切分

#垃圾邮件过滤器
def textParse(bigString):
    """切分文本"""
    bigString = re.sub('\\W','',bigString) #去掉bigString中除单词、数字外的任意字符串
    listOfTokens = re.split(' ',bigString)   
    print(listOfTokens)
    return [tok.lower() for tok in listOfTokens if len(tok)>2]  #lower(),python函数,将字符串所有字母转化为小写字母,过滤掉长度小于3的无效词汇
def spamTest():
    """使用朴素贝叶斯进行交叉验证,过滤垃圾邮件
    #导入并解析文档
    #随机构建训练集:测试集、训练集中的邮件都是随机选出的
    #训练模型,返回训练集中不同类别文档的概率p(w|ci)
    #testSet测试集验证模型准确率计算错误率"""
    docList = []    #文档的列表list of list
    classList = []  #标签
    fullText = []   #保存全部的文档
    for i in range(1,26):
        #导入并解析文档
        fileContent = open('./M_04_bayes_email/spam/%d.txt' % i).read()
        print(fileContent)
        wordList = textParse(open('./M_04_bayes_email/spam/%d.txt' % i).read())
        print(wordList)
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        wordList = textParse(open('./M_04_bayes_email/ham/%d.txt' % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList)
    print(vocabList)
    trainingSet = list(range(50)) #一共五十个文档
    testSet = []
    for i in range(10):
        """这种随机选择数据的一部分作为训练集,而剩余部分作为测试集的过程称为留存交叉验证(hold-out cross validation),可多次迭代后获得较为精确的错误率"""
        #随机构建测试集
        randIndex = int(random.uniform(0,len(trainingSet))) #随机生成范围内的浮点数
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex]) #从训练集中删除
    trainMat = []
    trainClasses = []
    for docIndex in trainingSet:
        #训练模型,返回训练集中不同类别文档的概率p(w|ci)
        trainMat.append(setOfWord2Vec(vocabList,docList[docIndex]))
        trainClasses.append(classList[docIndex])
    print(trainMat)
    p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses))
    errorCount = 0
    for docIndex in testSet:
        #testSet测试集验证模型准确率计算错误率
        wordVector = setOfWord2Vec(vocabList,docList[docIndex])
        if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
            errorCount += 1
            print('the misclassify document is number %s ' % docIndex)
    print('the error rate is:',float(errorCount)/len(testSet))

4.7 示例:使用朴素贝叶斯分类器从个人广告中获取区域倾向

目标:从两个城市中选取一些人,通过分析这些人发布的征婚广告信息,来比较这两个城市的人们在广告用词上是否不同

#从个人广告中获取区域倾向
def clacMostFreq(vocabList,fullText:list):
    """计算单词表中的词汇在fullText中出现的频率,返回频率前30的词语"""
    freqDict = {}
    for token in vocabList:
        freqDict[token] = fullText.count(token)
    sortedFreq = sorted(freqDict.iteritems(),key=operator.itemgetter(1),reverse=True)
    print(sortedFreq[:30])
    return sortedFreq[:30]
def localWords(feed1,feed0):
    """从个人广告中获取区域倾向
    feed1,feed0为两个RSS源
    移除高频词汇,还可以再移除停用词词汇,这都会使错误率降低"""
    #一个RSS文件就是一段规范的XML数据,该文件一般以rss,xml或者rdf作为后缀。
    docList=[]
    classList = []
    fullText = []
    minLen = min(len(feed1['entries']),len(feed0['entries']))
    for i in range(minLen):
        wordList = textParse(feed1['entries'][i]['summary'])    #每次访问一条RSS源
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        wordList = textParse(feed0['entries'][i]['summary'])    #每次访问一条RSS源
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList)
    top30Words = clacMostFreq(vocabList,fullText)
    for pairW in top30Words:
        #去掉出现次数最高的那些词,如a,the
        if pairW[0] in vocabList:
            vocabList.remove(pairW[0])
    trainingSet = list(range(2*minLen))
    testSet = []
    for i in range(20):
        randIndex = int(random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])
    trainMat = []
    traingClasses = []
    for docIndex in trainingSet:
        #向量化训练集
        trainMat.append(bagOfWord2VecMN(vocabList,docList[docIndex]))
        traingClasses.append(classList[docIndex])
    p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(traingClasses))
    errorCount = 0.0
    for docIndex in testSet:
        wordVector = bagOfWord2VecMN(vocabList,docList[docIndex])
        if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
            errorCount += 1
    print('the error rate is:',float(errorCount)/len(testSet))
    return vocabList,p0V,p1V

4.8 贝叶斯的应用

推荐:搜集一个人的相关信息,各类广告中判断他对一个广告是否感兴趣
过滤器:垃圾邮件过滤
分类:例子中的内容

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值