朴素贝叶斯基本概念

简单示例

In [1]:
# 用 0 代表没有下雨,而1代表下雨。
import numpy as np

# 过去7天是否下雨可以用数组表示 
y=np.array( [0,1,1,0,1,0,0] )

# 其他气象信息: 北风、闷热、多云、天气预报是否下雨
X=np.array([
	[0, 1, 0, 1],
	[1, 1, 1, 0],
	[0, 1, 1, 0],
	[0, 0, 0, 1],
	[0, 1, 1, 0],
	[0, 1, 0, 1],
	[1, 0, 0, 1],
])

# 分析下雨、不下雨时每个气象条件的频数
counts={}
for label in np.unique(y):
	counts[label]=X[ y==label ].sum(axis=0)

print("Feature counts:\n{}".format(counts))
# 没下雨时(y=0), 4天天气预报都说下雨,1天北风,2天闷热,0天多云。
# 下雨时(y=1),天气预报都说没有下雨,1天北风,3天闷热,3天多云。
Feature counts:
{0: array([1, 2, 0, 4]), 1: array([1, 3, 3, 0])}

使用 伯努利贝叶斯分类器

In [2]:
from sklearn.naive_bayes import BernoulliNB
# 拟合数据
clf=BernoulliNB()
clf.fit(X,y)

# 预测一下训练集
print( clf.predict(X) )
# 打分
print( clf.score(X, y) )
[0 1 1 0 1 0 0]
1.0
In [3]:
# 如果天气预报说没有下雨,且出现多云,倾向于归类到“下雨”
print(clf.predict( [[0,0,1,0]] ))

clf.predict_proba( [[0,0,1,0]] ) 
[1]
Out[3]:
array([[0.13848881, 0.86151119]])
In [4]:
# 如果天气预报说下雨,且北风,闷热,无云,倾向于归类到“不下雨”
print(clf.predict( [[1,1,0,1]] ))

clf.predict_proba( [[1,1,0,1]] )
[0]
Out[4]:
array([[0.92340878, 0.07659122]])

朴素贝叶斯的不同方法

伯努利朴素贝叶斯

In [1]:
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split

# 生成样本,数量500,分类数5
X,y=make_blobs(n_samples=500, centers=5, random_state=8)

# 拆分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=8)

# 使用伯努利贝叶斯拟合数据
from sklearn.naive_bayes import BernoulliNB
nb=BernoulliNB()
nb.fit(X_train, y_train)

# 打分
print("trainning score: {:0.3f}".format( nb.score(X_train, y_train) ) )
print("testing score: {:0.3f}".format( nb.score(X_test, y_test) ) )

# 只有一半分类是正确的,很糟糕!
trainning score: 0.499
testing score: 0.544
In [2]:
# 可视化,为什么这么糟糕
import numpy as np
import matplotlib.pyplot as plt
x_min, x_max = X[:,0].min()-0.5, X[:,0].max()+0.5
y_min, y_max = X[:,1].min()-0.5, X[:,1].max()+0.5

# 用不同背景色表示不同分类
xx, yy=np.meshgrid(np.arange(x_min, x_max, 0.02),
                  np.arange(y_min, y_max, 0.02))
z=nb.predict(np.c_[(xx.ravel(), yy.ravel())]).reshape(xx.shape)
plt.pcolormesh(xx, yy, z, cmap=plt.cm.Pastel1, shading='auto')

# 画训练集和测试集散点图
plt.scatter(X_train[:,0], X_train[:,1], c=y_train, cmap=plt.cm.cool, edgecolor='k')
plt.scatter(X_test[:,0], X_test[:,1], c=y_test, cmap=plt.cm.cool, edgecolors='k', marker='*')
plt.xlim( xx.min(), xx.max())
plt.ylim( yy.min(), yy.max())
plt.title("Classifier: BernoulliNB")
plt.show()
# 这就是简单把2条线,分为4个象限,注意有3个颜色。
# 因为使用了伯努利朴素贝叶斯的默认参数 binarize=0.0,所以模型对于数据的判断是
#  如果特征1大于或等于0,且特征2大于或等于0,归为一类;
#  如果特征1小于0,且特征2小于0,归为一类;
#  其余归为一类。
# 所以分类效果很烂。

# 对于多分类,不能使用伯努利朴素贝叶斯模型了。可以使用高斯朴素贝叶斯模型。

高斯朴素贝叶斯

In [3]:
# 就是假设样本的特征符合高斯分布/正态分布。

from sklearn.naive_bayes import GaussianNB
gnb=GaussianNB()
gnb.fit(X_train, y_train)

# 打分
print("trainning score: {:0.3f}".format( gnb.score(X_train, y_train) ) )
print("testing score: {:0.3f}".format( gnb.score(X_test, y_test) ) )
trainning score: 0.939
testing score: 0.968
In [4]:
# 可视化,为什么分类效果这么好

# 用不同背景色表示不同分类
xx, yy=np.meshgrid(np.arange(x_min, x_max, 0.02),
                  np.arange(y_min, y_max, 0.02))
z=gnb.predict(np.c_[(xx.ravel(), yy.ravel())]).reshape(xx.shape)
plt.pcolormesh(xx, yy, z, cmap=plt.cm.Pastel1, shading='auto')

# 画训练集和测试集散点图
plt.scatter(X_train[:,0], X_train[:,1], c=y_train, cmap=plt.cm.cool, edgecolor='k')
plt.scatter(X_test[:,0], X_test[:,1], c=y_test, cmap=plt.cm.cool, edgecolors='k', marker='*')
plt.xlim( xx.min(), xx.max())
plt.ylim( yy.min(), yy.max())
plt.title("Classifier: GaussianNB")
plt.show()
# 可见,高斯NB的分类边界比伯努利NB复杂的多,且基本分类正确。 # 最常用,因为自然科学和社会科学,大量现象都符合正态分布。

