支持向量机

支持向量机(Support Vector Machines)是一种二分类模型,在机器学习、计算机视觉、数据挖掘中广泛应用,主要用于解决数据分类问题,它的目的是寻找一个超平面来对样本进行分割,分割的原则是间隔最大化(即数据集的边缘点到分界线的距离d最大,如下图),最终转化为一个凸二次规划问题来求解。通常SVM用于二元分类问题,对于多元分类可将其分解为多个二元分类问题,再进行分类。所谓“支持向量”,就是下图中虚线穿过的边缘点。支持向量机就对应着能将数据正确划分并且间隔最大的直线(下图中红色直线)

svm_1.png

  • 最优分类边界

svm_2.png

如图中的A,B两个样本点,B点被预测为正类的确信度要大于A点,所以SVM的目标是寻找一个超平面,使得离超平面较近的异类点之间能有更大的间隔,即不必考虑所有样本点,只需让求得的超平面使得离它近的点间隔最大。超平面可以用如下线性方程来描述:

$$w^T x + b = 0$$

  • 其中,\(x=(x_1;x_2;...;x_n)\),\(w=(w_1;w_2;...;w_n)\),\(b\)为偏置项. 可以从数学上证明,支持向量到超平面距离为:

$$\gamma = \frac{1}{||w||}$$

  • 为了使距离最大,只需最小化\(||w||\)即可

SVM最优边界要求

SVM寻找最优边界时,需满足以下几个要求:

  1. 正确性:对大部分样本都可以正确划分类别;
  2. 安全性:支持向量,即离分类边界最近的样本之间的距离最远;
  3. 公平性:支持向量与分类边界的距离相等;
  4. 简单性:采用线性方程(直线、平面)表示分类边界,也称分割超平面。如果在原始维度中无法做线性划分,那么就通过升维变换,在更高维度空间寻求线性分割超平面. 从低纬度空间到高纬度空间的变换通过核函数进行。

线性可分与线性不可分

线性可分

  • 如果一组样本能使用一个线性函数将样本正确分类,称这些数据样本是线性可分的。那么什么是线性函数呢?在二维空间中就是一条直线,在三维空间中就是一个平面,以此类推,如果不考虑空间维数,这样的线性函数统称为超平面。

如果一组样本,无法找到一个线性函数将样本正确分类,则称这些样本线性不可分。以下是一个一维线性不可分的示例:

svm_3.png

以下是一个二维不可分的示例:

svm_4.png

对于该类线性不可分问题,可以通过升维,将低纬度特征空间映射为高纬度特征空间,实现线性可分,如下图所示:

svm_5.png

  • 一维空间升至二维空间实现线性可分
  • 二维空间升至三维空间实现线性可分,需要用到核函数

svm_6.png

核函数

通过名为核函数的特征变换,增加新的特征,使得低维度线性不可分问题变为高维度线性可分问题。如果低维空间存在K(x,y),x,y∈Χ,使得K(x,y)=ϕ(x)·ϕ(y),则称K(x,y)为核函数,其中ϕ(x)·ϕ(y)为x,y映射到特征空间上的内积,ϕ(x)为X→H的映射函数。以下是几种常用的核函数

线性核函数

  • 线性核函数(Linear)表示不通过核函数进行升维,仅在原始空间寻求线性分类边界,主要用于线性可分问题
  • multiple2.txt
# 支持向量机示例
import numpy as np
import sklearn.model_selection as ms
import sklearn.svm as svm
import sklearn.metrics as sm
import matplotlib.pyplot as mp

x, y = [], []
with open("multiple2.txt", "r") as f:
    for line in f.readlines():
        data = [float(substr) for substr in line.split(",")]
        x.append(data[:-1])  # 输入
        y.append(data[-1])  # 输出

# 列表转数组
x = np.array(x)
y = np.array(y, dtype=int)

model = svm.SVC(kernel="linear")  # 线性核函数
model.fit(x, y)

# 计算图形边界
l, r, h = x[:, 0].min() - 1, x[:, 0].max() + 1, 0.005
b, t, v = x[:, 1].min() - 1, x[:, 1].max() + 1, 0.005

# 生成网格矩阵
grid_x = np.meshgrid(np.arange(l, r, h), np.arange(b, t, v))
flat_x = np.c_[grid_x[0].ravel(), grid_x[1].ravel()]  # 合并
flat_y = model.predict(flat_x)  # 根据网格矩阵预测分类
grid_y = flat_y.reshape(grid_x[0].shape)  # 还原形状

mp.figure("SVM Classifier", facecolor="lightgray")
mp.title("SVM Classifier", fontsize=14)

mp.xlabel("x", fontsize=14)
mp.ylabel("y", fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap="gray")

C0, C1 = (y == 0), (y == 1)
mp.scatter(x[C0][:, 0], x[C0][:, 1], c="orangered", s=80)
mp.scatter(x[C1][:, 0], x[C1][:, 1], c="limegreen", s=80)
mp.show()

svm_linear.png

练习代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import sklearn.model_selection as ms
import sklearn.svm as svm
import sklearn.metrics as sm

data = pd.read_csv('multiple2.txt',
                   header=None,
                   names=['x1','x2','y'])

data.plot.scatter(x='x1',y='x2',c='y',cmap='brg')

# plt.show()

#整理输入集和输出集
x = data.iloc[:,:-1]
y = data.iloc[:,-1]

train_x,\
test_x,\
train_y,\
test_y = ms.train_test_split(x,y,
                             test_size=0.1,
                             random_state=7)

model = svm.SVC(kernel='linear')
model.fit(train_x,train_y)
pred_test_y = model.predict(test_x)
print(sm.classification_report(test_y,pred_test_y))


#暴力绘制分类边界线

# 1.将x1的最小值,x1的最大值,拆成200个点
# 2.将x2的最小值,x2的最大值,拆成200个点
# 3.组合x1和x2的所有的点 4W个
# 4.将4w个点带入模型进行预测,得到预测类别
# 5.根据预测类别设置不同颜色

