朴素贝叶斯分类算法的原理及其python实现

文章目录

  • 一、模型描述与假设
    • 模型描述
    • 期望风险最小化
    • 生成式模型
    • 朴素假设
  • 二、模型学习与参数估计
    • 伯努利模型(文本型)
    • 多项式模型(词频型)
    • 高斯模型
  • 三、python实现
    • 伯努利模型
    • 多项式模型
    • 高斯模型
  • 四、多项式模型之文本分类


一、模型描述与假设

模型描述

假设输入随机向量 X ⊂ X ∈ R n X\sub{\mathcal X}\in \R^n XXRn,输出随机向量 Y ⊂ Y = { c 1 , c 2 , ⋯   , c K } Y\sub\mathcal Y=\{c_1,c_2,\cdots,c_K\} YY={c1,c2,,cK} P ( X , Y ) P(X, Y) P(X,Y) X X X Y Y Y的联合概率分布,训练集 T = { ( x 1 , y 1 ) , ⋯   , ( x N , y N ) } T=\{(x_1,y_1),\cdots,(x_N,y_N)\} T={(x1,y1),,(xN,yN)} P ( X , Y ) P(X, Y) P(X,Y)独立同分布产生。

对于给定的输入 x = { x 1 , x 2 , ⋯   , x n } \bm x=\{x^1, x^2, \cdots, x^n\} x={x1,x2,,xn},朴素贝叶斯分类算法通过联合概率分布 P ( X , Y ) P(X,Y) P(X,Y),计算输入 x \bm x x下各类别 c k c_k ck的概率 P ( c k ∣ x ) P(c_k|\bm x) P(ckx),并通过期望风险最小化准则,决定最终输出类别,即 f : X → Y f:\mathcal X \to \mathcal Y f:XY

期望风险最小化

假设模型 f f f对训练集 X X X的预测类别为 f ( X ) f(X) f(X),训练集 X X X的真实类别为 Y Y Y,模型 f f f的损失函数为 L ( Y , f ( X ) ) L(Y, f(X)) L(Y,f(X))。对于单一样本 x \bm x x,若模型 f f f基于后验概率 P ( c ∣ x ) P(c|\bm x) P(cx)将样本的类别预测为 c c c,则损失为
R ( c ∣ x ) = ∑ k = 1 K L ( c k , f ( x ) = c ) P ( c k ∣ x ) R(c|\bm x) = \sum_{k=1}^K L(c_k, f(\bm x) = c)P(c_k|\bm x) R(cx)=k=1KL(ck,f(x)=c)P(ckx)

实际的预测任务应使得总体风险最小,显然,若对单一样本 x \bm x x的预测都能保证风险最小,则最终的总体风险也一定是最小。因此,朴素贝叶斯算法的预测判定准则为:对每个样本选择使条件风险 R ( c ∣ x ) R(c|\bm x) R(cx)最小的类别标记,即
f ( x ) = arg ⁡ min ⁡ c ∈ Y R ( c ∣ x ) f(\bm x)=\arg\min_{c\in\mathcal Y}R(c|\bm x) f(x)=argcYminR(cx)

若损失函数 L ( Y , f ( X ) ) L(Y, f(X)) L(Y,f(X)) 0 / 1 0/1 0/1损失,即当预测类别 c c c等于真实类别时损失为0,否则损失为1,则
f ( x ) = arg ⁡ min ⁡ c ∈ Y ∑ k = 1 K P ( c k ≠ c ∣ x ) = arg ⁡ min ⁡ c ∈ Y ( 1 − P ( c k = c ∣ x ) ) = arg ⁡ max ⁡ c ∈ Y P ( c ∣ x ) \begin{aligned}f(\bm x) &=\arg\min_{c\in\mathcal Y}\sum_{k=1}^KP(c_k \neq c|\bm x) \\ &= \arg\min_{c\in\mathcal Y}(1-P(c_k=c|\bm x)) \\ &= \arg\max_{c\in\mathcal Y}P(c|\bm x) \end{aligned} f(x)=argcYmink=1KP(ck=cx)=argcYmin(1P(ck=cx))=argcYmaxP(cx)

