机器学习入门

机器学习

机器学习是一种功能、方法,或者更具体的说是一种算法,它能够赋予机器进行学习的能力,从而使机器完成一些通过编程无法直接实现的功能。

机器学习是利用大量数据训练出一个最优模型,然后再利用此模型预测出其他数据的一种方法。

1、人工智能

人工智能(Artificial Intelligence)是计算机科学技术的一个分支,指的是通过机器和计算机来模拟人类智力活动的过程。

三者关系

2、学习形式分类

2.1、有监督学习

有监督学习(supervised learning),需要事先需要准备好要输入数据(训练样本)与真实的输出结果(参考答案),然后通过计算机的学习得到一个预测模型,再用已知的模型去预测未知的样本

2.2、无监督学习

无监督学习(unsupervised learning)就是在没有“参考答案”的前提下,计算机仅根据样本的特征或相关性,就能实现从样本数据中训练出相应的预测模型。

还有半监督学习和强化学习等

3、预测结果分类

3.1、回归&分类

连续和离散是统计学中的一种概念,全称为“连续变量”和“离散变量”

3.2、聚类

用一个成语表述“物以类聚,人以群分”,将相似的样本聚合在一起后,然后进行分析

4、机器学习术语

4.1、模型

可以把它看做一个“魔法盒”,你向它许愿(输入数据),它就会帮你实现愿望(输出预测结果)

4.2、数据集

表示一个承载数据的集合,数据集可划分为“训练集”和“测试集”,它们分别在机器学习的“训练阶段”和“预测输出阶段”起着重要的作用

4.3、样本&特征

样本指的是数据集中的数据,一条数据被称为“一个样本”,通常情况下,样本会包含多个特征值用来描述数据

4.4、向量

向量在线性代数中有着严格的定义,指具有大小和方向的量,数据集中的每一个样本都是一条具有向量形式的数据

4.5、矩阵

可以看成由向量组成的二维数组,数据集就是以二维矩阵的形式存储数据的

4.6、假设函数&损失函数

假设函数和损失函数是机器学习中的两个概念,它并非某个模块下的函数方法,而是我们根据实际应用场景确定的一种函数形式

4.6.1、假设函数

假设函数(Hypothesis Function)可表述为y=f(x)其中 x 表示输入数据,而 y 表示输出的预测结果,而这个结果需要不断的优化才会达到预期的结果,否则会与实际值偏差较大。

4.6.2、损失函数

损失函数(Loss Function)又叫目标函数,简写为 L(x),这里的 x 是假设函数得出的预测结果“y”,如果 L(x) 的返回值越大就表示预测结果与实际偏差越大,越小则证明预测值越来越“逼近”真实值,这才是机器学习最终的目的。因此损失函数就像一个度量尺,能度量“假设函数”预测结果的优劣,从而做出相应的优化策略。

4.6.3、优化方法

“优化方法”可以理解为假设函数和损失函数之间的沟通桥梁。通过 L(x) 可以得知假设函数输出的预测结果与实际值的偏差值,当该值较大时就需要对其做出相应的调整,这个调整的过程叫做“参数优化”,可以通过梯度下降、牛顿方与拟牛顿法、共轭梯度法进行参数调优

函数关系

4.7、拟合&过拟合&欠拟合

拟合是机器学习中的重要概念,机器学习的研究对象就是让模型能更好的拟合数据

4.7.1、拟合

“拟合”就是把平面坐标系中一系列散落的点,用一条光滑的曲线连接起来,因此拟合也被称为“曲线拟合”,拟合的曲线一般用函数进行表示,但是由于拟合曲线会存在许多种连接方式,因此就会出现多种拟合函数。通过研究、比较确定一条最佳的“曲线”也是机器学习中一个重要的任务

4.7.2、过拟合

过拟合(overfitting)与是机器学习模型训练过程中经常遇到的问题,所谓过拟合,通俗来讲就是模型的泛化能力较差,也就是过拟合的模型在训练样本中表现优越,但是在验证数据以及测试数据集中表现不佳

4.7.3、欠拟合

欠拟合(underfitting)恰好与过拟合相反,它指的是“曲线”不能很好的“拟合”数据。在训练和测试阶段,欠拟合模型表现均较差,无法输出理想的预测结果。造成欠拟合的主要原因是由于没有选择好合适的特征值

5、机器学习环境搭建

5.1、numpy

是 Python 科学计算的基础库,提供了多维数组处理、线性代数、傅里叶变换、随机数生成等非常有用的数学工具。

5.2、pandas

属于 Python 第三方数据处理库,它基于 NumPy 构建而来,主要用于数据的处理与分析。Pandas 提供了一个简单高效的 DataFrame 对象(类似于电子表格),它能够完成数据的清洗、预处理以及数据可视化工作等。除此之外,Pandas 能够非常轻松地实现对任何文件格式的读写操作,比如 CSV 文件、json 文件、excel 文件。

5.3、scikit-learn

是一个基于 Python 语言的机器学习算法库,建立在 NumPy、Scipy 与 Matplotlib 之上,它提供了大量机器学习算法接口(API)。

Scikit-Learn 的基本功能主要被分为六大部分:分类,回归,聚类,数据降维,模型选择和数据预处理。

6、线性回归算法

线性回归算法(Linear Regression),“线性”代表线性模型,而“回归”则表示回归问题。线性回归主要用来解决回归问题,也就是预测连续值的问题,是利用线性模型来“预测”真实值的过程,而能满足这样要求的数学模型被称为“回归模型”。最简单的线性回归模型是我们所熟知的一次函数(即 y=kx+b)

还有另外一种回归模型,也就是非线性模型(nonlinear model),它指因变量与自变量之间的关系不能表示为线性对应关系(即不是一条直线),比如我们所熟知的对数函数、指数函数、二次函数等。

6.1、线性回归方程

$$
\large
y = w_1x + b
$$

以上函数可作为线性模型的“假设函数”,其中 x 表示输入的样本数据,y 表示输出的预测结果,而 ==w1== 指的是线性回归模型的权值参数,b 指的是线性回归模型的“偏差值”。

权值,可理解为个不同“特征”对于预测结果的重要性。权值系数越大,那么这一项属性值对最终结果的影响就越大。

7、数学解析线性回归

7.1、假设函数

$$
\large
Y_1 = w^TX_1 + b \text{ 或 } \
f(x) = w^TX_1 + b
$$

f(x) 仍然代表预测结果, ==X1== 表示数据样本, b 表示用来调整预测结果的“偏差度量值”,而 ==w^T^== 表示权值系数的转置。矩阵相乘法是一个求两个向量点积的过程,也就是按位相乘,然后求和。

7.2、损失函数

损失函数就像一个衡量尺,这个函数的返回值越大就表示预测结果与真实值偏差越大。可以用预测值 Y1 减去真实值 Y,再用平方消除负数。
$$
\large
loss = \frac{\sum(w^TX_1 + b - Y)^2}{n}
$$

8、梯度下降求极值

梯度下降是机器学习中常用的一种优化方法,主要用来解决求极小值的问题,某个函数在某点的梯度指向该函数取得最大值的方向,那么它的反反向自然就是取得最小值的方向。在解决线性回归和 Logistic(逻辑) 回归问题时,梯度下降方法有着广泛的应用。

梯度是微积分学的术语,它本质上是一个向量,表示函数在某一点处的方向导数上沿着特定的方向取得最大值,即函数在该点处沿着该方向变化最快,变化率最大。梯度下降法的计算过程就是沿梯度方向求解极小值,当然你也可以沿梯度上升的方向求解极大值。

以线性回归的损失函数为例,梯度下降作为一种优化方法,其目的是要使得损失值最小。因此“梯度下降”就需要控制损失函数中的 wb 参数来找到最小值。比如控制 w 就会得到如下方法:
$$
\large
w_新 = w_旧 - 学习率 * 损失值
$$

  • 通过梯度下降计算极小值时,需要对损失函数的 w 求偏导求得,这个偏导也就是“梯度”,通过损失值来调节 w,不断缩小损失值直到最小
  • “学习率”是一个由外部输入的参数,被称为“超参数”,可以形象地把它理解为下山时走的“步长”大小,想要 w 多调整一点,就把学习率调高一点。不过学习率也不是越高越好,过高的学习率可能导致调整幅度过大,导致无法求得真正的最小值。当损失函数取得极小值时,此时的参数值被称为“最优参数”。因此,在机器学习中最重要的一点就是寻找“最优参数”
  • 梯度下降还包括:批量梯度下降(BGD)、随机梯度下降(SGD)、小批量梯度下降(MBGD),其中批量梯度下降是最常用的

9、sklearn

Scikit-learn 简称 sklearn 是基于 Python 语言实现的机器学习算法库,它包含了常用的机器学习算法,比如回归、分类、聚类、支持向量机、随机森林等等。同时,它使用 NumPy 库进行高效的科学计算,比如线性代数、矩阵等等。

常用算法库:

  • ·linear_model:线性模型算法族库,包含了线性回归算法,以及 Logistic 回归算法,它们都是基于线性模型
  • .naiv_bayes:朴素贝叶斯模型算法库
  • .tree:决策树模型算法库
  • .svm:支持向量机模型算法库
  • .neural_network:神经网络模型算法库
  • .neightbors:最近邻算法模型库

9.1、线性回归算法的简单实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from sklearn import linear_model
# 使用matplotlib绘制图像
import matplotlib.pyplot as plt
# 使用numpy准备数据集
import numpy as np


def line_model_learn(data):
# 准备自变量x,3到6的区间均分间隔30份数
x = np.linspace(3, 6, 40)
# 准备因变量y,这一个关于x的假设函数
y = 3 * x + 2

# 添加代码,扰乱点的分布
x = x + np.random.rand(40)

# 由于fit 需要传入二维矩阵数据,因此需要处理x,y的数据格式,将每个样本信息单独作为矩阵的一行
x = [[i] for i in x]
y = [[i] for i in y]

# 构建线性回归模型
model = linear_model.LinearRegression()
# 训练模型,"喂入"数据
model.fit(x, y)
y_ = model.predict(data)
print(y_)

# 查看w和b的值
print("w值为:", model.coef_)
print("b截距值为:", model.intercept_)

# 数据集绘制,散点图,图像满足函假设函数图像
plt.scatter(x, y)
# 绘制最佳拟合直线
plt.plot(data, y_, color="red", linewidth=3.0, linestyle="-")
plt.legend(["func", "Data"], loc=0)
plt.show()


if __name__ == '__main__':
# 准备测试数据 x_
x_ = [[4], [5], [6]]
line_model_learn(x_)

拟合直线绘制

9.2、线性回归总结

线性回归适用于有监督学习的回归问题,首先在构建线性模型前,需要准备好待输入的数据集,数据集按照需要可划分为训练集测试集,使用训练集中的向量 X 与向量 Y 进行模型的训练,其中向量 Y 表示对应 X 的结果数值(也就是“参考答案”);而输出时需要使用测试集,输入测试 X 向量输出预测结果向量 Y。

线性回归主要解决了以下三个问题:

  • 第一,为假设函数设定了参数 w,通过假设函数画出线性“拟合”直线。
  • 第二,将预测值带入损失函数,计算出一个损失值。
  • 第三,通过得到的损失值,利用梯度下降等优化方法,不断调整 w 参数,使得损失值取得最小值。我们把这个优化参数值的过程叫做“线性回归”的学习过程

10、Logistic回归算法

Logistic 回归算法,又叫做逻辑回归算法,或者 LR 算法(Logistic Regression),是针对“分类问题”的算法。分类问题同样也可以基于“线性模型”构建。“线性模型”最大的特点就是“直来直去”不会打弯,而我们知道,分类问题的预测结果是“离散的”,即对输出数据的类别做判断。比如将类别预设条件分为“0”类“1”类(或者“是”或者“否”)那么图像只会在 “0”和“1”之间上下起伏,如下图所示:

离散型数据

10.1、Logistic 函数

在机器学习中,Logistic 函数通常用来解决二元分类问题,也就是涉及两个预设类别的问题,而当类别数量超过两个时就需要使用 Softmax 函数来解决。

在神经网络算法中被称为 Sigmoid 函数,也有人称它为 Logistic 曲线

函数图像如下图所示:

Logistic 曲线函数

数学表达式如下:
$$
\large
Logistic(z)=\frac{1}{1 + e^{-z}}
$$
e 称为自然常数,也就是一个固定值的“常量”,e^-z^ 是以 e 为底、z 为变量的指数函数。

Logistic 函数也称为 S 型生长曲线,取值范围为 (0,1),它可以将一个实数映射到 (0,1) 的区间,非常适合做二元分类。当 z=0 时,该函数的取值为 0.5,随着 z 的增大,对应的函数值将逼近于 1;而随着 z 的减小,其函数值将逼近于 0。

对于 Logistic 函数而言,坐标轴 0 是一个有着特殊意义坐标,越靠近 0 和越远离 0 会出现两种截然不同的情况:任何大于 0.5 的数据都会被划分到 “1”类中;而小于 0.5 会被归如到 “0”类

特别是:

  1. 当 x 轴坐标取值缩小时会出现以下图像:

    x 取值缩小时 Logistic 函数

    由此可见 Logistic 回归算法属于“线性”模型

  2. 当 x 逐渐放大时则会出现以下图像:

    x 取值放大时 Logistic 函数

    当 x 增大到一定程度时,Logistic 函数图像变成了“台阶”式图像,由此可知,该函数能够很好的“拟合”二分类问题函数图像。在数学像 Logistic 函数这样的函数被称为”阶跃函数“

10.2、分类数据表示形式

10.2.1、向量形式

向量形式是应用最多的形式,向量中的元素按顺序代表“类别”,例如有以下三个类别,分别是 a/b/c 三类,此时就可以使用 [1,2,3] 来分别代表上述三类。预测结果为哪一类,向量中的元素就对应哪个元素,比如当预测结果为 c 类的时候,则输出以下数据:

1
[0,0,3]

10.2.2、数字形式

数字形式是一种最简单的分类方式,我们可以用 0 代表“负类”(即 x < 0时的取值),而用“1”代表正类(即 x>0 时的取值),那么当预测结果输出“1”就代表正类,而预测结果输出“0”代表“负类”。按照约定俗成,我们一般采用 “1”代表正类,而 “-1”或者“0”代表“负类”。用代码的表示数字形式的中心思想,如下所示:

1
2
3
4
5
#以 0 为节将其分开
if (logistic函数输出的是连续值>0):
return 1
else:
return 0

10.2.3、概率形式

在有些实际场景中,无法准确的判断某个“样本”属于哪个类别,此时就可以使用“概率”的形式来判断“样本”属于哪个类别的几率大,比如对某个“样本”有如下预测结果:

1
[0.8,0.1,0.1]

该样本属于 a 类的概率最大,因此可以认定该样本从属于 a 类。

10.3、Logistic函数数学解析

10.3.1、假设函数

$$
\large
H(x) = \frac{1}{1 + e^{-(w^Tx_i + b)}}
$$

上述公式和 Logistic 函数基本一致,只不过把它换成了关于 x 的表达式,并将幂指数 x 换成了 “线性函数”表达式。H(x) 的函数图像呈现 S 形分布,从而能够预测出离散的输出结果。

10.3.2、损失函数

$$
\large
L(x) = -ylogH(x) - (1-y)log(1 - H(x))
$$

由以下公式演化而来,解决该公式不能使用梯度下降等优化方法的问题:
$$
\large
L(x) = -H(x_i)^{y_i}(1 - H(x_i))^{1-y_i}
$$
当 y=1 时,如果预测正确,预测值则无限接近 1,也即 H(xi)yi 的值为 1,损失值则为 -1;如果预测错误,H(xi)yi 的值为 0,损失值也为 0。预测错误的损失值确实比预测正确的损失值大(0 > -1),满足要求。

10.3.3、优化方法

将 Logistic 函数的输出记作 z 可得如下公式:
$$
\large
z = w_0x_0 + w_1x_1 + \cdots + w_nx_n
$$
采用向量的形式可以写为:
$$
\large
z = w^Tx
$$
表示将这两个数值向量对应元素相乘然后全部加起来即得到 z 值。其中的 x 是分类器的输入数据,向量 w (最佳参数)会使得分类器尽可能的精确。

10.3.4、梯度上升优化方法

梯度上升基于的思想是:要找到某函数的最大值,最好的方法是沿着该函数的梯度方向寻找,如果把梯度记为 ,那么关于 f(x,y) 有以下表达式:
$$
\Large
▽f(x,y) = \left(
\begin{matrix}
\frac{\partial f(x,y)}{\partial x} \
\
\frac{\partial f(x,y)}{\partial y}
\end{matrix}
\right)
$$
该函数分别对 x 与 y 求的偏导数,其中关于 x 的偏导数表示沿着 x 的方向移动,而关于 y 的偏导数一个表示沿 y 的方向移。其中,函数f(x,y) 必须要在待计算的点上可导。在梯度上升的过程中,梯度总是指向函数值增长最快的方向,我们可以把每移动一次的“步长”记为 α 。用向量来表示的话,其公式如下:
$$
\large
w_1 = w + \partial▽_wf(w)
$$
在梯度上升的过程中,上述公式将一直被迭代执行,直至达到某个停止条件为止,比如达到某个指定的值或者某个被允许的误差范围之内。

10.4、sklearn应用Logistic回归算法

10.4.1、范数

范数又称为“正则项”,它是机器学习中会经常遇到的术语,它表示了一种运算方式,常见的范数主要分为两种:L1 和 L2。

  1. L1 范数

    表示向量中每个元素绝对值的和,根据定义,L1 范数的计算分两步,首先逐个求得元素的绝对值,然后相加求和即可。
    $$
    \large
    ||x||1 = \sum\limits{i = 1}^n|x_i|
    $$
    两个绝对值符号表示范数。

  2. L2 范数

    表示向量中每个元素的平方和的平方根,根据定义,L2 范数的计算分三步,首先逐个求得元素的平方,然后相加求和,最后求和的平方根。
    $$
    \large
    ||x||2 = \sqrt{\sum\limits{i = 1}^n{x_i^2}}
    $$

10.4.2、回归类算法

  1. Rodge 类

    Ridge 回归算法,又称“岭回归算法”主要用于预测回归问题,是在线性回归的基础上添加了 L2 正则项,使得权重 w 的分布更加均匀,其损失函数如下:
    $$
    \large
    L(x) = ||X_w - y||_2^2 + a||w||_2^2
    $$
    损失函数的左侧与线性回归算法的损失函数一致。只是在最后添加右侧的 L2 正则项,其中 a 只是一个常数,需要根据经验设置。

    线性回归函数的 1/n 在优化过程的运算中不会影响结果,它只是一个常量而已,而常量的导数是 0。

  2. Lasso 类

    Lasso 回归算法,使用 L1 正则项,可以预测回归问题,其损失函数的表达式如下(求最小损失值):
    $$
    \large
    L(x) = \frac{1}{2n}||X_w - y||_2^2 + a||w||_1
    $$
    上述表达式的左侧与 Ridge 回归算法的损失函数基本一致,只是将右侧的 L2 范数替换成了 L1 范数,而且左侧式子相比线性回归表达式而言,多了一个 1/2n,但实际的优化过程中,它并不会对权重 w 产生影响。

10.4.3、Logistic 回归的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# logistic算法
# 从 scikit-learn库导入线性模型中的logistic回归算法
from sklearn.linear_model import LogisticRegression
# 导入sklearn 中的自带数据集 鸢尾花数据集
from sklearn.datasets import load_iris
# skleran 提供的分割数据集的方法
from sklearn.model_selection import train_test_split

if __name__ == '__main__':
# 载入鸢尾花数据集
iris_dataset = load_iris()

# data 数组的每一行对应一朵花,列代表每朵花的四个测量数据,分别是:花瓣的长度,宽度,花萼的长度、宽度
print("data数组类型: {}".format(type(iris_dataset['data'])))
# 前五朵花的数据
print("前五朵花数据:\n{}".format(iris_dataset['data'][:5]))

# 分割数据集训练集,测试集
# 打乱数据集,并对其进行拆分。该函数默认将 75% 的行数据及对应标签作为训练集,另外 25% 数据作为测试集。
X_train, X_test, Y_train, Y_test = train_test_split(iris_dataset['data'], iris_dataset['target'], random_state=0)

# 训练模型
# 设置最大迭代次数为3000,默认为1000.不更改会出现警告提示
log_reg = LogisticRegression(max_iter=3000)

# 给模型喂入数据
clm = log_reg.fit(X_train, Y_train)

# 使用模型对测试集分类预测,并打印分类结果
print(clm.predict(X_test))
# 最后使用性能评估器,测试模型优良,用测试集对模型进行评分
print(clm.score(X_test, Y_test))

Logistic 算法适用于分类问题,该算法在处理二分类问题上表现优越,但在多分类(二个以上)问题上容易出现欠拟合。Logistic 算法除了适用于回归分类问题,还可以作为神经网络算法的激活函数(即 Sigmoid 函数)。

算法只有合适不合适,每个算法都有其适用的场景,没有优劣之分。

11、KNN 最邻近分类算法

K 最近邻分类算法,简称 KNN(K-Nearest-Neighbor),它是有监督学习分类算法的一种。所谓 K 近邻,就是 K 个最近的邻居。比如对一个样本数据进行分类,可以用与它最邻近的 K 个样本来表示它,这与俗语“近朱者赤,近墨者黑”是一个道理。

核心关键词:“少数服从多数”、“距离

11.1、KNN 算法原理

为了判断未知样本的类别,以所有已知类别的样本作为参照来计算未知样本与所有已知样本的距离,然后从中选取与未知样本距离最近的 K 个已知样本,并根据少数服从多数的投票法则(majority-voting),将未知样本与 K 个最邻近样本中所属类别占比较多的归为一类。

如果一个样本在特征空间中存在 K 个与其相邻的的样本,其中某一类别的样本数目较多,则待预测样本就属于这一类,并具有这个类别相关特性。该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别

KNN 算法简单易于理解,无须估计参数,与训练模型,适合于多分类问题、OCR光学模式识别、文本分类等领域。但它的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有很能导致当输入一个新样本时,该样本的 K 个邻居中大容量类的样本占多数,而此时只依照数量的多少去预测未知样本的类型,就会可能增加预测错误概率。此时,可以采用对样本取“权值”的方法来改进。

11.2、KNN 算法流程

  • 准备数据,对数据进行预处理 。
  • 计算测试样本点(也就是待分类点)到其他每个样本点的距离(选定度量距离的方法)。
  • 对每个距离进行排序,然后选择出距离最小的 K 个点。
  • 对 K 个点所属的类别进行比较,按照少数服从多数的原则(多数表决思想),将测试样本点归入到 K 个点中占比最高的一类中。

11.3、sklearn 实现 KNN 分类算法

Pyhthon Sklearn 机器学习库提供了 neighbors 模块,该模块下提供了 KNN 算法的常用方法,如下所示:

类方法 说明
KNeighborsClassifier KNN 算法解决分类问题
KNeighborsRegressor KNN 算法解决回归问题
RadiusNeighborsClassifier 基于半径来查找最近邻的分类算法
NearestNeighbors 基于无监督学习实现KNN算法
KDTree 无监督学习下基于 KDTree 来查找最近邻的分类算法
BallTree 无监督学习下基于 BallTree 来查找最近邻的分类算法

调用 KNeighborsClassifier 实现 KNN 分类算法。下面对 Sklearn 自带的“红酒数据集”进行 KNN 算法分类预测。最终实现向训练好的模型喂入数据,输出相应的红酒类别,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 加载红酒数据集
from sklearn.datasets import load_wine
# KNN分类算法
from sklearn.neighbors import KNeighborsClassifier
# 分割训练集与测试集
from sklearn.model_selection import train_test_split
# 导入numpy
import numpy as np


if __name__ == '__main__':
# 加载数据集
wine_dataset = load_wine()
# 查看数据集对应的键
print("红酒数据集的键:\n{}".format(wine_dataset.keys()))
print("数据集描述:\n{}".format(wine_dataset['data'].shape))

# data 为数据集数据;target 为样本标签
# 分割数据集,比例为 训练集:测试集 = 8:2
X_train, X_test, y_train, y_test = train_test_split(wine_dataset['data'], wine_dataset['target'],
test_size=0.2, random_state=0)