x1 = np.linspace(x['x1'].min(),x['x1'].max(),200)
x2 = np.linspace(x['x2'].min(),x['x2'].max(),200)

x = []
for i in x1:
    for j in x2:
        x.append([i,j])
x = np.array(x)
y = model.predict(x)

# data.plot.scatter(x=x[:,0],y=x[:,1],c=y,cmap='gray')
plt.scatter(x=x[:,0],y=x[:,1],c=y,cmap='gray')
plt.scatter(x=data['x1'],y=data['x2'],c=data['y'],cmap='brg')

plt.show()

输出结果

QQ截图20230627135950.jpg

多项式核函数

多项式核函数(Polynomial Kernel)用增加高次项特征的方法做升维变换,当多项式阶数高时复杂度会很高,其表达式为:

$$K(x,y)=(α*(x^T*y)+c)^d$$

$$y = x_1 + x_2$$

$$y = x_1^2 + 2x_1x_2+x_2^2$$

$$y=x_1^3 + 3x_1^2x_2 + 3x_1x_2^2 + x_2^3$$

  • 参数α用于缩放内积的结果,可以控制相似度的权重。
  • 参数c是偏置项,用于调整映射后的特征空间的原点位置。
  • 参数d是多项式核函数的次数,它定义了映射后特征空间中的多项式的阶数。
  • multiple2.txt
# 支持向量机示例
import numpy as np
import sklearn.model_selection as ms
import sklearn.svm as svm
import sklearn.metrics as sm
import matplotlib.pyplot as mp

x, y = [], []
with open("multiple2.txt", "r") as f:
    for line in f.readlines():
        data = [float(substr) for substr in line.split(",")]
        x.append(data[:-1])  # 输入
        y.append(data[-1])  # 输出

# 列表转数组
x = np.array(x)
y = np.array(y, dtype=int)

# 线性核函数支持向量机分类器
model = svm.SVC(kernel="poly", degree=3)  # 多项式核函数
# print("gamma:", model.gamma)
model.fit(x, y)

# 计算图形边界
l, r, h = x[:, 0].min() - 1, x[:, 0].max() + 1, 0.005
b, t, v = x[:, 1].min() - 1, x[:, 1].max() + 1, 0.005

# 生成网格矩阵
grid_x = np.meshgrid(np.arange(l, r, h), np.arange(b, t, v))
flat_x = np.c_[grid_x[0].ravel(), grid_x[1].ravel()]  # 合并
flat_y = model.predict(flat_x)  # 根据网格矩阵预测分类
grid_y = flat_y.reshape(grid_x[0].shape)  # 还原形状

mp.figure("SVM Classifier", facecolor="lightgray")
mp.title("SVM Classifier", fontsize=14)

mp.xlabel("x", fontsize=14)
mp.ylabel("y", fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap="gray")

C0, C1 = (y == 0), (y == 1)
mp.scatter(x[C0][:, 0], x[C0][:, 1], c="orangered", s=80)
mp.scatter(x[C1][:, 0], x[C1][:, 1], c="limegreen", s=80)
mp.show()

poli_kerl_func.png

练习代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import sklearn.model_selection as ms
import sklearn.svm as svm
import sklearn.metrics as sm

data = pd.read_csv('multiple2.txt',
                   header=None,
                   names=['x1','x2','y'])

data.plot.scatter(x='x1',y='x2',c='y',cmap='brg')

# plt.show()

#整理输入集和输出集
x = data.iloc[:,:-1]
y = data.iloc[:,-1]

train_x,\
test_x,\
train_y,\
test_y = ms.train_test_split(x,y,
                             test_size=0.1,
                             random_state=7)

model = svm.SVC(kernel='poly',degree=2)
model.fit(train_x,train_y)
pred_test_y = model.predict(test_x)
print(sm.classification_report(test_y,pred_test_y))


#暴力绘制分类边界线

# 1.将x1的最小值,x1的最大值,拆成200个点
# 2.将x2的最小值,x2的最大值,拆成200个点
# 3.组合x1和x2的所有的点 4W个
# 4.将4w个点带入模型进行预测,得到预测类别
# 5.根据预测类别设置不同颜色

x1 = np.linspace(x['x1'].min(),x['x1'].max(),200)
x2 = np.linspace(x['x2'].min(),x['x2'].max(),200)

x = []
for i in x1:
    for j in x2:
        x.append([i,j])
x = np.array(x)
y = model.predict(x)

# data.plot.scatter(x=x[:,0],y=x[:,1],c=y,cmap='gray')
plt.scatter(x=x[:,0],y=x[:,1],c=y,cmap='gray')
plt.scatter(x=data['x1'],y=data['x2'],c=data['y'],cmap='brg')

plt.show()

输出结果

QQ截图20230627140525.jpg

径向基核函数

径向基核函数(Radial Basis Function Kernel)具有很强的灵活性,应用很广泛。与多项式核函数相比,它的参数少,因此大多数情况下,都有比较好的性能。在不确定用哪种核函数时,可优先验证高斯核函数。由于类似于高斯函数,所以也称其为高斯核函数。表达式如下:

$$K(x, y) = exp(-γ * ||x - y||^2)$$

  • 其中,||x - y||表示欧氏距离,γ是径向基核函数的参数,控制了数据点对最终核函数值的影响程度。
  • 在计算核函数值时,首先计算样本x和y之间的欧氏距离,然后将其乘以负的γ,再取指数,得到最终的核函数值。
  • 径向基核函数在机器学习中常用于非线性分类和回归问题。它的作用是将样本映射到更高维的特征空间,通过衡量样本之间的相似度来进行分类或回归任务。通过调整γ的值,可以控制径向基核函数的平滑程度和决策边界的形状,从而适应不同类型的数据分布。
  • multiple2.txt
# 支持向量机示例
import numpy as np
import sklearn.model_selection as ms
import sklearn.svm as svm
import sklearn.metrics as sm
import matplotlib.pyplot as mp