由上式结果可见,为使总体期望风险最小化,朴素贝叶斯算法对输入x,输出类别为具有后验概率最大的类别。

生成式模型

基于有限训练集,对于给定的 x \bm x x估计后验概率 P ( c k ∣ x ) P(c_k|\bm x) P(ckx)有两种策略:

  • 通过直接建模预测 P ( c k ∣ x ) P(c_k| \bm x) P(ckx),这种模型称之为判别式模型
  • 先建模求出联合概率分布 P ( Y , X ) P(Y,X) P(Y,X),再通过联合概率分布得到 P ( c k ∣ x ) P(c_k|\bm x) P(ckx),这种模型称之为生成式模型

朴素贝叶斯分类算法属于生成式模型,由贝叶斯定理知
P ( c k ∣ x ) = P ( c k , x ) P ( x ) = P ( x ∣ c k ) P ( c k ) ∑ k = 1 K P ( x ∣ c k ) P ( c k ) P(c_k|\bm x) =\frac{P(c_k, \bm x)}{P(\bm x)} =\frac{P(\bm x|c_k)P(c_k)}{\sum_{k=1}^KP(\bm x|c_k)P(c_k)} P(ckx)=P(x)P(ck,x)=k=1KP(xck)P(ck)P(xck)P(ck)

上式中,先验概率 P ( c k ) P(c_k) P(ck)和条件概率 P ( x ∣ c k ) P(\bm x|c_k) P(xck)为未知量,其计算方法是朴素贝叶斯算法的核心。

朴素假设

基于贝叶斯公式估计后验概率 P ( c k ∣ x ) P(c_k|\bm x) P(ckx),涉及到条件概率 P ( x ∣ c k ) P(\bm x|c_k) P(xck)的计算,由于 P ( x 1 , ⋯   , x n ∣ c k ) P(x^1, \cdots, x^n|c_k) P(x1,,xnck)是所有属性上的联合概率,难以从有限的训练集中直接估计得到。

若每个特征取值范围的较大,在计算上也会遭遇组合爆炸样本稀疏等问题,如对于 n n n维的输入 x \bm x x,每个特征 x j x^j xj S j S_j Sj种取值, Y Y Y可取值有 K K K种,则模型的参数个数(不同的概率数)为 K ∏ j = 1 n S j K\prod_{j=1}^nS_j Kj=1nSj

为了简化条件概率的计算,朴素贝叶斯算法提出条件假设 (朴素的意义):用于分类的特征在类别确定的条件下相互独立,即
P ( x ∣ c k ) = P ( x 1 , x 2 , ⋯   , x n ∣ c k ) = ∏ j = 1 n P ( x j ∣ c k ) \begin{aligned}P(\bm x|c_k) & = P(x^1,x^2, \cdots,x^n|c_k) \\ & = \prod_{j=1}^n P(x^j |c_k) \end{aligned} P(xck)=P(x1,x2,,xnck)=j=1nP(xjck)

朴素假设下,模型的参数个数变为 K ∑ j = 1 n S j K\sum_{j=1}^nS_j Kj=1nSj