# 构建knn分类模型,并指定 k 值
KNN = KNeighborsClassifier(n_neighbors=10)

# 使用训练集训练模型
KNN.fit(X_train, y_train)

# 评估模型的得分
score = KNN.score(X_test, y_test)
print(score)
# 给出一组数据对酒进行分类
X_wine_test = np.array([[11.8, 4.39, 2.39, 29, 82, 2.86, 3.53, 0.21, 2.85, 2.8, .75, 3.78, 490]])
predict_result = KNN.predict(X_wine_test)
print(predict_result)
print("分类结果:{}".format(wine_dataset['target_names'][predict_result]))

12、朴素贝叶斯分类算法

朴素贝叶斯(Naive Bayesian algorithm)是有监督学习的一种分类算法,它基于“贝叶斯定理”实现,而贝叶斯定理是基于概率论和统计学的相关知识实现的。

12.1、贝叶斯公式

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

  • P(A) 这是概率中最基本的符号,表示 A 出现的概率。比如在投掷骰子时,P(2) 指的是骰子出现数字“2”的概率,这个概率是 六分之一,也称之为“先验概率”
  • P(B|A) 是条件概率的符号,表示事件 A 发生的条件下,事件 B 发生的概率,条件概率是“贝叶斯公式”的关键所在,它也被称为“似然度”
  • P(A|B) 是条件概率的符号,表示事件 B 发生的条件下,事件 A 发生的概率,这个计算结果也被称为“后验概率”

贝叶斯公式可以预测事件发生的概率,两个本来相互独立的事件,发生了某种“相关性”,此时就可以通过“贝叶斯公式”实现预测。

朴素贝叶斯是一种简单的贝叶斯算法,因为贝叶斯定理涉及到了概率学、统计学,其应用相对复杂,因此我们只能以简单的方式使用它,比如天真的认为,所有事物之间的特征都是相互独立的,彼此互不影响

12.2、算法原理

贝叶斯公式将多特征分类问题表达出来,如下所示:
$$
\large
P(y|x_1,\cdots,x_n) = \frac{P(y)P(x_1,\cdots,x_n|y)}{P(x_1,\cdots,x_n)}
$$
数据集有时并不是很完全的,总会因为某些原因存在一些缺失和收集不全的现象,所以特征 x 越多这个问题就会越突出,统计这些特征出现的概率就越困难。为了避免这一问题,朴素贝叶斯算法做了一个假设,即特征之间相互独立,互不影响,由此以来,就可以简化为以下式子来求解某个特征的似然度:
$$
\large
P(x_i|y,x_1,\cdots,x_{i-1},x_{i+1},\cdots,x_n) = P(x_i|y)
$$
“朴素贝叶斯算法”利用后验概率进行预测,其核心方法是通过似然度预测后验概率。在使用朴素贝叶斯算法解决分类问题,其实就是不断提高似然度的过程,可以理解为后验概率正比于似然度,如果提高了似然度,那么也会达到提高后验概率的目的,记做如下式子:
$$
\large
P(y|x_1,\cdots,x_n) \propto P(y)\prod\limits_{i = 1}^{n}P(x_i|y)
$$
上述式子中 表示正比于,而 则是连乘符号(即概率相乘)表示了不同特征同时发生的概率。

12.3、朴素贝叶斯优化方法

朴素贝叶斯算法更像是一种统计方法,通过比较不同特征与类之间的似然度关系,最后把似然度最大的类作为预测结果。

每个类与特征的似然度是不同的,也就是 ==P(xi|y)== 不同,因此某一类别中某个特征的概率越大,就更容易对该类别进行分类。根据求解后验概率的公式,可以得出以下优化方法:
$$
\large
y = P(y)\prod\limits_{i = 1}^nP(x_i|y)
$$
此时将后验概率记做类别 y,P(y) 是一个固定的概率值,因此要想让 y 取得最大值,只能通过 ==P(xi|y)== 实现,不妨把被统计的数据看成是一张大表格,朴素贝叶斯算法就是从中找到 ==P(xi|y)== 值最大的那一项,该项对应的 y 是什么,则最终输出的预测结果就是什么。

12.4、sklearn应用朴素贝叶斯算法

在 sklearn 库中,基于贝叶斯定理的算法集中在 sklearn.naive_bayes 包中,根据对“似然度 P(xi|y)”计算方法的不同,我们将朴素贝叶斯大致分为三种:多项式朴素贝叶斯(MultinomialNB)、伯努利分布朴素贝叶斯(BernoulliNB)、高斯分布朴素贝叶斯(GaussianNB)。另外一点要牢记,朴素贝叶斯算法的实现是基于假设而来,在朴素贝叶斯看来,特征之间是相互独立的,互不影响的。

高斯朴素贝叶斯适用于特征呈正态分布的,多项式贝叶斯适用于特征是多项式分布的,伯努利贝叶斯适用于二项分布。

  1. 算法使用流程

    • 统计样本数,即统计先验概率 P(y) 和 似然度 P(x|y)

    • 根据待测样本所包含的特征,对不同类分别进行后验概率计算。

    • 比较 y1,y2,…yn 的后验概率,哪个的概率值最大就将其作为预测输出

  2. 朴素贝叶斯算法的应用

    以鸢尾花数据集对朴素贝叶斯分类算法的应用为例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 鸢尾花数据集
    from sklearn.datasets import load_iris
    # 导入朴素贝叶斯模型,这里选用高斯分类器
    from sklearn.naive_bayes import GaussianNB

    if name == 'main':
    # 载入数据集
    X, y = load_iris(return_X_y=True)
    bayes_model = GaussianNB()
    # 训练数据
    bayes_model.fit(X, y)
    # 使用模型进行分类预测
    result = bayes_model.predict(X)
    print(result)
    # 对模型评分
    model_score = bayes_model.score(X, y)
    print(model_score)

13、决策树算法

决策树算法算是一类算法,这类算法逻辑模型以“树形结构”呈现。

13.1、if-else 原理

条件判断的常用语句,可以简单理解为“如果满足条件就….,否则…..”。

if-else 有两个特性:一是能够利用 if-else 进行条件判断,但需要首先给出判断条件;二是能无限嵌套,也就是在一个 if-else 的条件执行体中,能够再嵌套另外一个 if-else,从而实现无限循环嵌套。

13.2、决策树算法关键

决策树算法涉及了几个重要的知识点:“决策树的分类方法”,“分支节点划分问题”以及“纯度的概念”,还会涉及到“信息熵”、“信息增益”、“基尼指数”的概念。

特征维度&判别条件

分类问题的数据集由许多样本构成,而每个样本数据又会有多个特征维度,在决策算法中这些特征维度属于一个集合,称为“特征维度集”。数据样本的特征维度与最终样本的分类都可能存在着某种关联,因此决策树的判别条件将从特征维度集中产生。

13.3、纯度

是对单一类样本在子集内所占重的的度量。

在每一次判别结束后,如果集合中归属于同一类别的样本越多,那么就说明这个集合的纯度就越高。比如,二元分类问题的数据集都会被分成两个子集,通过自己的纯度就可以判断分类效果的好与坏,子集的纯度越高,就说明分类效果越好

决策树算法是一类算法,并非某一种算法,其中最著名的决策树算法有三种,分别是 ID3、C4.5 和 CART。在衡量“纯度”的方法上,它们分别采用了信息增益、增益率和基尼指数

13.3.1、纯度度量规则

度量“纯度”的规则(将类别分为“正类与负类”):

  • 某个分支节点下所有样本都属于同一个类别,纯度达到最高值。
  • 某个分支节点下样本所属的类别一半是正类一半是负类,此时,纯度取得最低值。
  • 纯度代表一个类在子集中的占比多少,它并不在乎该类究竟是正类还是负类。比如,某个分支下不管是正类占比 60% 还是负类占比 60%,其纯度的度量值都是一样的。

13.3.2、纯度度量方法

  1. 纯度函数

    横轴表示某个类的占比,纵轴表示纯度值。

    纯度函数图像

    当在 a 点时某一类的占比纯度最小,但是对于二元分类来说,一个类小,另一个类就会高,因此 a 点时的纯度也最高(与 b 恰好相反),当某类的纯度占比在 c 点时,对于二元分类来说,两个类占比相同,此时的纯度值最低,此时通过 c 点无法判断一个子集的所属类别。

  2. 纯度度量函数

    纯度度量函数

    纯度度量函数与纯度函数恰好相反,纯度度量函数图像适应于所有决策树算法,比如 ID3、C4.5、CART 等经典算法。

13.4、信息熵

信息熵是用来解决对信息的量化问题的

信息熵是用于衡量不确定性的指标,也就是离散随机事件出现的概率,简单地说“情况越混乱,信息熵就越大,反之则越小”。

信息熵的计算公式:
$$
\large
H(x) = -\sum\limits_{k=1}^{N}P_klog_2(P_k)
$$
其中 p 代表概率的意思,这里 “X” 表示进行信息熵计算的集合。在决策树分类算法中,可以按各个类别的占比(占比越高,该类别纯度越高)来理解,其中 N 表示类别数目,而 Pk 表示类别 K 在子集中的占比。信息熵的计算过程分为三次四则运算,即相乘、求和最后取反。

13.5、信息增益

决策树算法是以包含所有类别的集合为计算对象,并通过条件判别,从中筛选出纯度较高的类别

以 ID3 算法为例,ID3(Iterative Dichotomiser 3,迭代二叉树3代)算法是决策树算法的其中一种,它是基于奥卡姆剃刀原理实现的,这个原理的核心思想就是“大道至简,用尽量少的东西去做更多的事情”。

ID3 算法的核心思想:越小型的决策树越优于大的决策树,也就是使用尽可能少的判别条件。

ID3 算法选择信息增益最大的特征维度进行 if-else 判别。

  1. 理解信息增益

    信息增益是针对一个具体的特征而言的,某个特征的有无对于整个系统、集合的影响程度就可以用“信息增益”来描述。

    “纯度”最高的划分条件,也就是要找的“最合适”的特征维度判别条件。

  2. 信息增益公式

    比较划分前后集合的信息熵来判断,也就是做减法,用划分前集合的信息熵减去按特征维度属性划分后的信息熵,就可能够得到信息增益值。公式如下所示:
    $$
    G(S,t) = H(x) - \sum\limits_{k=1}^{K}\frac{|S^k|}{|S|}H(S^k)
    $$

G(S,t) 表示集合 S 选择特征属性 t 来划分子集时的信息增益。H(x) 表示集合的信息熵。

减数的含义:

  • 大写字母 K 表示:按特征维度 t 划分后,产生了几个子集的意思,比如划分后产生了 5 个子集吗,那么 K = 5。

  • 小写字母 k 表示:按特征维度 t 划分后,5 个子集中的某一个子集,k = 1 指的是从第一个子集开始求和计算。

    • |S| 与 |Sk| 表示:集合 S 中元素的个数,这里的||并不是绝对值符号,而 |Sk| 表示划分后,某个集合的元素个数。
    • |S|/|Sk| 表示:一个子集的元素个数在原集合的总元素个数中的占比,指该子集信息熵所占的权重,占比越大,权重就越高。

    最后,比较不同特征属性的信息增益,增益值越大,说明子集的纯度越高,分类的效果就越好,我们把效果最好的特征属性选为 if-else 的最佳判别条件。

13.6、决策树算法原理

决策树算法是一种树形分类结构,要通过这棵树实现样本分类,就要根据 if-else 原理设置判别条件。

决策树特征属性是 if-else 判别条件的关键所在,可以把这些特征属性看成一个集合,要选择的判别条件都来自于这个集合,通过分析与计算选择与待分类样本最合适的“判别条件”。被选择的“判别条件”使得样本集合的某个子树节点“纯度”最高。

上述过程就好比从众多的样本中提取“类别纯度”最高的样本集合,因此可以起一个形象化的名字“提纯”,过程示意图如下所示:

决策树流程图

决策树算法通过判别条件从根节点开始分裂为子节点,子节点可以继续分裂,每一次分裂都相当于一次对分类结果的“提纯”,周而复始,从而达到分类的目的,在这个过程中,节点为“否”的不在分裂,判断为“是”的节点则继续分裂。

