第四章--基于概率论的分类方法:朴素贝叶斯--过滤垃圾邮件(二)
朴素贝叶斯对电子邮件进行分类的步骤:
收集数据:提供文本文件。
准备数据:将文本文件解析成词条向量。
分析数据:检查词条确保解析的正确性。
训练算法:使用我们之前建立的trainNB0()函数。
测试算法:使用classifyNB(),并构建一个新的测试函数来计算文档集的错误率。
使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上。
1 import re 2 3 """ 4 函数说明:接收一个大字符串并将其解析为字符串列表 5 Parameters: 6 无 7 Returns: 8 无 9 """ 10 def textParse(bigString): #将字符串转换为字符列表 11 listOfTokens = re.split(r'\W*', bigString) #将特殊符号作为切分标志进行字符串切分,即非字母、非数字 12 return [tok.lower() for tok in listOfTokens if len(tok) > 2] #除了单个字母,例如大写的I,其它单词变成小写 13 """ 14 函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表 15 Parameters: 16 dataSet - 整理的样本数据集 17 Returns: 18 vocabSet - 返回不重复的词条列表,也就是词汇表 19 """ 20 def createVocabList(dataSet): 21 vocabSet = set([]) #创建一个空的不重复列表 22 for document in dataSet: 23 vocabSet = vocabSet | set(document) #取并集 24 return list(vocabSet) 25 26 if __name__ == '__main__': 27 docList = []; classList = [] 28 for i in range(1, 26): #遍历25个txt文件 29 wordList = textParse(open('email/spam/%d.txt' % i, 'r').read()) #读取每个垃圾邮件,并字符串转换成字符串列表 30 docList.append(wordList) 31 classList.append(1) #标记垃圾邮件,1表示垃圾文件 32 wordList = textParse(open('email/ham/%d.txt' % i, 'r').read()) #读取每个非垃圾邮件,并字符串转换成字符串列表 33 docList.append(wordList) 34 classList.append(0) #标记非垃圾邮件,1表示垃圾文件 35 vocabList = createVocabList(docList) #创建词汇表,不重复 36 print(vocabList) 37 38 #根据词汇表,我们就可以将每个文本向量化。我们将数据集分为训练集和测试集, 39 #使用交叉验证的方式测试朴素贝叶斯分类器的准确性。 40 import numpy as np 41 import random 42 """ 43 函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0 44 Parameters: 45 vocabList - createVocabList返回的列表 46 inputSet - 切分的词条列表 47 Returns: 48 returnVec - 文档向量,词集模型 49 """ 50 def setOfWords2Vec(vocabList, inputSet): 51 returnVec = [0] * len(vocabList) #创建一个其中所含元素都为0的向量 52 for word in inputSet: #遍历每个词条 53 if word in vocabList: #如果词条存在于词汇表中,则置1 54 returnVec[vocabList.index(word)] = 1 55 else: print("the word: %s is not in my Vocabulary!" % word) 56 return returnVec #返回文档向量 57 """ 58 函数说明:根据vocabList词汇表,构建词袋模型 59 Parameters: 60 vocabList - createVocabList返回的列表 61 inputSet - 切分的词条列表 62 Returns: 63 returnVec - 文档向量,词袋模型 64 """ 65 def bagOfWords2VecMN(vocabList, inputSet): 66 returnVec = [0]*len(vocabList) #创建一个其中所含元素都为0的向量 67 for word in inputSet: #遍历每个词条 68 if word in vocabList: #如果词条存在于词汇表中,则计数加一 69 returnVec[vocabList.index(word)] += 1 70 return returnVec #返回词袋模型 71 """ 72 函数说明:朴素贝叶斯分类器训练函数 73 Parameters: 74 trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵 75 trainCategory - 训练类别标签向量,即loadDataSet返回的classVec 76 Returns: 77 p0Vect - 侮辱类的条件概率数组 78 p1Vect - 非侮辱类的条件概率数组 79 pAbusive - 文档属于侮辱类的概率 80 """ 81 def trainNB0(trainMatrix, trainCategory): 82 numTrainDocs = len(trainMatrix) #计算训练的文档数目 83 numWords = len(trainMatrix[0]) #计算每篇文档的词条数 84 pAbusive = sum(trainCategory)/float(numTrainDocs) #文档属于侮辱类的概率 85 p0Num = np.ones(numWords); p1Num = np.ones(numWords) #创建numpy.ones数组,词条出现数初始化为1,拉普拉斯平滑 86 p0Denom = 2.0; p1Denom = 2.0 #分母初始化为2,拉普拉斯平滑 87 for i in range(numTrainDocs): 88 if trainCategory[i] == 1: #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)··· 89 p1Num += trainMatrix[i] 90 p1Denom += sum(trainMatrix[i]) 91 else: #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)··· 92 p0Num += trainMatrix[i] 93 p0Denom += sum(trainMatrix[i]) 94 p1Vect = np.log(p1Num/p1Denom) #取对数,防止下溢出 95 p0Vect = np.log(p0Num/p0Denom) 96 return p0Vect, p1Vect, pAbusive #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率 97 """ 98 函数说明:朴素贝叶斯分类器分类函数 99 Parameters: 100 vec2Classify - 待分类的词条数组 101 p0Vec - 侮辱类的条件概率数组 102 p1Vec -非侮辱类的条件概率数组 103 pClass1 - 文档属于侮辱类的概率 104 Returns: 105 0 - 属于非侮辱类 106 1 - 属于侮辱类 107 """ 108 def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): 109 p1 = sum(vec2Classify * p1Vec) + np.log(pClass1) #对应元素相乘。logA * B = logA + logB,所以这里加上log(pClass1) 110 p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1) 111 if p1 > p0: 112 return 1 113 else: 114 return 0 115 """ 116 函数说明:朴素贝叶斯分类器训练函数 117 Parameters: 118 trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵 119 trainCategory - 训练类别标签向量,即loadDataSet返回的classVec 120 Returns: 121 p0Vect - 侮辱类的条件概率数组 122 p1Vect - 非侮辱类的条件概率数组 123 pAbusive - 文档属于侮辱类的概率 124 """ 125 def trainNB0(trainMatrix, trainCategory): 126 numTrainDocs = len(trainMatrix) #计算训练的文档数目 127 numWords = len(trainMatrix[0]) #计算每篇文档的词条数 128 pAbusive = sum(trainCategory)/float(numTrainDocs) #文档属于侮辱类的概率 129 p0Num = np.ones(numWords); p1Num = np.ones(numWords) #创建numpy.ones数组,词条出现数初始化为1,拉普拉斯平滑 130 p0Denom = 2.0; p1Denom = 2.0 #分母初始化为2,拉普拉斯平滑 131 for i in range(numTrainDocs): 132 if trainCategory[i] == 1: #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)··· 133 p1Num += trainMatrix[i] 134 p1Denom += sum(trainMatrix[i]) 135 else: #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)··· 136 p0Num += trainMatrix[i] 137 p0Denom += sum(trainMatrix[i]) 138 p1Vect = np.log(p1Num/p1Denom) #取对数,防止下溢出 139 p0Vect = np.log(p0Num/p0Denom) 140 return p0Vect, p1Vect, pAbusive #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率 141 """ 142 函数说明:接收一个大字符串并将其解析为字符串列表 143 Parameters: 144 无 145 Returns: 146 无 147 """ 148 def textParse(bigString): #将字符串转换为字符列表 149 listOfTokens = re.split(r'\W*', bigString) #将特殊符号作为切分标志进行字符串切分,即非字母、非数字 150 return [tok.lower() for tok in listOfTokens if len(tok) > 2] #除了单个字母,例如大写的I,其它单词变成小写 151 """ 152 函数说明:测试朴素贝叶斯分类器 153 Parameters: 154 无 155 Returns: 156 无 157 """ 158 def spamTest(): 159 docList = []; classList = []; fullText = [] 160 for i in range(1, 26): #遍历25个txt文件 161 wordList = textParse(open('email/spam/%d.txt' % i, 'r').read()) #读取每个垃圾邮件,并字符串转换成字符串列表 162 docList.append(wordList) 163 fullText.append(wordList) 164 classList.append(1) #标记垃圾邮件,1表示垃圾文件 165 wordList = textParse(open('email/ham/%d.txt' % i, 'r').read()) #读取每个非垃圾邮件,并字符串转换成字符串列表 166 docList.append(wordList) 167 fullText.append(wordList) 168 classList.append(0) #标记非垃圾邮件,1表示垃圾文件 169 vocabList = createVocabList(docList) #创建词汇表,不重复 170 trainingSet = list(range(50)); testSet = [] #创建存储训练集的索引值的列表和测试集的索引值的列表 171 for i in range(10): #从50个邮件中,随机挑选出40个作为训练集,10个做测试集 172 randIndex = int(random.uniform(0, len(trainingSet))) #随机选取索索引值 173 testSet.append(trainingSet[randIndex]) #添加测试集的索引值 174 del(trainingSet[randIndex]) #在训练集列表中删除添加到测试集的索引值 175 trainMat = []; trainClasses = [] #创建训练集矩阵和训练集类别标签系向量 176 for docIndex in trainingSet: #遍历训练集 177 trainMat.append(setOfWords2Vec(vocabList, docList[docIndex])) #将生成的词集模型添加到训练矩阵中 178 trainClasses.append(classList[docIndex]) #将类别添加到训练集类别标签系向量中 179 p0V, p1V, pSpam = trainNB0(np.array(trainMat), np.array(trainClasses)) #训练朴素贝叶斯模型 180 errorCount = 0 #错误分类计数 181 for docIndex in testSet: #遍历测试集 182 wordVector = setOfWords2Vec(vocabList, docList[docIndex]) #测试集的词集模型 183 if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]: #如果分类错误 184 errorCount += 1 #错误计数加1 185 print("分类错误的测试集:", docList[docIndex]) 186 print('错误率:%.2f%%' % (float(errorCount) / len(testSet) * 100)) 187 188 if __name__ == '__main__': 189 spamTest()