此时,模型变为
f ( x ) = arg ⁡ max ⁡ c k ∈ Y P ( c k ∣ x ) = arg ⁡ max ⁡ c k ∈ Y P ( x ∣ c k ) ⋅ P ( c k ) P ( x , c k ) = arg ⁡ max ⁡ c k ∈ Y P ( x ∣ c k ) ⋅ P ( c k ) ∑ k = 1 K P ( x ∣ c k ) ⋅ P ( c k ) = arg ⁡ max ⁡ c k ∈ Y P ( c k ) ∏ j = 1 n P ( x j ∣ c k ) ∑ k = 1 K P ( c k ) ∏ j = 1 n P ( x j ∣ c k ) \begin{aligned}f(\bm x) &=\arg\max_{c_k\in\mathcal Y}P(c_k|\bm x) =\arg\max_{c_k\in\mathcal Y}\frac{P(\bm x|c_k) \cdot P(c_k)}{P(\bm x,c_k)} \\[1ex] & = \arg\max_{c_k\in\mathcal Y}\frac{P(\bm x|c_k) \cdot P(c_k)}{\displaystyle\sum_{k=1}^KP(\bm x |c_k)\cdot P(c_k)}\\[1ex] & = \arg\max_{c_k\in\mathcal Y}\frac{P(c_k)\displaystyle\prod_{j=1}^n P(x^j|c_k)}{\displaystyle\sum_{k=1}^K P(c_k)\displaystyle\prod_{j=1}^n P(x^j|c_k)}\end{aligned} f(x)=argckYmaxP(ckx)=argckYmaxP(x,ck)P(xck)P(ck)=argckYmaxk=1KP(xck)P(ck)P(xck)P(ck)=argckYmaxk=1KP(ck)j=1nP(xjck)P(ck)j=1nP(xjck)
分母对所有的 c k c_k ck具有相同值,且多个条件概率的乘积可能导致浮点数下界溢出,一般是将概率对数化,最终得到决策模型
f ( x ) = arg ⁡ max ⁡ c k ∈ Y [ log ⁡ P ( c k ) + ∑ j = 1 n log ⁡ P ( x j ∣ c k ) ] f(\bm x)=\arg\max_{c_k\in\mathcal Y}[\log P(c_k)+\sum_{j=1}^n\log P(x^j|c_k)] f(x)=argckYmax[logP(ck)+j=1nlogP(xjck)]


二、模型学习与参数估计

假设样本集 X = { x 1 , ⋯   , x N } X=\{\bm x_1, \cdots, \bm x_N\} X={x1,,xN},样本 x i = { x i 1 , ⋯   , x i n } \bm x_i=\{x_i^1, \cdots, x_i^n\} xi={xi1,,xin},特征 x i j ∈ { a j 1 , ⋯   , a j S j } x_i^j \in \{a_j^1, \cdots, a_j^{S_j}\} xij{aj1,,ajSj},标签集 Y = { y 1 , y 2 , ⋯   , y N } Y=\{y_1, y_2, \cdots, y_N\} Y={y1,y2,,yN},标签 y i ∈ { c 1 , ⋯   , c K } y_i\in\{c_1, \cdots, c_K\} yi{c1,,cK}

朴素贝叶斯算法的学习,意味着由给定的样本信息估计出先验概率 P ( c k ) P(c_k) P(ck)和类条件概率 P ( a j l ∣ c k ) P(a_j^l|c_k) P(ajlck)。若模型已知,对于给定的新实例 x = { x 1 , x 2 , ⋯   , x n } \bm x=\{x^1, x^2, \cdots, x^n\} x={x1,x2,,xn},特征允许重复,则后验概率的计算公式为:
P ( c k ∣ x ) = P ( c k ) ∏ i = 1 n P ( x i ∣ c k ) P(c_k|\bm x)=P(c_k)\prod_{i=1}^nP(x^i|c_k) P(ckx)=P(ck)i=1nP(xick)

由于朴素贝叶斯有多种模型,各模型中计算先验概率和类条件概率的方法有所不同,如伯努利模型(文本型)和多项式模型(词频型),由于朴素贝叶斯多用于文档分类,因此才有了文本性和词频型的区分,其实这些模型并不局限于文本分类。当特征值存在连续值时,可使用高斯模型。

伯努利模型(文本型)

伯努利模型中每个特征的取值范围为 { 0 , 1 } \{0,1\} {0,1},即 S j = 2 S_j=2 Sj=2,忽略了特征出现的次数。伯努利模型以实例数(文本数)为粒度,在计算类条件概率时,对于未出现的特征(特征值为0),依旧需要参与概率的计算。

