《机器学习实战》学习总结3——朴素贝叶斯

朴素贝叶斯

p(xy)=p(x|y)p(y)=p(y|x)p(x)
so:p(x|y)=p(y|x)p(x)/p(y)
优点:在数据较少的情况下仍然有效,可以处理多类别问题。
缺点:对于输入数据的准备方式比较敏感。
适用数据类型:标称型数据(有限)。
注释:“朴素”,这里指整个形式化过程只做最原始、最简单的假设。
本文中大量注释来源于Machelearning学习的Submission整理得到,方便自己和读者学习。

'''
Created on Oct 19, 2010

@author: Peter
'''
import numpy as np
#创建数据集 
def loadDataSet():
    """
    生成一个文本数据集和标签
    参数:
        无
    返回:
        postingList -- 文本列表
        classVec -- 标签分类
    
    """
    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']]  # 定义邮件列表
    classVec = [0,1,0,1,0,1]  # 1 代表侮辱性文字, 0 代表正常言论
    return postingList, classVec

def createVocabList(dataSet):  # 创建词汇列表
    """
    建立词汇表,也就是所有文本的并集
    参数:
        dataSet -- 数据集
    返回:
        vocabSet -- 词汇表
    """
    vocabSet = set([])  # 定义词汇集
    for document in dataSet:  # 遍历文档
        vocabSet = vocabSet | set(document)  # 将每个document合并到vocabSet,|用来联合两个集合
    return list(vocabSet)  
    
def setOfWords2Vec(vocabList, inputSet):  # 把单词转换成向量
    """
    把句子嵌入到词汇表中,也就是用表达一个句子
    向量中为1,代表词汇表对应该处的词汇;反之,不对应该处词汇
    参数:
        vocabList -- 词汇表
        inputSet -- 输入词汇集合
    返回:
        returnVec -- 返回句子对应的向量
    """
    returnVec = [0]*len(vocabList)  # 定义要返回的向量
    for word in inputSet:   # 遍历输出集中的单词
        if word in vocabList:   # 单词在词汇集中
            returnVec[vocabList.index(word)] = 1   # 对应的位置设为1
        else:
            print("the word: %s is not in my Vocabulary!" % word)
    return returnVec

def trainNB0(trainMatrix, trainCategory):
	"""
		函数说明:朴素贝叶斯分类器训练函数
		
		Parameters:
		    trainMatrix - 文档矩阵
		    trainCategory - 每篇文档类别标签所构成的向量
		Returns:
		    p0Vect - 非侮辱类的条件概率数组
		    p1Vect - 侮辱类的条件概率数组
		    pAbusive - 文档属于侮辱类的概率
	"""
    numTrainDocs = len(trainMatrix)   # 计算文档的数目
    numWords = len(trainMatrix[0])   # 计算单词的数目
    pAbusive = sum(trainCategory)/float(numTrainDocs)   # 计算类别的概率,abusive为1,not abusive为0
    p0Num = zeros(numWords)   # 初始化计数器,p0是not abusive
    p1Num = zeros(numWords) # 初始化计数器
    p0Denom = 0.0  # 初始化分母
    p1Denom = 0.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:   # 计算abusive对应的词汇的数目,trainMatrix为0-1值形成的向量
            p1Num += trainMatrix[i]    # p1Num存储的是每个词出现的次数
            p1Denom += sum(trainMatrix[i])   # p1Denom存储的是词的总数目
        else:
            p0Num += trainMatrix[i]   # 每个词在not abusive下出现的次数
            p0Denom += sum(trainMatrix[i])   # not abusive下的总词数
    p1Vect = p1Num/p1Denom   # change to log()   # 计算abusive下每个词出现的概率
    p0Vect = p0Num/p0Denom   # change to log()   # 计算not abusive下每个词出现的概率
    return p0Vect,p1Vect,pAbusive
    
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
	"""
	vec2Classify:待测数据
	p0Vec:类别1,即侮辱性文档的列表
	p1Vec:类别0,即正常性文档的列表
	pClass1:类别1,侮辱性文档出现的概率
	return:返回类别1 or 0
    计算概率,比较概率,得出结果
    由于所有概率均取了对数,所以乘法变加法,除法变减法
    由于只需要比较相对大小,相同的p(W)也就不用除了
    参数:
        vec2Classify -- 要分类的文本向量
        p0Vec -- p0向量
        p1Vec -- p1向量
        pClass1 -- 类别1的概率
    返回:
        判断结果 -- 0或1
    """
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)    #element-wise mult
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0

def bagOfWords2VecMN(vocabList, inputSet):   # 把单词转换成向量,用词袋模型,计算词出现的次数
    """
    文档的词袋模型
    参数:
        vocabList -- 词汇列表
        inputSet -- 输入词集
    返回:
        returnVec -- 返回向量
    """
    returnVec = [0]*len(vocabList)   # 定义要返回的向量
    for word in inputSet:  # 遍历输出集中的单词
        if word in vocabList:  # 单词在词汇集中
            returnVec[vocabList.index(word)] += 1    #对应的词出现次数 加1
    return returnVec

