【机器学习实战】第四章朴素贝叶斯

代码如下(无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

 

posted @ 2020-07-13 16:44  DJames23  阅读(241)  评论(0编辑  收藏  举报