文本可理解为训练集中的样例,由极大似然估计可得
P ( c k ) = 类 c k 下 所 有 样 本 数 训 练 集 所 有 样 本 数   P ( a j l ∣ c k ) = 类 c k 下 第 j 个 特 征 为 a j l 的 样 本 数 类 c k 下 所 有 样 本 数 P(c_k)=\frac{类c_k下所有样本数}{训练集所有样本数} \\ \,\\ P(a_j^l |c_k) = \frac{类c_k下第j个特征为a_j^l的样本数}{类c_k下所有样本数} P(ck)=ckP(ajlck)=ckckjajl

考虑到训练集容量有限,直接计算先验概率和条件概率可能出现概率值为0的情况,概率计算引入平滑因子 λ \lambda λ
P ( a j l ∣ c k ) = 类 c k 下 第 j 个 特 征 为 a j l 的 样 本 数 + λ 类 c k 下 所 有 样 本 数 + 2 λ P(a_j^l |c_k) = \frac{类c_k下第j个特征为a_j^l的样本数+\lambda}{类c_k下所有样本数+2\lambda} P(ajlck)=ck+2λckjajl+λ

特殊地, λ = 1 \lambda=1 λ=1时,上式称为拉普拉斯平滑,分母加2,可理解为对于任何一类均添加包含所有特征和不包含所有特征的两个样本。注:sklearn源码实现,先验概率未引入平滑因子。

引理:先验概率 P ( c k ) P(c_k) P(ck)的极大似然估计
θ = P ( y = c k ) \theta=P(y =c_k) θ=P(y=ck),显然 1 − θ = P ( y ≠ c k ) 1 - \theta=P(y\neq c_k) 1θ=P(y=ck),因此可将标签集 Y Y Y看作为服从0-1分布,即
P ( y ) = θ I ( y = c k ) ( 1 − θ ) 1 − I ( y = c k ) P(y)=\theta^{I(y=c_k)}(1-\theta)^{1-I(y=c_k)} P(y)=θI(y=ck)(1θ)1I(y=ck)
上式中, I I I为指示函数,即当 y = c k y=c_k y=ck时指示函数为1,否则为0。

因此, θ \theta θ的对数似然函数
log ⁡ P ( Y ∣ θ ) = log ⁡ ∏ i = 1 N θ I ( y i = c k ) ( 1 − θ ) 1 − I ( y i = c k ) \log P(Y|\theta) = \log\prod_{i=1}^N \theta^{I(y_i=c_k)}(1-\theta)^{1-I(y_i=c_k)} logP(Yθ)=logi=1NθI(yi=ck)(1θ)1I(yi=ck)
θ \theta θ的偏导并令其为0,得 θ = ∑ i = 1 N I ( y i = c k ) / N \theta=\displaystyle\sum_{i=1}^N I(y_i=c_k)/N θ=i=1NI(yi=ck)/N

实例1

No. doc label
1 apple orange apple fruit
2 apple orange potato fruit
3 orange orange grape fruit
4 potato cabbage vegetable

词集:【apple,cabbage,grape,orange,potato】,标签集:【fruit,vegetable】,求【apple apple grape】的类别?

解: 将每一个文档向量化(特征值为0或1),则四个文档的特征向量分别为

No. apple cabbage grape orange potato label
1 1 0 0 1 0 fruit
2 1 0 0 1 1 fruit
3 0 0 1 1 0 fruit
4 0 1 0 0 1 vegetable