决策树停止分类的条件:

  1. 子节点属于同一类别

    决策树算法的目的是为了完成有效的样本分类。当某个数据集集合分类完成,也就分类后的子节点集合都属于同一个类别,不可再分,此时代表着分类任务完成,分裂也就会终止。

  2. 特征属性用完

    决策树依赖特征属性作为判别条件,如果特征属性已经全部用上,自然也就无法继续进行节点分裂,此处可能就会出现两种情况:一种是分类任务完成,也就是子节点属于同一类别,还有另外一种情况就是分类还没有完成,比如,在判断为“是”的节点集合中,有 8 个正类 3 个负类,此时我们将采用占比最大的类作为当前节点的归属类。

  3. 设置停止条件

    可以在外部设置一些阈值,把决策树的深度,或者叶子节点的个数当做停止条件。

13.7、决策树剪枝策略

决策树分类算法最容易出现的问题就是“过拟合”。

“过拟合”使决策树模型学习到了并不具备普遍意义的分类决策条件,从而导致模型的分类效率、泛化能力降低。

“剪枝策略”是解决决策树算法过拟合问题的核心方法,也是决策树算法的重要组成部分。据剪枝操作触发时间的不同,可以将它们分成两种,一种称为预剪枝,另一种称为后剪枝

  1. 预剪枝

    分支划分之前就进行剪枝判断,如果判断结果是需要剪枝,则不进行该分支划分。

  2. 后剪枝

    分支划分之后,通常是决策树的各个判断分支已经形成后,才开始进行剪枝判断。

在实际情况中后剪枝策略使用较多。在分支生成后,使用后剪枝策略将冗余的子树及其判别条件直接剪掉,然后使用上个节点中占比最大的类做为最终的分类结果。

13.8、sklearn决策树算法分类

  1. .DecisionTreeClassifier()

    这是一个经典的决策树分类算法,它提供了许多有用的参数,比如 criterion,该参数有两个参数值,分别是 gini(基尼指数)entropy(信息增益),默认情况下使用“基尼指数”,其中“gini”用于创建 CART 分类决策树,而“entropy”用于创建 ID3 分类决策树。

    注意:在其余三个决策树算法中都可以使用 criterion 参数。

  2. .DecisionTreeRegressor()

    它表示用决策树算法解决回归问题。

  3. .ExtraTreeClassifier()

    该算法属于决策树分类算法,但又不同于.DecisionTreeClassifier()算法,因为.ExtraTreeClassifier()选择“特征维度”作为判别条件时具有随机性,它首先从特征集合中随机抽取 n 个特征维度来构建新的集合,然后再从新的集合中选取“判别条件”。

  4. .ExtraTreeRegressor()

    该算法同样具有随机性,它与.ExtraTreeClassifier()随机过程类似,它主要解决机器学习中的回归问题。

13.9、决策树实现步骤

决策树分类算法的关键在于选择合适的“判别条件”,该判别条件会使正确的分类的样本“纯度”最高。想要选取合适的特征属性就需要使用“信息熵”“信息增益”等计算公式。

  1. 确定纯度指标

    用来衡量不同“特征属性”所得到的纯度,并选取使得纯度取得最大值的“特征属性”作为“判别条件”

  2. 切分数据集

    通过特征属性做为“判别条件”对数据集集合进行切分。注意,使用过的“特征属性”不允许重复使用,该属性会从特征集合中删除

  3. 获取正确分类

    选择特征集合内的特征属性,直至没有属性可供选择,或者是数据集样本已经完成分类为止。切记要选择占比最大的类别做为分类结果

13.10、决策树算法的应用

下面使用决策树算法对 Sklearn 库中的红酒数据进行模型训练,与数据预测,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 加载红酒数据集
from sklearn.datasets import load_wine
# 导入决策树分类器
from sklearn.tree import DecisionTreeClassifier
# 导入分割数据集的方法
from sklearn.model_selection import train_test_split
# 导入科学计算包
import numpy as np


if __name__ == '__main__':
# 加载红酒数据集
wine_dataset = load_wine()

# 分割训练集与测试集
X_train, X_test, y_train, y_test = train_test_split(wine_dataset['data'], wine_dataset['target'], test_size=0.2,
random_state=0)

# 创建决策时分类器--ID3算法
tree_model = DecisionTreeClassifier(criterion="entropy")
# 喂入数据
tree_model.fit(X_train, y_train)
# 打印模型评分
print(tree_model.score(X_test, y_test))

# 给出一组数据预测分类
X_wine_test = np.array([[11.8, 4.39, 2.39, 29, 82, 2.86, 3.53, 0.21, 2.85, 2.8, .75, 3.78, 490]])
predict_result = tree_model.predict(X_wine_test)
print(predict_result)
print("分类结果:{}".format(wine_dataset['target_names'][predict_result]))

14、支持向量机 SVM 分类算法

支持向量机,英文全称“Support Vector Machines”(简称 SVM),是有监督学习中最有影响力的机器学习算法之一。

14.1、支持向量机组成

支持向量机是一个分类器算法,主要用于解决二分类的问题,有三个重要构件,分别是:

  • 最大间隔
  • 高维映射
  • 核函数

14.2、支持向量机本质

支持向量机本质上是从在线性分类算法的基础上发展而来的,就如同 Logistic 逻辑回归算法一样,只需给线性函数“套”上一层 Logistic “马甲”,就可以用线性模型来解决离散数据的分类问题。

  1. 间隔和支持向量

    支持向量机中有一个非常重要的概念就是“间隔最大化”,它是衡量 SVM 分类结果是否最优的标准之一。

    以“象棋”的例子来理解什么是“间隔”:

    中国象棋的棋子分为黑子和红子,并用“楚河汉界”将其分开。如果用一条直线将不同颜色的棋子进行分类,这显然信手拈来,只需要在楚河汉界的空白附带画一条“中轴线”就能以最佳的方式将它们分开,这样就能保证两边距离最近的棋子保有充分的“间隔”。

    上述示例中产生的“间隔”实际上是依据两侧不同颜色的棋子划分而成的,我们把这些棋子统称为“样本点”。虽然这些样本点都参与了分类,但对于分类效果影响最大的是处于间隔“边缘”的样本,只要将处于边缘的样本正确分类,那么这两个类别也就分开了,因此我们把处于边缘样本点称为“支持向量”

  2. 软件隔和硬间隔

    当使用直线分类时会本着尽可能将类别全都区分开来的原则,但总存在一些另类的“样本点”不能被正确的分类,如果允许这样的“样本点存在”,那么画出的间隔就成为“软间隔”,反之态度强硬必须要求“你是你,我是我”,这种间隔就被称为“硬间隔”,在处理实际业务中,硬间隔只是一种理想状态。

  3. 最大间隔

    上述所说的保有充分的“间隔”,其实就是“最大间隔”,如果将数据样本分割的不留余地,就会对随机扰动的噪点特别敏感,这样就很容易破坏掉之前的分类结果,学术称为“鲁棒性差”,因此我们在分类时要尽可能使正负两类分割距离达到最大间隔

14.3、SVM 高维映射

高维映射主要是用来解决“你中我,我中有你”的分类问题的,也就是“线性不可分问题”,所谓高维映射就是站在更高的维度来解决低维度的问题

超平面

如图所示经过高维映射后,二维分布的样本点就变成了三维分布,而那张恰好分开样本点的面(绿色的平面), SVM 统称其为“超平面”

通过增加一个维度的方法(给平面增加一个高度,使其变成三维空间),解决“线性不可分的问题”。

14.4、SVM 核函数

核函数是一类功能性函数,类似于 Logistic 函数。SVM 规定,只要能够完成高维映射功能的数学函数都称为“核函数”,它在支持向量机中承担着两项任务,一是增加空间的维度,二是完成现有数据从原空间到高维空间的映射

14.5、SVM 算法总结

SVM 算法是用来解决线性不可分的“非线性”问题, 从而突破线性分类的局限性,使得线性分类器依然可以适用于“非线性”问题。在这个过程中起到关键作用的就是“高维映射”。而“间隔最大化”可以看做支持向量机的损失函数,它衡量分类效果是否最佳的“标尺”,让间隔达到最大就是 SVM 追求的至臻境界,要实现这个目标就要不断地训练模型,使模型的泛化能力最佳。

最后对 SVM 算法进行分类的大致过程进行总结,大致分为以下三步:

  • 选取一个合适的数学函数作为核函数
  • 使用核函数进行高维映射,解决样本点线性不可分的问题;
  • 最后用间隔作为度量分类效果的损失函数,找到使间隔最大的超平面,最终完成分类的任务。

14.6、SVM 算法应用

Sklearn 库中的 SVM 算法如下所示:

SVM算法类别 描述
LinearSVC类 基于线性核函数的支持向量机分类算法
LinearSVR类 基于线性核函数的支持向量机回归算法
SVC类 可选择多种核函数的支持向量机分类算法,通过“kernel”参数可以传入
linear:选择线性函数;
polynomial:选择多项式函数;
rbf:选择径向基函数;
sigmoid:选择 Logistics 函数作为核函数;
precomputed:使用预设核值矩阵;
SVC 类默认以径向基函数作为核函数
SVR类 可选择多种核函数的支持向量机回归算法
NuSVC类 与 SVC 类非常相似,但可通过参数“nu”设置支持向量的数量
NuSVR类 与SVR类非常相似,但可通过参数“nu”设置支持向量的数量
OneClassSVM类 用支持向量机算法解决无监督学习的异常点检测问题

SVM 主要用于解决二分类的问题,上述表格中最常使用的是 SVC 类。下面对使用该算法的步骤进行总结:

  • 读取数据,将原始数据转化为 SVM 算法所能识别的数据格式;
  • 数据标准化,防止样本中不同特征数值大小相差较大影响分类器性能;
  • 选择核函数,在不清楚何种核函数最佳时,推荐使用“rbf”(径向基核函数)
  • 利用交叉验证网格搜索寻找最优参数;(交叉验证的目的是防止过拟合,利用网格搜索可以在指定的范围内寻找最优参数)
  • 使用最优参数来训练模型;
  • 测试得到的分类模型。

使用支持向量机(SVM)算法对鸢尾花数据集进行分类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from sklearn.datasets import load_iris  # 导入鸢尾花数据集
from sklearn.svm import SVC # 使用支持向量机算法
import matplotlib.pyplot as plt

if __name__ == '__main__':
# 加载鸢尾花数据集,返回特征值 X 以及标签 y
X, y = load_iris(return_X_y=True)
# 使用SVM.SVC分类算法搭建预测模型,并以径向基函数做为核函数的实现高维映射
clf = SVC(kernel='rbf')
# 训练模型,使用fit喂入数据X,y,即特征值和标签
clf.fit(X, y)
# 预测分类
result = clf.predict(X)
print(result)
# 对模型进行评分
score = clf.score(X, y)
print(score)
plt.figure()
# 分割图1行1列第一个图
plt.subplot(111)
# 选择X特征值中的第一列特征值和第三列特征值进行绘图
plt.scatter(X[:, 0], X[:, 3], c=y.reshape((-1)), edgecolor='k', s=50)
plt.show()

支持向量机算法在分类问题中有着非常出色的表现,它的特点是能够解决非线性问题,并且训练模型的时候不必依赖于全部数据,主要使用处于分类边缘的样本点,因此它也适用解决小样本群体的分类问题,并且泛化能力较强

当然,SVM 也有一些不足之处,比如核函数的寻找难度较大,并且最原始的 SVM 算法只适用于二分类问题。后经过不断的拓展、延伸,目前的 SVM 算法可以解决多分类问题,同时能够解决文本分类问题

15、K-means 聚类算法

常见的无监督学习算法,包括 K-means 聚类算法、均值漂移聚类算法、主成分分析法(即 PCA 算法)、EM算法(期望最大化算法)等。

无监督学习中最为经典的是 K-means 算法,它是聚类算法簇中的一个,也是最为经典的聚类算法,其原理简单、容易理解,因此得到广泛的应用。

15.1、聚类和分类的区别

聚类算法与分类算法的最终的目的都是将数据区分开来,但是两者的实现过程完全不同。

