暑假期间,总结一下以前学过的知识,如有错误,还请指导.
本系列皆是基于<机器学习实战>、李航的<统计学习方法>以及<西瓜书>所记的一些笔记
先提一个最简单的例子:
现在用p1(x,y)表示数据点(x,y)属于类别1的概率,
用p2(x,y)表示数据点(x,y)属于类别2(图中用三角形表示的类别)的概率,
那么对于一个新数据点(x,y),可以用下面的规则来判断它的类别:
如果 p1(x,y) > p2(x,y),那么类别为1。
如果 p2(x,y) > p1(x,y),那么类别为2
这就是分类的准则,已知参数(x1、x2...xn)求每个类别的概率,然后选择最大的概率,作为该类别
下面是贝叶斯准则:
贝叶斯准则告诉我们如何交换条件概率中
的条件与结果,即如果已知P(x|c),要求P(c|x),那么可以使用下面的计算方法
注意,这里的X代表特征向量,如果用例子来表示,可以看成这样
不过可以使用贝叶斯准则
来交换概率中条件与结果。具体地,应用贝叶斯准则得到:
上面这个公式表示:已知特征向量(x,y),求属于类别Ci的概率,找一个最大的概率,作为类别
即求max(P(ci|x, y)),可以看成有上面等式右边,分子的最大值
这时候还要提及朴素贝叶斯:假设特征之间相互独立
如果将w展开为一个个独立特征,那么就可
以将上述概率写作p(w0,w1,w2..wN|ci)。这里假设所有词都互相独立,该假设也称作条件独立性
假设,它意味着可以使用p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)来计算上述概率,这就极大地
简化了计算的过程。
即求
计算该函数的伪代码如下:
计算每个类别中的文档数目
对每篇训练文档:
对每个类别:
如果词条出现在文档中→ 增加该词条的计数值
增加所有词条的计数值
对每个类别:
对每个词条:
将该词条的数目除以总词条数目得到条件概率
返回每个类别的条件概率
来从代码深入了解吧,其中test1是检测一句话有没有侮辱性言论,test2是垃圾邮件过滤的小demo
#coding=utf-8
import numpy as np
def loadDataSet():
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):
'''
:param dataSet: 数据集,由单词单词
:return: 单词集合,注意,里面没有重复的
'''
vocabSet = set([]) # 创建一个空的集合
for document in dataSet:
vocabSet = vocabSet | set(document) # 求集合的并集
return list(vocabSet) # 注意,必须要有list,因为集合是无序的,无法通过索引来找元素,所以需要list
def setOfWords2Vec(vocabSet, inputSet):
'''
:param vocabSet: 已经在标记过的词条数目
:param inputSet: 输入的词条
:return: 返回词条向量,看输入的词条石头在已经标记过的词条中,已经出现过的为1,没有的为0,方便后面统计数目\计算概率
'''
resVec = [0] * len(vocabSet)
for word in inputSet:
if(word in vocabSet):
resVec[vocabSet.index(word)] = 1 # 词集模型,遇到一个单词就为1,另外一种情况是词带
else:
print("the word: %s is not in my vocabulary!" %word)
return resVec
def bagOfWords2Vec(vocabSet, inputSet):
'''
:param vocabSet: 已经在标记过的词条数目
:param inputSet: 输入的词条
:return: 返回词条向量,看输入的词条石头在已经标记过的词条中,已经出现过的为1,没有的为0,方便后面统计数目\计算概率
'''
resVec = [0] * len(vocabSet)
for word in inputSet:
if(word in vocabSet):
resVec[vocabSet.index(word)] += 1 # 词袋模型,有的单词会出现多次
else:
print("the word: %s is not in my vocabulary!" %word)
return resVec
def trainNB0(trainMatrix, trainCategory):
'''
:param trainMatrix:词向量,既已经有单词转化成特征数字,这里表示0和1
:param trainCategory:对应的label,这里对应每一行的词向量的label
:return:p0Vec, 类别为0时,各特征出现的条件概率
p1Vec, 类别为1时,各特征出现的条件概率
p1bsuive 类别1的概率,由于是二分类,所以类别0可以看成1-p1
'''
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
p1bsuive = 1.0 * sum(trainCategory) / numTrainDocs #因为矩阵里只有0和1,所以sum就是统计1的个数
# 然后就可以得到样本中侮辱性的概率
# p0Num = np.zeros(numWords)
# p1Num = np.zeros(numWords)
# p0Sum = 0.0
# p1Sum = 0.0
p0Num = np.ones(numWords) # 因为小数相乘会变的很小,这里要用log代替,防止出现数学溢出,所以先加1
p1Num = np.ones(numWords)
p0Sum = 2.0
p1Sum = 2.0
for i in range(numTrainDocs):
if(trainCategory[i] == 0): #统计类别为0时,哥哥特征出现的次数
p0Num += trainMatrix[i]
p0Sum += sum(trainMatrix[i])# 统计类别为0时,特征总数,作为分母
else:
p1Num += trainMatrix[i]
p1Sum += sum(trainMatrix[i])
#
# p0Vec = 1.0 * p0Num / p0Sum
# p1Vec = 1.0 * p1Num / p1Sum
p0Vec = np.log(1.0 * p0Num / p0Sum) # log(A) + log(B) = log(AB)
p1Vec = np.log(1.0 * p1Num / p1Sum)
return p0Vec, p1Vec, p1bsuive
def classifyNb(vec2Classify, p0Vec, p1Vec, pClass1):
'''
:param vec2Classify: 需要被分类的词向量
:param p0Vec: 类别为0时,各特征出现的条件概率
:param p1Vec: 类别为1时,各特征出现的条件概率
:param pClass1: 类别1的概率,由于是二分类,所以类别0可以看成1-p1
:return: 预测类别
'''
p0 = sum(vec2Classify * p0Vec) + np.log(1-pClass1)
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
if(p0>p1):
return 0
else:
return 1
def test1(): # 第一个例子,网站留言是否带有侮辱性词汇
listPosts, listLabels = loadDataSet() # 加载数据
myVecabList = createVocabList(listPosts) # 词集合,里面没有重复的单词,有训练集里面得出
print myVecabList
trainMat = []
for postDoc in listPosts:
vec = setOfWords2Vec(myVecabList, postDoc) # 将单词转化成词向量
trainMat.append(vec) #最终将所有训练集都变成词向量
p0Vec, p1Vec, p1bsuive = trainNB0(np.array(trainMat), np.array(listLabels)) # 得出贝叶斯条件概率
testVec = ['love', 'my', 'dalmation'] # 这是我们的一个测试向量
vecMat = setOfWords2Vec(myVecabList, testVec) # 同样需要转换成词向量
# print vecTest
classRes = classifyNb(np.array(vecMat), p0Vec, p1Vec, p1bsuive) # 开始测试
print(testVec,'classified is ',classRes)
testVec = ['stupid', 'garbage']
vecMat = setOfWords2Vec(myVecabList, testVec)
# print vecTest
classRes = classifyNb(vecMat, p0Vec, p1Vec, p1bsuive)
print(testVec, 'classified is ', classRes)
def txtParse(bigString):
import re
listOfTokens = re.split(r'\W*', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def spamTest2():
docList=[]; classList = []; fullText =[]
for i in range(1,26):
wordList = txtParse(open('email/spam/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = txtParse(open('email/ham/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList) # 词集合,里面没有重复的单词,有训练集里面得出
trainingSet = range(50); testSet=[] # 这边是用过创建训练集和测试集,用下标表示
for i in range(10):
randIndex = int(np.random.uniform(0,len(trainingSet))) #随机生成下标
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex]) # 从训练集中删除
trainMat=[]; trainClasses = []
for docIndex in trainingSet:#生成训练集的词向量
trainMat.append(bagOfWords2Vec(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V,p1V,pSpam = trainNB0(np.array(trainMat), np.array(trainClasses))
errorCount = 0
for docIndex in testSet: #开始测试
wordVector = bagOfWords2Vec(vocabList, docList[docIndex])
if classifyNb(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
errorCount += 1
print "classification error",docList[docIndex]
print 'the error rate is: ',float(errorCount)/len(testSet)
if __name__ == '__main__':
# test1()
spamTest2()
print '#########################################################'