待预测实例【apple apple grape】的特征向量为[1, 0, 1, 0, 0],因此
P ( a p p l e ∣ f r u i t ) = 2 + 1 3 + 2 = 3 5 , P ( c a b b a g e ∣ f r u i t ) = 0 + 1 3 + 2 = 1 5 , P ( g r a p e ∣ f r u i t ) = 1 + 1 3 + 2 = 2 5   P ( o r a n g e ∣ f r u i t ) = 3 + 1 3 + 2 = 4 5 , P ( p o t a t o ∣ f r u i t ) = 1 + 1 3 + 2 = 2 5 , P ( f r u i t ) = 3 4 \begin{aligned} &P({\sf {apple}}|{\sf fruit}) =\frac{2+1}{3+2}=\frac{3}{5},\quad P({\sf \xcancel{cabbage}}|{\sf fruit}) =\frac{0+1}{3+2}=\frac{1}{5},\quad P({\sf {grape}}|{\sf fruit}) =\frac{1+1}{3+2}=\frac{2}{5}\\\,\\ &P({\sf \xcancel{orange}}|{\sf fruit}) =\frac{3+1}{3+2}=\frac{4}{5},\quad P({\sf \xcancel{potato}}|{\sf fruit}) =\frac{1+1}{3+2}=\frac{2}{5},\quad P({\sf fruit})=\frac{3}{4} \end{aligned} P(applefruit)=3+22+1=53,P(cabbage fruit)=3+20+1=51,P(grapefruit)=3+21+1=52P(orange fruit)=3+23+1=54,P(potato fruit)=3+21+1=52,P(fruit)=43

因此
P ( a p p l e , a p p l e , g r a p e ∣ f r u i t ) = P ( a p p l e ∣ f r u i t ) ⋯ P ( p o t a t o ∣ f r u i t ) P ( f r u i t ) = 3 5 ⋅ 1 5 ⋅ 2 5 ⋅ 4 5 ⋅ 2 5 ⋅ 3 4 = 0.01152 \begin{aligned}P({\sf apple, apple, grape|fruit}) &=P({\sf {apple}}|{\sf fruit}) \cdots P({\sf \xcancel{potato}}|{\sf fruit})P({\sf fruit})\\ &=\frac{3}{5}\cdot\frac{1}{5}\cdot\frac{2}{5}\cdot\frac{4}{5}\cdot\frac{2}{5}\cdot\frac{3}{4}=0.01152 \end{aligned} P(apple,apple,grapefruit)=P(applefruit)P(potato fruit)P(fruit)=535152545243=0.01152

同理
P ( a p p l e , a p p l e , g r a p e ∣ v e g e t a b l e ) = P ( a p p l e ∣ v e g e t a b l e ) ⋯ P ( p o t a t o ∣ v e g e t a b l e ) P ( v e g e t a b l e ) = 1 3 ⋅ 1 3 ⋅ 1 3 ⋅ 1 3 ⋅ 1 3 ⋅ 1 4 = 0.00103 \begin{aligned}P({\sf apple, apple, grape|vegetable}) &=P({\sf {apple}}|{\sf vegetable}) \cdots P({\sf \xcancel{potato}}|{\sf vegetable})P({\sf vegetable})\\ &=\frac{1}{3}\cdot\frac{1}{3}\cdot\frac{1}{3}\cdot\frac{1}{3}\cdot\frac{1}{3}\cdot\frac{1}{4}=0.00103 \end{aligned} P(apple,apple,grapevegetable)=P(applevegetable)P(potato vegetable)P(vegetable)=313131313141=0.00103

显然,样本【apple apple grape】属于fruit的概率更大。

多项式模型(词频型)

多项式模型中特征值为该特征出现的次数。多项式模型以特征数(词频)为粒度,在计算类条件概率时,对于未出现的特征(特征值为0),不需要参与概率的计算。

词频可理解为某特征出现的次数,概率计算公式如下:
P ( c k ) = 类 c k 下 所 有 特 征 数 训 练 集 所 有 特 征 数   P ( a j ∣ c k ) = 类 c k 下 第 j 个 特 征 a j 出 现 的 次 数 之 和 类 c k 下 所 有 特 征 数 P(c_k)=\frac{类c_k下所有特征数}{训练集所有特征数} \\\,\\ P(a_j |c_k) = \frac{类c_k下第j个特征a_j出现的次数之和}{类c_k下所有特征数} P(ck)=ckP(ajck)=ckckjaj

