编辑:可以在以下网址找到正确版本的代码: https://github.com/a7x/NaiveBayes-Classifier
我使用了来自openClassroom的数据,并开始在Python中使用Naive Bayes的小版本。步骤是通常的训练,然后是预测。我有几个问题,想知道为什么准确性很差。
对于培训,我通过以下公式计算了对数似然性:
log(P(word | spam)+1)/(spamSize + vocabSize。)
我的问题是:为什么我们在这种情况下添加vocabSize
:(这是正确的解决方法吗?使用的代码如下:
#This is for training. Calculate all probabilities and store them in a vector. Better to store it in a file for easier access
from __future__ import division
import sys,os
'''
1. The spam and non-spam is already 50% . So they by default are 0.5
2. Now we need to calculate probability of each word , in spam and non-spam separately
2.1 we can make two dictionaries, defaultdicts basically, for spam and non-spam
2.2 When time comes to calculate probabilities, we just need to substitute values
'''
from collections import *
from math import *
spamDict = defaultdict(int)
nonspamDict = defaultdict(int)
spamFolders = ["spam-train"]
nonspamFolders = ["nonspam-train"]
path = sys.argv[1] #Base path
spamVector = open(sys.argv[2],'w') #WRite all spam values into this
nonspamVector = open(sys.argv[3],'w') #Non-spam values
#Go through all files in spam and iteratively add values
spamSize = 0
nonspamSize = 0
vocabSize = 264821
for f in os.listdir(os.path.join(path,spamFolders[0])):
data = open(os.path.join(path,spamFolders[0],f),'r')
for line in data:
words = line.split(" ")
spamSize = spamSize + len(words)
for w in words:
spamDict[w]+=1
for f in os.listdir(os.path.join(path,nonspamFolders[0])):
data = open(os.path.join(path,nonspamFolders[0],f),'r')
for line in data:
words = line.split(" ")
nonspamSize = nonspamSize + len(words)
for w in words:
nonspamDict[w]+=1
logProbspam = {}
logProbnonSpam = {} #This is to store the log probabilities
for k in spamDict.keys():
#Need to calculate P(x | y = 1)
numerator = spamDict[k] + 1 # Frequency
print 'Word',k,' frequency',spamDict[k]
denominator = spamSize + vocabSize
p = log(numerator/denominator)
logProbspam[k] = p
for k in nonspamDict.keys():
numerator = nonspamDict[k] + 1 #frequency
denominator = nonspamSize + vocabSize
p = log(numerator/denominator)
logProbnonSpam[k] = p
for k in logProbnonSpam.keys():
nonspamVector.write(k+" "+str(logProbnonSpam[k])+"\n")
for k in logProbspam.keys():
spamVector.write(k+" "+str(logProbspam[k])+"\n")
对于预测,我只是接一封邮件,将其分成单词,分别添加所有概率 垃圾邮件/非垃圾邮件,并将它们乘以0.5。哪个更高的是类别标签。代码如下:
http://pastebin.com/8Y6Gm2my(Stackoverflow由于某种原因再次玩游戏: - /)
编辑:我删除了垃圾邮件=垃圾邮件+ 1件事。相反,我只是忽略这些词
问题:我的准确性很差。如下所述。
No of files in spam is 130
No. of spam in ../NaiveBayes/spam-test is 53 no. of non-spam 77
No of files in non-spam is 130
No. of spam in ../NaiveBayes/nonspam-test/ is 6 no. of non-spam 124
请告诉我我哪里出错了。我认为准确度低于50%意味着实施中必须存在一些明显的错误。
答案 0 :(得分:2)
这里至少有一个你正在犯的错误:你将日志概率存储在模型文件中(正如你应该的那样),但是在预测代码中你假装它们是直接的概率:
totalSpamP = spamP * 0.5
应该是
totalSpamP = spamP + math.log(0.5)
另外,我不知道这条线在做什么:
spamP = spamP + 1
似乎正在弥补训练集中垃圾邮件部分中未找到的功能,但这些单词应该被忽略。现在,它正在将 e (exp(1)
)添加到概率中,根据定义,该概率无效。
(顺便说一句,我只是尝试使用my own implementation of Naive Bayes对此训练集进行分类,准确率达到97.6%,这就是你应该瞄准的数字:)
答案 1 :(得分:2)
您的程序中存在多个错误和错误的假设 - 在其中的两个部分。这里有几个。
1
,而是log(1/(spamSize+vocabSize))
或log(1/(nonspamSize+vocabSize))
,具体取决于其组。这实际上非常重要 - 您需要将此概率存储在您的数据中,以使程序正常运行。 <强>更新即可。您已经修复了6.当您使用此数据集时,1并不是严格要求修复的,并且不需要3。
您的修改未正确修复4或5.首先,如果在某些集合中从未观察到该单词,则其中的消息概率应该降低。忽略这个词并不是一个好主意,你需要将其视为一个非常难以理解的词
其次,您当前的代码是不对称的,因为垃圾邮件中缺少的单词取消了对非垃圾邮件的检查(但不是另一种方式)。如果您不需要在异常处理程序中执行任何操作,请使用pass
,而不是continue
,因为后者会立即进入下一个for w in words:
次迭代。
问题2仍然存在 - 您使用的词汇量与真实词汇量不匹配。它必须是训练集中观察到的不同单词的数量,而不是所有消息中的单词总数。