分类问题:通过对已有标签的数据进行训练来确定最佳预测模型,然后对新样本的所属类别进行预测,在这个过程中算法模型只要尽可能的实现最佳拟合就 OK 了。

聚类问题:对没有任何标签的数据,通过”找相似“来实现分类,既在实现分类时,只能尽可能找相同点,相同点越多,说明他们就属于同一类,而不同点越多,就说明两者不是同一类。

15.2、簇

在聚类问题中,有一个非常重要的概念“簇”(Cluster),那到底什么是簇呢,样本数据集通过聚类算法最终会聚集成一个个“类”,这些类在机器学习中的术语称为“簇”(注意,这里的前提是使用“聚类算法”),因此“簇”是解决聚类问题的表现形式,数据集中的数据样本最终会以“簇”的形式分开。

对于解决一个聚类问题,到底要汇集成多少簇,不同聚类算法采取了不同的思路,主要分为划分法、层次法、密度法和网格法,这些方法大致可总结为两类,一类是预先设定有多少个簇,另一类则是在聚类的过程中形成

15.3、K-means 聚类算法中 K 的含义

K-means 就是一种采用了划分法的聚类算法,由于该算法是没有参考标准的。如果不加以限定的话,它会形多任意数量的“簇”,这就要求在使用该算法时要预先设定“簇”的数量,就像田忌赛马一样,根据马的自身的特点,将其分为上、中、下三个档次,因此 K-means 中 K 是聚集成几个“簇”,形成几个“类”的意思

15.4、量化相似

注意,这里所说的“相似”有时也称之为“相似度”与之含义相反的是“相异度”,相异度越低,相似度就越高,这些词语主用于是衡量对象之间的相似程度。

  1. 随机选择质心
    解决 K-means 聚类问题,需要一个“中心点”,假设聚类问题的样本数据能够找出 K 个中心点,就能以该点为中心,以距离为度量画出范围来,将同一范围内的样本点作为一个簇,从而解决聚类问题,在 K-means 聚类算法中,这样的中心点称为“质心”。

    聚类算法是无监督学习,因此数据中的样本点完全不知道自己属于哪一个簇,就更别谈确定“质心”了,为了解决这一问题,K-means 算法通过随机选择方式来确定质心,但由于是随机选择,因此无法保证随机选择的 K 个质心就恰好是完成聚类后的 K 个簇的中心点,这时就用到了“mean”,它是“均值”的意思,通过均值可以不断的调整质心,由此可知质心在 K-means 算法中是不断改变的

  2. 求出新质心点
    假设现在随机了 K 个质心得到了 K 个簇,K-means 算法选择求平均,让这 K 个簇形成新的质心。每个簇都有若干数据点,求出这些数据点的坐标值均值,就得到了新质心的坐标点。这其实也是一种变相的多数表决。根据全体拥有表决权的数据点的坐标来共同决定新的质心在哪里,而表决权则由簇决定。
    在 K-means 聚类的过程中会经历多次质心计算,数据点到底归属于哪个簇可能会频繁变动,比如同一个数据点可能在本轮与一群样本点进行簇 A 的质心计算,而在下一轮就与另一群样本点进行簇 B 的质心计算。

15.5、算法总结

K-means 聚类算法的聚类过程,可以看成是不断寻找簇的质心的过程,这个过程从随机设定 K 个质心开始,直到找到 K 个真正质心为止

K-means 聚类算法的大致过程如下所示:

  • 第一步,既然现在有了 K 个质心,对于其他数据点来说,根据其距离哪个质心近就归为哪个簇的办法,可以聚成 K 个簇。但请注意,这只是第一步,并不是最后完成聚类的结果;
  • 第二步,对于聚成的 K 个簇,需要重新选取质心。这里运用了多数表决原则,根据一个簇内所有样本点各自的维度值来求均值,得到该簇的新的坐标值;
  • 第三步是生成新的质心,其实就是重复上述过程。对于根据均值计算得到的 K 个新质心,重复第一步中离哪个质心近就归为哪个簇的过程,再次将全部样本点聚成 K 个簇,经过不断重复,当质心不再变化后,就完成了聚类

最后总结:K-means 算法首先逐个计算数据集中的点到各自质心的距离,根据距离的远近,将数据样本点分别划归到距离最近的质心,从而形成 K 个类,然后继续选取新的质心,即对聚类内所有数据点求均值。最后重复上述两个过程:生成新质心后重新进行聚类,然后根据聚类结果再次生成新的质心,直至划分的“类”不再变化时结束。

15.6、K-means 聚类算法原理解析

15.6.1、度量最小距离

对于 K-means 聚类算法而言,找到质心是一项既核心又重要的任务,找到质心才可以划分出距离质心最近样本点。从数学角度来讲就是让簇内样本点到达各自质心的距离总和最小

通过数学定义,将“质心”具象化,既然要使“距离的总和最小”,那么第一步就是确定如何度量距离,K-means 算法通过『欧几里得距离』来衡量质心与样本点之间的距离。如下所示:
$$
\large
d(x,y) = \sqrt{\sum\limits_{i=1}^n(x_i - y_i)^2}
$$
如果第 j 个簇内有若干个数据点(比如 m 个),根据上述欧几里得距离公式就可以计算出簇中各个点到质心z的距离总和,如下所示:
$$
\large
\sum\limits_{i=0}^n(||x_i - z_j||^2)
$$

注意,上述公式中的zj是簇内所有样本点求均值的结果

K-measn 算法中会有 K 个簇,因此就要使每个簇内的数据点到质心的距离都可以达到最小,最终使得距离的总和最小。可以这样理解,K 个簇共同组成了一个集合(这里定义为 A 集合),在 A 集合中每个簇的样本点到各自质心的距离都是最小的,因此可得如下表达式:
$$
\large
\sum\limits_{i=0}^n\mathop{min}\limits_{z_j \epsilon {A}}(||x_i - z_j||^2)
$$

15.6.2、总结

K-means 算法的流程回顾,可分以下四步:

  • 随机选取 K 个对象,并以它们为质心;
  • 计算数据集样本点到质心的距离;
  • 根据样本点距离质心的距离将其分簇(类),距离哪个近,划分到哪个簇(类);
  • 以簇内所有样本点的均值重新计算质心,,然后重复第二步,直到划分的簇(类)不在变化后停止。
    K-means 算法是属于无监督学习算法,常用于解决聚类问题,通过给算法模型输入一个包含多种特征信息的样本点,会返回一个相应的类别编号(或称簇别),从而完成样本数据点的类别划分。

注意,判定聚类任务完成的终止条件并不是唯一的,常用方法有三个:

  • 簇内数据点向质心靠拢、收敛,使得质心点不再发生明显的变化;
  • 使用误差平方和(即 SSE)来衡量,当误差平和的值越小时,表示数据点越接近于他们的质心,聚类效果越好;
  • 设定指定的定迭代次数,即最多选取几次质心点,不过这种方法,未必能达到最好的分类效果。

15.7、Sklearn使用K-means算法

K-means 算法适合于解决而特征维度为数值型的聚类问题,也适用于文本聚类,比如新闻网站会将相同话题的新闻聚集在一起,并自动生成一个个不同话题的新闻专栏,其实这就是利用聚类算法实现的,但是文本的特征维度并非数值类型,因此需要对其进行数值转化操作,将文本数据转换为数学信息,此时可以使用 TF-IDF 加权技术计算单个词的权值。

TF-IDF 是一种用于信息检索与数据挖掘的常用加权技术。TF 是词频(Term Frequency),IDF 是逆文本频率指数(Inverse Document Frequency)。

下表对 K-means 聚类算法的特点做了简单说明:

项目 内容
优点 原理简单,实现容易,运算效率高。
不足 需要人为设置簇的个数与随机初始化质心点可能影响聚类的最终效果,同时 K-measn 算法对孤立点(离群点)特别敏感,会对最终的聚类结果产生明显扰动。
应用领域 适用于特征维度为数据类型的聚类问题,比如体育赛事等,而对特征维度不是数据类型的需要提前进行转换,比如文本分类等。

在 Sklearn 机器学习库中,与聚类相关的算法模型都在 cluster 模块下,除 k-measn 外,还有十种聚类最近邻算法,下表对最常用的算法做了简单介绍:

类名 说明
KMeans 类 本节介绍的算法,也是应用最多的聚类算法
MiniBatchKMeans 类 该算法是 K-measn算法变形算法,使用 mini-batch(一种采样数据的思想) 来减少一次聚类所需的计算时间,mini-batch 也是深度学习常使用的方法。
DBSCAN 类 DBNSCAN 算法是一种比较有代表性的基于密度的聚类算法,它的主要思想是将聚类的类视为被低密度区域分割的高密度区域。与划分聚类方法不同,它将簇定义为密度相连的点的最大集合,能够把具有足够高密度的区域划分为簇。
MeanShift 类 MeanShift 算法流程,以任意点作为质心的起点,根据距离均值将质心不断往高密度的地方移动,也即所谓均值漂移,当不满足漂移条件后说明密度已经达到最高,就可以划分成簇。
AffinityPropagation 类 AffinityPropagation 算法(简称 AP 算法),该算法是层次聚类的典型应用,聚类实现过程是一个“不断合并同类项”的过程,用类似于归纳法思想来完成聚类。
通过表格不难看出,每一种算法所采用的思想均不相同,但最终都能解决聚类问题,这也是整个聚类算法族的特点之一。

下面我们对Kmeans.Kmeans()的常用参数做简单介绍:

参数 说明
algorithm 字符串参数值,有三种选择: 1) “auto” :默认值,自动根据数据值是否稀疏,来决定使用 “full”还是”elkan”,采用默认值即可; 2) “full”:表示使用传统的 K-measn 算法; 3) “elkan”:表示使用 elkan-Means 算法,该算法可以减少不必要的距离计算,加快计算效率。
n_cluster 整型参数,表示分类簇的数量,默认值为 8
max_iter 整型参数,表示最大的迭代次数,默认值为 300
n_init 整型参数,表示用不同的质心初始化值运行算法的次数,默认值为 10
init 字符串参数,有三个可选参数: 1)” k-means++” ,默认值,用一种特殊的方法选定初始质心从而能加速迭代过程的收敛,效果最好; 2) “random” 表示从数据中随机选择 K 个样本作为初始质心点; 3) 提供一个 ndarray 数组,形如 (n_cluster,n_features),以该数组作为初始质心点。
precompute_distance 有三个可选值,分别是 “auto”, True, False: 1) “auto” :如果样本数乘以聚类数大于 12 million 的话则不予计算距离; 2) True:总是预先计算距离; 3) False:永远不预先计算距离。
tol 浮点型参数(float),表示算法收敛的阈值,默认值为 1e-4
n_jobs 整型参数,指定计算所用的进程数量, 1) 若值为 -1,则用所有 CPU 进行运算; 2) 若值为 1 ,则不进行并行运算,方便调试; 3) 若值小于 -1,则用到的 CPU 数为(n_cpus+1+n_jobs),因此若为 -2 ,则用到的 CPU 数为总 CPU 数减去1
random_state 表示随机数生成器的种子,参数值为整形或 numpy.RandomState 类型
verbose 整型参数,默认值为 0,表示不输出日志信息;1 表示每隔一段时间打印一次日志信息;如果大于 1时,打印次数变得频繁。

最后通过鸢尾花数据集对 K-means 算法进行简单的演示,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import matplotlib.pyplot as plt
import matplotlib
from sklearn.cluster import KMeans
from sklearn.datasets import load_iris


if __name__ == '__main__':
# 设置 matplotlib rc配置文件
matplotlib.rcParams['font.sans-serif'] = [u'SimHei'] # 用来设置字体样式以正常显示中文标签
matplotlib.rcParams['axes.unicode_minus'] = False # 设置为 Fasle 来解决负号的乱码问题

# 加载鸢尾花数据集
# 数据的特征分别是 sepal length(花萼长度)、sepal width(花萼宽度)、petal length(花瓣长度)、petal width(花瓣宽度)
iris = load_iris()
X = iris.data[:, :2] # 通过花萼的两个特征(长度和宽度)来聚类
k = 3 # 假设聚类为 3 类,默认分为 8 个 簇
# 构建算法模型
km = KMeans(n_clusters=k) # n_clusters参数表示分成几个簇(此处k=3)
km.fit(X)