对于输入向量 x ∈ R n \bm x \in R^n xRn,加入平滑因子 λ \lambda λ,如下:
P ( a j ∣ c k ) = 类 c k 下 第 j 个 特 征 a j 出 现 的 次 数 之 和 + λ 类 c k 下 所 有 特 征 数 + λ n P(a_j |c_k) = \frac{类c_k下第j个特征a_j出现的次数之和 + \lambda}{类c_k下所有特征数 + \lambda n} P(ajck)=ck+λnckjaj+λ

因此,对于实例1中的文档,使用多项式模型向量化得

No. apple cabbage grape orange potato label
1 2 0 0 1 0 fruit
2 1 0 0 1 1 fruit
3 0 0 1 2 0 fruit
4 0 1 0 0 1 vegetable

待预测实例【apple apple grape】的特征向量为[2, 0, 1, 0, 0],因此
P ( a p p l e ∣ f r u i t ) = 2 + 1 9 + 5 = 3 14 , P ( g r a p e ∣ f r u i t ) = 1 + 1 9 + 5 = 2 14 , P ( f r u i t ) = 9 11 \begin{aligned} &P({\sf {apple}}|{\sf fruit}) =\frac{2+1}{9+5}=\frac{3}{14 },\quad P({\sf {grape}}|{\sf fruit}) =\frac{1+1}{9+5}=\frac{2}{14},\quad P({\sf fruit})=\frac{9}{11} \end{aligned} P(applefruit)=9+52+1=143,P(grapefruit)=9+51+1=142,P(fruit)=119

因此
P ( a p p l e , a p p l e , g r a p e ∣ f r u i t ) = P ( a p p l e ∣ f r u i t ) 2 P ( g a r p e ∣ f r u i t ) P ( f r u i t ) = ( 3 14 ) 2 ⋅ 2 14 ⋅ 9 11 = 0.00537 \begin{aligned}P({\sf apple, apple, grape|fruit}) &=P({\sf {apple}}|{\sf fruit})^2P(\sf{garpe|fruit})P({\sf fruit})\\ &=(\frac{3}{14})^2\cdot\frac{2}{14}\cdot\frac{9}{11}=0.00537 \end{aligned} P(apple,apple,grapefruit)=P(applefruit)2P(garpefruit)P(fruit)=(143)2142119=0.00537

同理
P ( a p p l e , a p p l e , g r a p e ∣ v e g e t a b l e ) = P ( a p p l e ∣ v e g e t a b l e ) 2 P ( g a r p e ∣ v e g e t a b l e ) P ( v e g e t a b l e ) = ( 1 14 ) 2 ⋅ 1 14 ⋅ 2 11 = 0.00007 \begin{aligned}P({\sf apple, apple, grape|vegetable}) &=P({\sf {apple}}|{\sf vegetable})^2P(\sf{garpe|vegetable})P({\sf vegetable})\\ &=(\frac{1}{14})^2\cdot\frac{1}{14}\cdot\frac{2}{11}=0.00007 \end{aligned} P(apple,apple,grapevegetable)=P(applevegetable)2P(garpevegetable)P(vegetable)=(141)2141112=0.00007

显然,样本【apple apple grape】属于fruit的概率更大。

高斯模型

对连续属性可考虑概率密度函数,假设 P ( a j ∣ c ) ∼ N ( μ c , j , σ c , j 2 ) P(a_j|c)\sim \mathcal N(\mu_{c,j},\sigma_{c,j}^2) P(ajc)N(μc,j,σc,j2),其中 μ c , j \mu_{c,j} μc,j σ c , j 2 \sigma_{c,j}^2 σc,j2分别是类 c c c样本下第 j j j个属性取值的均值和方差。则有
P ( a j ∣ c ) = 1 2 π σ c , j exp ⁡ ( − ( a j − u c , j ) 2 2 σ c , j 2 ) P(a_j|c) = \frac{1}{\sqrt{2\pi}\sigma_{c,j}}\exp\left(-\frac{(a_j-u_{c,j})^2}{2\sigma_{c,j}^2}\right) P(ajc)=2π σc,j1exp(2σc,j2(ajuc,j)2)