x, y = [], []
with open("multiple2.txt", "r") as f:
    for line in f.readlines():
        data = [float(substr) for substr in line.split(",")]
        x.append(data[:-1])  # 输入
        y.append(data[-1])  # 输出

# 列表转数组
x = np.array(x)
y = np.array(y, dtype=int)

# 径向基核函数支持向量机分类器
model = svm.SVC(kernel="rbf",
                gamma=0.01, # 概率密度标准差
                C=600)  # 概率强度,该值越大对错误分类的容忍度越小,分类精度越高,但泛化能力越差;该值越小,对错误分类容忍度越大,但泛化能力强
model.fit(x, y)

# 计算图形边界
l, r, h = x[:, 0].min() - 1, x[:, 0].max() + 1, 0.005
b, t, v = x[:, 1].min() - 1, x[:, 1].max() + 1, 0.005

# 生成网格矩阵
grid_x = np.meshgrid(np.arange(l, r, h), np.arange(b, t, v))
flat_x = np.c_[grid_x[0].ravel(), grid_x[1].ravel()]  # 合并
flat_y = model.predict(flat_x)  # 根据网格矩阵预测分类
grid_y = flat_y.reshape(grid_x[0].shape)  # 还原形状

mp.figure("SVM Classifier", facecolor="lightgray")
mp.title("SVM Classifier", fontsize=14)

mp.xlabel("x", fontsize=14)
mp.ylabel("y", fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap="gray")

C0, C1 = (y == 0), (y == 1)
mp.scatter(x[C0][:, 0], x[C0][:, 1], c="orangered", s=80)
mp.scatter(x[C1][:, 0], x[C1][:, 1], c="limegreen", s=80)
mp.show()

rbf_kerl_func.png

练习代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import sklearn.model_selection as ms
import sklearn.svm as svm
import sklearn.metrics as sm

data = pd.read_csv('multiple2.txt',
                   header=None,
                   names=['x1','x2','y'])

data.plot.scatter(x='x1',y='x2',c='y',cmap='brg')

# plt.show()

#整理输入集和输出集
x = data.iloc[:,:-1]
y = data.iloc[:,-1]

train_x,\
test_x,\
train_y,\
test_y = ms.train_test_split(x,y,
                             test_size=0.1,
                             random_state=7)

model = svm.SVC(kernel='rbf',gamma=0.1,C=10)
model.fit(train_x,train_y)
pred_test_y = model.predict(test_x)
print(sm.classification_report(test_y,pred_test_y))


#暴力绘制分类边界线

# 1.将x1的最小值,x1的最大值,拆成200个点
# 2.将x2的最小值,x2的最大值,拆成200个点
# 3.组合x1和x2的所有的点 4W个
# 4.将4w个点带入模型进行预测,得到预测类别
# 5.根据预测类别设置不同颜色

x1 = np.linspace(x['x1'].min(),x['x1'].max(),200)
x2 = np.linspace(x['x2'].min(),x['x2'].max(),200)

x = []
for i in x1:
    for j in x2:
        x.append([i,j])
x = np.array(x)
y = model.predict(x)

# data.plot.scatter(x=x[:,0],y=x[:,1],c=y,cmap='gray')
plt.scatter(x=x[:,0],y=x[:,1],c=y,cmap='gray')
plt.scatter(x=data['x1'],y=data['x2'],c=data['y'],cmap='brg')

plt.show()

输出结果

QQ截图20230627140721.jpg

支持向量机总结

  1. 支持向量机是二分类模型
  2. 支持向量机通过寻找最优线性模型作为分类边界
  3. 边界要求:正确性、公平性、安全性、简单性
  4. 可以通过核函数将线性不可分转换为线性可分问题,核函数包括:线性核函数、多项式核函数、径向基核函数
  5. 支持向量机适合少量样本的分类
  6. 关于参数调优可以尝试网格搜索网格搜索

朴素贝叶斯

朴素贝叶斯是一组功能强大且易于训练的分类器,它使用贝叶斯定理来确定给定一组条件的结果的概率,“朴素”的含义是指所给定的条件都能独立存在和发生. 朴素贝叶斯是多用途分类器,能在很多不同的情景下找到它的应用,例如垃圾邮件过滤、自然语言处理等

概率

概率是反映随机事件出现的可能性大小. 随机事件是指在相同条件下,可能出现也可能不出现的事件. 例如:

  • 我们可以将随机事件记为A或B,则P(A),P(B)表示事件A或B的概率
  1. 抛一枚硬币,可能正面朝上,可能反面朝上,这是随机事件. 正/反面朝上的可能性称为概率
  2. 掷骰子,掷出的点数为随机事件. 每个点数出现的可能性称为概率
  3. 一批商品包含良品、次品,随机抽取一件,抽得良品/次品为随机事件. 经过大量反复试验,抽得次品率越来越接近于某个常数,则该常数为概率

联合概率与条件概率

  • 联合概率 : 联合概率表示多个事件同时发生的概率,记作\(P(A,B)\)或\(P(AB)\)或\(P(A \bigcap B)\).假设有事件A和事件B,那么它们的联合概率\(P(A \bigcap B)\)表示事件A和事件B同时发生的概率。这可以理解为两个事件的交集所占的比例。联合概率可以通过考察样本空间中同时满足事件A和事件B的样本点的概率来计算。
  • 条件概率 : 条件概率表示在已知某一事件发生的条件下,另一个事件发生的概率,记作\(P(A|B)\).假设有事件A和事件B,条件概率P(A|B)表示在事件B发生的条件下,事件A发生的概率。它描述了对于已经发生的事件B,事件A发生的可能性。条件概率可以通过计算事件A和事件B的交集的概率除以事件B发生的概率来计算。
  • 事件的独立性 : 事件A不影响事件B的发生,称这两个事件独立,记为:\(P(A \bigcap B)=P(A)*P(B)\)
  • 可以理解为,给定或不给定B的条件下,A的概率都一样大
  • 因为A和B不相互影响,则有:

$$P(A|B) = P(A)$$

