机器学习 朴素贝叶斯算法数学过程及Python实现:女生说这话是不是对你有意思

一、文本分析的需求

有时,我们会有分析文本的需求,如分析邮件是否为垃圾邮件。进行文本分析时,首先,我们需要一个特征向量用来表征一段文本,即把文本的特征映射为一个向量。特征向量的创建往往可以通过一个几百词到几万词的字典,通过依次遍历字典中的字符串是否包含与文本中,若包含则该位置为1,不包含为0,以此创建特征向量。如,字典有10000个字符串,包括"a",“abstract”…“buy”…“sale”“ShiZhanfei”…“Zipcy”(一位插画家)等共一万个词。特征向量也为10000*1的一个向量。若字典中包含"a",则该向量第一位为1,否则第一位为0。若包含"abstract",第二位为1,否则第二位为0,以此类推可以得到形如 X ^ = [ 1 , 0 , 0 , 1 , . . . . . . 0 , 1 , 1 ] T \hat X=[1,0,0,1,......0,1,1]^T X^=[1,0,0,1,......0,1,1]T的一个特征向量。假如这段文本来自一封邮件,若"buy",“sale"对应的位置为1,可以推测分类结果很可能为垃圾推销邮件。若"ShiZhanfei”(我的名字~)为1,则分类结果很可能为发给我或者我发给别人的邮件(非垃圾推销邮件)。
对于该特征向量概率 P ( X ^ ) P(\hat X) P(X^)可以用多项式分布 P ( X ^ ) = ϕ 1 y 1 ϕ 2 y 2 . . ϕ 10000 y 10000 P(\hat X)=\phi_1^{y1}\phi_2^{y2}..\phi_{10000}^{y10000} P(X^)=ϕ1y1ϕ2y2..ϕ10000y10000 ϕ i \phi_i ϕi为第i个字符串取1的概率,yi=1或0)以及 ∑ P ( X ^ ) = 1 \sum P(\hat X)=1 P(X^)=1来计算。但字典往往有数百到数万个词, ϕ i \phi_i ϕi也对应有数百到数万个,此时需要估计的参数为 P ( X ^ ) P(\hat X) P(X^)所有可能结果的概率,总共有210000个,实在太多了。实际中,往往就采用朴素贝叶斯的假设进行建模。

二、朴素贝叶斯