def testingNB():
	"""
	函数说明:朴素贝叶斯分类函数
	
	Parameters:
	    vec2Classify - 待分类的词条数组
	    p0Vec - 非侮辱类的条件概率数组
	    p1Vec - 侮辱类的条件概率数组
	    pClass1 - 文档属于侮辱类的概率
	Returns:
	    类别标签 - 1或0
	"""
	#加载数据集
    listOPosts, listClasses = loadDataSet()
    #创建单词集合
    myVocabList = createVocabList(listOPosts)
    trainMat = []
    #计算单词是否出现并创建矩阵
    #仅看单词是否出现,出现就为1,出现多次也为1
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    #训练数据
    p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses))#计算概率,计算不同类别下出现的概率
    #测试数据
    testEntry = ['love', 'my', 'dalmation']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
    testEntry = ['stupid', 'garbage']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))

def textParse(bigString):  # 文本解析   ### 输入是字符串,输出是单词列表
    """
    输入很长的字符串,转换为向量
    参数:
        bigString -- 长字符串
    返回:
        去掉少于两个字符,转换为小写的字符串
    """
    import re
    listOfTokens = re.split(r'\W*', bigString)
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]
    
def spamTest():
    docList = []  # 定义docList文档列表
    classList = []  # classList类别列表
    fullText = []   # fullText所有文档词汇
    for i in range(1,26):
        # 定义并读取垃圾邮件文件的词汇分割列表
        wordList = textParse(open('email/spam/%d.txt' % i).read())   # !!! 为了避免文件中非法字符的影响,尽量用下一行
        #wordList = textParse(open('email/spam/%d.txt' % i, "rb").read().decode('GBK','ignore'))
        
        docList.append(wordList)   # 将词汇列表加到文档列表中
        fullText.extend(wordList)   # 将所有词汇列表汇总到fullText中
        classList.append(1)   # 文档类别为1,spam
        
        #wordList = textParse(open('email/ham/%d.txt' % i).read())   # !!! 原书本中 bug 因为有可能文件中存在类似“�”非法字符。
        # 提示:UnicodeDecodeError: 'gbk' codec can't decode byte 0xae in position 199: illegal multibyte sequence
        wordList = textParse(open('email/ham/%d.txt' % i,  "rb").read().decode('GBK','ignore') )  # 读取非垃圾邮件的文档
        
        docList.append(wordList)  
        fullText.extend(wordList)
        classList.append(0)  # 类别为0,非垃圾邮件
        
    vocabList = createVocabList(docList)
    #trainingSet = range(50)  # !!! 原书本中 python2.7   提示:TypeError: 'range' object doesn't support item deletion
    #因为是python3中range不返回数组对象,而是返回range对象 
    trainingSet = list(range(50))   # 定义训练集的索引和测试集
    testSet = []
    for i in range(10):   # 随机的选择10个作为测试集
        randIndex = int(random.uniform(0,len(trainingSet)))  # 随机索引
        testSet.append(trainingSet[randIndex])  # 将随机选择的文档加入到测试集中
        del(trainingSet[randIndex])  # 从训练集中删除随机选择的文档
    trainMat = []   # 定义训练集的矩阵和类别 
    trainClasses = []
    for docIndex in trainingSet:    # 遍历训练集,求得先验概率和条件概率
        trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))   # 将词汇列表变为向量放到trainMat中
        trainClasses.append(classList[randIndex])   # 训练集的类别标签
    p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))    # 计算先验概率,条件概率
    errorCount = 0    
    for docIndex in testSet:
        wordVector = setOfWords2Vec(vocabList,docList[docIndex])   # 将测试集词汇向量化
        if classifyNB(array(wordVector), p0V, p1V,pSpam) != classList[docIndex]:   # 对测试数据进行分类
            errorCount += 1   # 分类不正确,错误计数加1
    print("the error rate is:",float(errorCount)/len(testSet))   
def calcMostFreq(vocabList, fullText):
    import operator
    freqDict = {}
    for token in vocabList:
        freqDict[token] = fullText.count(token)
    sortedFreq = sorted(freqDict.items(), key=operator.itemgetter(1), reverse=True)
    return sortedFreq[:30]

def localWords(feed1, feed0):
    import feedparser
    docList = []; classList = []; fullText = []
    minLen = min(len(feed1['entries']), len(feed0['entries']))
    for i in range(minLen):
        wordList = textParse(feed1['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1) #NY is class 1
        wordList = textParse(feed0['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList)#create vocabulary
    top30Words = calcMostFreq(vocabList, fullText)   #remove top 30 words
    for pairW in top30Words:
        if pairW[0] in vocabList: vocabList.remove(pairW[0])
    trainingSet = range(2*minLen); testSet = []           #create test set
    for i in range(20):
        randIndex = int(np.random.uniform(0, len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(list(trainingSet)[randIndex])
    trainMat = []; trainClasses = []
    for docIndex in trainingSet:#train the classifier (get probs) trainNB0
        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:        #classify the remaining items
        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
        if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            errorCount += 1
    print('the error rate is: ', float(errorCount)/len(testSet))
    return vocabList, p0V, p1V

def getTopWords(ny, sf):
    import operator
    vocabList, p0V, p1V = localWords(ny, sf)
    topNY = []; topSF = []
    for i in range(len(p0V)):
        if p0V[i] > -6.0: topSF.append((vocabList[i], p0V[i]))
        if p1V[i] > -6.0: topNY.append((vocabList[i], p1V[i]))
    sortedSF = sorted(topSF, key=lambda pair: pair[1], reverse=True)
    print("SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**")
    for item in sortedSF:
        print(item[0])
    sortedNY = sorted(topNY, key=lambda pair: pair[1], reverse=True)
    print("NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**")
    for item in sortedNY:
        print(item[0])

猜你喜欢

转载自blog.csdn.net/qq_41319343/article/details/83831820
今日推荐