0%

机器学习:电子邮件分类 笔记

1. 简介

垃圾邮件有一些共同的特征,尤其表现在一系列词汇上,有监督的机器学习通过从大量预标注的预料中分别学习垃圾邮件额正常邮件的用词频率,从而对新邮件预测分类。

表达文本的向量有两种代表性的向量:1.词频向量 2.tf-idf向量

2. NLP 文本特征向量化

使用停用词

停用词是像 "and"、"the"、”him" 这样的词,这些词在表示文本内容时被认为是没有信息的,无意义的,在邮件分类问题上作用微乎其微,可以删除它们,然而,有时类似的词对预测很有用,比如在对写作风格或性格进行分类时。

在选择停用词时应当尽可能地谨慎,流行的停止词列表可能包括对某些任务(如计算机)具有高度信息性的词。

同时还应该确保停止单词列表具有与矢量化器中使用的相同的预处理和标记。

词袋模型

tf-idf

哈希向量化文本

3. Logistic 回归 二分类

算法评价

4. 朴素贝叶斯

以下参考自 《机器学习实战》[美] Peter Harrington

对于社区留言分类:侮辱性,非侮辱性

基于概率论的分类方法:朴素贝叶斯

要求分类器给出一个最优的类别猜测结果,同时给出这个猜测的概率估计值。

之所以称之为 “朴素”,

是因为整个形式化过程只做最原始、最简单的假设

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

朴素贝叶斯 

优点:在数据较少的情况下仍然 有效,可以处理多类别问题。 缺点:对于输入数据的准备方式较为敏感。

适用数据类型:标称型数据。

朴素贝叶斯是贝叶斯决策理论的一部分

我们现在用 \(p1(x,y)\) 表示数据点 \((x,y)\) 属于类别1(图中用圆点表示的类别)的概率,

\(p2(x,y)\) 表示数据点 \((x,y)\) 属于类别2(图中用三角形表示的类别)的概率,

那么对于一个新数据点 \((x,y)\) ,可以用下面的规则来判断它的类别:

  • p1(x,y) > p2(x,y),那么类别为1
  • p2(x,y) > p1(x,y),那么类别为2

即 选择高概率对应的类别

贝叶斯概率以18世纪的一位神学家托马斯·贝叶斯(Thomas Bayes)的名字命名。

贝叶斯概率引入 先验知识逻辑推理 来处理不确定命题。

另一种概率解释称为频数概率(frequency probability),它只从数据本身获得结论,并不考虑逻辑推理及先验知识。

条件概率

B桶取到灰色石头的概率,这个概率可以记作P(gray|bucketB),我们称之为“在已知石头出自B桶的条件下,取出灰色石头的概率”

P(gray|bucketA)值为2/4,P(gray|bucketB) 的值为1/3

条件概率计算公式:

\[ P(gray|bucketB) = P(gray \ and \ bucketB)/P(bucketB)\]

另一种有效计算条件概率的方法称为贝叶斯准则。

贝叶斯准则告诉我们如何交换条件概率中的条件与结果,

即如果已知P(x|c),要求P(c|x),那么可以使用下面的计算方法:

下面讨论 如何结合贝叶斯决策理论使用条件概率?

使用条件概率来分类

  • 如果p1(x, y) > p2(x, y),那么属于类别1;
  • 如果p2(x, y) > p1(x, y),那么属于类别2。

但这两个准则并不是贝叶斯决策理论的所有内容。

使用p1( )p2( )只是为了尽可能简化描述,而真正需要计算和比较的是p(c₁|x, y)p(c₂|x, y)

这些符号所代表的具体意义是:

给定某个由x、y表示的数据点,那么该数据点来自类别c₁的概率是多少?数据点来自类别c₂的概率又是多少?

注意这些概率与刚才给出的概率p(x, y|c₁)并不一样,不过可以使用贝叶斯准则来交换概率中条件与结果。

具体地,应用贝叶斯准则得到:

使用这些定义,可以定义贝叶斯分类准则为:

  • 如果P(c₁|x, y) > P(c₂|x, y),那么属于类别c₁
  • 如果P(c₁|x, y) < P(c₂|x, y),那么属于类别c₂

使用贝叶斯准则,可以通过已知的三个概率值来计算未知的概率值。

使用朴素贝叶斯进行文档分类

我们可以观察文档中出现的词,并把每个词的出现或者不出现作为一个特征,这样得到的特征数目就会跟词汇表中的词目一样多。

朴素贝叶斯是上节介绍的贝叶斯分类器的一个扩展,是用于文档分类的常用算法。

