机器学习系列--Naive Bayes Classification

Native Bayes

贝叶斯决策理论的核心思想:选择最高概率的决策。朴素贝叶斯是贝叶斯决策理论的一部分。
下面不加证明地直接给出贝叶斯定理:
朴素贝叶斯分类的正式定义如下:
这里写图片描述

这里写图片描述

因为分母对于所有类别为常数,因为我们只要将分子最大化皆可。又因为各特征属性是条件独立的,所以有:

这里写图片描述

分类问题

现在实际的来研究一个文本分类的问题,下面是朴素贝叶斯分析问题的一般过程:

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

为了简单起见,先研究一个社区的评论条目,构建一个快速过滤器,来分辨每条评论属于侮辱性评论还是非侮辱性评论,分别用1和0来代表,接下来的代码演示如何将词条数据转换成布尔型或数值型的数据:

词表到向量的转换函数:

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 abusive, 0 not
    return postingList,classVec

上面这个load数据的函数,功能很简单就是创建两个数组,并返回它们,一个是词条列表即评论,共6个评论,一个是每个词条所对应的类别列表。这里所说的词条也就是一个句子,比如例子中的一个评论,后续还可能是一篇文章,一封邮件等,而词条列表则是由这些词条组成的数组。后面会这么用这个函数:

这里写图片描述

def createVocabList(dataSet):
    vocabSet = set([])  #create empty set
    for document in dataSet:
        vocabSet = vocabSet | set(document) #union of the two sets
    return list(vocabSet)

这个函数旨在创建包含上面词条列表所有词汇,但不会重复的数组。先创建一个空的set数据类型,然后遍历每个词条,用 ‘|’求并集,返回不重复的单词。看一下函数的效果:
这里写图片描述

返回的不重复的词汇列表,可能是没按照顺序的,不过这个无关紧要。

def setOfWords2Vec(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或0,表示词汇表中的单词在输入文档(词条)中是否出现。实现原理是遍历文档每个词,再判断每个词是否出现在词汇表中,进而对输出向量进行赋值操作。看一下效果:
这里写图片描述

从词向量计算概率

将朴素贝叶斯的定理应用到这个社区评论过滤的案例中,前面已经提到用x,y表示的贝叶斯定理,现在用w 代表一个向量,该向量由多个数值组成,其实际意义就是社区每条评论的数值化结果即[0,1,0,0,1,1,1….]这种形式,用字母Ci代表评论的类别,在这个案例中只有两个类别:C1=1,侮辱性评论,C2=非侮辱性评论。那我们这个案例的研究目的就是分辨新输入的评论属于哪一类别,也就是给定评论内容(词条数组向量)的条件下,属于哪种类别的概率,用符号表示就是P(Ci | w)。有贝叶斯定理可以得到:

这里写图片描述

利用这个公式,每当输入一个文档,我们会计算这个文档属于每一个类别的概率,选择最大概率的那个类别作为评判结果,体现了贝叶斯决策理论的核心思想:选择最高概率的决策。
主要分析一下右边的式子:

分子:P(w | Ci)代表着在已知类别的情况下,对每个单词求其出现的概率,这个就跟前一篇介绍生成学习算法中大象和小狗的例子差不多了,在这里w回事由很多值组成的向量,即可写成P(w0,w1,w2,w3,…,wn|Ci),按照前面朴素贝叶斯的假设:w中每个特征条件独立,所以可以写成:P(w0 | Ci)P(w1 | Ci)P(w2 | Ci)P(w3 | Ci)…..P(wn | Ci) ,按照这样来计算上述概率。P(Ci)为ci类别的个数占总个数的概率,比如这个案例中p(C1)=p(c2)=3/6=0.5。

分母:是表示某个词向量的概率,在实际情况中,词向量是给定的也就是输入数据,所以分母通常为常数,所以可忽略不计,不影响整体概率结果。

下面这个函数是求P(w |ci)的过程:

def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix) #trainMatrix的长度,也就是几篇文档
    numWords = len(trainMatrix[0])  #trainMatrix中每篇文档的长度,即有多少个单词
    pAbusive = sum(trainCategory)/float(numTrainDocs) #trainCategory中类别1的个数除以文档总数,也就是类别1的比例
    p0Num = ones(numWords); p1Num = ones(numWords)   #创建全是0的数组,后面为了防止出现全0导致概率无效,换成了全1数组,change to ones() 
    p0Denom = 2.0; p1Denom = 2.0                        #分母基数初始化为2 ,防止出现分母为0,或者比分子小情况,change to 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 = log(p1Num/p1Denom)     #这里原本是不加log运算,得出的结果就是概率,不过后面会有乘法计算,为了避免最后结果变成0,采用log运算 #change to log()
    p0Vect = log(p0Num/p0Denom)          #change to log()
    return p0Vect,p1Vect,pAbusive   #分别返回类别0中,各个单词的概率;类别1中各个单词的概率;类别为1的概率 

