以在线社区留言为例,为了不影响社区的发展,我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标志为内容不当。
过滤这类内容是一个很常见的需求。 对此问题建立两个类型:侮辱类和非侮辱类,使用1和0分别表示。我们把文本看成单词向量或者词条向量,也就是说将句子转换为向量。简单起见,我们先假设已经将本文切分完毕,存放到列表中,并对词汇向量进行分类标注。
示例:使用朴素贝叶斯对电子邮件进行分类
- 收集数据:提供文本文件。
- 准备数据:将文本文件解析成词条向量。
- 分析数据:检查词条确保解析的正确性。
- 训练算法:使用我们之前建立的trainNB0()函数。
- 测试算法:使用classifyNB(),并且构建一个新的测试函数来计算文档集的错误率。
- 使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上。
收集数据:从文本文件中读入
对于一个文本字符串,可以使用Python的string.split()将其切分。
另外,我们发现有些句子中的单词是大写的。如果目的是句子查找,那么这个特点会很有用。但这里的文本只看成词袋,所以我们希望所有词的形式都是统一的, 不论它们出现在句子中间、结尾还是开头。
Python中有一些内嵌的方法, 可以将字符串全部转换成小写( .1ower() )或者大写( .upper()),借助这些方法可以达到目的。于是,可以进行如下处理:
函数说明:接收一个大字符串并将其解析为字符串列表
def textParse(bigString):
import re
wordList = []
listOfTokens = re.split(r'\W', bigString)
for tok in listOfTokens:
if len(tok) > 2:
wordList.append(tok.lower())
return wordList
函数说明:从文件中读取数据
def getDataset():
postingList = []
classVec = []
for i in range(1, 26):
wordList = textParse(open('email/spam/%d.txt' % i,
'r').read())
postingList.append(wordList)
classVec.append(1)
wordList = textParse(open('email/ham/%d.txt' % i,
'r').read())
postingList.append(wordList)
classVec.append(0)
return postingList, classVec
做出以上处理后,大致的结果如下:

准备数据:将文本文件解析成词条向量
函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表
Parameters:
dataSet – 整理的样本数据集
Returns:
vocabSet – 返回不重复的词条列表,也就是词汇表
def createVocabList(postingList):
vocabList = set([])
for document in postingList:
vocabList = vocabList | set(document)
vocabList = list(vocabList)
return vocabList
做出以上处理后,大致的结果如下:

分析数据:检查词条确保解析的正确性
函数说明:根据vocabList词汇表,将postingList转化成词袋模式数据,数据的每个元素为n,n用来统计该词汇在文中出现的次数
Parameters:
vocabList – createVocabList返回的列表
postingList -整理的样本数据集
Returns:
trainMat – 转化为词袋模式的数据集
def setOfWords2Vec(vocabList, postingList):
trainMat = []
for postinDoc in postingList:
returnVec = [0] * len(vocabList)
for word in postinDoc:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
trainMat.append(returnVec)
return trainMat
做出以上处理后,大致的结果如下:

训练算法:使用我们之前建立的trainNB0()函数
函数说明:朴素贝叶斯分类器训练函数
Parameters:
trainMatrix – 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
trainCategory – 训练类别标签向量,即loadDataSet返回的classVec
Returns:
p0Vect – 侮辱类的条件概率数组
p1Vect – 非侮辱类的条件概率数组
pAbusive -文档属于侮辱类的概率
def trainNB0(trainMat, classVec):
numTrainDocs = len(trainMat)
numWords = len(trainMat[0])
pAbusive = sum(classVec)/float(numTrainDocs)
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
if classVec[i] == 1:
p1Num += trainMat[i]
p1Denom += sum(trainMat[i])
else:
p0Num += trainMat[i]
p0Denom += sum(trainMat[i])
p1Vect = np.log(p1Num/p1Denom)
p0Vect = np.log(p0Num/p0Denom)
return p0Vect, p1Vect, pAbusive
构建算法:实施朴素贝叶斯进行分类
函数说明:实施文本预测的分类
Parameters:
testEntry – 待分类的词条数组
p0Vec – 侮辱类的条件概率数组
p1Vec – 非侮辱类的条件概率数组
pClass1 – 文档属于侮辱类的概率
Returns:
result – 预测结果
def classifyNB(testEntry, p0Vect, p1Vect, pAbusive, vocabList):
testMat = setOfWords2Vec(vocabList, testEntry)
p1 = np.sum(testMat * p1Vect, axis=1) + np.log(pAbusive)
p0 = np.sum(testMat * p0Vect, axis=1) + np.log(1.0-pAbusive)
result = [0]*len(testEntry)
for i in range(len(testMat)):
print("p1=%.2f,p0=%.2f" % (p1[i], p0[i]))
if p1[i] > p0[i]:
print("%s属于侮辱类" % testEntry[i])
result[i] = 1
else:
print("%s属于非侮辱类" % testEntry[i])
return result
使用算法:使用分类器进行分类
函数说明:测试朴素贝叶斯分类器
Parameters:
无
Returns:
无
def testingNB():
postingList, classVec = getDataset()
vocabList = createVocabList(postingList)
trainMat = setOfWords2Vec(vocabList, postingList)
p0V,p1V,pAb = trainNB0(trainMat, classVec)
testEntry = [[' program', 'professional', 'connection'],
['volume', 'only']]
classifyNB(testEntry, p0V, p1V, pAb, vocabList)
做出以上处理后,最终预测结果如下:

交叉验证:计算分类器的错误率
函数说明:交叉验证。从实验数据中随机选择一定比例的数据作为测试数据,其余数据作为训练数据
Parameters:
postinglist – 文中读取到的实验数据特征值部分
lasslist – 文中读取到的实验数据目标值部分
testRatio – 测试数据占比,默认为0.20
Returns:
trainSet: – 训练数据特征值部分
testSet – 测试数据特征值部分
trainlasses – 测试数据目标值部分
testClasses – 测试数据分类目标值部分
def getTrainTest(postingList, classList, testRatio=0.20):
m = len(postingList)
numTestVecs = int(m * testRatio)
randomIndex = np. random. randint(0, 50, numTestVecs)
testSet = []
testClasses = []
trainSet = []
trainClasses = []
for i in range(50):
if i in randomIndex:
testSet.append(postingList[i])
testClasses.append(classList[i])
else:
trainSet.append(postingList[i])
trainClasses.append(classList[i])
return trainSet, testSet, trainClasses, testClasses
函数说明:执行5次交叉验证,计算平均错误率
Parameters:
无
Returns:
无
def spanTest():
relist = []
for j in range(5):
postingList, classList = getDataset()
trainSet, testSet, trainClasses, testClasses
= getTrainTest(postingList, classList)
vocabList = createVocabList(trainSet)
trainMat = setOfWords2Vec(vocabList, trainSet)
p0V, p1V, pAb = trainNB0(trainMat, trainClasses)
errorCount = 0.0
result = classifyNB(testSet, p0V, p1V, pAb, vocabList)
for i in np.arange(len(testClasses)):
if result[i] != testClasses[i]:
errorCount += 1.0
relist.append(errorCount / float(len(testClasses)))
print("第%d次错误率为:%.2f" % (j + 1, relist[j]))
error = sum(relist)
error /= 5
print("平均的错误率为:%.2f" % error)
做出以上处理后,错误率如下:

写在最后
这就是实施朴素贝叶斯算法的全部过程,包括预测分类以及计算分类器错误率,并且错误率也在接受范围之内。到此为止也就基本解决了简单的邮件分类这个实际问题,这也是朴素贝叶斯的重要应用。