朴素贝叶斯的一般过程:

  1. 收集数据:可以使用任何方法。本章使用RSS源。

  2. 准备数据:需要数值型或者布尔型数据

  3. 分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果更好。

  4. 训练算法:计算不同的独立特征的条件概率。

  5. 测试算法:计算错误率。

  6. 使用算法:一个常见的朴素贝叶斯应用是文档分类。可以在任意的分类场景中使用朴素贝叶斯分类器,不一定非要是文本。

假设词汇表中有1000个单词。

要得到好的概率分布,就需要足够的数据样本,假定样本数为N

前面讲到的约会网站示例中有1000个实例,手写识别示例中每个数字有200个样本,而决策树示例中有24个样本。

其中,24个样本有点少,200个样本好一些,而1000个样本就非常好了。

约会网站例子中有三个特征。

由统计学知,如果每个特征需要N个样本,那么对于10个特征将需要N10个样本,对于包含1000个特征的词汇表将需要N1000个样本。

可以看到,所需要的样本数会随着特征数目增大而迅速增长。

如果特征之间相互独立,那么样本数就可以从N1000减少到1000×N

所谓独立(independence)指的是统计意义上的独立,即一个特征或者单词出现的可能性与它和其他单词相邻没有关系。

举个例子讲,假设单词bacon出现在unhealthy后面与出现在delicious后面的概率相同。

当然,我们知道这种假设并不正确,bacon常常出现在delicious附近,而很少出现在unhealthy附近,这个假设正是朴素贝叶斯分类器中朴素(naive)一词的含义。朴素贝叶斯分类器中的另一个假设是,每个特征同等重要2。其实这个假设也有问题。

如果要判断留言板的留言是否得当,那么可能不需要看完所有的1000个单词,而只需要看10~20个特征就足以做出判断了。

尽管上述假设存在一些小的瑕疵,但朴素贝叶斯的实际效果却很好。

使用Python进行文本分类

这里的特征是来自文本的词条(token),一个词条是字符的任意组合

将每一个文本片段表示为一个词条向量,其中值为1表示词条出现在文档中,0表示词条未出现

接下来首先给出将文本转换为数字向量的过程,然后介绍如何基于这些向量来计算条件概率,并在此基础上构建分类器,

最后还要介绍一些利用Python实现朴素贝叶斯过程中需要考虑的问题。

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

词表到向量的转换函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def loadDataSet():
'''
返回的第一个变量: 进行词条切分后的文档集合
返回的第二个变量:类别标签集合
:return:
'''
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']]
# 1: 侮辱性文字,0: 正常言论
classVec = [0,1,0,1,0,1]
return postingList,classVec

def createVocabList(dataSet):
'''
创建一个包含在所有文档中出现的 不重复词 的列表
:param dataSet:
:return:
'''
# 创建一个空集
vocabSet = set([])
for document in dataSet:
# 创建两个集合的并集
# 将词条列表输给set构造函数,set就会返回一个不重复词表
# 操作符|用于求两个集合的并集,这是一个按位或(OR)操作符
# 在数学符号表示上,按位或 操作 与 集合求并 操作使用相同记号。
vocabSet = vocabSet | set(document)
return list(vocabSet)

def setOfWords2Vec(vocabList, inputSet):
'''
:param vocabList: 词汇表
:param inputSet: 某个文档(分词后)
:return: 文档向量,向量的每一元素为1或0,分别表示词汇表中的单词在输入文档中是否出现
'''
# 1. 首先创建一个和词汇表等长 其中所含元素都为0的向量
returnVec = [0]*len(vocabList)
# 2. 遍历文档中的所有单词,若出现了词汇表中的单词,则将输出的文档向量中的对应值设为1
for word in inputSet:
# 3. 是否存在于词汇表
if word in vocabList:
# 4. one-hot 编码:词汇表中有这个单词,则将此词汇表的对应位置 记 1,否则 0
returnVec[vocabList.index(word)] = 1
else:
print("the word: %s is not in my Vocabulary!" % word)
return returnVec




if __name__ == "__main__":
listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
print(myVocabList)
print("-"*100)
print(setOfWords2Vec(myVocabList, listOPosts[0]))

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

前面介绍了如何将一组单词转换为一组数字,接下来看看如何使用这些数字计算概率。

现在已经知道一个词是否出现在一篇文档中,也知道该文档所属的类别。

我们重写贝叶斯准则,将之前的 x、y 替换为w粗体w表示这是一个向量,即它由多个数值组成。

在这个例子中,数值个数与词汇表中的词个数相同。

我们将使用上述公式,对每个类计算该值,然后比较这两个概率值的大小。