与高斯判别分析一样,朴素贝叶斯也是一种针对不同分类结果分别建模的生成学习算法。它主要应用于文本分析。
首先,由概率论乘法公式可得, P ( x 1 , x 2 . . . , x 9999 , x 10000 ) = P ( x 1 ) ∗ P ( x 2 ∣ x 1 ) ∗ P ( x 3 ∣ x 1 , x 2 ) . . . ∗ P ( x 10000 ∣ , x 1 , x 2 . . . x 9999 ) P(x_1,x_2...,x_{9999},x_{10000})=P(x_1)*P(x_2|x_1)*P(x_3|x_1,x_2)...*P(x_{10000}|,x_1,x_2...x_{9999}) P(x1,x2...,x9999,x10000)=P(x1)P(x2x1)P(x3x1,x2)...P(x10000,x1,x2...x9999)。多加一个条件y后为 P ( x 1 , x 2 . . . , x 9999 , x 10000 ∣ y ) = P ( x 1 ∣ y ) ∗ P ( x 2 ∣ y , x 1 ) ∗ P ( x 3 ∣ y , x 1 , x 2 ) . . . ∗ P ( x 10000 ∣ y , x 1 , x 2 . . . x 9999 ) P(x_1,x_2...,x_{9999},x_{10000}|y)=P(x_1|y)*P(x_2|y,x_1)*P(x_3|y,x_1,x_2)...*P(x_{10000}|y,x_1,x_2...x_{9999}) P(x1,x2...,x9999,x10000y)=P(x1y)P(x2y,x1)P(x3y,x1,x2)...P(x10000y,x1,x2...x9999)
朴素贝叶斯算法的假设为:xi条件相互独立。即 P ( x 2 ∣ y , x 1 ) = P ( x 2 ∣ y ) P(x_2|y,x_1)=P(x_2|y) P(x2y,x1)=P(x2y) P ( x 3 ∣ y , x 1 , x 2 ) = P ( x 3 ∣ y ) P(x_3|y,x_1,x_2)=P(x_3|y) P(x3y,x1,x2)=P(x3y),任一字符串在给定y时的概率和别的字符串无关。知道一个文本是表白信(y=1),知道里面有字符串"署名:XXX",但并不能确定表白信中是否有字符串“XXX(你的名字)”,即知道这些信息并不能判断别的字符串是否存在(值是否为1)。
显然,这假设并不合理。文本中如果有"buy",往往有"sale";如果有你的心上人的名字,往往也有你的名字,即字典里各个词语并不是相互独立的。但是,实践证明,虽然这个假设并不成立,但分类效果依然较好。所以,工程上可以做这个假设。
也就是说 P ( x 1 , x 2 . . . , x 9999 , x 10000 ∣ y ) = ∏ i = 1 i = 10000 P ( x i ∣ y ) P(x_1,x_2...,x_{9999},x_{10000}|y) = \prod_{i=1}^{i=10000}P(x_i|y) P(x1,x2...,x9999,x10000y)=i=1i=10000P(xiy)
该模型的参数为:
ϕ i ∣ y = 1 = p ( x i ∣ y = 1 ) \phi_i|_{y=1}=p(x_i|y=1) ϕiy=1=p(xiy=1)
ϕ i ∣ y = 0 = p ( x i ∣ y = 0 ) \phi_i|_{y=0}=p(x_i|y=0) ϕiy=0=p(xiy=0)
ϕ y = p ( y = 1 ) \phi_y=p(y=1) ϕy=p(y=1)
可以得到,若有m个样本,似然函数 l ( ϕ y , ϕ i ∣ y = 1 , ϕ i ∣ y = 0 ) = ∏ i = 1 i = m P ( X ^ i , y i ) l(\phi_y,\phi_i|_{y=1},\phi_i|_{y=0})=\prod_{i=1}^{i=m}P(\hat X_i,y_i) l(ϕy,ϕiy=1,ϕiy=0)=i=1i=mP(X^i,yi),然后,进行最大似然性估计可以得到参数应选选择为:
ϕ i ∣ y = 1 = ∑ j = 1 j = m 1 [ x i = 1 , y i = 1 ] ∑ j = 1 j = m 1 [ y i = 1 ] = 标 签 为 1 的 样 本 数 中 第 i 个 字 符 串 出 现 ( x i = 1 ) 的 个 数 标 签 为 1 的 样 本 数 \phi_i|_{y=1}=\frac{\sum_{j=1}^{j=m}1[x_i=1,y_i=1]}{\sum_{j=1}^{j=m}1[y_i=1]}=\frac{标签为1的样本数中第i个字符串出现(x_i=1)的个数}{标签为1的样本数} ϕiy=1=j=1j=m1[yi=1]j=1j=m1[xi=1,yi=1]=11i(xi=1),即标签为1的样本中第i个字符串出现的概率
ϕ i ∣ y = 0 = ∑ j = 1 j = m 1 [ x i = 1 , y i = 0 ] ∑ j = 1 j = m 1 [ y i = 0 ] = 标 签 为 0 的 样 本 数 中 第 i 个 单 词 出 现 ( x i = 0 ) 的 个 数 标 签 为 0 的 样 本 数 \phi_i|_{y=0}=\frac{\sum_{j=1}^{j=m}1[x_i=1,y_i=0]}{\sum_{j=1}^{j=m}1[y_i=0]}=\frac{标签为0的样本数中第i个单词出现(x_i=0)的个数}{标签为0的样本数} ϕiy=0=j=1j=m1[yi=0]j=1j=m1[xi=1,yi=0]=00i(xi=0)
ϕ y = 样 本 标 签 为 1 的 数 目 样 本 总 数 \phi_y=\frac{样本标签为1的数目}{样本总数} ϕy=1即样本标签为1的概率
得到这些参数,就得到了p(xi|y=1),p(xi|y=0)以及p(y=1)的表达式。然后,对给定文本进行预测时,就可以用贝叶斯公式 P ( y = 1 ∣ X ^ ) = P ( X ^ ∣ y = 1 ) ∗ P ( y = 1 ) P ( X ^ ) = P ( X ^ ∣ y = 1 ) ∗ P ( y = 1 ) P ( X ^ ∣ y = 0 ) ∗ P ( y = 0 ) + P ( X ^ ∣ y = 1 ) ∗ P ( y = 1 ) = ( ∏ j = 1 j = n i P ( x j ∣ y = 1 ) ) ∗ P ( y = 1 ) ( ∏ j = 1 j = n i P ( x j ∣ y = 0 ) ) ∗ P ( y = 0 ) + ( ∏ j = 1 j = n i P ( x j ∣ y = 1 ) ) ∗ P ( y = 1 ) = ( ∏ j = 1 j = n i ϕ j ∣ y = 1 ) ∗ ϕ y ( ∏ j = 1 j = n i ϕ j ∣ y = 0 ) ∗ ( 1 − ϕ y ) + ( ∏ j = 1 j = n i ϕ j ∣ y = 1 ) ∗ ϕ y P(y=1|\hat X)=\frac{P(\hat X|y=1)*P(y=1)}{P(\hat X)}=\frac{P(\hat X|y=1)*P(y=1)}{P(\hat X|y=0)*P(y=0)+P(\hat X|y=1)*P(y=1)}=\frac{(\prod_{j=1}^{j=n_i}P(x_j|y=1)) *P(y=1)}{(\prod_{j=1}^{j=n_i}P(x_j|y=0))*P(y=0)+(\prod_{j=1}^{j=n_i}P(x_j|y=1))*P(y=1)}=\frac{(\prod_{j=1}^{j=n_i}\phi_j|y=1)*\phi_y}{(\prod_{j=1}^{j=n_i}\phi_j|y=0)*(1-\phi_y)+(\prod_{j=1}^{j=n_i}\phi_j|y=1)*\phi_y} P(y=1X^)=P(X^)P(X^y=1)P(y=1)=P(X^y=0)P(y=0)+P(X^y=1)P(y=1)P(X^y=1)P(y=1)=(j=1j=niP(xjy=0))P(y=0)+(j=1j=niP(xjy=1))P(y=1)(j=1j=niP(xjy=1))P(y=1)=(j=1j=niϕjy=0)(1ϕy)+(j=1j=niϕjy=1)ϕy(j=1j=niϕjy=1)ϕy计算给定 X ^ \hat X X^时y=1和0的概率,比较得出结果。其中,ni X ^ \hat X X^的长度,即 X ^ \hat X X^有ni个词语。
字典的得到,可以通过遍历样本集中的所有词语。(若数量过多,可以设置遍历得到样本集中出现三次及以上,十次及以上或更多的词语,以此生成字典)。