先验概率与后验概率

  • 先验概率 : 先验概率也是根据以往经验和分析得到的概率,例如:在没有任何信息前提的情况下,猜测对面来的陌生人姓氏,姓李的概率最大(因为全国李姓为占比最高的姓氏),这便是先验概率
  • 后验概率 : 后验概率是指在接收了一定条件或信息的情况下的修正概率,例如:在知道对面的人来自“牛家村”的情况下,猜测他姓牛的概率最大,但不排除姓杨、李等等,这便是后验概率
  • 两者的关系 : 事情还没有发生,求这件事情发生的可能性的大小,是先验概率(可以理解为由因求果). 事情已经发生,求这件事情发生的原因是由某个因素引起的可能性的大小,是后验概率(由果求因). 先验概率与后验概率有不可分割的联系,后验概率的计算要以先验概率为基础

先验概率与后验概率详细解释 - ChatGPT

  • 于描述在观测到某些证据之前和观测到这些证据之后,关于某个事件发生的概率。
  • 先验概率是指在观测到任何新的信息或证据之前,我们对事件发生概率的初始估计。它是基于以往的经验、领域知识或先前的观察而得出的概率。先验概率通常表示为 P(A),其中 A 表示某个事件。
  • 后验概率是在观测到特定证据或信息之后,基于这些证据对事件发生概率进行更新后的概率。后验概率是通过贝叶斯定理计算得到的,它结合了先验概率和观测到的证据。后验概率通常表示为 P(A|B),其中 A 表示事件,B 表示观测到的证据。

贝叶斯定理

贝叶斯定理由英国数学家托马斯.贝叶斯(Thomas Bayes)提出,用来描述两个条件概率之间的关系,定理描述为:

$$P(A|B) = \frac{P(A)P(B|A)}{P(B)}$$

其中,\(P(A)\)和\(P(B)\)是A事件和B事件发生的概率. \(P(A|B)\)称为条件概率,表示B事件发生条件下,A事件发生的概率. 推导过程:

$$P(A,B) =P(B)P(A|B)$$

$$P(B,A) =P(A)P(B|A)$$

其中\(P(A,B)\)称为联合概率,指事件B发生的概率,乘以事件A在事件B发生的条件下发生的概率. 因为\(P(A,B)=P(B,A)\), 所以有:

$$P(B)P(A|B)=P(A)P(B|A)$$

两边同时除以\(P(B)\),则得到贝叶斯定理的表达式. 其中,\(P(A)\)是先验概率,\(P(A|B)\)是已知B发生后A的条件概率,也被称作后验概率

贝叶斯定理详细解释 - ChatGPT

贝叶斯定理表示为:

$$P(A|B) = (P(B|A) * P(A)) / P(B)$$

  • 其中,P(A|B) 表示在观测到证据 B 的情况下事件 A 发生的概率,P(B|A) 表示在事件 A 发生的情况下观测到证据 B 的概率,P(A) 表示先验概率,P(B) 表示观测到证据 B 的概率。
  • 通过贝叶斯定理,我们可以根据观测到的证据来更新我们对事件发生概率的估计,从而得到后验概率。先验概率和后验概率的关系可以帮助我们进行推断、决策和预测,特别在贝叶斯统计、机器学习和人工智能领域有广泛应用。

贝叶斯定理示例

计算诈骗短信的概率

事件 概率 表达式
所有短信中,诈骗短信 5% P(A) = 0.05
所有短信中,含有“中奖”两个字 4% P(B) = 0.04
所有短信中,是诈骗短信,并且含有“中奖”两个字 50% P(B|A) = 0.5
  • 求:收到一条新信息,含有“中奖”两个字,是诈骗短信的概率?

$$P(A|B) = P(A) P(B|A) / P(B) = 0.05 * 0.5 / 0.04 = 0.625$$

计算喝酒驾车的概率

事件 概率 表达式
所有客人中,驾车 20% P(A)= 0.2
所有客人中,喝酒 10% P(B)= 0.1
所有客人中,开车并且喝酒 5% P(B|A)= 0.05
  • 求:喝过酒仍然会开车的人的比例是多少?

$$P(A|B) = P(A) P(B|A) / P(B) = 0.2 * 0.05 / 0.1 = 0.1$$

假设一个学校中 60%的男生和40%的女生,女生穿裤子的人数和穿裙子的人数相等,所有的男生都穿裤子 一个人随机在远处眺望,看一个穿裤子的学生,请问这个学生是女生的概率

  • p(女) = 0.4
  • p(裤子|女) = 0.5
  • p(裤子) = 0.8
  • P(女|裤子) = 0.4 * 0.5 / 0.8 = 0.25

$$P(A|B) = \frac{P(A)P(B|A)}{P(B)}$$

朴素贝叶斯分类器

朴素贝叶斯分类器就是根据贝叶斯公式计算结果进行分类的模型,“朴素”指事件之间相互独立无影响. 例如:有如下数据集:

Text Category
A great game(一个伟大的比赛) Sports(体育运动)
The election was over(选举结束) Not sports(不是体育运动)
Very clean match(没内幕的比赛) Sports(体育运动)
A clean but forgettable game(一场难以忘记的比赛) Sports(体育运动)
It was a close election(这是一场势均力敌的选举) Not sports(不是体育运动)
  • 求:A very close game是体育运动的概率?数学上表示为\(P(Sports | a\ very\ close\ game)\)​. 根据贝叶斯定理,是运动的概率可以表示为:

$$P(Sports | a \ very \ close \ game)$$ $$= \frac{P(a \ very \ close \ game | sports) * P(sports)}{P(a \ very \ close \ game)}$$

  • 不是运动概率可以表示为:

$$P(Not \ Sports | a \ very \ close \ game)$$ $$= \frac{P(a \ very \ close \ game | Not \ sports) * P(Not \ sports)}{P(a \ very \ close \ game)}$$

$$P(a \ very \ close \ game | sports) * P(sports)$$