如何计算呢?首先可以通过类别i(侮辱性留言或非侮辱性留言)中文档数除以总的文档数来计算概率 \(p(c_i)\)

接下来计算 \(p(w|c_i)\) ,这里就要用到朴素贝叶斯假设。

如果将w展开为一个个独立特征,那么就可以将上述概率写作 \(p(w_0,w_1,w_2 ... w_N|c_i)\)

这里假设 所有词都互相独立,该假设也称作 条件独立性假设,它意味着可以使用 \[ p(w_0|c_i) p(w_1|c_i) p(w_2|c_i) ... p(w_N|c_i) \] 来计算上述概率,这就极大地简化了计算的过程。

该函数的伪代码如下:

1
2
3
4
5
6
7
8
9
计算每个类别中的文档数目
对每篇训练文档:
对每个类别:
如果词条出现在文档中→ 增加该词条的计数值
增加所有词条的计数值
对每个类别:
对每个词条:
将该词条的数目除以总词条数目得到条件概率
返回每个类别的条件概率 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 2. 朴素贝叶斯分类器训练函数
import numpy as np

def trainNB0(trainMatrix, trainCategory):
"""
:param trainMatrix: 文档矩阵
:param trainCategory: 由每篇文档类别标签所构成的向量
"""

# numTrainDocs 个训练样本(分词后的文档)
numTrainDocs = len(trainMatrix)
# 每个样本 numWords 个单词
numWords = len(trainMatrix[0])
# sum(trainCategory) 侮辱性留言 个数, 因为侮辱性留言标签 1,非侮辱性 0
# float(numTrainDocs) 总训练样本数,float 以便计算出小数点
# pAbusive 侮辱性留言占比
pAbusive = sum(trainCategory) / float(numTrainDocs)
# (以下两行)初始化概率
# 例如每个样本有5个单词,则初始化为 [1, 1, 1, 1, 1]
# p0Num: 0分类的样本 词汇表对应分词出现次数

p0Num = np.zeros(numWords); p1Num = np.zeros(numWords) # 改进前
# p0Num = np.ones(numWords); p1Num = np.ones(numWords) # 改进后

p0Denom = 0.0; p1Denom = 0.0 # 改进前
# p0Denom = 2.0; p1Denom = 2.0 # 改进后

for i in range(numTrainDocs):
if trainCategory[i] == 1:
# 1分类,
# 注意:向量加法:对应元素相加
# 当前样本 分词向量 累加
# 由于是 one-hot 编码,注重单词有没有在此文档中出现,用0,1标记,于是正好之后用来计算 概率,1就是出现该单词,而词汇表顺序是固定了的
# 最后的累加结果:1分类 所有文档,词汇表对应分词出现次数 形成的向量
# [w0,w1,w2...wN]
p1Num += trainMatrix[i]

# 累加上 当前样本的 分词出现次数 和
# p1Denom: 1分类 分词出现次数 和(共有多少个单词出现在了1分类)
p1Denom += sum(trainMatrix[i])
else:
# 0分类
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
# numpy广播
# p0Denom 会被复制成 [p1Denom*len(p1Num)]
#

# 改进前
p1Vect = p1Num / p1Denom
p0Vect = p0Num / p0Denom

# 改进后
# p1Vect = np.log(p1Num / p1Denom)
# p0Vect = np.log(p0Num / p0Denom)

return p0Vect, p1Vect, pAbusive

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

利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,

即计算 \(p(w_0|1) p(w_1|1) p(w_2|1)\)

如果其中一个概率值为0,那么最后的乘积也为0。

为降低这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2。

1
2
# p0Num = np.zeros(numWords); p1Num = np.zeros(numWords) # 改进前
p0Num = np.ones(numWords); p1Num = np.ones(numWords) # 改进后
1
2
# p0Denom = 0.0; p1Denom = 0.0 # 改进前
p0Denom = 2.0; p1Denom = 2.0 # 改进后

另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。

当计算乘积p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。(读者可以用Python尝试相乘许多很小的数,最后四舍五入后会得到0。)一种解决办法是对乘积取自然对数。

在代数中有ln(a*b) = ln(a)+ln(b),于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。

同时,采用自然对数进行处理不会有任何损失。

图4-4给出函数f(x)ln(f(x))的曲线。

检查这两条曲线,就会发现它们在相同区域内同时增加或者减少,并且在相同点上取到极值。

它们的取值虽然不同,但不影响最终结果。通过修改return前的两行代码,将上述做法用到分类器中:

1
2
3
4
5
6
7
# 改进前
# p1Vect = p1Num / p1Denom
# p0Vect = p0Num / p0Denom

# 改进后
p1Vect = np.log(p1Num / p1Denom)
p0Vect = np.log(p0Num / p0Denom)

图4-4 函数f(x)ln(f(x))会一块增大。

这表明想求函数的最大值时,可以使用该函数的自然对数来替换原函数进行求解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 3. 朴素贝叶斯分类函数
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
"""

:param vec2Classify:
:param p0Vec:
:param p1Vec:
:param pClass1:
:return:
"""
# 元素相乘
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
p0 = sum(vec2Classify * p0Vec) + np.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(setOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses))
# testEntry = ['love', 'my', 'dalmation']
testEntry = ['stupid', 'garbage']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, "classified as: ", classifyNB(thisDoc, p0V, p1V, pAb))

准备数据:文档词袋模型

词集模型(set-of-words model):将每个词的出现与否作为一个特征

词袋模型(bag-of-words model):如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息

在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。

为适应词袋模型,需要对函数setOfWords2Vec()稍加修改,修改后的函数称为bagOfWords2Vec()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 4. 基于词袋模型的朴素贝叶斯
# 它与函数setOfWords2Vec()几乎完全相同,唯一不同的是每当遇到一个单词时,它会增加词向量中的对应值,而不只是将对应的数值设为1
def bagOfWords2VecMN(vocabList, inputSet):
"""
根据词汇表、输入分词向量,返回编码后向量
:param vocabList:
:param inputSet:
:return:
"""
# 词汇表长度,初始化全0
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
# 以出现次数 计数:在 对应词汇表的位置 计出现次数
returnVec[vocabList.index(word)] += 1
return returnVec

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

  1. 收集数据:提供文本文件。
  2. 准备数据:将文本文件解析成词条向量。
  3. 分析数据:检查词条确保解析的正确性。
  4. 训练算法:使用我们之前建立的trainNB0()函数。
  5. 测试算法:使用classifyNB(),并且构建一个新的测试函数来计算文档集的错误率。
  6. 使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上。

准备数据:切分文本

1
2
3
4
5
6
7
8
9
10
11
mySent='This book is the best book on Python or M.L. I have ever laid eyes upon.'

import re
regEx = re.compile('\W+')
# 按正则切割字符串
listOfTokens = regEx.split(mySent)

# 小写化,去掉空字符串
[tok.lower() for tok in listOfTokens if len(tok) > 0]

# ['this', 'book', 'is', 'the', 'best', 'book', 'on', 'python', 'or', 'm', 'l', 'i', 'have', 'ever', 'laid', 'eyes', 'upon']

测试算法:使用朴素贝叶斯进行交叉验证

随机选择其中10个文件。

选择出的数字所对应的文档被添加到测试集,同时也将其从训练集中剔除。

这种随机选择数据的一部分作为训练集,而剩余部分作为测试集的过程称为留存交叉验证(hold-out cross validation)

假定现在只完成了一次迭代,那么为了更精确地估计分类器的错误率,就应该进行多次迭代后求出平均错误率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 5. 文件解析及完整的垃圾邮件测试函数
def textParse(bigString):
import re
listOfTokens = re.split(r'\W+', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]