三、拉普拉斯平滑

在大多数情况下,朴素贝叶斯可以做出分类。但在一些特殊情况下,仅仅依靠朴素贝叶斯会无法进行分类。
比如,文本中有一个特别特别生僻,一般文本中都不会出现的字符串,如“母猪的产后护理”。进行邮件分类时,在样本集中无论是垃圾邮件,还是非垃圾的正常邮件,都不会包含这个字符串,字典里根本没有这个词。也就是说,最大似然估计的结果为, ϕ j ∣ y = 0 = 0 y = 0 的 样 本 数 = 0 \phi_j|_{y=0}=\frac{0}{y=0的样本数}=0 ϕjy=0=y=00=0 ϕ j ∣ y = 1 = 0 y = 1 的 样 本 数 = 0 \phi_j|_{y= 1}=\frac{0}{y=1的样本数}=0 ϕjy=1=y=10=0,也就是说 P ( x j ∣ y = 1 ) = ϕ j ∣ y = 1 = 0 P(x_j|y=1)=\phi_j|_{y= 1}=0 P(xjy=1)=ϕjy=1=0, P ( x j ∣ y = 0 ) = ϕ j ∣ y = 0 = 0 P(x_j|y=0)=\phi_j|_{y= 0}=0 P(xjy=0)=ϕjy=0=0,也就是说 P ( x 1 , x 2 . . . , x 9999 , x 10000 ∣ y = 1 ) = ∏ i = 1 i = 10000 P ( x i ∣ y = 1 ) = 0 P(x_1,x_2...,x_{9999},x_{10000}|y=1) = \prod_{i=1}^{i=10000}P(x_i|y=1)=0 P(x1,x2...,x9999,x10000y=1)=i=1i=10000P(xiy=1)=0, P ( x 1 , x 2 . . . , x 9999 , x 10000 ∣ y = 0 ) = ∏ i = 1 i = 10000 P ( x i ∣ y = 0 ) = 0 P(x_1,x_2...,x_{9999},x_{10000}|y=0) = \prod_{i=1}^{i=10000}P(x_i|y=0)=0 P(x1,x2...,x9999,x10000y=0)=i=1i=10000P(xiy=0)=0(因为连乘式中第j项为0)。于是, P ( y = 1 ∣ X ^ ) = P ( X ^ ∣ y = 1 ) ∗ P ( y = 1 ) P ( X ^ ) = P ( X ^ ∣ y = 1 ) ∗ P ( y = 1 ) P ( X ^ ∣ y = 0 ) ∗ P ( y = 0 ) + P ( X ^ ∣ y = 1 ) ∗ P ( y = 1 ) = 0 0 + 0 P(y=1|\hat X)=\frac{P(\hat X|y=1)*P(y=1)}{P(\hat X)}=\frac{P(\hat X|y=1)*P(y=1)}{P(\hat X|y=0)*P(y=0)+P(\hat X|y=1)*P(y=1)}=\frac{0}{0+0} P(y=1X^)=P(X^)P(X^y=1)P(y=1)=P(X^y=0)P(y=0)+P(X^y=1)P(y=1)P(X^y=1)P(y=1)=0+00, P ( y = 0 ∣ X ^ ) = P ( X ^ ∣ y = 0 ) ∗ P ( y = 0 ) P ( X ^ ) = P ( X ^ ∣ y = 1 ) ∗ P ( y = 1 ) P ( X ^ ∣ y = 0 ) ∗ P ( y = 0 ) + P ( X ^ ∣ y = 1 ) ∗ P ( y = 1 ) = 0 0 + 0 P(y=0|\hat X)=\frac{P(\hat X|y=0)*P(y=0)}{P(\hat X)}=\frac{P(\hat X|y=1)*P(y=1)}{P(\hat X|y=0)*P(y=0)+P(\hat X|y=1)*P(y=1)}=\frac{0}{0+0} P(y=0X^)=P(X^)P(X^y=0)P(y=0)=P(X^y=0)P(y=0)+P(X^y=1)P(y=1)P(X^y=1)P(y=1)=0+00均无法计算。
此时,需要进行拉普拉斯平滑。
拉普拉斯平滑为对应项均加1,即 P ( y = 1 ∣ X ^ ) = P ( X ^ ∣ y = 1 ) ∗ P ( y = 1 ) + 1 ( P ( X ^ ∣ y = 0 ) ∗ P ( y = 0 ) + 1 ) + ( P ( X ^ ∣ y = 1 ) ∗ P ( y = 1 ) + 1 ) = 1 1 + 1 = 1 2 P(y=1|\hat X)=\frac{P(\hat X|y=1)*P(y=1)+1}{(P(\hat X|y=0)*P(y=0)+1)+(P(\hat X|y=1)*P(y=1)+1)}=\frac{1}{1+1}=\frac{1}{2} P(y=1X^)=(P(X^y=0)P(y=0)+1)+(P(X^y=1)P(y=1)+1)P(X^y=1)P(y=1)+1=1+11=21

