R中的朴素贝叶斯分类 - 从头开始
Posted
技术标签:
【中文标题】R中的朴素贝叶斯分类 - 从头开始【英文标题】:Naive Bayes Classification in R - from scratch 【发布时间】:2017-07-25 17:09:00 【问题描述】:我编写了一些代码,使用 iris
数据集在 R 中手工制作 朴素贝叶斯分类器。我做了以下事情:
dnorm
计算概率
乘以每个类的先验
每个结果的概率都非常低。我想知道 后验函数 的部分是否正确?这是我的代码:
set.seed(1) #reproducibility
training_rows <- sort(c(sample(1:50, 40), sample(51:100, 40), sample(101:150, 40)))
training_x <- as.data.frame(iris[training_rows, 1:4])
training_y <- iris[training_rows, 5]
iris_nb <- function(x, trainx, trainy)
train <- cbind(trainx, trainy)
class_virginica <- train[which(train$trainy == 'virginica'),]
class_setosa <- train[which(train$trainy == 'setosa'),]
class_versicolor <- train[which(train$trainy == 'versicolor'),]
posterior <- function(x, classtype)
# Warning: bug here.
p_Sepal.Length <- dnorm(x, mean(classtype[,1]), sd(classtype[,1]))
p_Sepal.Width <- dnorm(x, mean(classtype[,2]), sd(classtype[,2]))
p_Petal.Length <- dnorm(x, mean(classtype[,3]), sd(classtype[,3]))
p_Petal.Width <- dnorm(x, mean(classtype[,4]), sd(classtype[,4]))
vec <- 0.33* p_Sepal.Length * p_Sepal.Width * p_Petal.Length * p_Petal.Width #for each species
return(vec)
return(list(virginica = sum(posterior(x, class_virginica)),
setosa = sum(posterior(x, class_setosa)),
versicolor = sum(posterior(x, class_versicolor))))
这是输出:
test_case_1 <- as.matrix(iris[1, 1:4])
iris_nb(test_case_1, training_x, training_y)
## $virginica
## [1] 1.167108e-16
## $setosa
## [1] 2.136291e-54
## $versicolor
## [1] 1.636154e-32
感谢您的帮助!
【问题讨论】:
【参考方案1】:TL;DR;
代码中有一个错误:
您给dnorm
一个向量作为第一个参数,它返回一个向量。给它一个值以返回一个值。
说明
使用您提供的示例考虑以下内容:
trainx <- training_x; trainy <- training_y
train <- cbind(trainx, trainy)
class_setosa <- train[which(train$trainy == 'setosa'),]
mu <- mean(class_setosa[,1])
sigma <- sd(class_setosa[,1])
dnorm(test_case_1, mu, sigma)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1 1.045569 0.0002481138 4.945912e-22 9.622888e-39
请注意,当给定一个向量时,dnorm 返回一个向量。
posterior
的返回值立即传递给 sum
,从而隐藏了 bug。
有很多方法可以解决这个问题,但按照原始代码的意图,最简单的解决方案是索引观察向量。
解决方案是对向量进行索引。
dnorm(test_case_1[1], mu, sigma)
## [1] 1.045569
请注意,dnorm 不会返回有效概率,这在本例中很明显,因为返回的值大于 1。
最终解决方案
最终的解决方案是这样的
iris_nb <- function(x, trainx, trainy)
train <- cbind(trainx, trainy)
class_virginica <- train[which(train$trainy == 'virginica'),]
class_setosa <- train[which(train$trainy == 'setosa'),]
class_versicolor <- train[which(train$trainy == 'versicolor'),]
posterior <- function(x, classtype)
p_Sepal.Length <- dnorm(x[1], mean(classtype[,1]), sd(classtype[,1]))
p_Sepal.Width <- dnorm(x[2], mean(classtype[,2]), sd(classtype[,2]))
p_Petal.Length <- dnorm(x[3], mean(classtype[,3]), sd(classtype[,3]))
p_Petal.Width <- dnorm(x[4], mean(classtype[,4]), sd(classtype[,4]))
vec <- 0.33* p_Sepal.Length * p_Sepal.Width * p_Petal.Length * p_Petal.Width #for each species
return(vec)
return(list(virginica = sum(posterior(x, class_virginica)),
setosa = sum(posterior(x, class_setosa)),
versicolor = sum(posterior(x, class_versicolor))))
并且使用与原始问题相同的测试用例:
iris_nb(test_case_1, training_x, training_y)
## $virginica
## [1] 1.861745e-24
## $setosa
## [1] 2.82984
## $versicolor
## [1] 8.334494e-22
请注意,答案已更改,现在 argmax 是 setosa 而不是 virginica。 此外,这些不是有效的后验概率,但与相关概率成正比,因此 argmax 仍然有效,这就是用于朴素贝叶斯分类的方法。
【讨论】:
【参考方案2】:两个cmets:
1) 您计算的后验是基于它与prior * likelihood
成比例的事实。这一切都很好,但请记住贝叶斯定理说posterior = prior * likelihood / marginal
。边际非常小,因此从您的计算中删除它会使您的后验概率也非常小(因为您没有除以它)。
一般来说,我们并不关心这些概率本身,我们关心的是它们的相对大小。所以virginica
这个测试用例的可能性似乎比其他物种高很多倍。所以朴素贝叶斯会输出virginica
,argmax
。
2) 通常在使用概率乘积(即您的联合可能性)时,我们会改用对数概率,因为它们是相加的并且不会导致数值问题(这通常发生在将许多小数相乘时) )。
【讨论】:
以上是关于R中的朴素贝叶斯分类 - 从头开始的主要内容,如果未能解决你的问题,请参考以下文章