$$P(a \ very \ close \ game | Not \ sports) * P(Not \ sports)$$

  • 概率更大者即为分类结果. 由于分母相同,即比较分子谁更大即可. 我们只需统计A very close game多少次出现在Sports类别中,就可以计算出上述两个概率. 但是A very close game并没有出现在数据集中,所以这个概率为0,要解决这个问题,就假设每个句子的单词出现都与其它单词无关(事件独立即朴素的含义),所以,\(P(a\ very\ close\ game)\)可以写成:

$$P(a \ very \ close \ game) $$ $$= P(a) * P(very) * P(close) * P(game)$$

$$P(a \ very \ close \ game|Sports)$$ $$= P(a|Sports)*P(very|Sports)*P(close|Sports)*P(game|Sports)$$

统计出a,ery,close,game出现在Sports类别中的概率,就能算出其所属的类别. 具体计算过程如下:

  • 第一步:计算总词频:Sports类别词语总数14,Not Sports类别词语总数9
  • 第二步:计算每个类别的先验概率,分子部分加1,是为了避免分子为0的情况;分母部分都加了词语总数14,是为了避免分子增大的情况下计算结果超过1的可能
# Sports和Not Sports概率
P(Sports) = 3 / 5 = 0.6 
P(Not Sports) = 2 / 5 = 0.4 

# Sports条件下各个词语概率
P(a | Sports) = (2 + 1) / (11 + 14) = 0.12
P(very | Sports) = (1 + 1) / (11 + 14) = 0.08
P(close | Sports) = (0 + 1) / (11 + 14) = 0.04
P(game | Sports) = (2 + 1) / (11 + 14) = 0.12

# Not Sports条件下各个词语概率
P(a | Not Sports) = (1 + 1) / (9 + 14) = 0.087
P(very | Not Sports) = (0 + 1) / (9 + 14) = 0.043
P(close | Not Sports) = (1 + 1) / (9 + 14) =  = 0.087
P(game | Not Sports) = (0 + 1) / (9 + 14) = 0.043
  • 第三步:将先验概率带入贝叶斯定理,计算概率:
  • 是体育运动的概率:

$$P(a \ very \ close \ game|Sports)$$ $$= P(a|Sports)*P(very|Sports)*P(close|Sports)*P(game|Sports)$$ $$= 0.12 * 0.08 * 0.04 * 0.12 = 0.00004608$$

  • 不是体育运动的概率:

$$P(a \ very \ close \ game|Not \ Sports)$$ $$= P(a|Not \ Sports)*P(very|Not \ Sports)*P(close|Not \ Sports)*P(game|Not \ Sports)$$ $$= 0.087 * 0.043 * 0.087 * 0.043 = 0.000013996$$

  • 分类结果:P(Sports) = 0.00004608 , P(Not Sports) = 0.000013996, 是体育运动

实现朴素贝叶斯分类器

在sklearn中,提供了三个朴素贝叶斯分类器,分别是:

  • GaussianNB(高斯朴素贝叶斯分类器):适合用于样本的值是连续的,数据呈正态分布的情况(比如人的身高、城市家庭收入、一次考试的成绩等等)
  • MultinominalNB(多项式朴素贝叶斯分类器):适合用于大部分属性为离散值的数据集
  • BernoulliNB(伯努利朴素贝叶斯分类器):适合用于特征值为二元离散值或是稀疏的多元离散值的数据集

该示例中,样本的值为连续值,且呈正态分布,所以采用GaussianNB模型. 代码如下:

# 朴素贝叶斯分类示例
import numpy as np
import sklearn.naive_bayes as nb
import matplotlib.pyplot as mp

# 输入,输出
x, y = [], []

# 读取数据文件
with open("multiple1.txt", "r") as f:
    for line in f.readlines():
        data = [float(substr) for substr in line.split(",")]
        x.append(data[:-1])  # 输入样本:取从第一列到倒数第二列
        y.append(data[-1])  # 输出样本:取最后一列

x = np.array(x)
y = np.array(y, dtype=int)

# 创建高斯朴素贝叶斯分类器对象
model = nb.GaussianNB()
model.fit(x, y)  # 训练

# 计算显示范围
left = x[:, 0].min() - 1
right = x[:, 0].max() + 1

buttom = x[:, 1].min() - 1
top = x[:, 1].max() + 1

grid_x, grid_y = np.meshgrid(np.arange(left, right, 0.01),np.arange(buttom, top, 0.01))

mesh_x = np.column_stack((grid_x.ravel(), grid_y.ravel()))
mesh_z = model.predict(mesh_x)
mesh_z = mesh_z.reshape(grid_x.shape)

mp.figure('Naive Bayes Classification', facecolor='lightgray')
mp.title('Naive Bayes Classification', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x, grid_y, mesh_z, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], c=y, cmap='brg', s=80)
mp.show()

输出结果

naive_bayes.png

实验代码

import pandas as pd
import matplotlib.pyplot as plt
import sklearn.naive_bayes as nb
import sklearn.model_selection as ms
import sklearn.metrics as sm
import numpy as np


data = pd.read_csv('multiple1.txt',header=None,names=['x1','x2','y'])

# data.plot.scatter(x='x1',y='x2',c='y',cmap='brg')
# plt.show()

#整理输入集和输出集
x = data.iloc[:,:-1]
y = data.iloc[:,-1]

train_x,test_x,train_y,test_y = ms.train_test_split(x,y,test_size=0.1,random_state=7)


model = nb.GaussianNB()
# model = nb.MultinomialNB()
# model = nb.BernoulliNB()
model.fit(train_x,train_y)
pred_test_y = model.predict(test_x)
print(sm.classification_report(test_y,pred_test_y))

#暴力绘制分类边界线

# 1.将x1的最小值,x1的最大值,拆成200个点
# 2.将x2的最小值,x2的最大值,拆成200个点
# 3.组合x1和x2的所有的点 4W个
# 4.将4w个点带入模型进行预测,得到预测类别
# 5.根据预测类别设置不同颜色

x1 = np.linspace(x['x1'].min(),x['x1'].max(),200)
x2 = np.linspace(x['x2'].min(),x['x2'].max(),200)