四、更一般化的朴素贝叶斯

上述为最简单的0-1分布的伯努利类型的贝叶斯,还有两种更一般化的贝叶斯分类。

1)xi取值变为多项式分布

依然假设xi条件相互独立 P ( x 1 , x 2 . . . , x 9999 , x 10000 ∣ y ) = ∏ i = 1 i = 10000 P ( X ^ ∣ y ) P(x_1,x_2...,x_{9999},x_{10000}|y) = \prod_{i=1}^{i=10000}P(\hat X|y) P(x1,x2...,x9999,x10000y)=i=1i=10000P(X^y)。但是,xi不仅仅只取0,1两个值,而是可以取k个值(记为1,2,…k)。
此时,根据朴素贝叶斯xi条件相互独立的假设依然可以得到 P ( X ^ ∣ y ) = ∏ i = 1 i = 10000 P ( x i ∣ y ) P(\hat X|y)=\prod_{i=1}^{i=10000}P(x_i|y) P(X^y)=i=1i=10000P(xiy)。此时,P(xi|y)不再是一个伯努利分布而是一个多项式分布。拉普拉斯平滑为分子加1,分母加K(0-1二项分布有2个结果所以分母加了2。)
针对连续目标变量的分类,就可以将其离散化后如上述进行分类。如对房价进行预测时,可以分为100w以下时x=1,100w到200w时x=2,200w到300w时x=3等。一般会将目标分为10个类别,分类结果较好。

