【写在前面】
不知不觉已经第二篇了,希望可以坚持下去总结一年多的积累
曾无数次遇到bug在CSDN找到了解答,这个系列的总结也算是回馈给有需要的广大读者
感谢群主提供的学习机会@AI蜗牛车,群KOL@老表
推荐一下群主的公众号:AI蜗牛车,文中对于文本分类相关内容很多整理自公众号的白话机器学习部分
再推荐下此系列的上一篇博文——基于逻辑回归的分类预测
那么我们开始吧~~
朴素贝叶斯算法(Naive Bayes, NB) 是应用最为广泛的分类算法之一。基于贝叶斯公式计算得到,它是基于贝叶斯定义和特征条件独立假设的分类器方法。
之所以称为朴素贝叶斯,是因为它假设每个输入变量是独立的。
其有着坚实的数学基础,以及稳定的分类效率,且NB模型所需估计的参数很少,对缺失数据不太敏感,算法也比较简单。适用于垃圾邮件过滤、情感预测、推荐系统等常见分类预测场景。尤其值得一提的是,对于文本分类任务,尤其是对于英文等语言来说,分类效果很好。
优劣势:
什么是条件概率,我们从一个摸球的例子来理解。我们有两个桶:灰色桶和绿色桶,一共有7个小球,4个蓝色3个紫色,分布如下图:
从这7个球中,随机选择1个球是紫色的概率p是多少?选择过程如下:
p ( 球 = 紫 色 ) = p ( 选 择 灰 桶 ) ⋅ p ( 从 灰 桶 中 选 择 紫 色 ) + p ( 选 择 绿 桶 ) ⋅ p ( 从 绿 桶 中 选 择 紫 色 ) = 1 2 ⋅ 2 4 + 1 2 ⋅ 1 3 p(球=紫色) \\ =p(选择灰桶) \cdot p(从灰桶中选择紫色) + p(选择绿桶) \cdot p(从绿桶中选择紫色) \\ =\frac{1}{2} \cdot \frac{2}{4} + \frac{1}{2} \cdot \frac{1}{3} p(球=紫色)=p(选择灰桶)⋅p(从灰桶中选择紫色)+p(选择绿桶)⋅p(从绿桶中选择紫色)=21⋅42+21⋅31
上述我们选择小球的过程就是条件概率的过程,在选择桶的颜色的情况下是紫色的概率,另一种计算条件概率的方法是贝叶斯准则。
贝叶斯公式是英国数学家提出的一个数据公式:
p ( A ∣ B ) = p ( A , B ) p ( B ) = p ( B ∣ A ) ⋅ p ( A ) ∑ a ∈ F A p ( B ∣ a ) ⋅ p ( a ) p(A|B)=\frac{p(A,B)}{p(B)}=\frac{p(B|A) \cdot p(A)}{\sum_{a \in ℱ_A}p(B|a) \cdot p(a)} p(A∣B)=p(B)p(A,B)=∑a∈FAp(B∣a)⋅p(a)p(B∣A)⋅p(A)
p(A,B):表示事件A和事件B同时发生的概率。
p(B):表示事件B发生的概率,叫做先验概率;p(A):表示事件A发生的概率。
p(A|B):表示当事件B发生的条件下,事件A发生的概率叫做后验概率。
p(B|A):表示当事件A发生的条件下,事件B发生的概率。
我们用一句话理解贝叶斯:世间很多事都存在某种联系,假设事件A和事件B。人们常常使用已经发生的某个事件去推断我们想要知道的之间的概率。
例如,医生在确诊的时候,会根据病人的舌苔、心跳等来判断病人得了什么病。对病人来说,只会关注得了什么病,医生会通道已经发生的事件来
确诊具体的情况。这里就用到了贝叶斯思想,A是已经发生的病人症状,在A发生的条件下是 B i B_i Bi的概率。
朴素贝叶斯法 = 贝叶斯定理 + 特征条件独立。
输入 X ∈ R n X \in R^n X∈Rn空间是n维向量集合,输出空间 y = { c 1 , c 2 , . . . , c K } y=\{c_1,c_2,...,c_K\} y={ c1,c2,...,cK}. 所有的X和y都是对应空间上的随机变量. P ( X , Y ) P(X,Y) P(X,Y)是X和Y的联合概率分别. 训练数据集(由 P ( X , Y ) P(X,Y) P(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)}
计算测试数据x的列表,我们需要依次计算 P ( Y = c k ∣ X = x ) P(Y=c_k|X=x) P(Y=ck∣X=x),取概率最大的值,就是x对应的分类。
P ( Y = c k ∣ X = x ) P(Y=c_k|X=x) P(Y=ck∣X=x)我们一般这样解释,当给定 ( X = x ) (X=x) (X=x)的条件下, Y = c k Y=c_k Y=ck的概率,这就是条件概率. 这就简单了,我们只需要每个的x,计算其对应的 c k , k ∈ [ 1 , 2 , . . . , K ] c_k,k \in [1,2,...,K] ck,k∈[1,2,...,K]的概率,选择最大的概率作为这个x的类别进行了.
通过贝叶斯公式进行变形,得到预测的概率计算公式:
P ( Y = c k ∣ X = x ) = P ( X = x ∣ Y = c k ) P ( Y = c k ) ∑ 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_{k}P(X=x|Y=c_k)P(Y=c_k)} P(Y=ck∣X=x)=∑kP(X=x∣Y=ck)P(Y=ck)P(X=x∣Y=ck)P(Y=ck)
我们只需要计算以下两个概率即可,又由于朴素贝叶斯假设条件独立,我们可以单独计算每个特征的条件概率: P ( X ( i ) = x ( i ) ∣ Y = c k ) P(X^{(i)}=x^{(i)}|Y=c_k) P(X(i)=x(i)∣Y=ck)和类目 c k c_k ck的先验概率: P ( Y = c k ) P(Y=c_k) P(Y=ck)。为了更好的理解这个公式,看下图解释:
其中:
P ( Y = c k ) = ∑ i = 1 N I ( y i = c k ) N , k = 1 , 2 , . . . , K P(Y=c_k)=\frac{\sum_{i=1}^{N}I(y_i=c_k)}{N}, k=1,2,...,K P(Y=ck)=N∑i=1NI(yi=ck),k=1,2,...,K
当涉及到多个条件时,朴素贝叶斯有一个提前的假设,我们称之为 条件独立性假设(或者 简单假设:Naive):公式如下
P ( A , B ∣ Y ) = P ( A ∣ Y ) ⋅ P ( B ∣ Y ) P(A,B|Y) = P(A|Y) \cdot P(B|Y) P(A,B∣Y)=P(A∣Y)⋅P(B∣Y)
这个公式是朴素贝叶斯的基础假设,即各个条件概率是相互独立的,A不影响B,B不影响A。
而对这里来说,假设 X = [ x 1 , x 2 , . . . , x n ] X = [x_1,x_2,...,x_n] X=[x1,x2,...,xn]
P ( X = x ∣ Y = c k ) = P ( X ( 1 ) = x ( 1 ) , X ( 2 ) = x ( 2 ) , . . . , X ( n ) = x ( n ) ∣ Y = c k ) = ∏ i = 1 n P ( x i ∣ y ) P(X=x|Y=c_k) \\ =P(X^{(1)}=x^{(1)},X^{(2)}=x^{(2)},...,X^{(n)}=x^{(n)}|Y=c_k) \\ =\prod_{i=1}^{n} P(x_i | y) P(X=x∣Y=ck)=P(X(1)=x(1),X(2)=x(2),...,X(n)=x(n)∣Y=ck)=i=1∏nP(xi∣y)
由此原式可以等价为:
P ( Y = c k ∣ X = x ) = ∏ i = 1 n P ( x i ∣ Y = c k ) P ( Y = c k ) ∑ k ∏ i = 1 n P ( x i ∣ Y = c k ) P ( Y = c k ) P(Y=c_k|X=x)=\frac{\prod_{i=1}^{n} P(x_i | Y=c_k)P(Y=c_k)}{\sum_{k}\prod_{i=1}^{n} P(x_i | Y=c_k)P(Y=c_k)} P(Y=ck∣X=x)=∑k∏i=1nP(xi∣Y=ck)P(Y=ck)∏i=1nP(xi∣Y=ck)P(Y=ck)
我们为了选择后验概率最大的结果,进行概率的比较,由于分母一致,这里直接去掉分母,得到最后的计算公式。
y = a r g m a x c k P ( Y = c k ) ∏ j P ( X ( j ) = x ( j ) ∣ Y = c k ) y=arg max_{c_k}P(Y=c_k)\prod_{j}P(X^{(j)}=x^{(j)}|Y=c_k) y=argmaxckP(Y=ck)j∏P(X(j)=x(j)∣Y=ck)
我们来看一个实例,更好的理解贝叶斯的计算过程,根据天气和是否是周末预测一个人是否会出门。
index | X 1 : X_1: X1:天气的好坏 | X 2 : X_2: X2:是否周末 | Y : Y: Y:是否出门 |
---|---|---|---|
1 | 好 | 是 | 出门 |
2 | 好 | 否 | 出门 |
3 | 好 | 是 | 不出门 |
4 | 好 | 否 | 出门 |
5 | 不好 | 是 | 出门 |
6 | 不好 | 否 | 不出门 |
根据上述数据,为了更好的理解计算过程,我们给出几个计算公式:
a. 当出门的条件下,X_1是天气不好的概率:
p ( X 1 = 不 好 ∣ Y = 出 门 ) = p ( X 1 = 不 好 , Y = 出 门 ) p ( Y = 出 门 ) = 1 4 p(X_1=不好|Y=出门) =\frac{p(X_1=不好,Y=出门)}{p(Y=出门)}=\frac{1}{4} p(X1=不好∣Y=出门)=p(Y=出门)p(X1=不好,Y=出门)=41
b. 出门的概率
p ( Y = 出 门 ) = 4 6 p(Y=出门)=\frac{4}{6} p(Y=出门)=64
c. X_1天气不好的概率、
p ( X 1 = 不 好 ) = 2 6 p(X_1=不好)=\frac{2}{6} p(X1=不好)=62
d. 在X_1天气不好的情况下,出门的概率:
p ( Y = 出 门 ∣ X 1 = 不 好 ) = p ( X 1 = 不 好 ∣ Y = 出 门 ) ⋅ p ( Y = 出 门 ) p ( X = 不 好 ) = 1 4 ⋅ 4 6 2 6 = 1 2 p(Y=出门|X_1=不好)=\frac{p(X_1=不好|Y=出门) \cdot p(Y=出门)}{p(X=不好)} \\ =\frac{\frac{1}{4} \cdot \frac{4}{6}}{\frac{2}{6}}=\frac{1}{2} p(Y=出门∣X1=不好)=p(X=不好)p(X1=不好∣Y=出门)⋅p(Y=出门)=6241⋅64=21
f. 在X_1天气不好的情况下,不出门的概率:
p ( Y = 出 门 ∣ X 1 = 不 好 ) = 1 − p ( Y = 不 出 门 ∣ X 1 = 不 好 ) = 1 − 1 2 = 1 2 p(Y=出门|X_1=不好)=1-p(Y=不出门|X_1=不好)=1-\frac{1}{2}=\frac{1}{2} p(Y=出门∣X1=不好)=1−p(Y=不出门∣X1=不好)=1−21=21
朴素贝叶斯算法假设所有特征的出现相互独立互不影响,每一特征同等重要,又因为其简单,而且具有很好的可解释性一般。相对于其他精心设计的更复杂的分类算法,朴素贝叶斯分类算法是学习效率和分类效果较好的分类器之一。朴素贝叶斯算法一般应用在文本分类,垃圾邮件的分类,信用评估,钓鱼网站检测等。
sklearn库提供了三个朴素贝叶斯分类算法:
# 导入高斯朴素贝叶斯分类器
from sklearn.naive_bayes import GaussianNB
# sklearn内置的dataset支持直接返回自变量与因变量
X, y = datasets.load_iris(return_X_y=True)
# 拆分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
我们需要计算两个概率分别是:条件概率: P ( X ( i ) = x ( i ) ∣ Y = c k ) P(X^{(i)}=x^{(i)}|Y=c_k) P(X(i)=x(i)∣Y=ck)和类目 c k c_k ck的先验概率: P ( Y = c k ) P(Y=c_k) P(Y=ck)。
通过分析发现训练数据是数值类型的数据,这里假设每个特征服从高斯分布(正态分布),因此我们选择高斯朴素贝叶斯来进行分类计算。
高斯朴素贝叶斯假设每个特征都服从高斯分布,我们把一个随机变量X服从数学期望为μ,方差为 σ 2 σ^2 σ2 的数据分布称为高斯分布。对于每个特征我们一般使用平均值来估计μ和使用所有特征的方差估计 σ 2 σ^2 σ2。
P ( X ( i ) = x ( i ) ∣ Y = c k ) = 1 2 π σ y 2 exp ( − ( x ( i ) − μ c k ) 2 2 σ c k 2 ) P(X^{(i)}=x^{(i)}|Y=c_k) = \frac{1}{\sqrt{2\pi\sigma^2_y}} \exp\left(-\frac{(x^{(i)} - \mu_{c_k})^2}{2\sigma^2_{c_k}}\right) P(X(i)=x(i)∣Y=ck)=2πσy21exp(−2σck2(x(i)−μck)2)
# 使用高斯朴素贝叶斯进行计算
clf = GaussianNB(var_smoothing=1e-8) # var_smoothing进行拉普拉斯平滑,方便有效的避免零概率问题
clf.fit(X_train, y_train)
# 评估
y_pred = clf.predict(X_test)
acc = np.sum(y_test == y_pred) / X_test.shape[0] # 分类正确的除以整体数量
print("Test Acc : %.3f" % acc) # 0.967
# 预测
y_proba = clf.predict_proba(X_test[:1])
print(clf.predict(X_test[:1]))
print("预计的概率值:", y_proba) # 软分类模型,可输出各种类别的置信概率
# [1.63542393e-232 2.18880483e-006 9.99997811e-001]
从上述例子中的预测结果中,我们可以看到类别2对应的后验概率值最大,所以我们认为类目2是最优的结果。
# 使用基于类目特征的朴素贝叶斯
from sklearn.naive_bayes import CategoricalNB
# 模拟数据
rng = np.random.RandomState(1)
# 随机生成600个100维的数据,每一维的特征都是[0, 4]之前的整数
X = rng.randint(5, size=(600, 100))
y = np.array([1, 2, 3, 4, 5, 6] * 100)
data = np.c_[X, y]
# X和y进行整体打散
random.shuffle(data)
X = data[:,:-1]
y = data[:, -1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
至此,所有的数据特征都是离散特征,我们引入基于离散特征的朴素贝叶斯分类器。
# 当 alpha=1 时,使用的是 Laplace 平滑; 当 0
clf = CategoricalNB(alpha=1)
clf.fit(X_train, y_train)
acc = clf.score(X_test, y_test)
print("Test Acc : %.3f" % acc) # 0.683
# 随机数据测试,分析预测结果,贝叶斯会选择概率最大的预测结果
x = rng.randint(5, size=(1, 100))
print(clf.predict_proba(x))
print(clf.predict(x))
# [3.48859652e-04, 4.34747491e-04, 2.23077189e-03, 9.90226387e-01, 5.98248900e-03, 7.76745425e-04]
# 可见第四个类别概率最大
这里的测试数据的准确率没有任何意义,因为数据是随机生成的,不一定具有贝叶斯先验性,这里只是作为一个列子
alpha=1这个参数表示什么?
我们知道贝叶斯法一定要计算两个概率:条件概率: P ( X ( i ) = x ( i ) ∣ Y = c k ) P(X^{(i)}=x^{(i)}|Y=c_k) P(X(i)=x(i)∣Y=ck)和类目 c k c_k ck的先验概率: P ( Y = c k ) P(Y=c_k) P(Y=ck)。
对于离散特征:
P ( X ( j ) = x ( j ) ∣ Y = c k ) = ∑ i = 1 N I ( x i j = a j l , y i = c k ) + α ∑ i = 1 N I ( y i = c k ) + S j α P(X^{(j)}=x^{(j)}|Y=c_k)=\frac{\sum_{i=1}^{N}I(x_i^j=a_{jl},y_i=c_k)+\alpha}{\sum_{i=1}^{N}I(y_i=c_k)+S_j\alpha} P(X(j)=x(j)∣Y=ck)=∑i=1NI(yi=ck)+Sjα∑i=1NI(xij=ajl,yi=ck)+α
我们可以看出就是对每一个变量的多加了一个频数alpha。当alphaλ=0时,就是极大似然估计。通常取值alpha=1,这就是拉普拉斯平滑(Laplace smoothing),这有叫做贝叶斯估计,主要是因为如果使用极大似然估计,如果某个特征值在训练数据中没有出现,这时候会出现概率为0的情况,导致整个估计都为0,因为引入贝叶斯估计。
其中:
S j S_j Sj:表示第j个特征的个数。
x i j x_i^j xij:表示第i个样本的第j维元素。
y i y_i yi:第i个样本的label。
sklearn 中我们直接使用 TfidfVectorizer 类,它可以帮我们计算单词 TF-IDF 向量的值。
TfidfVectorizer(stop_words=stop_words, token_pattern=token_pattern)
# 停用词 stop_words 是一个列表 List 类型。停用词就是在分类中没有用的词,这些词一般词频 TF 高,但是 IDF 很低,起不到分类的作用
# 过滤规则 token_pattern 是正则表达式
在英文文档中,最常用的是 NTLK 包。NTLK 包中包含了英文的停用词 stop words、分词和标注方法。
import nltk
word_list = nltk.word_tokenize(text) #分词
nltk.pos_tag(word_list) #标注单词的词性
在中文文档中,最常用的是 jieba 包,jieba 包中包含了中文的停用词 stop words 和分词方法。
import jieba
word_list = jieba.cut (text) #中文分词
加载停用词表我们需要自己读取停用词表文件
stop_words = [line.strip().decode('utf-8') for line in io.open('stop_words.txt').readlines()]
直接创建 TfidfVectorizer 类,然后使用 fit_transform 方法进行拟合,得到 TF-IDF 特征空间 features,可以理解为选出来的分词就是特征。
# max_df 参数用来描述单词在文档中的最高出现率,一般很少设置 min_df,因为 min_df 通常都会很小。
tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5)
features = tf.fit_transform(train_contents)
将特征训练集的特征空间 train_features,以及训练集对应的分类 train_labels 传递给贝叶斯分类器 clf,它会自动生成一个符合特征空间和对应分类的分类器。
其中,alpha 为平滑参数,因为如果一个单词在训练样本中没有出现,这个单词的概率就会被计算为 0。但训练集样本只是整体的抽样情况,我们不能因为一个事件没有观察到,就认为整个事件的概率为 0。
# 多项式贝叶斯分类器
from sklearn.naive_bayes import MultinomialNB
clf = MultinomialNB(alpha=0.001).fit(train_features, train_labels)
首先我们需要得到测试集的特征矩阵,用训练集的分词创建一个 TfidfVectorizer 类,使用同样的 stop_words 和 max_df,然后用这个 TfidfVectorizer 类对测试集的内容进行 fit_transform 拟合,得到测试集的特征矩阵 test_features。
test_tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5, vocabulary=train_vocabulary)
test_features=test_tf.fit_transform(test_contents)
用训练好的分类器对新数据做预测。方法是使用 predict 函数,传入测试集的特征矩阵 test_features,得到分类结果 predicted_labels,predict 函数做的工作就是求解所有后验概率并找出最大的那个。
算准确率实际上是对分类模型的评估。我们可以调用 sklearn 中的 metrics 包,在 metrics 中提供了 accuracy_score 函数
from sklearn import metrics
print metrics.accuracy_score(test_labels, predicted_labels)
关于自然语言处理、关于朴素贝叶斯模型其实还有很多打法,比如半朴素贝叶斯算法通过考虑部分关联性适度改进变量非独立分布的限制。这部分我也还在学习中,暂不做展开~
欢迎一起交流讨论~~
END :happy:
references:
阿里云天池龙珠计划-机器学习训练营
【白话机器学习】算法理论+实战之朴素贝叶斯