统计学习方法3
例如:第三章 贝叶斯分类器
前言
最近拜读了机器学习领域,经典书籍,李航老师的《统计学习方法》,深受裨益。现实现算法,记录分享。
一、贝叶斯分类器是什么?
贝叶斯分类器基于贝叶斯定理与特征条件独立假设的分类方法。对于给定的训练数据集,首先基于特征条件独立假设学习输入输出的联合概率分布模型,然后基于此模型,对于给定的输入 x x x,利用贝叶斯定理求出后验概率最大的输出 y y y,贝叶斯分类器实现简单,学习与预测的效率也很高。
二、贝叶斯分类器,数学原理分析
输入空间 X ⊆ R n X\subseteq R^n X⊆Rn 为 n 维向量集合,输出空间为类标记集合 Y = { c 1 , c 2 , . . . c k } Y = \{c_1, c_2, ...c_k\} Y={c1,c2,...ck}。输入为特征向量 x ⊊ X x\subsetneq X x⊊X,输出 y ⊊ Y y\subsetneq Y y⊊Y。 P ( X , Y ) P(X, Y) P(X,Y) 是 X , Y X, Y X,Y的联合概率分布。训练数据集: T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . ( x n , y n ) } T = \{(x_1, y_1), (x_2, y_2), ...(x_n, y_n)\} T={(x1,y1),(x2,y2),...(xn,yn)}由 P ( X , Y ) P(X, Y) P(X,Y) 独立分布产生。因有: P ( X , Y ) = P ( X ∣ Y ) P ( Y ) P(X,Y) = P(X|Y) P(Y) P(X,Y)=P(X∣Y)P(Y)我们可以从训练集中得到先验概率分布 P ( Y ) P(Y) P(Y),以及条件概率分布 P ( X ∣ Y ) P(X|Y) P(X∣Y)。这里补充一句,啥叫先验概率?就是数数,举个栗子: P ( Y = c k ) P(Y=c_k) P(Y=ck) 怎么求。那便是 C k / C C_k/C Ck/C,数一下第 k 种类别的数量占总数的比例便是 P ( Y = c k ) P(Y=c_k) P(Y=ck)。以前总被陌生的数学名词唬住,了解后也不过如此。接着便是求条件概率分布 P ( X ∣ Y ) P(X|Y) P(X∣Y)。先验概率好求,但条件概率 : P ( X = x ∣ Y = c k ) = P ( X = x 1 , X = x 2 . . . X ( n ) = x ( n ) ∣ Y = c k ) P(X=x|Y=c_k)=P(X=x^1, X=x^2...X^{(n)}=x^{(n)} | Y=c_k) P(X=x∣Y=ck)=P(X=x1,X=x2...X(n)=x(n)∣Y=ck) 有指数级别的参数。假设 x ( j ) x^{(j)} x(j) 取值有 S j S_j Sj 个,Y 的取值有 K 个,那么总共参数为: K ∏ j = 1 n S j K\prod_{j=1}^nS_j K∏j=1nSj 。实在是臣妾办不到啊。
于是这里强作 了一个假设,假设特征向量 X 的各个分量的取值是独立的。也就是说 X ( 1 ) = a X^{(1)} =a X(1)=a 与 X ( 2 ) = b X^{(2)} = b X(2)=b 概率上是独立的。于是有: P ( X = x ∣ Y = c k ) = P ( X ( 1 ) = x ( 1 ) , X ( 2 ) = x ( 2 ) . . . , X ( n ) = x ( n ) ∣ Y = c k ) = ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) P(X=x|Y=c_k) = P(X^{(1)}=x^{(1)}, X^{(2)}=x^{(2)}...,X^{(n)}=x^{(n)}|Y=c_k) \\=\prod_{j=1}^nP(X^{(j)} = x^{(j)}|Y=c_k) P(X=x∣Y=ck)=P(X(1)=x(1),X(2)=x(2)...,X(n)=x(n)∣Y=ck)=j=1∏nP(X(j)=x(j)∣Y=ck)得到这个公式后。我们要干嘛呢?绕来绕去,差点忘记要干啥了。
贝叶斯分类器的目的是给定输入的 x x x, 输出 y = c k y=c_k y=ck。那便是通过学习到的模型计算后验概率分布: P ( Y = c k ∣ X = x ) P(Y=c_k|X=x) P(Y=ck∣X=x)。根据联合概率与条件概率的公式有: P ( Y = c k ∣ X = x ) = P ( X , Y ) P ( X ) = P ( X = x ∣ Y = c k ) P ( Y = c k ) P ( X ) P(Y=c_k|X=x) = \frac {P(X, Y)}{P(X)}=\frac{P(X=x|Y=c_k)P(Y=c_k)}{P(X)} P(Y=ck∣X=x)=P(X)P(X,Y)=P(X)P(X=x∣Y=ck)P(Y=ck)而根据边缘概率公式: P ( X ) = ∑ Y = c k P ( X , Y ) = ∑ Y = c k P ( X = x ∣ Y = c k ) P ( Y = c k ) P(X)=\sum_{Y=c_k} P(X, Y)=\sum_{Y=c_k}P(X=x|Y=c_k)P(Y=c_k) P(X)=Y=ck∑P(X,Y)=Y=ck∑P(X=x∣Y=ck)P(Y=ck)两者结合便有: P ( Y = c k ∣ X = x ) = P ( X = x ∣ Y = c k ) P ( Y = c k ) ∑ Y = c k P ( X = x ∣ Y = c k ) P ( Y = c k ) P(Y=c_k|X=x)=\frac{P(X=x|Y=c_k)P(Y=c_k)}{\sum_{Y=c_k}P(X=x|Y=c_k)P(Y=c_k)} P(Y=ck∣X=x)=∑Y=ckP(X=x∣Y=ck)P(Y=ck)P(X=x∣Y=ck)P(Y=ck)再加上前面所述条件独立假设后的条件概率分布,最终得到后验概率分布如下: P ( Y = c k ∣ X = x ) = P ( Y = c k ) ∏ j P ( X ( j ) = x ( j ) ∣ Y = c k ) ∑ k P ( Y = c k ) ∏ j P ( X ( j ) = x ( j ) ∣ Y = c k ) , k = 1 , 2 , 3... , K P(Y=c_k|X=x)=\frac{P(Y=c_k)\prod_{j}P( X^{(j)}=x^{(j)}|Y=c_k)}{\sum_kP(Y=c_k)\prod_jP(X^{(j)}=x^{(j)}|Y=c_k)}, k=1,2,3...,K P(Y=ck∣X=x)=∑kP(Y=ck)∏jP(X(j)=x(j)∣Y=ck)P(Y=ck)∏jP(X(j)=x(j)∣Y=ck),k=1,2,3...,K以上朴素贝叶斯法分类的基本公式,作为贝叶斯分类器可表示为: y = f ( x ) = a r g m a x c k P ( Y = c k ) ∏ j P ( X ( j ) = x ( j ) ∣ Y = c k ) ∑ k P ( Y = c k ) ∏ j P ( X ( j ) = x ( j ) ∣ Y = c k ) y=f(x)=argmax_{c_k}\frac{P(Y=c_k)\prod_{j}P( X^{(j)}=x^{(j)}|Y=c_k)}{\sum_kP(Y=c_k)\prod_jP(X^{(j)}=x^{(j)}|Y=c_k)} y=f(x)=argmaxck∑kP(Y=ck)∏jP(X(j)=x(j)∣Y=ck)P(Y=ck)∏jP(X(j)=x(j)∣Y=ck)由于下面那坨分母对于求每个 C k C_k Ck 的概率都是一样的,于是我们的朴素贝叶斯分类其最终可以简化为以下公式: y = f ( x ) = a r g m a x c k P ( Y = c k ) ∏ j P ( X ( j ) = x ( j ) ∣ Y = c k ) y=f(x)=argmax_{c_k}P(Y=c_k)\prod_{j}P( X^{(j)}=x^{(j)}|Y=c_k) y=f(x)=argmaxckP(Y=ck)j∏P(X(j)=x(j)∣Y=ck)
三、贝叶斯分类器,训练预测
那么,我们学习数据 T 时,训练出来的模型到底是什么呢?实际上其模型包含两部分:
1、
P
(
Y
=
c
k
)
P(Y=c_k)
P(Y=ck) ,计算出每个类别的概率。在代码实现中,使用一个 Hashmap 存储计算结果。其键值对为<
c
k
,
P
r
o
b
c_k, Prob
ck,Prob>。
2、
∏
j
P
(
X
(
j
)
=
x
(
j
)
∣
Y
=
c
k
)
\prod_{j}P( X^{(j)}=x^{(j)}|Y=c_k)
∏jP(X(j)=x(j)∣Y=ck),计算出在
C
k
C_k
Ck 类别的情况下,及每个特征向量的每一个分量
x
j
x_j
xj取值
S
j
S_j
Sj 的情况下的概率。在代码实现中,使用一个 Hashmap 存储结果。其键值对为<{
C
k
C_k
Ck,
S
j
Sj
Sj},
P
r
o
b
Prob
Prob>。
3、在计算得到以上两个结果后,按照公式
P
(
Y
=
c
k
)
∏
j
P
(
X
(
j
)
=
x
(
j
)
∣
Y
=
c
k
)
P(Y=c_k)\prod_{j}P( X^{(j)}=x^{(j)}|Y=c_k)
P(Y=ck)∏jP(X(j)=x(j)∣Y=ck),计算每个类别得到的概率,取概率最大的类别作为预测结果。
四、上代码
以下是贝叶斯分类器伪代码
int bayes_classifier_train(matrix_t* data, matrix_t* labal, hashmap_t* Ck, hashmap_t* Ck_Si)
{
// 将所有的类别去重。
matrix_t* unique_ck = matrix_unique(label);
// 得类别的数量
int type_number = unique_ck->size;
// 得到每个类别的数量
int* ck_number = count_ck_number(labbel);
// 计算每个类别的概率
for (i : type_number)
{
// 将每个类别的数量除以总数,便是每个类别的概率。
double prob = ck_number[i]/label->rows;
Ck->push(unique[i], prob);
}
for (i : type_number){
// 获取所有类别为 ck[i] 的数据
matrix_t* data_cki = fatch_rows_of_ck(data, unique_ck[i]);
for (j : data_cki->cols){
// 截取每一列数据
matrix_t* data_cki_col = matrix_slice_col(data_cki, j);
// 去掉重复的值
matrix_t* unique_si = matrix_unique(data_cki_col);
// 统计有多少种数值
int si_type_number = unique_si->size;
// 统计每种数值的数量
int* si_number = count_si_number(data_cki);
for (k : si_type_number) {
// 统计每种值的数量占的比例,即是概率
double prob = si_number / data_cki_col->rows;
// 保存下这个概率。
Ck_Si->push({unique_ck[i], unique_si[k]}, prob);
}
}
}
return 0;
}
贝叶斯分类器,推理代码
int bayes_classifier_predict(matrix_t* _Input, hashmap_t* Ck, hashmap_t* Ck_Si)
{
float probs[Ck->size];
int cks[Ck->size];
for (i : Ck->size){
int ck_type = Ck->keys[i];
// 根据 Ck 类型获取其概率
double prob = hashmap_get(Ck, ck_type);
for (j : _Input-cols) {
int si_type = _Input[j];
// 连乘在当前类别 Ck 与 当前取值 Si 的情况下所统计的概率。
prob *= hashmap_get({ck_type, si_type});
}
probs[i] = prob;
cks[i] = ck_type;
}
int max_prob_ck = get_max(probs, cks);
return max_prob_ck;
}
源码:https://github.com/zuweie/boring-code/blob/main/src/statistical_learning/naive_bayes.c
总结
朴素贝叶斯法是经典生成学习方法。生成方法由训练数据学习联合概率分布
P
(
X
,
Y
)
P(X, Y)
P(X,Y), 然后求得后验概率分布
P
(
Y
∣
X
)
P(Y|X)
P(Y∣X)。这个也是挺简单的算法。
完