对于特征向量,拥有标签,即分类个数。我们对每一类标签计算概率:
然后取概率最大的那个标签作为我们的分类。
为了实现这个思路,我们首先需要构造特征向量。
拿文本分类的任务举例,我们构造一个数据集:
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
postingList, classVec = loadDataSet()
一共有6条文本,classVec对每条文本的分类进行了标注。
接着我们通过词袋的方式把这些文本转化成向量的形式,先统计所有文本中出现过的词汇,构成词汇表:
def createVocabList(dataSet):
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
vocabList = createVocabList(postingList)
print(vocabList)
'''
['worthless', 'is', 'I', 'not', 'flea', 'maybe', 'steak', 'help', 'quit', 'stop', 'so', 'garbage', 'stupid', 'mr', 'dalmation', 'dog', 'park', 'please', 'to', 'cute', 'him', 'my', 'take', 'licks', 'buying', 'food', 'posting', 'how', 'love', 'ate', 'has', 'problems']
'''
之后统计每个文本的每个单词的出现次数,构成一个稀疏表示的向量:
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:
print("the word: %s is not in my Vocabulary!" % word)
return returnVec
vec = setOfWords2Vec(vocabList, postingList[0])
print(vec)
# [0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
上面是第一个文本的向量表示,0就表示词汇表中该位置的单词在这个文本中出现了1次,0表示没有出现过,当然也可能出现多次,那个位置就会使出现的次数n。
然后我们计算,其中是这类文本占所有样本的比例。。假设所有单词的出现都独立,则。而每个只需要计算这个在这类文本中出现的次数除以这类文本单词的总数就可以得到。我们通过以下代码得到和:
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) # post个数
numWords = len(trainMatrix[0]) # 词汇表大小
pAbusive = sum(trainCategory) / float(numTrainDocs) # 侮辱性的post占所有post的比例
p0Num, p1Num = zeros(numWords), zeros(numWords) # 初始化词汇表大小的两个0向量
p0Denom, p1Denom = 0.0, 0.0
for i in range(numTrainDocs):
# 统计所有侮辱性post中每个单词的出现次数和侮辱性post的总长度
if trainCategory[i] == 1: # 侮辱性post
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
# 统计所有非侮辱性post中每个单词的出现次数和非侮辱性post的总长度
else: # 非侮辱性post
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = p1Num / p1Denom # 词汇表中每个单词在侮辱性post中出现的概率
p0Vect = p0Num / p0Denom # 词汇表中每个单词在非侮辱性post中出现的概率
return p0Vect, p1Vect, pAbusive
trainMat = []
for postinDoc in postingList:
trainMat.append(setOfWords2Vec(vocabList, postinDoc))
p0V, p1V, pAb = trainNB0(trainMat, classVec)
print(p0V)
# [0. 0.04166667 0.04166667 0. 0.04166667 0.
# 0.04166667 0.04166667 0. 0.04166667 0.04166667 0.
# 0. 0.04166667 0.04166667 0.04166667 0. 0.04166667
# 0.04166667 0.04166667 0.08333333 0.125 0. 0.04166667
# 0. 0. 0. 0.04166667 0.04166667 0.04166667
# 0.04166667 0.04166667]
print(p1V)
# [0.10526316 0. 0. 0.05263158 0. 0.05263158
# 0. 0. 0.05263158 0.05263158 0. 0.05263158
# 0.15789474 0. 0. 0.10526316 0.05263158 0.
# 0.05263158 0. 0.05263158 0. 0.05263158 0.
# 0.05263158 0.05263158 0.05263158 0. 0. 0.
# 0. 0. ]
print(pAb) # 0.5
我们可以看到在三个侮辱性文本中都出现的单词stupid,当文本是侮辱性的时候他出现的概率也最高,有0.157,即p1V下标12的单词。
有了这些数据,我们就可以计算了。但是这样还会存在问题,中只要存在一个为0,那么乘积就会等于0。所以我们初始化时假设每个单词已经都出现过了一次,就避免了这个问题。并且,因为每个的数值可能非常的小,他们之间的乘积就也会非常的小,几个非常的小的结果就不太好比较大小,并且数值上也容易出错。所以就通过连续单调递增函数log计算,比较不同类别之间的大小。分母对于不同类别的计算来说都相等,我们就不对它进行统计,只需比较分子的大小。
改进后的代码如下
def trainNB(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) # post个数
numWords = len(trainMatrix[0]) # 词汇表大小
pAbusive = sum(trainCategory) / float(numTrainDocs) # 侮辱性的post占所有post的比例
p0Num, p1Num = ones(numWords), ones(numWords) # 初始化词汇表大小的两个0向量
p0Denom, p1Denom = 2.0, 2.0
for i in range(numTrainDocs):
# 统计所有侮辱性post中每个单词的出现次数和侮辱性post的总长度
if trainCategory[i] == 1: # 侮辱性post
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
# 统计所有非侮辱性post中每个单词的出现次数和非侮辱性post的总长度
else: # 非侮辱性post
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = log(p1Num / p1Denom) # 词汇表中每个单词在侮辱性post中出现的概率
p0Vect = log(p0Num / p0Denom) # 词汇表中每个单词在非侮辱性post中出现的概率
return p0Vect, p1Vect, pAbusive
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + log(pClass1)
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
def testingNB():
listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = trainNB(array(trainMat), array(listClasses))
testEntry = ['love', 'my', 'dalmation']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
testEntry = ['stupid', 'garbage']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
testingNB()
# ['love', 'my', 'dalmation'] classified as: 0
# ['stupid', 'garbage'] classified as: 1
网友评论