【机器学习实战】第四章朴素贝叶斯
代码如下(无4.7节代码):
1 import numpy as np 2 import re 3 4 def loadDataSet(): 5 # 创建一些实验样本,每一行为一条评论 6 # 该函数返回的第一个变量是进行词条切分后的文档集合,这些文档来自斑点犬爱好者留言板。 7 # 该函数返回的第二个变量是一个类别标签的集合。这里有两类,侮辱性和非侮辱性。这些标注信息用于训练程序以便自动检测侮辱性留言。 8 postingList=[['my','dog','has','flea','problems','help','please'], 9 ['maybe','not','take','him','to','dog','park','stupid'], 10 ['my','dalmation','is','so','cute','I','love','him'], 11 ['stop','posting','stupid','worthless','garbage'], 12 ['mr','licks','ate','my','steak','how','to','stop','him'], 13 ['quit','buying','worthless','dog','food','stupid']] 14 classVec = [0,1,0,1,0,1] # 1代表侮辱性文字,0代表正常言论 15 return postingList,classVec 16 17 18 def createVocabList(dataSet): 19 # 创建一个包含在所有文档中出现的不重复词的列表 20 vocabSet = set([]) # 将词条列表输给set构造函数,set就会返回一个不重复词表。此处创建一个空集合 21 for document in dataSet: 22 vocabSet = vocabSet | set(document) # 创建两个集合的并集,将每篇文档返回的新词集合添加到vocabSet集合中 23 return list(vocabSet) 24 25 26 def setOfWords2Vec(vocabList, inputSet): 27 ''' 28 词集模型 29 :param vocabList: 词汇表 30 :param inputSet: 某个文档 31 :return: 文档向量,向量的每一元素为1或0,分别表示词汇表中的单词在输入文档中是否出现。 32 ''' 33 returnVec = [0] * len(vocabList) # 创建一个和词汇表等长的向量,并将其元素都设置为0。 34 for word in inputSet: 35 # 遍历文档中的所有单词,如果出现了词汇表中的单词,则将输出的文档向量中的对应值设为1。 36 if word in vocabList: 37 returnVec[vocabList.index(word)] = 1 38 else: 39 print("the word: %s is not in my Vocabulary!" % word) 40 return returnVec 41 42 43 def bagOfWords2Vec(vocabList, inputSet): 44 ''' 45 词袋模型 46 :param vocabList: 词汇表 47 :param inputSet: 某个文档 48 :return: 文档向量。 49 ''' 50 returnVec = [0] * len(vocabList) # 创建一个和词汇表等长的向量,并将其元素都设置为0。 51 for word in inputSet: 52 # 遍历文档中的所有单词,如果出现了词汇表中的单词,则将输出的文档向量中的对应值设为1。 53 if word in vocabList: 54 returnVec[vocabList.index(word)] += 1 55 else: 56 print("the word: %s is not in my Vocabulary!" % word) 57 return returnVec 58 59 60 def trainNB0(trainMatrix, trainCategory): 61 ''' 62 63 :param trainMatrix: 文档矩阵 64 :param trainCategory: 由每篇文档类别标签所构成的向量 65 :return: 66 ''' 67 numTrainDocs = len(trainMatrix) 68 numWords = len(trainMatrix[0]) 69 pAbusive = sum(trainCategory)/float(numTrainDocs) # 侮辱性类别的评论出现的概率(侮辱性类别的评论数除以所有评论数) 70 p0Num = np.ones(numWords) 71 p1Num = np.ones(numWords) 72 p0Denom = 2.0 73 p1Denom = 2.0 74 # 初始化程序中的分子变量和分母变量 75 for i in range(numTrainDocs): 76 # 遍历训练集trainMatrix中的所有文档 77 if trainCategory[i] == 1: 78 p1Num += trainMatrix[i] # 在侮辱性类别下的每条评论中的每个单词出现的词数 79 p1Denom += sum(trainMatrix[i]) # 侮辱类别共有词数19 80 else: 81 p0Num += trainMatrix[i] # 在正常类别下的每条评论中的每个单词出现的次数 82 p0Denom += sum(trainMatrix[i]) # 当前类别的总词数,正常类别共有词数24 83 p1Vect = np.log(p1Num/p1Denom) 84 # 侮辱类别中每个单词出现的词数除以该类别中的总词数,这里计算的是p(w|c1)/p(w1),乘以pAbusive即为条件概率p(c1|w), 85 # 注意这里的w仅为当前类别的总词数,并不是所有的词数 86 p0Vect = np.log(p0Num/p0Denom) # 正常类别中每个单词出现的词数除以该类别中的总词数,这里计算的是p(w|c0)/p(w0) 87 return p0Vect, p1Vect, pAbusive 88 89 90 def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): 91 p1 = sum(vec2Classify * p1Vec) + np.log(pClass1) # 本来是概率相乘,但使用了log处理所以是相加 92 # vec2Classify记录的是当前测试邮件中的每一个单词在所有邮件单词的集合中出现的位置 93 # p1Vec是垃圾邮件中每个单词出现的概率,是条件概率;同理p0Vec是正常邮件中每个单词出现的概率 94 # vec2Classify与p1Vec相乘就是保留当前邮件的单词在垃圾邮件的条件下出现的概率,再乘上垃圾邮件的概率就是当前邮件是垃圾邮件的概率 95 p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1) 96 if p1 > p0: 97 return 1 98 else: 99 return 0 100 101 102 def testingNB(): 103 104 listOposts, listClasses = loadDataSet() 105 myVocabList = createVocabList(listOposts) 106 print(myVocabList) 107 # resVect1 = setOfWords2Vec(myVocabList, listOposts[0]) 108 # resVect2 = setOfWords2Vec(myVocabList, listOposts[3]) 109 # print(resVect1) 110 # print(resVect2) 111 trainMat = [] 112 for postinDoc in listOposts: 113 trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) 114 p0V, p1V, pAb = trainNB0(trainMat, listClasses) 115 # print("p0V",p0V) 116 # print("p1V",p1V) 117 # print('pAb=',pAb) 118 testEntry = ['love', 'my', 'dalmation'] 119 thisDoc = setOfWords2Vec(myVocabList, testEntry) 120 print(testEntry, 'classified as:',classifyNB(thisDoc, p0V, p1V, pAb)) 121 testEntry = ['stupid', 'garbage'] 122 thisDoc = setOfWords2Vec(myVocabList, testEntry) 123 print(testEntry, 'classified as:', classifyNB(thisDoc, p0V, p1V, pAb)) 124 125 126 def textParse(bigString): 127 ''' 128 接受一个大字符串并将其解析为字符串列表,该函数去掉少于两个字符的字符串,并将所有字符串转换为小写 129 :param bigString:大字符串 130 :return: 131 ''' 132 listOfTokens = re.compile(r'\b[a-zA-Z0-9]+\b').findall(bigString) # 正则表达式匹配字母和数字 133 reslist = [tok.lower() for tok in listOfTokens if len(tok)>2] 134 return reslist 135 136 137 def spamTest(): 138 ''' 139 对贝叶斯垃圾邮件分类器进行自动化处理。 140 :return: 141 ''' 142 docList = [] 143 classList = [] 144 fullText = [] 145 maxepoic = 100 146 for i in range(1,26): 147 # print(i) 148 wordList = textParse(open('../machinelearninginaction/Ch04/email/spam/%d.txt'%i).read()) 149 docList.append(wordList) 150 fullText.extend(wordList) 151 # 导入文件夹spam与ham下的文本文件,并将他们解析为词列表。 152 classList.append(1) # 垃圾邮件 153 wordList = textParse(open('../machinelearninginaction/Ch04/email/ham/%d.txt'%i).read()) 154 docList.append(wordList) 155 fullText.extend(wordList) 156 classList.append(0) 157 print(docList) 158 vocabList = createVocabList(docList) 159 errorsum =0 160 for k in range(maxepoic): 161 # print(k) 162 trainingSet = list(range(50)) # 构建训练集,整数列表,值从0到49 163 testSet = [] # 构建测试集,两个集合中的邮件都是随机选出的 164 for i in range(10): 165 randIndex = int(np.random.uniform(0,len(trainingSet))) # 10封电子邮件被随机选为测试集 166 # 这里随机生成0~50之间服从正态分布的随机数 167 testSet.append(trainingSet[randIndex]) # 将训练集中对应索引的样本添加到测试集中 168 del(trainingSet[randIndex]) # 删除训练集中的样本 169 # 这种随机选择数据的一部分作为训练集,而剩余部分作为测试集的过程称为留存交叉验证(hold-out cross validation) 170 trainMat = [] 171 trainClasses = [] 172 classificationError = [] 173 for docIndex in trainingSet: 174 # 遍历训练集的所有文档,对每封邮件基于词汇表并使用setOfWords2Vec函数来构建词向量。 175 trainMat.append(setOfWords2Vec(vocabList, docList[docIndex])) 176 trainClasses.append(classList[docIndex]) 177 p0V,p1V,pSpam = trainNB0(trainMat, trainClasses) # 计算分类所需的概率 178 errorCount = 0 179 for docIndex in testSet: 180 # 遍历测试集,对其中每封电子邮件进行分类 181 wordVector = setOfWords2Vec(vocabList, docList[docIndex]) 182 if classifyNB(wordVector,p0V,p1V,pSpam) != classList[docIndex]: 183 # 如果邮件分类错误,则错误数加1,最后给出总的错误百分比 184 errorCount += 1 185 classificationError.append(docList[docIndex]) 186 #print(docIndex) 187 errorrate = float(errorCount)/len(testSet) 188 errorsum += errorrate 189 #print('the error rate is:', float(errorCount)/len(testSet)) 190 #print('classification error', classificationError) 191 mean_errorrate = errorsum/maxepoic 192 print('平均错误率为',mean_errorrate) 193 194 195 if __name__ == '__main__': 196 spamTest() 197 # testingNB()
运行结果:
平均错误率为 0.05000000000000001