首先,来看贝叶斯公式
P(AT)=P(A/T)P(T)=P(T/A)P(A)
这里我们设定
P(A)=A类别出现的概率
P(T/A)=一批词汇(T)在A类别里面出现的概率
现在求P(A/T),即A类别在T词汇出现的情况下的概率
P(A/T) = P(T/A)P(A) / P(T)
同理,可以类似的算出P(B/T),即B类别在T词汇出现的情况下的概率
P(B/T) = P(T/B)P(B) / P(T)
因为最后的结果是比较P(A/T),P(B/T)各自的比率大小,所以P(T)可以忽略
SPARK(v2.0)的实现技巧,算概率全程使用了math.log(即ln对数,以自然数为底),把多项式
概率的相乘转化为相加。
模型包含两个数据结构
piArray
各类别各自所占的比重
thetaMatrix
行数:类别个数
列数:特征个数,即所有不同词汇的个数
预测时,就是把各个词汇的词频乘上thetaMatrix,看似相乘其实就是利用了math.log的相加。
以下是个列子
|label|features |rawPrediction |probability |prediction|
+-----+--------------------------------+----------------------------------------+---------------------------+----------+
|0.0 |(4,[0,1,2,3],[1.0,2.0,...])|[-17.819799539498465,........]|[1.0,8.437648.........]|0.0 |
+-----+--------------------------------+----------------------------------------+---------------------------+----------+
以下说一下两个核心实现
预测算出概率,即P(T/A)P(A)
private def multinomialCalculation(features: Vector) = {
val prob = theta.multiply(features)
BLAS.axpy(1.0, pi, prob) //y += a * x
prob
}
即多个词汇概率的连乘。
归一化处理:
override protected def raw2probabilityInPlace(rawPrediction: Vector): Vector = {
rawPrediction match {
case dv: DenseVector =>
var i = 0
val size = dv.size
val maxLog = dv.values.max
while (i < size) {
dv.values(i) = math.exp(dv.values(i) - maxLog) //归一化处理,除以最大的那个值
i += 1
}
val probSum = dv.values.sum
i = 0
while (i < size) {
dv.values(i) = dv.values(i) / probSum //归一化之后,再算各个比率
i += 1
}
dv
case sv: SparseVector =>
throw new RuntimeException("Unexpected error in NaiveBayesModel:" +
" raw2probabilityInPlace encountered SparseVector")
}
}