2)多项式事件模型

依然以文本分类为例。(朴素贝叶斯在文本分类中效果好用得多)
如果把每个词考虑为0-1伯努利分布,则仅仅捕获了该词是否出现的特征,对该词数量这个特征没有捕获。而在实际中,若一份文本中出现十次"buy",则它是垃圾推销邮件的可能应该是大于只出现一次“buy"的。下述建模方式考虑到了词数这个特征。
若把第i个训练样本用特征向量 X ^ i = [ x i 1 , x i 2 , . . . x i ( n i − 1 ) , x i ( n i ) , ] T \hat X_i=[x_{i1},x_{i2},...x_{i(n_i-1)},x_{i(n_i)},]^T X^i=[xi1,xi2,...xi(ni1),xi(ni),]T,其中,ni为该训练样本的长度,即该训练样本所包含的词数。xi1为该文字中第1个词在字典中的索引。如,该文本第一个词为"Hello","Hello"在字典中为第2000个词,则xi1=2000-1=1999(计算机下标从0开始)。以此类推。
此时,对某份文本而言,它的概率为 ( ∏ i = 1 i = n i P ( x i ∣ y ) ) P ( y ) (\prod_{i=1}^{i=n_i}P(x_i|y))P(y) (i=1i=niP(xiy))P(y)。如对一封垃圾邮件来说,某个垃圾邮件发送者决定给你发一封垃圾邮件,此时y=1,他做这个决定的概率为P(y=1)。然后他用了ni个词来写这封垃圾邮件,每个词出现的概率为P(xi|y=1),此时这封垃圾邮件的存在概率就为 ( ∏ i = 1 i = n i P ( x i ∣ y = 1 ) ) P ( y = 1 ) (\prod_{i=1}^{i=n_i}P(x_i|y=1))P(y=1) (i=1i=niP(xiy=1))P(y=1)
这个数学模型的参数有
ϕ k ∣ y = 1 = P ( x i = k ∣ y = 1 ) \phi_{k|y=1}=P(x_i=k|y=1) ϕky=1=P(xi=ky=1)
ϕ k ∣ y = 0 = P ( x i = k ∣ y = 0 ) \phi_{k|y=0}=P(x_i=k|y=0) ϕky=0=P(xi=ky=0)
ϕ y = P ( y = 1 ) \phi_y=P(y=1) ϕy=P(y=1)
进行最大似然估计可以得到,参数应该选择为:
ϕ k ∣ y = 1 = ∑ i = 1 i = m ∑ j = 1 j = n i 1 [ x i j = k , y i = 1 ] ∑ i = 1 i = m 1 [ y i = 1 ] ∗ n i = 标 签 为 1 的 文 本 中 , 字 典 中 第 k 个 词 语 出 现 的 次 数 标 签 为 1 的 文 本 的 词 数 之 和 \phi_{k|y=1}=\frac{\sum_{i=1}^{i=m}\sum_{j=1}^{j=n_i}1[x_{ij}=k,y_i=1]}{\sum_{i=1}^{i=m}1[y_i=1]*n_i}=\frac{标签为1的文本中,字典中第k个词语出现的次数}{标签为1的文本的词数之和} ϕky=1=i=1i=m1[yi=1]nii=1i=mj=1j=ni1[xij=k,yi=1]=11k
ϕ k ∣ y = 0 = ∑ i = 1 i = m ∑ j = 1 j = n i 1 [ x i j = k , y i = 0 ] ∑ i = 1 i = m 1 [ y i = 0 ] ∗ n i = 标 签 为 0 的 文 本 中 , 字 典 中 第 k 个 词 语 出 现 的 次 数 标 签 为 0 的 文 本 的 词 数 之 和 \phi_{k|y=0}=\frac{\sum_{i=1}^{i=m}\sum_{j=1}^{j=n_i}1[x_{ij}=k,y_i=0]}{\sum_{i=1}^{i=m}1[y_i=0]*n_i}=\frac{标签为0的文本中,字典中第k个词语出现的次数}{标签为0的文本的词数之和} ϕky=0=i=1i=m1[yi=0]nii=1i=mj=1j=ni1[xij=k,yi=0]=00k
ϕ y = 标 签 为 1 的 样 本 数 样 本 总 数 \phi_y=\frac{标签为1的样本数}{样本总数} ϕy=1
注意,xij为一个靠字典长度确定范围的值。所以可以直接判断xij是否等于k。
此时,拉普拉斯平滑为 ϕ k ∣ y = 1 = ∑ i = 1 i = m ∑ j = 1 j = n i 1 [ x i j = k , y i = 1 ] + 1 ∑ i = 1 i = m 1 [ y i = 1 ] ∗ n i + 10000 = 标 签 为 1 的 文 本 中 , 字 典 中 第 k 个 词 语 出 现 的 次 数 + 1 标 签 为 1 的 文 本 的 词 数 之 和 + 字 典 长 度 ( 所 有 可 能 的 词 的 数 量 ) \phi_{k|y=1}=\frac{\sum_{i=1}^{i=m}\sum_{j=1}^{j=n_i}1[x_{ij}=k,y_i=1]+1}{\sum_{i=1}^{i=m}1[y_i=1]*n_i+10000}=\frac{标签为1的文本中,字典中第k个词语出现的次数+1}{标签为1的文本的词数之和+字典长度(所有可能的词的数量)} ϕky=1=i=1i=m1[yi=1]ni+10000i=1i=mj=1j=ni1[xij=k,yi=1]+1=1+()1k+1,y=0同理