x = []
for i in x1:
    for j in x2:
        x.append([i,j])
x = np.array(x)
y = model.predict(x)

plt.scatter(x=x[:,0],y=x[:,1],c=y,cmap='gray')
plt.scatter(x=data['x1'],y=data['x2'],c=data['y'],cmap='brg')

plt.show()

输出结果

QQ截图20230627152216.jpg

朴素贝叶斯总结

什么是朴素贝叶斯

  • 朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。“朴素”的含义为:假设问题的特征变量都是相互独立地作用于决策变量的,即问题的特征之间都是互不相关的。

优点

  • 逻辑性简单
  • 算法较为稳定。当数据呈现不同的特点时,朴素贝叶斯的分类性能不会有太大的差异。
  • 当样本特征之间的关系相对比较独立时,朴素贝叶斯分类算法会有较好的效果。

缺点

  • 特征的独立性在很多情况下是很难满足的,因为样本特征之间往往都存在着相互关联,如果在分类过程中出现这种问题,会导致分类的效果大大降低。

什么情况下使用朴素贝叶斯

  • 根据先验概率计算后验概率的情况,且样本特征之间独立性较强

聚类

聚类(cluster)与分类(class)问题不同,聚类是属于无监督学习模型,而分类属于有监督学习。聚类使用一些算法把样本分为N个群落,群落内部相似度较高,群落之间相似度较低。在机器学习中,通常采用“距离”来度量样本间的相似度,距离越小,相似度越高;距离越大,相似度越低

相似度度量方式

欧氏距离

相似度使用欧氏距离来进行度量. 坐标轴上两点\(x_1, x_2\)之间的欧式距离可以表示为:

$$|x_1-x_2| = \sqrt{(x_1-x_2)^2}$$

平面坐标中两点\((x_1, y_1), (x_2, y_2)\)欧式距离可表示为:

$$|(x_1,y_1)-(x_2, y_2)| = \sqrt{(x_1-x_2)^2+(y_1-y_2)^2}$$

三维坐标系中\((x_1, y_1, z_1), (x_2, y_2, z_2)\)欧式距离可表示为:

$$|(x_1, y_1, z_1),(x_2, y_2, z_2)| = \sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}$$

以此类推,可以推广到N维空间.

$$|(x_1, y_1, z_1...,n_1),(x_2, y_2, z_2,...n_2)|$$ $$= \sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2+...(n_1-n_2)^2}$$

曼哈顿距离

二维平面两点\(a(x_1, y_1)\)与\(b(x_2, y_2)\)两点间的曼哈顿距离为:

$$d(a, b) = |x_1 - x_2| + |y_1 - y_2|$$

推广到N维空间,\(x(x_1, x_2, ..., x_n)\)与\(y(y_1, y_2, ..., y_n)\)之间的曼哈顿距离为:

$$d(x,y) = |x_1 - y_1| + |x_2 - y_2| + ... + |x_n - y_n| = \sum_{i=1}^n|x_i - y_i|$$

  • 在上图中,绿色线条表示的为欧式距离,红色线条表示的为曼哈顿距离,黄色线条和蓝色线条表示的为曼哈顿距离的等价长度

manhaton_dist.png

闵可夫斯基距离

闵可夫斯基距离(Minkowski distance)又称闵氏距离,其定义为:

$$D(x, y) = (\sum_{i=1}^n |x_i - y_i|^p)^{\frac{1}{p}}$$

  • 当\(p=1\)时,即为曼哈顿距离
  • 当\(p=2\)时,即为欧式距离
  • 当\(p \rightarrow \infty\)时,即为切比雪夫距离

可见,曼哈顿距离、欧氏距离、切比雪夫距离都是闵可夫斯基的特殊形式

距离的性质

如果\(dist(x,y)\)度量标准为一个距离,它应该满足以下几个条件:

  • 非负性:距离一般不能为负,即 \(dist(x, y) >= 0\)
  • 同一性:\(dist(x_i, y_i) = 0\),当且仅当\(x_i = y_i\)
  • 对称性:\(dist(x_i, y_i) = dist(y_i, x_i)\)
  • 直递性:\(dist(x_i, x_j) <= dist(x_i, x_k) + dist(x_k, x_j)\)

聚类算法的划分

原型聚类

  • 原型聚类也称“基于原型的聚类”(prototype-based clustering),此类算法假设聚类结构能通过一组原型刻画,在现实聚类任务中极为常用. 通常情况下,算法先对原型进行初始化,然后对原型进行迭代更新求解. 采用不同的原型表示、不同的求解方式,将产生不同的算法. 最著名的原型聚类算法有K-Means.

密度聚类

  • 密度聚类也称“基于密度的聚类”(density-based clustering),此类算法假定聚类结构能通过样本分布的紧密程度确定. 通常情况下,密度聚类算法从样本密度的角度来考察样本之间的可连接性,并基于可连接样本不断扩展聚类簇以获得最终的聚类结果. 著名的密度聚类算法有DBSCAN.

层次聚类

  • 层次聚类(hierarchical clustering)试图在不同层次对数据集进行划分,从而形成树形的聚类结构. 数据集的划分可以采用“自底向上”或“自顶向下”的策略. 常用的层次聚类有凝聚层次算法等.

常用聚类算法

K均值聚类

K均值聚类(k-means clustering)算法是一种常用的、基于原型的聚类算法,简单、直观、高效。其步骤为:

  • 第一步:根据事先已知的聚类数,随机选择若干样本作为聚类中心,计算每个样本与每个聚类中心的欧式距离,离哪个聚类中心近,就算哪个聚类中心的聚类,完成一次聚类划分.
  • 第二步:计算每个聚类的几何中心,如果几何中心与聚类中心不重合,再以几何中心作为新的聚类中心,重新划分聚类. 重复以上过程,直到某一次聚类划分后,所得到的各个几何中心与其所依据的聚类中心重合或足够接近为止. 聚类过程如下图所示:

k-means.jpg

  1. 聚类数(K)必须事先已知,来自业务逻辑的需求或性能指标.
  2. 最终的聚类结果会因初始中心的选择不同而异,初始中心尽量选择离中心最远的样本.

sklearn关于k-means算法API:

import sklearn.cluster as sc

# 创建模型
model = sc.KMeans(n_clusters)  # n_cluster为聚类数量

 # 获取聚类(几何)中心
centers = model.cluster_centers_  

 # 获取聚类标签(聚类结果)
pred_y = model.labels_  

示例代码:

# k-means示例
import numpy as np
import sklearn.cluster as sc
import matplotlib.pyplot as mp
import sklearn.metrics as sm

x = []  # 没有输出(无监督学习)
with open("multiple3.txt", "r") as f:
    for line in f.readlines():
        line = line.replace("\n", "")
        data = [float(substr) for substr in line.split(",")]
        x.append(data)

x = np.array(x)

# K均值聚类器
model = sc.KMeans(n_clusters=4)  # n_cluster为聚类数量
model.fit(x)  # 训练

pred_y = model.labels_  # 聚类标签(聚类结果)
centers = model.cluster_centers_  # 获取聚类(几何)中心

print("centers:", centers)
print("labels.shape:", pred_y.shape)
#print("labels:", pred_y)

# 计算并打印轮廓系数
score = sm.silhouette_score(x, pred_y,sample_size=len(x),metric="euclidean")  # 欧式距离度量
print("silhouette_score:", score)

# 可视化
mp.figure("K-Means Cluster", facecolor="lightgray")
mp.title("K-Means Cluster")
mp.xlabel("x", fontsize=14)
mp.ylabel("y", fontsize=14)
mp.tick_params(labelsize=10)
mp.scatter(x[:, 0], x[:, 1], s=80, c=pred_y, cmap="brg")
mp.scatter(centers[:, 0], centers[:, 1], marker="+",c="black", s=200, linewidths=1)
mp.show()

输出结果

centers: [[7.07326531 5.61061224]
 [1.831      1.9998    ]
 [5.91196078 2.04980392]
 [3.1428     5.2616    ]]
labels.shape: (200,)
silhouette_score: 0.5773232071896658

k-means.png

实验代码

import sklearn.cluster as sc
import pandas as pd
import matplotlib.pyplot as plt
data = pd.read_csv('multiple3.txt',header=None)
# plt.scatter(x=data[0],y=data[1])
# data.plot.scatter(x=0,y=1)
# plt.show()
model = sc.KMeans(4)
model.fit(data)
label = model.labels_
print(label)
plt.scatter(x=data[0],y=data[1],c=label,cmap='brg')
plt.colorbar()
plt.show()

输出结果

QQ截图20230627153250.jpg

特点及使用

  • 优点
  1. 原理简单,实现方便,收敛速度快;
  2. 聚类效果较优,模型的可解释性较强;
  • 缺点
  1. 需要事先知道聚类数量;
  2. 聚类初始中心的选择对聚类结果有影响;
  3. 采用的是迭代的方法,只能得到局部最优解;
  4. 对于噪音和异常点比较敏感.
  • 什么时候选择k-means
  1. 事先知道聚类数量
  2. 数据分布有明显的中心

噪声密度