# 获取聚类后样本所属簇的对应编号(label_pred)
label_pred = km.labels_ # labels_属性表示每个点的分簇号,会得到一个关于簇编号的数组
centroids = km.cluster_centers_ # cluster_center 属性用来获取簇的质心点,得到一个关于质心的二维数组,形如[[x1,y1],[x2,y2],[x3,x3]]

# 未聚类前的数据分布图
plt.subplot(121)
plt.scatter(X[:, 0], X[:, 1], s=50)
plt.xlabel('花萼长度')
plt.ylabel('花萼宽度')
plt.title("未聚类之前")
# wspace 两个子图之间保留的空间宽度
plt.subplots_adjust(wspace=0.5) # subplots_adjust()用于调整边距和子图间距
# 聚类后的分布图
plt.subplot(122)
# c:表示颜色和色彩序列,此处与 cmap 颜色映射一起使用(cool是颜色映射值)s表示散点的的大小,marker表示标记样式(散点样式)
plt.scatter(X[:, 0], X[:, 1], c=label_pred, s=50, cmap='cool')
# 绘制质心点
plt.scatter(centroids[:, 0], centroids[:, 1], c='red', marker='o', s=100)
plt.xlabel('花萼长度')
plt.ylabel('花萼宽度')
plt.title("K-Means算法聚类结果")
plt.show()

16、神经网络分类算法

深度学习(Deep Learning)这一概念是由 Geoffrey Hinton(深度学习之父)于 2006 年提出,但它的起源时间要早得多,可追溯至 20 世纪四五十年代,也就是人类刚刚发明出电子计算机时就已经提出来了,但当时并非叫做深度学习,而是人工神经网络(artificial neural network, ANN),简称神经网络(NN),它是一种算法模型,其算法的构思灵感来源于生物神经网络。

16.1、MP神经元模型

人工神经网络是一种有监督学习算法,它试图通过模拟人脑神经系统对复杂信息的处理机制来构建一种数学模型。我们知道,神经元是构成生物神经系统的基本单元,而人工神经网络也不例外,它也是从神经元模型的基础上发展而来的。

1943 年,美国心理学家麦克洛奇(Mcculloch)和数学家皮兹(Pitts)提出了 M-P 神经元模型(取自两个提出者姓名的首字母),这是最早、也是最简单的神经网络算法的模型。

16.1.1、生物神经元

神经元是大脑神经系统重要组成单位,主要由细胞体、树突、轴突、突触组成。神经元是一种多输入单输出的信息处理单元,输入的电信号有两种,分别是兴奋性信号和抑制性信号。

  • 树突,可以看作输入端,接受从从其他细胞传递过来的电信号;
  • 轴突可以看作输出端,传递电信号给其他细胞;
  • 突触,则可以看成 I/O 接口,用于连接不同神经元,单个神经元可以和上千个神经元进行连接;

细胞体内存在膜电位,外界传递过来电流时会使膜电位发生变化,当电位升高到一个阈值时,神经元就会被激活,产生一个脉冲信号,传递到下一个神经元。

生物神经元组成

16.1.2、M-P神经元

M-P 模型就是基于生物神经构建的一种数学模型,只过不它将生物神经元信息传导过程进行了抽象化,并以网络拓扑相关知识来表示。

M-P 模型是神经网络的基本组成单位,在神经网络中也称为『节点(node)』或者『单元(unit)』。节点从其他节点接受输入,或从外部源接受输入(即 x1、x2、1),每个输入都带有一个权重值(weight,即 w),权重大小取决于输入值的相对重要性。函数 f 位于节点处,它是一个关于 ω、x 的线性函数,记做 f(x,ω) ,输入 b 表示函数的偏置项,最后经过 f(w,x) 的计算得输出 Y。模型如下所示:

神经元模型示例图

上述模型对于神经网络说来说具有重要的意义,它是神经网络研究的开端,所示模型由 3 部分组成,从左往右依次为:神经元的输入、输入信号处理单元,以及神经元的输出。

M-P 模型采用数学模型模拟了生物神经元所包含的细胞体、树突、轴突和突出等生理特征。通过 M-P 模型提出了神经元的形式化数学描述和网络结构方法,从而证明了单个神经元能执行逻辑功能,但由于模型中的权重和偏置是人为设置的,因此该模型并不具备学习的能力。

16.1.3、M-P模型解析

神经元是一种多端输入单端输出的信息处理单元,因此 M-P 神经元模型也遵循这个原理。神经元的输入端通常会被给予不同的权重,来权衡不同输入信号的重要程度,如上图所示是一个有 3 个输入,一个输出的神经元模型,该神经元模型接收 3 个输出信号,然后给予输入信号不同的权重,神经元的输入信号经过处理后得到神经元输出。注意,这里所说的信号可以理解为数据集中的数据样本。

16.1.4、信息处理单元

介于输入和输出之间的圆圈称为输入信息处理单元(即节点),之所以画成圆圈也是一种约定俗成的表示方式,而这个信息处理单元可以看成一个函数,当给这个模型“喂入”一个数据时,就会产生一个对应的输出。早期的 MP 神经元模型可以看成一种线性分类器,通过检验 f(x,ω) 的正负来识别两种不同类别的时输入。由此可知,该模型需要正确设置权重参数,才能使模型的输出对应所期望的类别。

16.2、感知器模型

20 世纪 50年代(1957年),美国学者罗森勃拉特提出了感知器(或称感知机)模型,从某种意义上来说,感知器模型是第一个具有学习能力的神经网络,该模型能根据每个类别的输入样本来学习权重。

感知器模型,也可称为单层感知器,它是最简单的神经网络,它包含输入层和输出层,并且层与层之间直接相连。该模型从神经元模型的基础上发展而来,单层感知器能模拟逻辑与、逻辑或、逻辑非和逻辑与非等操作,单层感知器模型如下:

感知器模型

虽然具备了学习的能力,但该模型只能解决简单的线性分类和线性回归问题,对于线性不可分问题(即异或问题,xor)仍无法解决(1969年,科学家明斯基和佩珀特证明)。如下图所示,无法找到一条直线可以把圆形和菱形分开:

线性不可分问题

感知器模型算法与神经元模型类似,是一个单层神经元结构,它首先对输入的数据进行加权求和,然后将得到的结果与阈值进行比较,假如与所期望的输出有较大误差,就对权值参数进行调整,反复多次,直到误差满足要求时为止。由上图可知单层感知器的输出为:
$$
\large
y(x_1,x_2) = f(\omega_1 * x_1 + \omega_2 * x_2 - \theta)
$$
下面举个简单例子,看看单层感知器如何完成逻辑与运算(即 And,x1 ∧ x2):

令 w1 = w2 =1,θ = 1.5,则 y =f(1x1+1x2-1.5),显然,当 x1 和 x2 均为 1 时,y 的值为 1;而当 x1 和 x2 中有一个为 0 时,y 的值就为 0(通过 y 值的正负来取值,正值取值 1,负值取值 0,从而实现线性分类),当然逻辑或运算、与逻辑非运算也可通过此方法验证。

异或是一个数学运算符号,使用 ⊕ 来表示,计算机一般用 ^ 来表示。异或也叫半加运算,其运算法则相当于不带进位的二进制加法,用 1 表示真,用 0 表示假,运算法则为“同为 0,异为 1”。:

1
2
3
4
0 ⊕ 0=0
1 ⊕ 0=1
0 ⊕ 1=1
1 ⊕ 1=0
1
2
3
4
0 + 0 - θ < 0 --> θ > 0
ω1 + 0 - θ ≥ 0 --> 0 ≥ θ - ω1
0 + ω2 - θ ≥ 0 --> 0 ≥ θ - ω2
ω1 + ω2 - θ < 0 --> θ > ω1 + ω2

16.2.1、激活函数

上述感知器模型依然模拟了神经元结构,有输入(input)、权重(weight)、前馈运算(feed forward)、激活函数(activation function)、输出(output)等部分组成。注意,这里的前馈运算指的是图感知器模型中的『加权求和』,即在没有使用激活函数时输入值的加权求和结果,有时也记做『logit』。

通过上述模型很容易实现二分类。只需将对加权求和的结果值进行判断即可,比如 x>0 为 1 类,若 x <=0 则为 0 类,这样就将输出结果值映射到了不同类别中,从而完成了二分类任务。激活函数公式如下:
$$
\large
f(x) =
\begin{cases}
1, & \text{x > 0} \
0, & \text{x<=0}
\end{cases}
$$
若想采用感知器模型解决线性回归问题就可以使用 sigmoid 函数,激活函数公式如下:
$$
\large
sigmoid(x) = \frac{1}{1+e^{-x}}
$$

16.2.2、多层感知器模型

由于单层感知器模型无法解决非线性可分问题,即 xor 问题(1969年,马文·明斯基证明得出),这也导致了神经网络热潮的第一次大衰退。直至 20 世纪 80 年代,多层感知器模型(Multi -Layer Perceptrons,缩写为 MLP)的提出(1981年,韦伯斯提出),神经网络算法再次回归大众视野。

与单层感知器模型相比,该模型在输入层与输出层之间增加了隐藏层(Hidden),同时输出端,由原来一个增至两个以上(至少两个),从而增强了神经网络的表达能力。注意,对于只有一层隐藏层的神经网路,称为单隐层神经网络或者二层感知器,网络拓扑图如下所示:

多层感知器模型

多层感知器模型是由多个感知器构造而成的,模型中每一个隐藏层节点(或称单元)都可以看做成一个感知器模型,当我们将这些感知器模型组合在一起时就可以得到“多层感知器模型”。输入层、隐藏层与输出层相互连接形成了神经网络,其中隐藏网络层、输出层都是拥有激活函数的功能神经元(或称节点)

在神经网络中的隐藏层可以有多层,当隐藏层有多层,且形成一定“深度”时,神经网络便称为深度学习(deep learning),这就是“深度学习”名字的由来。因此,深度学习就是包含了多个隐藏层的多层感知器模型。如下图所示,是具有两个隐藏层的神经网络:

多层感知器模型(两个隐藏层)

『深度学习』这一概念直到 2006 年才被提出,在这之前多层感知器模型被称为“人工神经网络”。从神经元模型到单层感知器模型再到多层感知器模型,这就是人工神经网络的发展过程。在神经网络中每层的节点与下一层节点相互连接,节点之间不存在同层连接,也不存跨层连接,这样的网络结构也被称为“多层前馈神经网络”(multi-layer feedforward neural),如果层与层之间的节点全部相互连接,则称为“全连接神经网络”,如下所示:

全连接神经网络

多层感知器的诞生,解决了单层感知器模型无法解决的异或问题。下面简单分析一下解决过程。如图所示是包含了一个隐藏层的多层感知器模型:

多层感知器解决异或问题

在多层感知器模型中,隐藏层中的每一个节点都是想当于一个感知器模型。下面将输入值(x1 和 x2)带入隐藏层节点,可得以下函数式(这里的函数指的是激活函数):

1
2
左隐藏层节点:f1(x1+x2-0.5)
右隐藏层节点:f2(-x1-x2+1.5)

由此可知输出层的函数式如下:

1
f3(f1+f2-1.5)

根据异或法则“同为 0,异为 1”,分别将 (0,1),(1,0),(0,0),(1,1) 带入上述三个函数分别进行计算,可得以下结果(正数为 1,负数为 0):

1
2
3
4
(0,1):f1(0+1-0.5)=1 f2(0-1+1.5)=1 --> f3(1+1-1.5)=1 
(1,0):f1(1+0-0.5)=1 f2(-1-0+1.5)=1 --> f3(1+1-1.5)=1
(0,0):f1(0+0-0.5)=0 f2(0-0+1.5)=1 --> f3(0+1-1.5)=0
(1,1):f1(1+1-0.5)=1 f2(-1-1+1.5)=0 --> f3(1+0-1.5)=0

可以看出输出层 f3 函数的结果完全符合异或运算法则,因此多层感知器可以解决“异或问题”。从函数图像上来看,多层感知器使用两条直线解决了线性不可分问题:

分类区域