五、朴素贝叶斯的简单实现

import numpy as np
import re
import random
import matplotlib.pyplot as plt
import matplotlib as mpl
#解决图表中文显示问题 
mpl.rcParams["font.sans-serif"] = [u"SimHei"]#黑体显示中文
mpl.rcParams["axes.unicode_minus"] = False#负号正常显示
#创建训练集
def loadDataset():
    #posinglist为样本集 list类型方便处理
    posinglist = [list('我喜欢你'),list('我不喜欢你'),list('舔狗滚'),list('一起去看电影吗'),
                  list('你是个好人但我们不合适'),list('我爱你'),list('我不爱你'),
                  list('我想你了'),list('我不想见到你'),list('可以来我家帮我修电脑吗'),
                  list('如果你在多好'),list('你真棒'),list('我暂时不想谈恋爱'),list('你喜欢什么样的女生'),
                  list('我和室友约好了'),list('我不需要你的关心'),list('送你礼物'),list('不想出去玩'),
                  list('哦'),list('教教我'),list('嘤嘤嘤'),list('有空约吗'),list('我们还是算了吧'),
                  list('学业为重'),list('我们只是朋友'),list('等你')]
    classVector = [1,0,0,1,0,1,0,1,0,1,1,1,0,1,0,0,1,0,0,1,1,1,0,0,0,1]#分类向量,1为喜欢0为不喜欢
    return posinglist,classVector
