解释朴素贝叶斯






4.67/5 (4投票s)
解释朴素贝叶斯
什么是朴素贝叶斯
力求不与维基百科重复,简而言之,朴素贝叶斯是一种流行的 AI 模型,用于使用分类数据集进行预测。常见用途是文档分类。换句话说,这个文档是否符合我正在寻找的标准?这是通过将一组预先确定的符合某些标准的词与不符合的词进行比较来完成的。每个词都使用概率规则进行称重以进行比较。
本文使用的工具
我将从一本教科书中借用一些数据,这些数据包含来自 Twitter 应用程序的推文集合,并尝试使用 R 作为我的编程语言来解释它。为什么是 R?我想我也可以使用 C++、C# 或 Python。Python 的语法与 R 相似,并且可以编译成可执行对象,但我认为 R 更易于阅读。因此,当人们查看我编写的代码时,解释就更容易看懂了。最重要的是……
- R 避免了更复杂语言中通常令人困惑的语法和算法。
- 它允许人们轻松地将数据整理成易于识别的框架或表格。
- 无需太多麻烦即可轻松比较多个表之间的数据。
现在许多语言,包括 R,都拥有朴素贝叶斯库和函数调用。一两行代码就可以搞定。在作为专业服务应用我的技能时,我会毫不犹豫地使用这些库。但对于这篇文章来说,那样有什么乐趣呢?我想自己创建一个粗糙版本的朴素贝叶斯来演示它的易懂性。
借来的数据比自己找的更容易处理,而且我非常小心地使用了自己的算法,而不是教科书提供的算法,这有几个好原因。我所说的这本书是解释各种计算机科学模型中最好的书籍之一,我打算继续写关于其中一些其他模型的文章。
这些数据基于这样的假设:在包含相同搜索标准的数百条推文中,只有一部分对研究人员很重要。因此,必须有一个预先选择的推文集合,其中包含符合要求的推文,还有一个不符合的集合。这些将作为朴素贝叶斯的训练模型。我们能够将更多内容放入这些集合中会更好,并且建议每次进行搜索时,将朴素贝叶斯的新结果放入这些集合中。
一旦训练模型就位,就应该将新的搜索结果进行比较——逐词比较每一条推文——与匹配和不匹配的集合。词语按概率百分比加权,并为每条推文求和。之后,我们只需比较哪一组对该推文的概率更大,是匹配集还是不匹配集。搞定!
用 R 解释朴素贝叶斯
完整的 R 脚本及数据示例已在本论文附带的下载文件中提供。那么,现在让我们来探讨一下脚本中的一些代码片段。
如前所述,只需几个步骤即可实现朴素贝叶斯,以确定新的推文搜索是否符合我的要求。以下是其实现方式
#########################################################################
# MAIN
#########################################################################
# TRAIN THE MODEL- CALCULATE FOR isClass and notClass INSTANCES
isClass = getTableWithCount( getDoc("./data/is_class.txt") )
notClass = getTableWithCount( getDoc("./data/not_class.txt") )
# PREPARE Test DOC
fdata = getDoc("./data/testdata.txt")
IsProbability = getTestTable( fdata, isClass )
NotProbability = getTestTable(fdata, notClass)
# CREATE table with final analysis
FinalAnalysis = getAnalysis(IsProbability, NotProbability)
首先,我们将训练数据加载到两个表中。isClass
是所需推文的集合,notClass
是…当然不是。接下来,我们必须准备两组表,它们计算测试数据属于一个类别还是另一个类别的概率。最后,在 FinalAnalysis
中生成一个报告,该报告比较了两个加权表,如果 isProbability
表中的推文大于应用于 NotProbability
表的相同推文,则返回 TRUE
。
这足够简单了。但现在让我们来看看图 1 中的每个调用。前两行将文本文件加载到表中,然后使用该表创建 isClass
和 notClass
表。(文本文件在本论文末尾提供)。
getDoc
在图 2 中进行了说明。在这里,我们看到我们只是在读取一个包含波浪号分隔符的记录集合。我们将所有数据转换为小写以简化单词识别,并删除标点符号和多余的空格。结果只是从每个文件中提取的推文列表。
getDoc <- function(filename) {
fdata = read.csv(filename, sep = "~", fill=FALSE) # load doc of words
fdata = sapply(fdata,tolower) # lowercase all words
fdata = gsub("'s","",fdata) # remove 's
fdata = gsub(": ", " ", fdata)
fdata = gsub("?","", fdata)
fdata = gsub("!","", fdata)
fdata = gsub(";","", fdata)
fdata = gsub(",", "", fdata)
fdata = gsub("\\. ", " ", fdata)
fdata = trimws(fdata)
fdata = gsub(" ", " ", fdata) # double space to single
fdata = gsub(" ", " ", fdata) # double space to single again
fdata = gsub(" ", " ", fdata) # double space to single once more
return (fdata)
}
getTableWithCount
在图 3 中进行了说明。在这里,我们创建一个表,该表拆分了推文中的所有单词,存储了它们的唯一值以及在整个集合中找到的单词数量。计数是不够的,因为我们可能需要在后面的计算中包含罕见词,这些词将获得 1
的值。此时,最好添加一个名为平滑(smoothing)的列,它是每个单词的计数 + 1。概率列将是平滑计数除以该表中所有平滑计数的总和,得到该单词在所有单词总数中所占权重的十进制版本。由于大量的单词会导致小数位数变长,我们可以在未来的计算中使用自然对数而不是百分比。这将被添加到表中。
表中的所有这些数据对于计算朴素贝叶斯来说并非真正必要。但为了让这个练习更容易理解,我们仍然保留了它们。图 4 展示了一部分结果。
# get count of each word in doc
getTableWithCount <- function(fdata) {
library(plyr)
wlist = unlist(strsplit(fdata, " ")) # a list of each word
wlist = sort(wlist)
# filter out words that have a length less than 4
df = data.frame(wlist)
df = ddply(df, .(wlist), nrow)
colnames(df) = c("words", "count")
# apply additive smoothing to df
v = df$count + 1
df["smoothing"] = v
# calculate the probability of each word (word-count/total-words)
v = df$smoothing / sum(df$smoothing)
df["probability"] = v
# calculate the natural log of each proability
v = log(df$probability)
df["log"] = v
return(df)
}
getTestTable
在图 5 中进行了说明。加载“testdata.txt”文件后,我们现在可以继续创建一张表,该表对测试数据每条推文中的每个单词的百分比权重进行求和。如前所述,这是针对匹配 isClass
和不匹配 notClass
的两个表进行的。
在图 5 中,我们创建了一个名为 rdata
的包含概率和推文的空表。它将用每条推文中所有单词的概率总和以及推文本身来填充。我让这部分比实际需要更复杂一些,以说明得到概率总和的过程。
此外,此时还包括了罕见词。由于 isClass
和 notClass
表中所有单词的平滑计数至少为二,如果一个单词在表中不存在,则赋予其平滑值为 1。因此,罕见词的概率可能是 1 除以各自类别表中单词的总数( 1 / sum(smooth) )。图 6 是 rdata
中可能看到的内容的剪辑。
#table created from lookups of each word in probability table
getTestTable <- function(fdata, tclass) {
library(plyr)
library(data.table)
rdata = data.frame(
"probability" = numeric(), "tweet" = character(), stringsAsFactors = FALSE)
df = data.frame(fdata)
df = data.frame(lapply(df, as.character), stringsAsFactors = FALSE)
deflog = log(1 / sum(tclass$count))
wlist = strsplit(df$Tweet, " ") # a list of each word
for (gidx in 1:length(wlist)) {
wgrp = wlist[[gidx]]
probability = 0.0
tweet = df[gidx, 2]
for (widx in 1:length(wgrp)) {
if (nchar(wlist[[gidx]][widx]) > 3) {
inclass = tclass[tclass$words == wlist[[gidx]][widx], 1:length(tclass)]
if (nrow(inclass) > 0) {
probability = probability + inclass$log
}
else { # use smoothing
probability = probability + deflog
}
}
}
newrow = data.frame(probability, tweet)
rdata = rbind(rdata, newrow)
}
return(rdata)
}
终于到了报告
getFinalAnalysis
(图 7)简单地创建一个包含所有测试数据推文的表,以及一个逻辑值 TRUE
或 FALSE
,告诉我们该推文是否符合所寻求的标准。公式非常简单。如果 isProbability
表的加权概率的每一行都大于 notProbability
表的相应行,那么答案就是 TRUE
。快速看一下图 8 就能告诉我们关于我们测试数据的所有信息。
getAnalysis <- function(isProbability, notProbability) {
df = data.frame("Is it in class" =
(isProbability$probability > notProbability$probability),
"tweet" = isProbability$tweet
)
return(df)
}
欢迎随意复制本文随附的信息并自行尝试。