三、python实现

伯努利模型

# -*- coding: utf-8 -*-
import numpy as np


class BernoulliNB(object):

    def fit(self, X, y, alpha=1.0):
        """模型训练"""
        X = self._check_array(X)
        self.classes_, Y = self.labelbin(y)
        if not np.any((X != 0) | (X != 1)):
            raise ValueError("Input X must be 0 or 1.")

        self.feature_count_ = np.dot(Y.T, X)
        self.class_count_ = Y.sum(axis=0)

        smoothed_fc = self.feature_count_ + alpha
        smoothed_cc = self.class_count_ + alpha * 2

        self.p_feature = (np.log(smoothed_fc) -
                          np.log(smoothed_cc.reshape(-1, 1)))
        self.p_feature_neg = np.log(1 - np.exp(self.p_feature))
        self.p_class = (np.log(self.class_count_) -
                        np.log(self.class_count_.sum()))

    def predict(self, X):
        """类别预测"""
        if not hasattr(self, 'p_feature'):
            raise ValueError('Instance must be fitted.')

        X = self._check_array(X)
        n_classes, n_features = self.p_feature.shape
        n_samples, n_features_X = X.shape
        if n_features_X != n_features:
            raise ValueError('Expect input with %d features.' % n_samples)

        p = np.dot(X, (self.p_feature - self.p_feature_neg).T)
        p += self.p_class + self.p_feature_neg.sum(axis=1)
        return self.classes_[np.argmax(p, axis=1)]

    def _check_array(self, array):
        """检查输入格式"""
        array = np.asarray(array)
        if array.ndim != 2:
            raise ValueError('Expected 2D array.')
        return array

    @staticmethod
    def labelbin(y):
        """标签二进制化"""
        Y = np.ravel(y)
        classes = np.array(sorted(set(Y)))
        indices = np.searchsorted(classes, Y)

        n_samples = Y.shape[0]
        n_classes = len(classes)

        Y = np.zeros((n_samples, n_classes), dtype=int)
        rows = np.arange(n_samples)
        Y[rows, indices] = 1
        return classes, Y


if __name__ == '__main__':
    X = np.random.randint(2, size=(6, 100))
    y = np.array([1, 2, 3, 4, 4, 5])
    clf = BernoulliNB()
    clf.fit(X, y)
    print(clf.predict(X[2:3]))

多项式模型

# -*- coding: utf-8 -*-
import numpy as np


class MultinomialNB(object):

    def fit(self, X, y, alpha=1.0):
        """模型训练"""
        X = self._check_array(X)
        if np.any(X < 0):
            raise ValueError("Input X must be non-negative")

        self.classes_, Y = self.labelbin(y)
        self.feature_count_ = np.dot(Y.T, X)
        self.class_count_ = Y.sum(axis=0)

        smoothed_fc = self.feature_count_ + alpha
        smoothed_cc = smoothed_fc.sum(axis=1)

        self.p_feature = (np.log(smoothed_fc) -
                          np.log(smoothed_cc.reshape(-1, 1)))
        cc = self.feature_count_.sum(axis=1)
        self.p_class = np.log(cc) - np.log(cc.sum())

    def predict(self, X):
        """类别预测"""
        X = self._check_array(X)
        if not hasattr(self, 'p_feature'):
            raise ValueError('Instance must be fitted.')

        n_classes, n_features = self.p_feature.shape
        n_samples, n_features_X = X.shape
        if n_features_X != n_features:
            raise ValueError('Expect input with %d features.' % n_samples)

        p = np.dot(X, self.p_feature.T) + self.p_class
        return self.classes_[np.argmax(p, axis=1)]

    def _check_array(self, array):
        """检查输入格式"""
        array = np.asarray(array)
        if array.ndim != 2:
            raise ValueError('Expected 2D array.')
        return array

    @staticmethod
    def labelbin(y):
        """标签二进制化"""
        Y = np.ravel(y)
        classes = np.array(sorted(set(Y)))
        indices = np.searchsorted(classes, Y)

        n_samples = Y.shape[0]
        n_classes = len(classes)

        Y = np.zeros((n_samples, n_classes), dtype=int)
        rows = np.arange(n_samples)
        Y[rows, indices] = 1
        return classes, Y
  
  