#创建无重复词库 返回dataset所有元的无序集合
def createVolcabularyList(dataset):
    voca_set = set([])#创建一个空集合
    for document in dataset:
        voca_set = voca_set | set(document)#求交集 集合交集结果为包含所有元的无序集合
    return list(voca_set)#转列表再返回
#得到表达式的特征向量 返回inputset字符串在voca_set字典下的特征向量
def featureVectorForExpression(voca_set,inputSet):
    returnVec = [0]*len(voca_set)#returnVec为全为0,长度为字典列表的长度的列表
    for word in inputSet:
        if word in voca_set:
            returnVec[voca_set.index(word)]=1#取得voca_set中word的索引,作为returnVec为1的对应下标
        #else:
            #print("The word %s is not in voca_set"% word)
    return returnVec
#根据文本集合posinglist,字典voca_set和分类向量feature_vector得到参数并返回
def NaiveBayersTrain(posinglist,voca_set,class_vector):
    phi_y = sum(class_vector)/len(class_vector)#计算y=1的概率,等于y=1的数目/总样本数。sum为对该分类向量求和,值就是1的数目
    wordnum = len(voca_set)#字典词数
    phi_i_y1 = np.zeros((wordnum,1))#创建y=1时xi的概率向量,第i个位置表示字典第i个字符串在y=1时的概率
    for i in range(0,wordnum):
        for j in range(0,len(class_vector)):
            if class_vector[j] == 1 and voca_set[i] in posinglist[j]:
                phi_i_y1[i] += 1#在整个字典,整个分类向量上进行遍历,若分类结果为1,该词在该正样本里,则计数+1
    #print('phi_i_y1:',phi_i_y1)
    phi_i_y1 /= sum(class_vector)
    #print('phi_i_y1:',phi_i_y1)
    phi_i_y0 = np.zeros((wordnum,1))#创建y=0时xi的概率向量,第i个位置表示字典第i个字符串在y=0时的概率
    for i in range(0,wordnum):
        for j in range(0,len(class_vector)):
            if class_vector[j] == 0 and voca_set[i] in posinglist[j]:
                phi_i_y0[i] += 1#在整个字典,整个分类向量上进行遍历,若分类结果为0,该词在该正样本里,则计数+1
    phi_i_y0 /= sum(class_vector)
    #print('phi_i_y0:',phi_i_y0)
    return phi_y,phi_i_y1,phi_i_y0