上图所示,位于红色直线之间的属于正类,而位于区域之外则属于负类。当然图像中只是包含了四个点而已,若是复杂的数据则可以选择不同的激活函数,比如 sigmoid 函数等。

16.3、神经网络工作流程

人工神经网络模型

神经网络通过赋予输入信息不同的权重值来区别不同信息的重要程度。在模型训练过程中通过调节线性函数的相应权值增加有价值信息的输入权值降低其他价值信息较低的输入权值,这是【调优权值】的核心思想,通过上述方法能够提高网络模型预测的预测准确率。

16.4、反向传播算法

多层感知器的虽然解决了线性不可分问题,但随着隐藏层网络的加深,多层网络的训练和参数计算也越来越困难,因此多层感知器也显得“食之无味”。简单来说,就是当时的人们还不知道应该怎么训练多层神经网络,甚至不相信多层神经网络也是同样能被训练的。

直到 1986 年,深度学习教父 Hinton 等人对反向传播算法(Backpropagation algorithm,即误差逆向传播算法,简称 BP算法)进行了重新描述,证明了该算法可以解决网络层数过深导致的参数计算困难和误差传递等问题。

反向传播算法是一种用于训练神经网络的有监督学习算法基于梯度下降(gradient descent)策略,以目标的负梯度方向对参数进行调整。但受限于当时(20世纪80年代)计算机算力不足等因素的影响,BP 算法只能以简单低效的方式来解决少数层神经网络训练问题,但即使如此,也已经弥足珍贵。

BP 算法的出现再次引发了 AI 研究的热潮,它是一款非常成功的神经网络算法,直到今天,该算法仍在深度学习领域发挥着重要的作用(用于训练多层神经网络)。

16.4.1、正向传播

人工神经网络是由一个个的神经元节点构成的,这些节点的作用就是负责接受和传导信息,如同大脑神经元一样,接受外接刺激,传递兴奋信号。

在一个人工神经网络模型中,从输入层开始,传递到输出层,最后返回结果,这种信号传播方式被称为“正向传播”(或称前向运算、前向传播)。在神经网络模型中,若输入一层层的传递下去的,直到输出层产生输出,正向传播就结束了。

反向传播的与前向传播类似,但由于传播方向相反,因此被称为反向传播算法(简称 BP 算法),该算法最早出现在 20 世纪 60 年代,但当时并没有引起重视,直到 1986 年经 Hinton 等人进行了重新描述,才再次进入大众的视野。该算法成功解决了少数层神经网络【权值参数】计算的问题。

前向运算和反向传播示意图

16.4.2、反向传播原理

反向传播算法(BP)是一种有监督学习算法,即通过有标记的训练数据来学习,它是训练人工神经网络模型的常用方法之一。简单的来说,BP 算法就是从错误中学习,直至将错误程度降到最低时结束,从而提高模型的可靠性

BP 算法的学习过程由正向传播过程和反向传播过程两部分组成。在正向传播过程中,输入信息通过输入层经隐含层,逐层处理并传向输出层,如果输出值与标记值存在误差,则将误差由输出层经隐藏层向输入层传播(即反向传播),并在这个过程中利用梯度下降算法对神经元的各个权值参数进行调优,当误差达到最小时,网络模型训练结束,也即反向传播结束。流程图如下所示:

神经网络模型训练

对上述过程进行总结:输入层接受一个输入数据 x,同时初始化一个权重参数 ω,通过隐藏层计算之后,由输出层输出结果,前向运算完成。之后,将输出层结果与标记值进行比较,获取偏差值,将此偏差值由输出层向输入层传播(反向传播阶段),这个阶段利用梯度下降算法对权值参数进行反复调优当偏差值最小时,获得一组最优的权值参数(ω)

16.4.3、总结:

神经网络分类算法是一种有监督学习算法,使用神经网络分类算法,大致需要以下五步:

  • 初始化神经网络中所有神经元节点的权值;
  • 输入层接收输入,通过正向传播产生输出;
  • 根据输出的预测值,结合实际值计算偏差;
  • 输出层接收偏差,通过反向传播机制(逆向反推)让所有神经元更新权值;
  • 从第 2 步到第 4 步是一次完整的训练模型的过程,重复该过程,直到偏差值最小。

神经网络算法通过反向传播机制让所有神经元实现了权值更新,当我们不断迭代上述训练过程,直到偏差值最小,最终就会得到一个最优的网络模型,实现了对数据的最佳拟合。

16.5、神经网络分类算法的应用及其实现

16.5.1、神经网络算法的特点

深度学习的本质就是神经网络算法(深度学习是神经网络算法的一个分支)。理论上来说,在数据量和隐藏层足够多的情况下,神经网络算法能够拟合任何方程(函数)。神经网络算法是一种具有网络结构的算法模型,这决定了它具有非常好的延展性,通过调节神经网络中各个节点的权值参数使得分类效果明显提升。总的来说,神经网络算法具有以下特点:

  1. 黑盒算法

    神经网络算法,也被称为“黑盒算法”,这是因为人们无法从外部得知神经网络模型究竟是如何完成训练的,比如使用一个预测准确率为 97% 的猫脸识别模型,有时会将小狗的脸部照片归纳到小猫中,而这种情况是无法解释的,因此神经网络算法又被人们形象地称之为“黑盒算法”。

    黑盒算法

    由于神经网络算法的这一特性,导致一些场景并不适合使用神经网络算法,比如银行不会使用神经网络算法来评判用户的是否具备信用,因为一旦出现预测错误,银行根本无法溯源找到评判错误的原因,也就无法向客户做出合理的解释。

  2. 数据量

    在互联网并不发达的七八十年代,数据量不足是阻碍神经网络发展的一大因素。与传统的机器学习算法相比,要想训练一个优秀的神经网络模型,往往需要更多的数据(至少需要数千甚至数百万个标记样本)。

    比如人脸识别,需要各种姿态样式的人脸,发怒的、喜悦的、悲伤的、戴眼镜的、模糊的等等,总之越多越好。海量数据集对于训练一个优秀的神经网络模型非常重要,神经网络获得数据越多,表现能力就越好,这样训练出来的模型才具有更好的泛化能力。

  3. 算力和开发成本高

    在计算方面,比传统算法下相比,神经网络算法要耗费更多的计算机资源,对于复杂的深度学习模型来说,若想训练出一个优秀的模型,甚至需要几周的时间。但以 20 世纪七八十年代的计算机硬件水平,想要实现如此大规模的计算,几乎是不可能的。因此计算机的硬件性能也是影响神经网络发展的因素之一。

    进入 21 世纪以后,计算机的硬件性能获得了飞速发展,这为神经网络的发展创造了有利的外部环境。

    同时神经网络模型搭建过程较为复杂,激活函数的选择,权值的调节,都是一个比较费时的过程,因此其开发周期相对较长。总之,神经网络算法是一种成本较高的算法,这也决定了它能够解决比传统机器学习算法更为复杂的问题。下表对神经网络的特点做了简单的总结:

    项目 说明
    优点 网络结构延展性好,能够拟合复杂的数据分布,比如非线性函数,通过调节权值参数来获取泛化能力较强的模型。
    缺点 可解释性差,调参依赖于经验,可能会陷入局部最优解,或者梯度消失、梯度爆炸等问题。
    应用领域 神经网络算法拟合能力强,应用领域广,比如文本分类等,而深度学习作为神经网络的分支,也是当前最为热门研究方向,在图像处理、语言识别和自然语言处理等多个领域都有着非常突出的表现。

16.5.2、神经网络算法应用

Python 机器学习 Sklearn 库提供了多层感知器算法(Multilayer Perceptron,即 MLP),也就是我们所说的神经网络算法,它被封装在 sklearn.neural_network 包中,该包提供了三个神经网络算法 API,分别是:

  • neural_network.BernoulliRBM,伯努利受限玻尔兹曼机算法,无监督学习算法;
  • neural_network.MLPClassifier,神经网络分类算法,用于解决分类问题;
  • neural_network.MLPRgression,神经网络回归算法,用于解决回归问题。

下面使用神经网络分类算法解决鸢尾花的分类问题。在这之前有必要先了解 neural_network.MLPClassifier 分类器常用参数,如下所示:

名称 说明
hidden_layer_sizes 元组或列表参数,序列内元素的数量表示有多少个隐藏层,每个元素的数值表示该层有多少个神经元节点,比如(10,10),表示两个隐藏层,每层10个神经元节点。
activation 隐藏层激活函数,参数值有 identity、logistic、tanh、relu,默认为 ‘relu’ 即线性整流函数(校正非线性)
solver 权重优化算法,lbfgs、sgd、adam,其中 lbfg 鲁棒性较好,但在大型模型或者大型数据集上花费的调优时间会较长,adam 大多数效果都不错,但对数据的缩放相当敏感,sgd 则不常用
alpha L2 正则项参数,比如 alpha = 0.0001(弱正则化)
learning_rate 学习率,参数值 constant、invscaling、adaptive
learning_rate_init 初始学习率,只有当 solver 为 sgd 或 adam 时才使用。
max_iter 最大迭代次数
shuffle 是否在每次迭代时对样本进行清洗,当 solver 参数值为 sgd 或 adam 时才使用该参数值
random_state 随机数种子
tol 优化算法中止的条件,当迭代先后的函数差值小于等于 tol 时就中止

Iris 鸢尾花数据集内包含 3 个类别,分别是山鸢花(Iris-setosa)、变色鸢尾(Iris-versicolor)和维吉尼亚鸢尾(Iris-virginica)共150 条记录,每一个类别有 50 条数据,每条记录有 4 项特征(单位为厘米):

  • sepallength:萼片长度
  • sepalwidth:萼片宽度
  • petallength:花瓣长度
  • petalwidth:花瓣宽度

选取两个类别(0 和 1,即山鸢尾花和变色鸢尾花)的样本标记值和两个特征属性(’sepal length (cm)’, ‘petal length (cm)’),之后使用神经网络分类算法对数据集中的 0 和 1 两类鸢尾花进行正确分类。代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier


def run():
iris = datasets.load_iris() # 加载鸢尾花数据集
# 用pandas处理数据集
data = pd.DataFrame(iris.data, columns=iris.feature_names)
print(iris.feature_names)
# 数据集标记值 iris.target
data['class'] = iris.target

# 此处只取两类 0/1 两个类别的鸢尾花,设置类别不等于 2
data = data[data['class'] != 2]
# 对数据集进行归一化和标准化处理
scaler = StandardScaler()
# 选择两个特征值(属性)
X = data[['sepal length (cm)', 'petal length (cm)']]
# 计算均值和标准差
scaler.fit(X)
# 标准化数据集(数据转化)
X = scaler.transform(X)
# 'class'为列标签,读取100个样本的的列表
Y = data[['class']]

# 划分数据集
X_train, X_test, Y_train, Y_test = train_test_split(X, Y)
# 创建神经网络分类器
mpl = MLPClassifier(solver='lbfgs', activation='logistic')
# 训练神经网络模型
mpl.fit(X_train, Y_train)
# 打印模型预测评分
print('Score:\n', mpl.score(X_test, Y_test))

# 划分网格区域
h = 0.02
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Z = mpl.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 画三维等高线图,并对轮廓线进行填充
plt.contourf(xx, yy, Z, cmap='summer')
# 绘制散点图
class1_x = X[Y['class'] == 0, 0]
class1_y = X[Y['class'] == 0, 1]
l1 = plt.scatter(class1_x, class1_y, color='b', label=iris.target_names[0])
class2_x = X[Y['class'] == 1, 0]
class2_y = X[Y['class'] == 1, 1]
l2 = plt.scatter(class2_x, class2_y, color='r', label=iris.target_names[1])

plt.legend(handles=[l1, l2], loc='best')
plt.grid(True)
plt.show()


if __name__ == '__main__':
run()

17、集成学习算法

集成学习算法并非一种机器学习算法,它更像是一种模型优化方法,是一种能在各种机器学习任务上提高准确率的强有力技术,这种技术的关键体现在“集成”两个字上,所谓集成就是“捏在一起”,因此集成学习算法可以理解成是一套组合了多种机器学习算法模型的框架,它关注的是框架内各个模型之间的组织关系,而非某个模型的具体内部结构。