def spamTest():
docList = []
classList = []
fullText = []
for i in range(1, 26):
# (以下8行) 导入并解析文本文件
wordList = textParse(open('email/spam/%d.txt' % i, encoding="ISO-8859-1").read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(open("email/ham/%d.txt" % i, encoding="ISO-8859-1").read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
# 创建字典(词汇表)
vocabList = createVocabList(docList)
trainingSet = range(50)
testSet = []
# (以下4行)随机构建训练集
for i in range(10):
randIndex = int(np.random.uniform(0, len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(list(trainingSet)[randIndex])
trainMat = []
trainClasses = []
for docIndex in trainingSet: # 训练分类器
trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V, p1V, pSpam = trainNB0(np.array(trainMat), np.array(trainClasses))
errorCount = 0
for docIndex in testSet: # 分类 测试集
wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
predCat = classifyNB(np.array(wordVector), p0V, p1V, pSpam)
realCat = classList[docIndex]
print("%d 预测: %d 真实: %d" % (docIndex, predCat, realCat))
if predCat != realCat:
errorCount += 1
print("classification error", docList[docIndex])
print("the error rate is:", float(errorCount)/len(testSet))

朴素贝叶斯 小结

首先我们需要先对文本进行切分,这被称为分词,由于训练集和测试集都是英文文本,所以在大多数情况我们只需要按空格切割即可,但是还是有部分符号,如逗号,点,问号,这些符号这被包含于正则表达式,然而还有部分字母是大写的,若目的是句子查找,那么这个特点会很有用,然而这里使用词袋模型,因此希望所有词的形式都是统一的,不论它们出现在句子中间、结尾还是开头,分词完成后,还需要转换为词向量,考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表,如 I, am, is 等可作为停用词不纳入词汇表,因为它们在两个类别中出现的频率是相同的,对于分类的作用也就微乎其微,接下来训练算法,从词向量计算概率,对每个类别都计算概率,选择最高概率的类别作为结果。

通过特征之间的条件独立性假设,降低对数据量的需求。

独立性假设是指 一个词的出现概率 并不依赖于文档中的其他词。

这个假设很简单,所以称之为 __朴素__贝叶斯

词袋模型在解决文档分类问题上比词集模型 有所提高

优化点:

  1. 切分词
  2. 转成词向量 所使用的模型(词袋,词集)
  3. 移除停用词

Q&A

Q: 模型选择:为什么选择 多项式贝叶斯 ?

A: 模型需要根据数据集中特征的特点来进行选取,垃圾邮件分类重点在于文档中单词出现的频率以及文档的重要性,

数据并不符合正态分布的特征,并且垃圾邮件判定过程是一个随机事件,单词在邮件中出现的次数并不是固定的,

因此不能选择高斯型分布模型,此处选择多项式分布模型。

多项式朴素贝叶斯分类器适用于具有离散特征的分类

补充

词向量特征构建

具体到文本分类问题中,经典的特征表示方法有词袋模型(Bag of words)和 tf-idf。

1. 词袋模型(Bag of words)

词袋模型故名思议,忽略其词序和语法,句法,将其仅仅看做是一个词集合,使用词袋模型将文本做特征表征就是一个根据词频(term frequency)表示成ones-hot的过程。

sklearn中有CountVectorizer即是它的实现

1
2
3
4
5
from sklearn.feature_extraction.text import CountVectorizer

vectorizer = CountVectorizer(stop_words='english')
train_x_fit_trans = vectorizer.fit_transform(train_x)
test_x_trans = vectorizer.transform(test_x)

2. tf-idf

根据词语出现次数 构建词袋 会出现一问题,长文章词语出现次数比短文章词语出现次数多,而实际上两篇文章可能同一主题。

于是,我们用 tf(term frequencies)---单词出现次数除以文章总单词书数,这样的方法来代替出现次数构建词袋字典。

除此之外,还有一问题:若一个词在许多文章中均有出现,那么它对于区分文章类别的作用就微乎其微了。

于是,有了 tf-idf(Term Frequency times Inverse Document Frequency) ---- 每个词再加上权重来构建词标记。

tf-idf算法 在 sklearn 中就是 TfidfTransformer

1
2
3
4
5
6
7
8
9
10
from sklearn.feature_extraction.text import TfidfTransformer

vectorizer = TfidfVectorizer(min_df=2,
ngram_range=(1,2),
stop_words='english',
strip_accents='unicode',
norm='l2')

train_x_fit_trans = vectorizer.fit_transform(train_x)
test_x_trans = vectorizer.transform(test_x)

分类准确率分数 accuracy_score

分类准确率分数 是指所有分类正确的百分比。

分类准确率这一衡量分类器的标准比较容易理解,但是它不能告诉你响应值的潜在分布,并且它也不能告诉你分类器犯错的类型。

召回率 recall_score

召回率 = 提取出的正确信息条数 / 样本中的信息条数。

通俗地说,就是所有准确的条目有多少被检索出来了。

1
sklearn.metrics.recall_score(y_true, y_pred, labels=None, pos_label=1,average='binary', sample_weight=None)

朴素贝叶斯的两种假设

由统计学知,若每个特征需要N个样本,那么对于10个特征将需要N的10次方个样本,对于包含1000个特征的词汇表将需要N的1000次 方个样本。可以看到,所需样本数会随着特征数目增大而迅速增长。

若特征之间相互独立,那么样本数就可以从N的1000次方减少到1000*N。所谓独立,即一个特征或者单词出现的可能性与它和其他单词相邻没有关系。这是朴素贝叶斯的一个假设,它的另一个假设是每个特征(单词)同等重要。

朴素贝叶斯分类器有两种实现方式:

  1. 基于伯努利模型实现
  2. 基于多项式模型实现 (伯努利模型方式并不考虑词在文档中出现的次数,只考虑出不出现,因此在这个意义上相当于假设词是等权重的)

多项式朴素贝叶斯

算法评价

参考

感谢帮助!