#根据参数和字典voca_set对输入testExpression计算概率并返回
def NaiveBayersCalculate(phi_y,phi_i_y1,phi_i_y0,testExpression,voca_set):
    FeatureVectorOfExpression = featureVectorForExpression(voca_set,testExpression)
    #print('该输入的特征向量为',FeatureVectorOfExpression)
    Pro_x_y1,Pro_x_y0 = 1,1
    for i in range(0,len(testExpression)):
        if (testExpression[i] in voca_set)==False:
            Pro_x_y0 = 0#有不在字典里的词,则正负样本中它的数量都为0,概率也为0,总概率为连乘,也为0.
            Pro_x_y1 = 0#同上
            print('word',testExpression[i],' is not in the dictionary')
            return Pro_x_y0,Pro_x_y1,0.5,0.5#此时P(y=1|x)和P(y=0|x)的拉普拉斯平滑结果为(0+1)/2=0.5,
    for i in range(0,len(FeatureVectorOfExpression)):
        if FeatureVectorOfExpression[i] ==1 :
            #print('word is:',voca_set[i])
            #print('phi_i_y1[i] is:',phi_i_y1[i])
            #print('phi_i_y0[i] is:',phi_i_y0[i])
            Pro_x_y1 *= phi_i_y1[i]#特征向量第i个位置为字典的第i个字符串,phi_i_y=1第i个位置为字典第i个字符串的概率
            Pro_x_y0 *= phi_i_y0[i]#同上
    Pro_x = Pro_x_y0*(1-phi_y)+Pro_x_y1*phi_y
    Pro_y1_x = (Pro_x_y1*phi_y+1)/(Pro_x+2)#拉普拉斯平滑
    Pro_y0_x = (Pro_x_y0*phi_y+1)/(Pro_x+2)#同上
    return Pro_x_y0,Pro_x_y1,Pro_y0_x,Pro_y1_x
#得到样本和标签向量
pl,cV = loadDataset()
#得到字典
v_set = createVolcabularyList(pl)
#得到概率参数
phi_y,phi_i_y1,phi_i_y0 = NaiveBayersTrain(pl,v_set,cV)
phi_i_y1 = np.ravel(phi_i_y1)#降为一维
phi_i_y0 = np.ravel(phi_i_y0)
#绘图显示概率
fig = plt.figure(figsize=(30,10))
ax1 = plt.subplot(121)  
ax1.bar(v_set,phi_i_y1,color='red')
ax2 = plt.subplot(122)  
ax2.bar(v_set,phi_i_y0,color='green')
plt.show()  
print('字典为',v_set)
#对testInput进行分类
def NaiveBayersClassify(testInput,pl,cv,v_set):
    #得到样本集和标签向量
    posinglist,classVector = pl,cV
    Input = list(testInput)#样本集为list,格式匹配
    #检查样本集里是否已经包含Input,如果已经有,直接根据标签完成分类即可
    for word in posinglist:
        if word == Input:
            print('样本集里有',''.join(Input),'其标签为', classVector[posinglist.index(word)])#''.join把list转回str
            if classVector[posinglist.index(word)] == 1:
                print('女生对你说',testInput,"表示她喜欢你")
            else:
                print('女生对你说',testInput,"表示她不喜欢你")
            return
    #得到字典
    vocabulary_set = v_set
    #计算参数
    phi_y,phi_i_y1,phi_i_y0 = NaiveBayersTrain(posinglist,vocabulary_set,classVector)
    #根据参数计算概率
    Pro_x_y0,Pro_x_y1,Pro_y0_x,Pro_y1_x = NaiveBayersCalculate(phi_y,phi_i_y1,phi_i_y0,testInput,vocabulary_set)
    print(testInput,'预测为喜欢的概率为:',Pro_y1_x)
    print(testInput,'预测为不喜欢的概率为:',Pro_y0_x)
    if Pro_y1_x==0.5 and Pro_y0_x==0.5:
        print("五五开,投硬币吧。。")
    if Pro_y1_x>Pro_y0_x:
        print('女生对你说',testInput,"朴素贝叶斯预测结果为:她喜欢你可能性更大")
    if Pro_y1_x

运行结果为

机器学习 朴素贝叶斯算法数学过程及Python实现:女生说这话是不是对你有意思_第1张图片
实际中的样本集和字典会大很多很多,这里只是简单在网上找了点语句。
2018.12.21 期末考试复习之前

你可能感兴趣的:(机器学习)