上面这些函数的过程,下面用手写草图详细介绍下其中数据的处理流程,便于理解:

这里写图片描述

上述函数的运行结果就是得到在两个类别下的每个单词的概率:

这里写图片描述

下面是加上log函数的结果:

p0v: [-2.56494936 -2.56494936 -2.56494936 -3.25809654 -2.56494936 -2.56494936
 -2.56494936 -3.25809654 -2.56494936 -3.25809654 -3.25809654 -2.56494936
 -2.56494936 -3.25809654 -2.56494936 -2.56494936 -2.56494936 -3.25809654
 -2.15948425 -3.25809654 -3.25809654 -3.25809654 -2.56494936 -1.87180218
 -3.25809654 -3.25809654 -2.56494936 -2.56494936 -2.56494936 -2.56494936
 -2.56494936 -2.56494936]
p1v: [-1.94591015 -3.04452244 -3.04452244 -2.35137526 -3.04452244 -3.04452244
 -3.04452244 -1.94591015 -3.04452244 -2.35137526 -2.35137526 -3.04452244
 -3.04452244 -1.65822808 -3.04452244 -3.04452244 -3.04452244 -2.35137526
 -2.35137526 -2.35137526 -2.35137526 -2.35137526 -3.04452244 -3.04452244
 -2.35137526 -2.35137526 -3.04452244 -2.35137526 -2.35137526 -3.04452244
 -3.04452244 -3.04452244]

经过数据训练之后我们得到两个类别下每个单词会出现的概率,以上数据仅为参考,我发现函数 createVocabList 每次出来的结果都不一样,是随机的,我用python是3.6的,所以这里就不一一对比数据的准确性,我再debug的时候对比是没错的。

关键部分理解完了,下面分析一下如何使用这个训练结果,我贴上全部的代码:

import numpy as np


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 abusive, 0 not
    return postingList, classVec

def createVocabList ( dataSet):
    vocabSet = set([])  #create empty set
    for document in dataSet:
        vocabSet = vocabSet | set(document) #union of the two sets
    return list(vocabSet)


def setOfWords2Vec(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

def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory)/float(numTrainDocs)
    p0Num = np.ones(numWords); p1Num = np.ones(numWords)
    p0Denom = 2.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 = np.log(p1Num/p1Denom)
    p0Vect = np.log(p0Num/p0Denom)
    return p0Vect, p1Vect, pAbusive


def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)  # element-wise mult
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0


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


if __name__ == '__main__':
    listOPosts, listClasses = loadDataSet()
    myVocabList = createVocabList(listOPosts)
    print(myVocabList)
    trainMat = []
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses))
    # print("p0v:",p0V)
    # print("p1v:",p1V)
    testEntry = ['love', 'my', 'dalmation']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print (testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
    testEntry = ['stupid', 'garbage']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))

主要业务逻辑看main函数,

p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses))