if __name__ == '__main__':
    X = np.random.randint(5, size=(6, 100))
    y = np.array([1, 1, 3, 3, 5, 6])
    clf = MultinomialNB()
    clf.fit(X, y)
    print(clf.predict(X[2:3]))

高斯模型

# -*- coding: utf-8 -*-
import numpy as np


class GaussianNB(object):

    def fit(self, X, y):
        """模型训练"""
        y = np.ravel(y)
        X = self._check_array(X)

        self.classes_ = np.array(sorted(set(y)))
        n_features = X.shape[1]
        n_classes = len(self.classes_)
        self.mu_ = np.zeros((n_classes, n_features))
        self.var_ = np.zeros((n_classes, n_features))

        self.class_count_ = np.zeros(n_classes, dtype=np.float64)

        for i, y_i in enumerate(self.classes_):
            X_i = X[y == y_i]
            self.class_count_[i] = np.shape(X_i)[0]
            self.mu_[i] = np.mean(X_i, axis=0)
            self.var_[i] = np.var(X_i, axis=0)

        epsilon = 1e-9 * np.var(X, axis=0).max()
        self.var_ += epsilon
        self.p_class = self.class_count_ / self.class_count_.sum()

    def predict(self, X):
        """类别预测"""
        X = self._check_array(X)
        n_samples = X.shape[0]
        n_classes = self.classes_.shape[0]

        p_log = np.zeros((n_classes, n_samples))

        for i in range(n_classes):
            cc_log = np.log(self.p_class[i])
            fc_log = -0.5 * np.sum(np.log(2 * np.pi * self.var_[i]))
            fc_log -= 0.5 * np.sum((X - self.mu_[i]) ** 2 / self.var_[i], axis=1)
            p_log[i] = cc_log + fc_log

        return self.classes_[np.argmax(p_log, axis=0)]

    def _check_array(self, array):
        """检查输入格式"""
        array = np.asarray(array)
        if array.ndim != 2:
            raise ValueError('Expected 2D array.')
        return array


if __name__ == '__main__':
    X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
    Y = np.array([1, 1, 1, 2, 2, 2])
    clf = GaussianNB()
    clf.fit(X, Y)
    print(clf.predict(X))

四、多项式模型之文本分类

文本的特征是词列表,应首先将文本向量化,文本特征向量的长度等于词表(所有文本中不重复的单次)长度。

class Words2Vec(object):

    def fit(self, X):
        vob = sorted(set(w for ws in X for w in ws))
        self.vec_length = len(vob)
        self.vob_dict = dict(zip(vob, range(self.vec_length)))

    def words2vec(self, n_words):
        """文本词列表转换为词向量"""
        if not hasattr(self, 'vob_dict'):
            raise ValueError('Instance must be fitted.')
        n_samples = len(n_words)
        vectors = np.zeros((n_samples, self.vec_length), dtype=int)
        for i, words in enumerate(n_words):
            vec = vectors[i]
            for w in words:
                index = self.vob_dict.get(w, None)
                if index is not None:
                    vec[index] += 1
        return vectors

X = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
     ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
     ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
     ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
     ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
     ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
y = ['0', '1', '0', '1', '0', '1']

wv = Words2Vec()
wv.fit(X)
X = wv.words2vec(X)

clf = MultinomialNB()
clf.fit(X, y)
print(clf.predict(X))

X = wv.words2vec([['dog', 'dog', 'ate']])
print(clf.predict(X))

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