可以说集成学习算法是“集”百家之长,使预测模型获得较高准确率,当然这也导致了模型的训练过程会稍加复杂,效率降低了一些,但在硬件性能发达的今天,几乎可以忽略不计。

17.1、集成学习发展史

集成学习算法的理论、应用体系的构建与完善经历一个漫长的过程,下面进行简单地介绍。

集成学习最早出现于 1979 年,Dasarathy 提出了集成系统(Ensemble system) 的思想,他使用线性分类器和最近邻居分类器组成的复合模型进行训练,得到了比单个分类器训练更好的预测效果。

1988 年 Kearns 提出了“弱学习器”概念,引发了“能否用一组弱学习器创造一个强学习器”的广泛讨论。(学习器,指的是某种机器学习算法模型),注意,所谓弱学习器,指的是一个个单独的算法模型,比如 KNN 算法模型、线性回归模型、朴素贝叶斯等,而强学习器指的是由多个不同类别的“弱学习器”集成的学习器,也称“异质集成”,这类学习器的预测准确率在 90% 以上。除此之外,还有一种“基学习器”(也称同质集成),它是由同一款机器学习算法组成的。

1990 年 Schapire 对这问题给出了答案,并且研发了著名的 Boosting 算法,该算法是集成学习常用方法之一;1992 年 Wolpert 首次提出“堆叠泛化”这一概念,即“堆叠”弱学习器训练的模型比任何单个弱学习器训练的模型具有更好的性能。

1996年,Breiman 开发了另一个集成学习方法 —— Bagging 算法(也称装袋算法),并对其原理和训练过程进行了详细的描述,并明确指出 Bagging 算法能够提高预测的准确性。其后几年,Breiman 在 Bagging 算法的基础上对“随机决策森林”进行另外重新描述,提出了集成学习中最广为人知的算法 —— 随机森林算法(RandomForest),该算法通过集成学习的思想将多棵“决策树”集成为一片“森林”,使其兼顾了解决回归问题和分类问题的能力。

截止到目前,已经有越来越多的集成学习算法被提出,比如 2010 年 Kalal 等人提出的 P-N 学习,以及近几年提出的以堆叠方式构建的深度网络结构、XGBoost 等算法,它们都能显著提升模型的预测效果。

17.2、集成学习组成方式

集成学习不是一种独立的机器学习算法,而是把互相没有关联的机器学习算法“集成”在一起,从而取得更好的效果。每个算法模型都有各自的局限性,集成学习方式的出现正好弥补了这一不足之处。

总的来说,集成学习算法主要使用两种结构来管理模型与模型之间的关系,一种是并联,另一种是串联(这和物理上串联电路、并联电路似乎有些相似之处)。下面对这两种方式进行简单介绍。

  1. 并联组织关系

    所谓并联,就是训练过程是并行的,几个学习器相对独立地完成预测工作,彼此互不干扰,当所有模型预测结束后,最终以某种方法把所有预测结果合在一起。并行式集成学习的典型代表是 Bagging 算法。并行结构示意图如下所示:

    集成学习并联结构
  2. 串联组织关系

    串联结构指的是训练过程是串行的,几个学习器在一起,通力合作一起来完成预测任务。第一个学习器拿到数据集完成预测,然后把预测结果以及相关数据传递给第二个学习器,第二个学习器也是在完成预测后把结果和相关数据继续传递下去,直至传递到最后一个学习器。串行式集成学习的典型代表是 Boosting 算法。串行结构示意图如下所示:

    集成学习串联结构

如果各个学习器势均力敌,分不出主次优劣,在这种情况下建议选择并联结构;如果学习器已经有了明确的分工,知道谁负责主攻,谁负责辅助,则可以使用串联结构

17.3、预测结果的方式

不管是串联结构,亦或是并联结构,最终都要输出一个预测结果,而在一个组织结构会有多个学习器,因此就会产生多个预测结果,那么我们要怎么将这些结果整合成一个结果对外输出呢,也就是使用什么方式来整合每个学习器的输出结果呢。对于集成学习算法来说,把多个结果整合成一个结果的方法主要有两种,分别是平均法和投票法,下面分别对它们进行介绍。

17.3.1、平均法

平均法,又分为简单平均法和加权平均法,简单平均法就是先求和然后再求均值,而加权平均则多了一步,即每个学习器通过训练被分别赋予合适的权值,然后求各个预测结果的加权和,最后再求均值。

17.3.2、投票法

投票法,具体分为三种:简单多数投票法、绝对多数投票法和加权投票法。

  1. 简单多数投票法就是哪个预测结果占大多数,就把这个结果就作为最终的预测结果;

  2. 绝对多数投票法就多了一个限制,这个“多数”必须达到半数,比如有共有 6 个学习器,得出同一预测结果的必须达到 3 个及以上,否则就拒绝进行预测。

  3. 加权投票法,有点类似加权平均,首先给不同的学习器分配权值,其次是查看哪个结果占大多数,注意,此处有一点儿不同,这里的“大多数”是权值相加后再比较得到的大多数,最后以得票最多的作为预测结果。

    关于加权投票法举一个简单的例子,比如预测结果为 A 的有 3 个学习器,权值分别为 0.1、0.2 和 0.3,那么结果 A 的票数就为三者之和,即 0.6,而预测结果为 B 的只有 2 个学习器,但权值分别为 0.4 和 0.5,那么结果 B 的票数就为 0.9,也就是结果 B 的票数高于结果 A,最终预测结果就是结果 B。

17.4、集成学习实现方式

根据个体学习器生成方式的不同,目前集成学习的实现方式主要分为两种:

  • 一种是 Bagging 算法为代表的并行式集成学习方法,其中最典型的应用当数“随机森林算法”;
  • 另一种是以 Boosting 算法为代表的串行式集成学习方法,其中应用频率较高的有两个 AdaBoost 算法和 XGBoost 算法。
  • 除上述两种主要的方法外,还有一种 Stacking 分层模型集成学习算法。

17.4.1、Bagging算法

Bagging 算法又称为“装袋算法”最初由 Leo Breiman 于 1996 年提出,它是并行式学习的典型代表,该算法主要是从数据层面上进行设计。并联结构中的每个学习器所使用的数据集均采用放回重采样的方式生成,也就是说,每个学习器生成训练集时,每个数据样本都有相同的被采样概率。训练完成后,Bagging 采用投票的方式进行预测。

通过放回重采样的方式来构建样本量相等、且相互独立的数据集,从而在同一算法中训练出不同的模型。Bagging 算法的集成策略比较简单,对于分类问题,一般通过投票法,以多数模型预测结果为最终结果;而对于回归问题,一般采用算术平均法,对所有模型的预测结果做算术平均得到最终结果。

17.4.2、Boosting算法

与 Bagging 算法相比,Boosting 是一种串行式集成学习算法,该算法基于错误来提升模型的性能,根据前面分类器分类错误的样本,调整训练集中各个样本的权重来重新构建分类器。

Boosting 可以组合多个弱学习器来形成一个强学习器,从而在整体上提高模型预测的准确率。在模型训练过程中,Boosting 算法总是更加关注被错误分类的样本,首先对于第一个弱学习器预测发生错误的数据,在后续训练中提高其权值,而正确预测的数据则降低其权值,然后基于调整权值后的训练集来训练第二个学习器,如此重复进行,直到训练完成所有学习器,最终将所有弱学习器通过集成策略进行整合(比如加权法),生成一个强学习器。

Boosting 算法的训练过程是呈阶梯状的,后一个学习器会在前一个学习器的基础上进行学习,最终以某种方式进行综合,比如加权法,对所有模型的预测结果进行加权来产生最终的结果。

17.5、随机森林算法

随机森林(Random Forest,简称RF)是通过集成学习的思想将多棵树集成的一种算法,它的基本单位是决策树模型,而它的本质属于机器学习的一大分支——集成学习(Ensemble Learning)方法。集成学习的实现方法主要分为两大类,即 Bagging 和 boosting 算法,随机森林就是通过【Bagging 算法+决策树算法】实现的。

17.5.1、随机森林

随机森林,顾名思义,即使用随机的方式建立一个森林,这个森林由很多的决策树组成,并且每一棵决策树之间是相互独立的。

如果训练集有 M 个样本,对于每棵数而言,以随机且有放回的方式从训练集中抽取 N 个训练样本(N<M),作为该棵决策树的训练集。除了采用样本随机之外,随机森林还采用了特征随机。假设每个样本有 K 个特征,从所有特征中随机选取 k 个特征(k<=K),选择最佳分割属性作为节点建立 CART 决策树,重复该步骤,建立 m 棵 CART 树,这些树就组成了森林,这也是随机森林名字的由来。随机采样和随机特征选取在一定程度上避免了过拟合现象的发生。

当有一个新的输入样本进入森林时,就让森林中的每一棵决策树分别对其进行判断,看看这个样本应该属于哪一类(对于分类算法而言),然后使用少数服从多数的【投票法】,看看哪一类被选择最多,就预测该样本为哪一类。在这个过程中,森林中每棵数都是独立地对若干个弱分类器的分类结果进行投票选择,从而组成一个强分类器。

随机森林既可以处理属性为离散值的样本(即分类问题),也可以处理属性为连续值的样本(即回归问题),另外随机森林还可以应用于无监督学习的聚类问题,以及异常点检测。

17.5.2、算法应用

在 Scikit-Learn 机器学习库中提供了 Bagging 和 Boosting 两种集成学习方法,且都在 ensemble 类库下,包括随机森林算法。除此之外,该类库下还包含了其他几类算法,较为知名有如下几种:

说明
RandomForestClassifier类 使用随机森林(Random Forest)算法解决分类问题,随机森林可谓 Bagging 集成学习算法的典型代表,它选择以 CART 决策树算法作为弱学习器,是一种常用的机器学习算法。
RandomForestRegressor类 使用随机森林算法解决回归问题
ExtraTreesClassifier类 使用极端随机树(Extra Tree)算法解决分类问题,极端随机树算法可以看作随机森林算法的一种变种,主要原理非常类似,但在决策条件选择时采用了随机选择的策略。
ExtraTreesRegressor类 使用极端随机树算法解决回归问题。
AdaBoostRegressor类 使用AdaBoost算法解决分类问题,AdaBoost算法是最知名的Boosting算法之一。
AdaBoostRegressor类 使用AdaBoost算法解决回归问题。
GradientBoostingClassifier类 使用Gradient Boosting算法解决分类问题,Gradient Boosting算法常常搭配CART决策树算法使用,这就是有名的梯度提升树(Gradient Boosting Decision Tree,GBDT)算法。
GradientBoostingRegressor类 使用Gradient Boosting算法解决回归问题。

Scikit-Learn 对于集成学习方法做了非常良好的封装,可以实现“开箱即用”,下面以随机森林算法为例进行演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from sklearn.datasets import load_iris
# 从Scikit-Learn库导入集成学习模型的随机森林分类算法
from sklearn.ensemble import RandomForestClassifier


if __name__ == '__main__':
# 载入鸢尾花数据集
X, y = load_iris(return_X_y=True)
# 训练模型
# 随机森林与决策树算法一样,其中有一个名为“criterion”的参数
# 同样可以传入字符串“gini”或“entropy”,默认使用的是基尼指数
clf = RandomForestClassifier()
clf.fit(X, y)
print("模型预测准确率", clf.score(X, y))
# 使用模型进行分类预测
print("预测结果:", clf.predict(X))

17.5.3、总结

随机森林算法是集成学习方法的典型代表,该算法具有以下特点:

  • 模型准确率高:随机森林既可以处理分类问题,也可以处理回归问题,即使存在部分数据缺失的情况,随机森林也能保持很高的分类精度;
  • 能够处理数量庞大的高维度的特征,且不需要进行降维(因为特征子集是随机选择的);
  • 能够评估各个特征在分类问题上的重要性:可以生成树状结构,判断各个特征的重要性;
  • 对异常值、缺失值不敏感。

当然随机森林算法也存在一些不足,比如:

  • 随机森林解决回归问题的效果不如分类问题;
  • 树之间的相关性越大,错误率就越大;
  • 当训练数据噪声较大时,容易产生过拟合现象。
  • Copyrights © 2022-2023 hqz

请我喝杯咖啡吧~

支付宝
微信