这一步过后就得到了每个单词的概率向量了。接下来开始分类,对于新的数据(语句),这边先得对照词汇表转化成向量。

testEntry = ['love', 'my', 'dalmation']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))

向量化的结果(这个也要看词汇表排序的,仅做参考):

[0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

下面进入分类:

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)  # element-wise mult
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0

输入的四个参数,第一个是新数据的向量化结果,第二第三分别是各类别下每个单词概率的向量,第四个为其中某一类别的概率。
函数主要实现了贝叶斯公式中 右边分子的部分,也就是 在给定类别下这个词向量的概率和这个类别的概率的相乘。

这里的sum(vec2Classify * p1Vec) 操作,实际上就是:
[0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] * [-2.56494936 -2.56494936 -2.56494936 -3.25809654 -2.56494936 -2.56494936
-2.56494936 -3.25809654 -2.56494936 -3.25809654 -3.25809654 -2.56494936
-2.56494936 -3.25809654 -2.56494936 -2.56494936 -2.56494936 -3.25809654
-2.15948425 -3.25809654 -3.25809654 -3.25809654 -2.56494936 -1.87180218
-3.25809654 -3.25809654 -2.56494936 -2.56494936 -2.56494936 -2.56494936
-2.56494936 -2.56494936]
一一对应相乘最后相加,可以看到这个过程,0所对应的项相乘都变成0了,只有在词汇表中出现过的单词为1 余对应概率相乘才会有有效结果,最后相加是求这整个词向量的概率结果,这里想加并没有和前面的“w中每个特征条件独立,所以可以写成:P(w0 | Ci)P(w1 | Ci)P(w2 | Ci)P(w3 | Ci)…..P(wn | Ci) ,按照这样来计算上述概率。”矛盾,这边是log函数相加,log(P(w0 | Ci)P(w1 | Ci)P(w2 | Ci)P(w3 | Ci)…..P(wn | Ci))= log((P(w0 | Ci))+log(P(w1 | Ci))+……+log(P(wn | Ci))。最后加上类别概率的log结果,其实就是公式中的P(w|Ci)*P(Ci)的相乘结果的log,可以拆成两个log相加。

分类函数中,把把每个类别的概率都算出来,然后进行对比,谁比较大就属于那个类别。这就是简单的朴素贝叶斯分类算法,选择概率最大的那个。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 以下是一个使用Scala进行文档分类的示例代码: ```scala import org.apache.spark.ml.Pipeline import org.apache.spark.ml.classification.NaiveBayes import org.apache.spark.ml.feature.{HashingTF, IDF, Tokenizer} import org.apache.spark.sql.SparkSession // 创建 SparkSession val spark = SparkSession.builder().appName("DocumentClassification").getOrCreate() // 读取数据 val dataset = spark.read.option("header", "true").option("inferSchema", "true").csv("path/to/dataset.csv") // 数据预处理 val tokenizer = new Tokenizer().setInputCol("text").setOutputCol("words") val hashingTF = new HashingTF().setInputCol(tokenizer.getOutputCol).setOutputCol("rawFeatures").setNumFeatures(10000) val idf = new IDF().setInputCol(hashingTF.getOutputCol).setOutputCol("features") // 划分数据集为训练集和测试集 val splits = dataset.randomSplit(Array(0.8, 0.2), seed = 1234) val trainingData = splits(0) val testData = splits(1) // 使用朴素贝叶斯进行分类 val nb = new NaiveBayes() val pipeline = new Pipeline().setStages(Array(tokenizer, hashingTF, idf, nb)) val model = pipeline.fit(trainingData) // 对测试集进行预测 val predictions = model.transform(testData) // 输出预测结果 predictions.select("label", "prediction", "text").show() // 关闭 SparkSession spark.stop() ``` 在这个示例中,我们使用了Spark的MLlib库来构建一个朴素贝叶斯分类器。首先,我们使用Tokenizer将文本数据分割成单词,然后使用HashingTF将单词特征向量化。接着,我们使用IDF对特征向量进行加权,以减少常见单词的影响。最终,我们将特征向量和标签作为输入,使用朴素贝叶斯进行分类。 ### 回答2: 机器学习对文档数据分类的Scala命令可以通过使用Scala编写的机器学习库来实现。以下是一种可能的方法来分类文档数据: 1. 导入所需的Scala机器学习库,如Apache Spark MLlib。 2. 加载文档数据集,可以使用Spark提供的读取文档的API来加载数据。例如,可以使用`sc.textFile("path_to_documents")`来加载文档数据集。 3. 对文档数据进行预处理,包括分词、移除停用词、提取特征等。可以使用相关的Scala库或自定义方法来实现这些步骤。 4. 将文档数据转换为机器学习算法所需的格式。根据使用的算法不同,可以将文档数据转换为词袋模型、TF-IDF向量等形式。 5. 根据具体需求选择合适的机器学习算法,并使用该算法对文档数据进行训练。例如,可以使用朴素贝叶斯分类器、支持向量机、随机森林等算法。 6. 对训练后的模型进行评估,可以使用交叉验证、准确度、混淆矩阵等指标来评估分类器的性能。 7. 使用训练好的分类器对新的文档数据进行分类预测。可以通过调用分类器的`predict`方法来实现。 8. 可选地,可以对分类结果进行后处理,如过滤低置信度的分类、合并相似的类别等。 9. 最后,保存模型以便将来使用。可以使用Scala的序列化机制将模型保存到磁盘,以便在需要时重新加载并使用。 上述是对机器学习对文档数据分类的Scala命令的一种介绍,具体的实现可能因使用的库和算法不同而有所变化。 ### 回答3: 在Scala中,可以使用Apache Spark来实现机器学习对文档数据的分类。以下是一个示例代码: ```scala import org.apache.spark.ml.feature.{Tokenizer, HashingTF, IDF} import org.apache.spark.ml.classification.{NaiveBayes, NaiveBayesModel} import org.apache.spark.ml.Pipeline import org.apache.spark.sql.SparkSession // 创建SparkSession val spark = SparkSession.builder().appName("Document Classification").getOrCreate() // 加载文档数据 val data = spark.read.format("text").load("/path/to/documents") // 对文档进行分词 val tokenizer = new Tokenizer().setInputCol("value").setOutputCol("words") val wordsData = tokenizer.transform(data) // 将分词后的文档数据转换为特征向量 val hashingTF = new HashingTF().setInputCol("words").setOutputCol("rawFeatures").setNumFeatures(10000) val featurizedData = hashingTF.transform(wordsData) // 计算TF-IDF值 val idf = new IDF().setInputCol("rawFeatures").setOutputCol("features") val idfModel = idf.fit(featurizedData) val rescaledData = idfModel.transform(featurizedData) // 划分训练集和测试集 val Array(trainingData, testData) = rescaledData.randomSplit(Array(0.8, 0.2)) // 创建朴素贝叶斯分类模型 val classifier = new NaiveBayes() .setLabelCol("label") .setFeaturesCol("features") // 创建机器学习流水线 val pipeline = new Pipeline().setStages(Array(tokenizer, hashingTF, idf, classifier)) // 训练模型 val model = pipeline.fit(trainingData) // 对测试集进行预测 val predictions = model.transform(testData) // 输出预测结果 predictions.select("prediction", "label", "features").show() // 保存模型 model.write.overwrite().save("/path/to/model") ``` 在以上代码中,我们首先使用`Tokenizer`对文档进行分词,然后使用`HashingTF`将分词后的文档转换为特征向量。接着,我们使用`IDF`计算TF-IDF值来进一步提取特征。随后,我们使用朴素贝叶斯分类算法进行文档分类,并创建一个机器学习流水线来自动化整个过程。最后,我们将模型保存到指定路径以供以后使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值