多项式朴素贝叶斯分布

# 二项分布通过抛硬币来理解,多项式分布可以通过掷骰子来理解。 # 均匀的6面骰子,每次投掷后朝上的一面是1-6这6个数字。如果投掷n次,则每个面朝上的次数的分布,符合多项式分布。
In [5]:
from sklearn.naive_bayes import MultinomialNB

mnb=MultinomialNB()
#mnb.fit(X_train, y_train) # 报错 ValueError: Negative values in data passed to MultinomialNB (input X)
#mnb.score(X_test, y_test)


# 只能传入非负数
# 导入数据预处理工具 MinMaxScaler,作用是把特征值全部转为0-1之间。
from sklearn.preprocessing import MinMaxScaler
scaler=MinMaxScaler()
scaler.fit(X_train)
X_train_scaled=scaler.transform(X_train)
X_test_scaled=scaler.transform(X_test)

# 使用多项式朴素贝叶斯拟合经过预处理的数据
mnb.fit(X_train_scaled, y_train)
mnb.score(X_test_scaled, y_test)
Out[5]:
0.32
In [6]:
# 这个打分很糟糕,比伯努利NB还差。可视化

# 用不同背景色表示不同分类
xx, yy=np.meshgrid(np.arange(x_min, x_max, 0.02),
                  np.arange(y_min, y_max, 0.02))
z=mnb.predict(np.c_[(xx.ravel(), yy.ravel())]).reshape(xx.shape)
plt.pcolormesh(xx, yy, z, cmap=plt.cm.Pastel1, shading='auto')

# 画训练集和测试集散点图
plt.scatter(X_train[:,0], X_train[:,1], c=y_train, cmap=plt.cm.cool, edgecolor='k')
plt.scatter(X_test[:,0], X_test[:,1], c=y_test, cmap=plt.cm.cool, edgecolors='k', marker='*')
plt.xlim( xx.min(), xx.max())
plt.ylim( yy.min(), yy.max())
plt.title("Classifier: MultinomialNB")
plt.show()
# 大部分数据放到了错误的分类中。

# 多项式NB只适合对非负离散数值特征进行分类。典型例子是转化为向量后的文本数据进行分类。

真实数据 - 判断中流是良性还是恶性

了解数据

In [1]:
from sklearn.datasets import load_breast_cancer
cancer=load_breast_cancer()

print( cancer.keys() )
dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename', 'data_module'])
In [2]:
# print(cancer.DESCR)
In [3]:
print( cancer.target_names)
print( cancer["feature_names"])
['malignant' 'benign']
['mean radius' 'mean texture' 'mean perimeter' 'mean area'
 'mean smoothness' 'mean compactness' 'mean concavity'
 'mean concave points' 'mean symmetry' 'mean fractal dimension'
 'radius error' 'texture error' 'perimeter error' 'area error'
 'smoothness error' 'compactness error' 'concavity error'
 'concave points error' 'symmetry error' 'fractal dimension error'
 'worst radius' 'worst texture' 'worst perimeter' 'worst area'
 'worst smoothness' 'worst compactness' 'worst concavity'
 'worst concave points' 'worst symmetry' 'worst fractal dimension']

建模

In [4]:
X, y=cancer.data, cancer.target

# 拆分数据
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=38)
print("train set size:", X_train.shape)
print("test set size:", X_test.shape)

# 建模
from sklearn.naive_bayes import GaussianNB
gnb=GaussianNB()
gnb.fit(X_train, y_train)

# 打分
print("trainning score: {:0.3f}".format( gnb.score(X_train, y_train) ) )
print("testing score: {:0.3f}".format( gnb.score(X_test, y_test) ) )
train set size: (426, 30)
test set size: (143, 30)
trainning score: 0.948
testing score: 0.944
In [5]:
# 随便预测一个
print("predict:", gnb.predict( [X[312]] ))
print("real:", y[312])
predict: [1]
real: 1

学习曲线 x=样本量 y=打分

In [6]:
import numpy as np
import matplotlib.pyplot as plt

# 导入学习曲线库
from sklearn.model_selection import learning_curve
# 导入随机拆分工具
from sklearn.model_selection import ShuffleSplit

# 定义一个函数绘制学习曲线
def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None, 
                       n_jobs=1, train_sizes=np.linspace(0.1, 1.0, 5)):
    plt.figure()
    plt.title(title)
    if ylim is not None:
        plt.ylim(*ylim)
    # xlab
    plt.xlabel("Traning examples")
    # ylab
    plt.ylabel("Score")
    train_sizes, train_scores, test_scores = learning_curve(
        estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes)
    train_scores_mean=np.mean(train_scores, axis=1)
    test_scores_mean=np.mean(test_scores, axis=1)
    plt.grid()
    
    plt.plot(train_sizes, train_scores_mean, 'o-', color='r', label="Training score")
    plt.plot(train_sizes, test_scores_mean, 'o-', color='g', label="Cross-validation score")
    
    plt.legend(loc="lower right")
    return plt

# setting
title="Learning Curves (Naive Bayes)"
cv=ShuffleSplit(n_splits=100, test_size=0.2, random_state=0)
estimator=GaussianNB()
plot_learning_curve(estimator, title, X, y, ylim=(0.9, 1.01), cv=cv, n_jobs=4)
plt.show()
# 可见,随着样本量的增大,训练集打分逐渐降低,因为要拟合的信息越来越多。
# 而测试集打分基本不变,说明高斯NB在预测方面,对样本量的要求没那么苛刻。如果样本量少,可以考虑NB建模。