噪声密度(Density-Based Spatial Clustering of Applications with Noise, 简写DBSCAN随机选择一个样本做圆心,以事先给定的半径做圆,凡被该圆圈中的样本都被划为与圆心样本同处一个聚类,再以这些被圈中的样本做圆心,以事先给定的半径继续做圆,不断加入新的样本,扩大聚类的规模,知道再无新的样本加入为止,即完成一个聚类的划分. 以同样的方法,在其余样本中继续划分新的聚类,直到样本空间被耗尽为止,即完成整个聚类划分过程. 示意图如下:

DBSCAN.png

DBSCAN算法中,样本点被分为三类:

  • 边界点(Border point):可以划分到某个聚类,但无法发展出新的样本
  • 噪声点(Noise):无法划分到某个聚类中的点
  • 核心点(Core point):除了孤立样本和外周样本以外的样本都是核心点

DBSCAN_3.png

上图中,A和B为核心点,C为边界点,D为噪声点. 此外,DBSCAN还有两个重要参数:

  • 邻域半径:设置邻域半径大小
  • 最少样本数目:邻域内最小样本数量,某个样本邻域内的样本超过该数,才认为是核心点

sklearn提供了DBSCAN模型来实现噪声密度聚类,原型如下:

# eps 半径
# min_samples 最小样本数
model = sc.DBSCAN(eps,min_samples)

示例代码:

# 噪声密度聚类示例
import numpy as np
import sklearn.cluster as sc
import matplotlib.pyplot as mp
import sklearn.metrics as sm

# 读取样本
x = []
with open("perf.txt", "r") as f:
    for line in f.readlines():
        line = line.replace("\n", "")
        data = [float(substr) for substr in line.split(",")]
        x.append(data)

x = np.array(x)

epsilon = 0.8  # 邻域半径
min_samples = 5  # 最小样本数

# 创建噪声密度聚类器
model = sc.DBSCAN(eps=epsilon,  # 半径
                  min_samples=min_samples)  # 最小样本数
model.fit(x)
score = sm.silhouette_score(x,
                            model.labels_,
                            sample_size=len(x),
                            metric='euclidean')  # 计算轮廓系数
pred_y = model.labels_
print(pred_y) # 打印所有样本类别
# print(model.core_sample_indices_) # 打印所有核心样本索引

# 区分样本
core_mask = np.zeros(len(x), dtype=bool)
core_mask[model.core_sample_indices_] = True  # 核心样本下标

offset_mask = (pred_y == -1)  # 孤立样本
periphery_mask = ~(core_mask | offset_mask)  # 核心样本、孤立样本之外的样本

# 可视化
mp.figure('DBSCAN Cluster', facecolor='lightgray')
mp.title('DBSCAN Cluster', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=14)
mp.grid(linestyle=':')
labels = set(pred_y)
print(labels)
cs = mp.get_cmap('brg', len(labels))(range(len(labels)))
print("cs:", cs)

# 核心点
mp.scatter(x[core_mask][:, 0],  # x坐标值数组
           x[core_mask][:, 1],  # y坐标值数组
           c=cs[pred_y[core_mask]],
           s=80, label='Core')
# 边界点
mp.scatter(x[periphery_mask][:, 0],
           x[periphery_mask][:, 1],
           edgecolor=cs[pred_y[periphery_mask]],
           facecolor='none', s=80, label='Periphery')
# 噪声点
mp.scatter(x[offset_mask][:, 0],
           x[offset_mask][:, 1],
           marker='D', c=cs[pred_y[offset_mask]],
           s=80, label='Offset')
mp.legend()
mp.show()

执行图像:

DBSCAN_2.png

实验代码

import sklearn.cluster as sc
import pandas as pd
import matplotlib.pyplot as plt
data = pd.read_csv('perf.txt',header=None)
# plt.scatter(x=data[0],y=data[1])
# data.plot.scatter(x=0,y=1)
# plt.show()
model = sc.DBSCAN(eps=0.55,min_samples=3)
model.fit(data)
label = model.labels_
print(label)
plt.scatter(x=data[0],y=data[1],c=label,cmap='brg')
plt.colorbar()
plt.show()

输出结果

QQ截图20230627154430.jpg

  • 算法优点
  1. 不用人为提前确定聚类类别数K
  2. 聚类速度快
  3. 能够有效处理噪声点(因为异常点不会被包含于任意一个簇,则认为是噪声)
  4. 能够应对任意形状的空间聚类
  • 算法缺点
  1. 当数据量过大时,要求较大的内存支持I/O消耗很大
  2. 当空间聚类的密度不均匀、聚类间距差别很大时、聚类效果有偏差
  3. 邻域半径和最少样本数量两个参数对聚类结果影响较大
  • 何时选择噪声密度
  1. 数据稠密、没有明显中心
  2. 噪声数据较多
  3. 未知聚簇的数量

凝聚层次聚类

凝聚层次(Agglomerative)算法,首先将每个样本看做独立的聚类,如果聚类数大于预期,则合并两个距离最近的样本作为一个新的聚类,如此反复迭代,不断扩大聚类规模的同时,减少聚类的总数,直到聚类数减少到预期值为止. 这里的关键问题是如何计算聚类之间的距离

依据对距离的不同定义,将Agglomerative Clustering的聚类方法分为三种:

  • ward:默认选项,挑选两个簇来合并,是的所有簇中的方差增加最小。这通常会得到大小差不多相等的簇。
  • average链接:将簇中所有点之间平均距离最小的两个簇合并。
  • complete链接:也称为最大链接,将簇中点之间最大距离最小的两个簇合并。

ward适用于大多数数据集。如果簇中的成员个数非常不同(比如其中一个比其他所有都大得多),那么average或complete可能效果更好。

sklearn提供了AgglomerativeClustering聚类器来实现凝聚层次聚类,示例代码如下:

# 凝聚层次聚类示例
import numpy as np
import sklearn.cluster as sc
import matplotlib.pyplot as mp

x = []
with open("multiple3.txt", "r") as f:
    for line in f.readlines():
        line = line.replace("\n", "")
        data = [float(substr) for substr in line.split(",")]
        x.append(data)

x = np.array(x)

# 凝聚聚类
model = sc.AgglomerativeClustering(n_clusters=4)  # n_cluster为聚类数量
model.fit(x)  # 训练
pred_y = model.labels_  # 聚类标签(聚类结果)

# 可视化
mp.figure("Agglomerative", facecolor="lightgray")
mp.title("Agglomerative")
mp.xlabel("x", fontsize=14)
mp.ylabel("y", fontsize=14)
mp.tick_params(labelsize=10)
mp.scatter(x[:, 0], x[:, 1], s=80, c=pred_y, cmap="brg")
mp.show()

输出结果

agglom.png

特点及使用

  1. 需要事先给定期望划分的聚类数(k),来自业务或指标优化
  2. 没有聚类中心,无法进行聚类预测,因为不依赖于中心的划分,所以对于中心特征不明显的样本,划分效果更佳稳定
  3. 适合于中心不明显的聚类

聚类的评价指标

理想的聚类可以用四个字概况:内密外疏,即同一聚类内部足够紧密,聚类之间足够疏远. 学科中使用“轮廓系数”来进行度量,见下图:

cluster_measure.jpg

假设我们已经通过一定算法,将待分类数据进行了聚类,对于簇中的每个样本,分别计算它们的轮廓系数。对于其中的一个点 i 来说:

  • a(i) = average(i向量到所有它属于的簇中其它点的距离)
  • b(i) = min (i向量到各个非本身所在簇的所有点的平均距离)

那么 i 向量轮廓系数就为:

$$S(i)=\frac{b(i)-a(i)}{max(b(i), a(i))}$$

由公式可以得出:

  1. 当\(b(i)>>a(i)\)时,\(S(i)\)越接近于1,这种情况聚类效果最好
  2. 当\(b(i)<<a(i)\)时,\(S(i)\)越接近于-1,这种情况聚类效果最差
  3. 当\(b(i)=a(i)\)时,\(S(i)\)的值为0,这种情况分类出现了重叠

sklearn提供的计算轮廓系数API:

score = sm.silhouette_score(x, # 样本
                            pred_y, # 标签
                            sample_size=len(x), # 样本数量
                            metric="euclidean")  # 欧式距离度量

聚类总结

  1. 聚类属于无监督学习;
  2. 聚类是根据数据的特征,将相似度最高的样本划分到一个聚簇中;
  3. 相似度的度量方式:曼哈顿距离、欧式距离、切比雪夫距离,都可以用闵式距离公式表示;
  4. 聚类算法
  • 基于原型聚类:k-means算法
  • 基于密度聚类:DBSCAN算法
  • 基于层次聚类:凝聚算法
  1. 评价指标:轮廓系数