《Python一行流》学习笔记6——机器学习

目录

1 监督式机器学习的基础知识

1.1 训练阶段(training phase)

1.2 推理阶段

2 线性回归

3 逻辑回归

3.1 Sigmoid 函数

3.2 最大似然模型

4 K-Means 聚类算法

 5 K-近邻算法

 6 神经网络分析

6.1 人工神经网路

6.2 在 Python 中使用神经网络

7 决策树

8 最小方差计算

9 基本统计

10 支持向量机(SVM)分类

11 随机森林分类


1 监督式机器学习的基础知识

        机器学习的主要目的就是,通过已有的数据进行准确的预测。例如,假设·写个算法来预测一只特定的股票在未来两天的价值,为了完成该目标,就需要训练一个机器学习模型。

        那么什么是模型呢?从机器学习用户的角度来说,机器学习模型(Model)就像一个黑盒子:把数据放进去,然后取出预测结果

         在这个模型中,输入特征被叫作特征(feature),并用变量 x 表示,它可以是一个数值,也可以是数值构成的多维向量。然后黑盒子就会发挥它的魔力,处理输入的数据。经过一段时间后,就会得到预测值 y,这就是模型针对给定输入特征的预测输出。对于回归问题,预测值也是由一个或多个数值组成,跟输入特征一样。

        监督式机器学习分成两个独立的阶段:训练阶段和推理阶段

1.1 训练阶段(training phase)

        在训练阶段,你将告诉模型,对于给定的输入 x,你期望输出的是 y',当模型输出预测值 y 时,将与 y' 进行比较,如果它们不一样,则更新模型,以输出一个更接近 y' 的结果。

        举个例子,假设训练一个模型来预测给定图像(输入)的水果名称(输出),具体输入是香蕉的图像 (x),但模型错位地预测其为苹果 (y) 。由于想要的预测 (y') 与模型的预测不一致,就需要修改模型,以便下一次模型更正确地预测其为香蕉。

        当不断告诉模型你对大量不同输入的期望输出,并持续调整模型时,你就在使用训练数据对模型进行训练。随着时间的推移,模型将会学习到你希望特定的输入将产生怎样的输出。这就是数据如此重要的原因:只有好的训练数据能产生好的模型。没有优质的训练数据,模型一定会失败。换句话说,训练数据监督着机器学习的过程,这就是把它叫作监督式学习的原因。

1.2 推理阶段

        在推理阶段,使用训练好的模型针对新输入的特征 x,预测其输出值 y。合适的机器学习模型拥有泛化能力:它们能利用从训练数据中获得的经验来预测新输入的输出结果。能对从未见过的输入数据进行泛化预测是机器学习的优势之一,也是其在广泛的实际场景中大受欢迎的主要原因。

2 线性回归

        线性回归常被用在回归问题中,在这种问题中,模型会通过现有的数据来预测缺失的数据。

        线性回归的一个巨大优势是其简单性,在市场研究、天文学和生物学等领域都有着广泛的运用。

        每个机器学习模型都由模型参数组成,模型参数是根据对现有数据的考量,在模型内部配置的变量。模型参数决定了基于给定的特征,模型具体会怎样计算出预测值。对于线性回归,模型参数被称为系数,比如二维直线的公式:f(x)=ax+c,变量 a 和 c 就是这个线性方程的系数。这个方程描述了任何一个输入 x 是如何转化为输出值 f(x) 的,所有这些输出值在二维空间离描述了一条直线,通过改变系数,就可以描述二维空间中的任何一条直线。

        更泛化一些,给定一组输入特征 x1, x2, ..., xk, 线性回归模型会把这些输入特征跟一组系数 a0, a1, ..., ak 组合在一起,用下面的公式计算出预测值 y:

y=f(x)=a0+a1*x1+a2*x2+...+ak*xk

         现在如何使用线性回归来预测某一天的股票价格呢?为了简化,这里假设只有一个输入特征 x,即日期(真实世界肯定没有这么简单)。对于输入日期 x,希望得到预测的股票价格 y。这样就可以把线性回归模型简化为一个二维直线模型了:

y=f(x)=a0+a1*x

         为了找到能准确描述数据的直线,已建立一个线性回归模型,需要确定其系数。这就是机器学习发挥作用的时候了。确定线性回归模型的参数主要有两种方法:1)可以分析计算出这些点之间的最佳拟合直线(线性回归的标准方法);2)可以尝试不同的模型。根据标注后的样本数据对其进行测试,并最终确定最佳模型。在每种情况下,都要通过一个叫作误差最小化的过程来确定”最佳“模型。这个过程会将预测值和理想输出值的方差最小化,以选择具有最小误差的模型(或为模型模型选择倒置最小方差的系数)。

        现在给定数据,日期为 [0, 1, 2];对应股票价格为 [155, 156, 157]],对于这组数据,你最后会得出系数为 a0=155.0、a1=1.0,把它们放到线性回归模型公式中,得出模型如下:

y=f(x)=a0+a1*x=155.0+1.0*x

         该模型预测出的直线跟训练模型数据的距离方差为 0(太完美了),因此你已经找到了误差最小化的模型。现在看可以使用该模型来预测任何 x 值对应的股票价格了,如,你想预测 x=3 时的股票价格,只需要将 x 值代入该模型计算f(x)=155.0+1.0*4=159.0,就能预测其股价为 159.0 了。

        下面展示了如何使用一行代码来构建一个简单的股票价格的线性回归模型,不过在此之前需要先安装两个库——numpy、scikit-learn:

# 此处只展示 Win10 系统的安装方法,其他系统自行百度
# 在终端安装 numpy 库
python -m pip install numpy

# 在终端安装 scikit-learn 库——同时需要安装其依赖库
# numpy 库,上面以安装
# matplotlib 库
python -m pip install matplotlib
# scipy 库
python -m pip install scipy 
# scikit-learn 库,注意不是安装 sklearn 库,但在 import 中使用 sklearn
python -m pip install scikit-learn
from sklearn.linear_model import LinearRegression
import numpy as np

# 数据(每天的股价)
prices = np.array([155, 156, 157])
n = len(prices)
'''
将 3 个数值放进 NumPy 数组中,并用一个单独的变量 n 来存储其长度,以使代码更简洁
'''

# 一行流
model = LinearRegression().fit(np.arange(n).reshape(n, 1), prices)
'''
通过调用 LinearRegression() 函数来建立模型。为了找到合适的参数,需要调用 fit() 函数
来训练模型。
fit() 函数接受两个参数:训练数据的输入特征值、这些输入对应的理想输出。
我们的理想输出就是真实的股价,但对于输入特征,fit() 需要一个如下格式的数组:
[<training_data_1>,
<training_data_2>,
...
<training_data_n>]
每个训练数据都是一组特征值的序列:
<training_data> = [feature_1, feature_2, ... , feature_k>
在该例子中,每组输入只包含了一个单独的特征 x(日期)。此外,预测值也只由 y(股价)构成。
为了把输入数组包装成正确的形状,需要把它们重塑乘下面矩阵的形式:
[[0],
[1],
[2]]
可以使用 np.arrange() 函数来创建一个递增的整数序列,然后使用 reshape((n, 1)) 将这个一
维 NumPy 数组重塑成具有 1 列 n 行的二维数组。注意,scikit-learn 允许输入的理想输出是一
维数组,否则需要将 prices 数据数组也进行重塑。
一旦有了训练数组和理想输出,fit() 就会开始进行误差最小化计算:寻找使预测值和理想输出差异最
小的模型参数。
当 fit() 对它得到的模型感到满意时,就会把该模型返回,你就可以使用它的 predict() 函数来预
测之后日期的股价了。predict() 和 fit() 对输入特征的格式要求时相同的,即都需要传入一个单列
矩阵。
'''

# 结果:使用 predict() 函数预测第 3 和第 4 天的股价
print(model.predict([[3], [4]]))
# [158. 159.]

3 逻辑回归

        逻辑回归常用于分类问题,即预测一个样本是否属于特定的类别(或类型),比如根据不容的输入特征,将用户分为男性和女性。这与回归问题形成鲜明的对比,在回归问题中,你拿到一个样本,并返回一个属于某连续域的预测值。

        一般来说,线性回归适合从一个连续域中找出预测值,这个值可以有无线多的可能选项。但如果预测值不是连续的,而是可分类的,属于数量有限的组或类别之一(如,给定一个盯人吸烟的数量来预测其患肺癌的可能性),此时就更适合使用逻辑回归了。

3.1 Sigmoid 函数

        线性回归对训练数据拟合出一条直线,而逻辑回归则拟合出一条 S 型曲线,叫作 sigmoid 函数。这条曲线将帮助你进行二元决策(如,是/否)。对于大多数输入值,sigmoid 函数将返回一个非常接近 0 的数(属于其中一类)或者非常接近 1 的数(属于另一类)。对于给定的输入值,它不太可能返回一个摸棱两可的结果,虽然确实可能会产生 0.5 这样的概率值,但在实际场景中会把这种可能性降至最小。下面的图展示了对于肺癌案例的逻辑回归曲线:

         上图的 sigmoid 函数会在给定病人吸烟数量时,返回其患肺癌概率的近似估计。当病人的吸烟数量使唯一掌握的数据时,这个概率会帮你做出可靠的判断:病人到底患癌了吗?

         上图展示了对两个新病人的预测(横坐标上的两个 X 记号)。除了他们的吸烟数量,你对他们一无所知。因为已经训练好了这个逻辑回归模型(sigmoid 函数),所以对于任何新输入的值,它将会返回一个概率。如果 sigmoid 函数返回的这个概率大于 50%,模型将会预测其患肺癌(阳性),否则会预测为没有患肺癌(阴性)。

       ( 注意:你也可以用逻辑回归进行多分类,将数据分为两个以上的类。可以使用 sigmoid 函数的泛化形式:softmax 函数来实现这一目标。它返回一个元组,有一组概率值组成,每个类对应一个元素。相比之下,sigmoid 函数只会将输入值转换成一个单一的概率值。)

3.2 最大似然模型

        逻辑回归的主要问题使如何选择最适合训练数据的正确 sigmoid 函数,答案是研究每个模型的似然,即模型产生观测到的训练数据的概率。想要选择的就是拥有最大似然°的模型,因为训练数据是真实世界产生的,最大似然的模型也就最接近真实世界的处理过程。

        要计算给定模型对于给定训练数据的似然,需要计算单个训练数据点的似然,然后把它们相乘得到整个数据集的似然。计算单个数据点的似然,只需要将该模型的 sigmoid 函数应用到数据点上,就能得到该数据点在此模型下的正确概率。如果要根据数据点选出最大似然模型,则要对不同的 sigmoid 函数(把该函数平移一点点)重复同样的似然计算,如下图所示:

         现在看看如何使用 Python 一行流的方式来实现逻辑回归吧:将逻辑回归用于健康领域(将香烟消费量与患癌概率关联):

from sklearn.linear_model import LogisticRegression
import numpy as np

# 数据(香烟消费量, 是否患癌)
x = np.array([[0, 'No'],
              [10, 'No'],
              [60, 'Yes'],
              [90, 'Yes']])
'''
训练数据 x 由四个患者记录(每行一个)组成。每行有两列,第一列存储了患者抽烟的数量(输入特征);
第二列存储的是类标签,记录了患者是否患癌
'''

# 一行流:创建逻辑回归模型
model = LogisticRegression().fit(x[:, 0].reshape(-1, 1), x[:, 1])
'''
通过调用 LogisticRegression() 构造函数来创建模型,然后对这个模型调用 fit() 函数,fit() 接受
两个参数,分别是输入(香烟消费量)和输出的类标签(是否患癌)。
fit() 函数期望输入参数是二维数组格式,每行是一个训练数据样本,每列是训练数据的其中一个特征。在这
个例子中,训练数据只有一个特征,所以使用 reshape() 操作把这个一维输入转换为二维 NumPy 数组。
传入 reshape() 的第一个参数指定了函数,第二个参数制定了列数。由于只需要关心列数(1 列),所欲在
行数的位置传入了 -1 作为特殊信号,要求 NumPy 自动确定行数。
'''

# 结果:根据得到的模型对新数据进行分类
print(model.predict([[2], [12], [13], [40], [90]]))
# ['No' 'No' 'No' 'Yes' 'Yes']

'''下面来详细看一下 sigmoid 函数是通过怎样的概率得到预测结果的'''
for i in range(0, 100, 5):
    print('x=' + str(i) + ' --> ' + str(model.predict_proba([[i]])))
'''
predict_proba() 函数将香烟数量作为输入,并返回一个包含肺癌阴性概率(索引 0)和肺癌阳性概率(索引 1)
的数组。如果肺癌阴性的概率大于阳性概率,则预测结果为肺癌阴性;反之。则预测结果为肺癌阳性。

x=0 --> [[9.99568507e-01 4.31492826e-04]]
x=5 --> [[0.99870568 0.00129432]]
x=10 --> [[0.99612419 0.00387581]]
x=15 --> [[0.98845352 0.01154648]]
x=20 --> [[0.96611811 0.03388189]]
x=25 --> [[0.90474105 0.09525895]]
x=30 --> [[0.75982023 0.24017977]]
x=35 --> [[0.51308165 0.48691835]]
x=40 --> [[0.25979831 0.74020169]]
x=45 --> [[0.10467066 0.89532934]]
x=50 --> [[0.03748068 0.96251932]]
x=55 --> [[0.01280434 0.98719566]]
x=60 --> [[0.00430168 0.99569832]]
x=65 --> [[0.00143695 0.99856305]]
x=70 --> [[4.79086111e-04 9.99520914e-01]]
x=75 --> [[1.59627706e-04 9.99840372e-01]]
x=80 --> [[5.31753572e-05 9.99946825e-01]]
x=85 --> [[1.77125760e-05 9.99982287e-01]]
x=90 --> [[5.89987437e-06 9.99994100e-01]]
x=95 --> [[1.96517115e-06 9.99998035e-01]]
'''

4 K-Means 聚类算法

        监督式学习的训练数据都是标注数据。换句话说,你知道训练数据中每个输入值应该对应什么样的输出。但在实践中,情况并不总是如此,你经常会发现自己面对的是未标注数据,尤其是在许多数据分析的场景下,一开始其实根本不知道“最佳的输出”是什么。在这种情况下,预测是不可能的(因为没有可参考的输出),但仍然可以从这些未标注的数据集中提炼出有用的知识(如,可以找到相似数据形成的类簇)。使用未标注数据的模型属于无监督学习的范畴。

        举个例子,加色和你在一家初创公司工作,服务于目标市场中各种手入水平与年龄的不同客户。现在要求你寻找一组符合公司目标市场的客户特征,那么就可以用聚类的方法来识别出公司所服务对象的平均客户图像。下面的图像举了一个示例。

        在上图中,可以容易地识别出对应于三种不同类型的收入与年龄的三种客户图像。但如何使用算法来找出来呢?这就是聚类算法的领域了,如广泛流行的 K-Means 算法。给定数据集和一个整数 k,K-Means 算法会从数据中找出 k 个类簇,使得类簇的中心点(也叫质点)与类簇中其他店之间的差距最小。即,可以通过在数据集上运行 K-Means 算法来找i出不同的客户图像,如下图所示:

         从上图可以看出,类簇中心(黑点)与类簇的客户数据相匹配,每个中心点都可以看作一个客户图像。这样,就有了三个理想化的图像:一个20岁收入2000美元的人;一个25岁收入3000美元的人;一个40岁收入4000美元的人。更棒的是,K-Means 算法即使在高维空间也能找到这些聚类的中心点。

        K-Means 算法需要类簇中心数量(k)作为输入。更高级的算法则可以自动找出类簇中心的数量。

        K-Means 算法执行过程可以简单概括成如下的布粥:

'''
随即地初始化聚类中心
重复,直到收敛
    把每个数据点指向它最近的聚类中心
重新计算每个聚类的中心,更新为指向中心点的所有数据点的中心
'''

        下面考虑这样一个问题:已知一组二维工资数据(工作小时数、工资收入),从给定的数据集中找出两个员工群类,每个都有相似的工作小时数和相近的工资收入。

from sklearn.cluster import KMeans
import numpy as np

# 数据(小时数(h),工资($))
x = np.array([[35, 7000],
              [45, 6900],
              [70, 7100],
              [20, 2000],
              [25, 2200],
              [15, 1800]])

# 一行流:创建 KMeans 对象
kmeans = KMeans(n_clusters=2).fit(x)
'''
这行代码创建了一个新的 kMeans 对象,当创建该对象时,使用参数 n_clusters 制定了聚类中心的数
量。然后只需调用实例方法 fit(x),在输入数据 x 上运行该算法,就会得到结果并存储在 KMeans对象
中,接下来就只剩下从对象属性中读取结果了。
'''

# 结构:得到各聚类的中心点
cc = kmeans.cluster_centers_
print(cc)
'''
[[  20. 2000.]
 [  50. 7000.]]
'''

         下图为上面代码的数据图,以及用 K-Means 算法找出的类簇中心点:

 5 K-近邻算法

        K-近邻算法(K-Nearest Neighbors,简称 KNN)常被用于回归和分类的许多场景,如推荐系统、图像分类,以及金融数据预测。它是很对高级机器学习技术的基础(如,信息检索)。

        上面讨论的机器学习模型都需要使用训练数据进行计算,并得到原始数据的某种重新呈现,可以用这些结果去对新数据进行预测、分类或聚类。如,线性和逻辑回归算法通过学习得到模型参数,而聚类算法基于训练数据计算出类簇中心。然后,KNN 算法与其他算法相比,它不会去计算出一个新的模型(或呈现形式),而是使用整个数据集作为模型本身。

        这种机器学习模型不过是一组可观测值,训练数据的每一个样本都是模型的一部分。这样做的一个缺点是,随着训练数据的增加,模型可能会迅速膨胀,这样可能需要采样或过滤作为预处理。但有个特别大的优点是,训练方法极其简单——把数据添加到模型中即可。KNN 算法可用于预测或分类。给定输入向量 x,对其执行下面的策略:

        1)找到 x 的 k 个最近的邻居(根据预先定义的距离度量方式)

        2)把 k 个最近邻居聚合成一个单独的预测或分类值。可以使用任何聚类函数,如均值、最大值或最小值。

        举个例子,你要为客户销售房屋,并且已经得到了一个包含客户和房价的庞大数据库(见下图)。现在,客户想知道一套 52 平方米的房子预计要花多少钱?你查询了 KNN 模型,它立刻给出了 33_167 美元的结果。事实上,客户在同一周内果然找到了一套 33_489 美元的房子。

         KNN 系统是如何得出如此准确的预测呢?首先,KNN 系统简单地用欧氏距离计算出 52 的 3 个(k=3)最近邻居,分别是 A、B 和 C,对应的房价是 34000 美元、33500 美元和 32000 美元。然后,它通过计算这三个最近邻居的平均数来聚合这三个值。荡然,也可以改变相似度函数、参数 k 以及聚合函数,来实现更复杂的预测模型。

        KNN 的另一个优点是,随着新的观测结果的出现,它可以很容易地调整适应。与此对应的一个明显的弱点是,随着添加越来越多的点,寻找 k 个最近邻的计算会变得越来越困难。为了适应这种情况,可以不断从偶行中删除旧的无用值。

        也可以使用 KNN 来解决分类问题。在这种情况下,可以采用 k 个近邻投票的机制而不是计算 k 个近邻的平均值,即每个近邻为它自己的分类投票,得到最高的分类获胜。

        上面针对上面举的例子来看看如何在 Python 中使用 KNN 算法:

from sklearn.neighbors import KNeighborsRegressor
import numpy as np

# 数据(房屋面积(平方米),房屋价格($))
x = np.array([[35, 30_000],
              [45, 45_000],
              [40, 50_000],
              [35, 35_000],
              [25, 32_500],
              [40, 40_000]])

# 一行流:创建 KNN 模型
KNN = KNeighborsRegressor(n_neighbors=3).fit(x[:, 0].reshape(-1, 1), x[:, 1])
'''
首先创建一个新的机器学习模型,类型叫做 KNeighborsRegressor(若用 KNN 进行分类,则需要使
用 KNeighborsClassifier)。
通过给 fit() 函数传入两个参数来训练该模型:第一个参数定义了输入值(房屋面积),第二个参数定
义输出值(房屋价格)。这两个参数的形状(shape)都要求是类似数组的结构。这样做的原因是,实际的
输入值一般都是多维而不是一维的。因此需要使用 reshape() 函数将 x[:, 0] 进行重塑。
'''

# 记过:预测新房屋面及的房价
print(KNN.predict([[30]]))
# [32500.]
'''
KNN 算法找到与新数据点的面及最接近的三栋房子,使用 k=3 最近邻居平均值的方式计算出预测的房价
为 32_500 美元
'''

 6 神经网络分析

6.1 人工神经网路

        人工神经网络的基本思路是把学习和推理的大型任务分解成多个微任务。这些微任务不是各组独立的,而是相互依赖的。在简化版的模型中,学习只是调整突触的强度(在人工神经网络中也叫做权重或参数)。那么如何在模型中“创造”一个突触呢?答案是,只需要把它的权重从 0 增加到一个非 0 值即可。

        下图展示了一个具有 3 层(输入、隐藏、输出)的基本神经网络,每层由多个神经元构成,从输入层到隐藏层和输出层,这些神经元相互连接在一起。

        在这个例子中,伸进网络被训练为检测图像中的动物。在实际使用中,你会把图像中的每个像素传给一个输入神经元,以构成输入层。这样就需要数百万个输入神经元,并于数百万个隐藏神经元相连。通常,每个输出神经元负责整体输出中的一个位(bit),如,要检测两种不同的动物(猫和狗),输出层只需要一个单独的神经元,以表达两种不同的状态(0=cat, 1=dog)。

        一个关键的理念是,当某种神经脉冲输入到神经元时,神经元会被激活(又叫发射),每个神经元根据输入脉冲的请读,独立地决定是否发射。这样一来,就模拟了人脑,其中的神经元通过脉冲相互激活。输入神经元产生的激活行为在神经网络中传播,直到抵达输出神经元。一些输出神经元会被激活,而另一些则不会。于是,所有输出神经元会形成某种特定的激活模式,作为人工神经网络最终的输出(或预测)。

        在模型中。可以把发射的输出神经元编码为 1,非发射的输出神经元编码为 0。这样就可以训练神经网络来预测任何可以表示为 0 和 1 的东西(即计算机可以表示的一一切东西)。

   

        上图展示了神经元工作的数学原理。每个神经元都与其他神经元相连,但并非所有的连接都是平等的——每个连接都有一个相关权重。从形式上看,一个发射神经元向它外面的邻居传播了值为 1 的脉冲,而非发射神经元传播i的脉冲为 0。可以认为权重是表示输入的发射神经元脉冲中有多少通过连接传递给了目标神经元。从数学上来说,把输出的脉冲乘上连接的权值来计算进入下一个神经元的输入。在该例子中,神经元简单地把所有的输入加在一起,以计算自己的输出,这就是激活函数,它描述了一个神经元的输入是如何产生输出的。

        学习算法的工作就是,使用训练数据选择神经网络的权重 w。给定一个训练输入值 x,不同的权重 w 会导致不同的输出,于是,学习算法可以在多次迭代中逐渐改变权重 w,直到输出层产生于训练数据相似的结果。换句话说,训练算法逐渐降低了于正确预测训练数据相比的误差。

6.2 在 Python 中使用神经网络

        目标是创建一个神经网络,通过 5 和输入特征(对 5 个问题的答案),来预测 Python 技能水平(得分):

        1)每周:最近七天在 Python 代码前的时间是多少小时?

        2)年限:从多少年前开始学习计算机科学?

        3)图书:书架上由多少本编程书籍?

        4)项目:花在 Python 的时间中有多少比例用于实现真实世界的项目?

        5)收入:每月通过(最广泛意义上)出售技术能力能挣到多少钱(以 1000 美元取整)?

from sklearn.neural_network import MLPRegressor
import numpy as np

# 数据(每周、年限、图书、项目、收入, 分数)
x = np.array([
    [20, 11, 20, 30, 4000, 3000],
    [12, 4, 0, 0, 1000, 1500],
    [2, 0, 1, 10, 0, 1400],
    [35, 5, 10, 70, 6000, 3800],
    [30, 1, 4, 65, 0, 3900],
    [35, 1, 0, 0, 0, 100],
    [15, 1, 2, 25, 0, 3700],
    [40, 3, -1, 60, 1000, 2000],
    [40, 1, 2, 95, 0, 1000],
    [10, 0, 0, 0, 0, 1400],
    [30, 1, 0, 50, 0, 1700],
    [1, 0, 0, 45, 0, 1762],
    [10, 32, 10, 5, 0, 2400],
    [5, 35, 4, 0, 13000, 3900],
    [8, 9, 40, 30, 1000, 2625],
    [1, 0, 1, 0, 0, 1900],
    [1, 30, 10, 0, 1000, 1900],
    [7, 16, 5, 0, 0, 3000],
])

# 一行流:创建 MLPRegressor 模型
neural_net = MLPRegressor(max_iter=10_000).fit(x[:, :-1], x[:, -1])
'''
该一行流代码通过使用 MLPRegressor 类的构造函数来创建了一个神经网络。传入 max_iter=10_000
作为参数,这是因为用默认迭代数(max_iter=200),该训练无法达到收敛的程度。
然后调用 fit() 函数,以确定神经网络的参数。调用 fit() 后,神经网络就被成功地初始化了。fit() 
接受一个多维输入数组(每行一个观测值,每列一个特征)和一个一维输出数组(数组大小为观测值的数量)
作为参数。
最后就是找一些输入值,调用 predict() 函数进行预测了。
'''

# 结果:预测新输入的分数
print(neural_net.predict([[0, 0, 0, 0, 0]]))
# [1147.14225674]

7 决策树

        决策树是机器学习工具箱中一种强大而值观的工具,其一大优势是,它是人类可读的。你可以轻松地训练一个决策树并将它展示给任何人,而他们并不需要直到任何机器学习的知识,就能看懂模型在做什么。

        于许多机器学习算法不同的是,决策树背后的理念会与自身的经验有颇多相似性。它表示一种结构化的决策方式,在此结构中,每个决策会开启新的分支,通过回答一堆问题,最终落在推荐的结果上,下图展示了一个简单的推荐学习专业的决策树:

         决策树可用于分类问题,如“鉴于兴趣,应该学习哪个专业”,你就可以从顶部开始,然后不断回答问题,选择最能描述你特征的答案。最后,到达树的一个叶子节点,即没有子节点的节点。这就是根据你的选取特征得到的推荐类。

        决策树学习过程中存在很对细微的差别。在上面的例子中,第一个问题比最后一个问题的权重更大。如果喜欢数学,决策树绝不会推荐艺术或语言学。这是很有用的,因为对分类决策来说,一些特征可能要比其他特征重要得多。

        因此,决策节点的顺序对性能有很大的影响,把对最终分类影响较大的特征放在上面,把对最终分类影响不大的问题聚合在一起,能有效地优化性能。

        假设完整的决策树看起来像下图左边的那样,对于任何特征组合,都有一个单独的分类结果(即节点)。不过,可能会存在一些特征,针对要分类的问题不会提供任何额外的信息(如左下方“语言”决策节点),决策树学习会出出于效率的原因把这些节点去掉,这个过程叫作剪枝。

         下面的代码展示了如何使用决策树来进行分类:

from sklearn import tree
import numpy as np

# 数据:学生成绩(数学、语言、创造力) --> 学习专业
x = np.array([[9, 5, 6, 'computer science'],
              [1, 8, 1, 'linguistics'],
              [5, 7, 9, 'art']])
'''
第一个学生的数学能力很强,选择学习 computer science
第二个学生在语言方便的禁能远超其他两项,选择学习 linguistics
第三个学生擅长创造力,选择学习 art
'''

# 一行流:建立决策树模型
Tree = tree.DecisionTreeClassifier().fit(x[:, :-1], x[:, -1])
'''
该行代码使用 DecisionTreeClassifier() 的构造函数来创建一个新的决策树对象,
并通过在已标注训练数据(最后一列数据)上使用 fit() 函数来训练模型。在内部,它
创建了三个节点,每个都对应一种特征:数学、语言和创造力。
'''

# 结果:根据新的学生成绩,为其推荐学习专业
print(Tree.predict([[8, 6, 5], [3, 7, 9]]))
# ['computer science' 'linguistics']
'''
注意,该算法是具有不确定性的,即当两次执行同样的代码时,可能会出现不同的结果。使用
随机数生成器的机器学习算法经常会出现这种情况。对于本代码的使用到的算法,特征的顺序
是随机组织的,所以最终生成的决策树就可能具有不同的特征顺序。
'''

8 最小方差计算

        方差(variance)衡量了数据在一维或多维空间中围绕其平均值的分散程度。实际上,方差是机器学习中最重要的属性之一,它以一种概括的方式来捕捉数据中的模式——而模式识别正是机器学习的核心。

        很多机器学习算法都依赖于某种形式的方差,如偏差-方差均衡(bias-variance trade-off)就是机器学习中一种众所周知的问题:复杂的机器学习模型会有过度拟合数据的风险(高方差),但却可以准确地表示训练数据(低偏差);另一方面,简单的模型往往能很好地泛化(低方差)但不能准确地表示数据(高偏差)。

        方差是一个简单的统计属性,反映了数据集相对其平均值的发散程度。下图展示了两个公司股价的方差对比:

         从上图可以看出,科技创业公司的股价围绕其均线的波动很大,食品公司则只围绕均线小幅度波动。即科技创业公司的股价具有高方差,而视频公司股价具有低方差。

        用数学术语老表达,就是通过下面的公式计算一个数值集合 X 上的方差 var(X):

var(X)=\sum_{x\epsilon X}\left ( x-\bar{x}\right )^{2}

         其中 \bar{x} 表示数据 X 的平均值。

        现在要求你找出投资组合中方差最小的股票,从而使得通过向这只股票投入更多的资金,以降低投资组合的整体风险。

import numpy as np

# 数据:(每行代表该某一股票四天的股价)
X = np.array([
    [25, 27, 29, 30],
    [1, 5, 3, 2],
    [12, 11, 8, 3],
    [1, 1, 2, 2],
    [2, 6, 2, 2],
])

# 一行流:找出方差最小的股票
min_row = min([(i, np.var(X[i, :])) for i in range(len(X))], key=lambda x: x[1])
'''
内层的元组序列 (i, np.var(X[i, :]),使用列表解析对每行生成一个元组,元组中的第一个元素就是该行
的索引 i;第二个元素是该行的方差,通过把 NumPy 数组的 var() 函数和切片结合使用,就可以计算出每行
的方差,
最外层的 min() 函数会对元组序列 (i, np.var(X[i, :]) 求最小值。通过向 min() 函数的 key 参数
传入一个比较函数,该函数对于给定的序列元素,会返回一个可比较大小的对象。由于方差是元组的第二个元素,
所以需要返回 X[1] 作为比较的标准,即得到的元组列表会按照第二元素的大小从小到大进行排序。
'''

# 结果
print('最小方差的行为:' + str(min_row[0]))
print('其方差为:' + str(min_row[1]))
'''
最小方差的行为:3
其方差为:0.25
'''

9 基本统计

        本节内容将解释如何沿着指定的轴向计算平均值、标准差和方差。这三种计算非常类似,了解了一个,也就理解了另外两个。

         下面是要达成的目标:给定一个表示股票数据 NumPy 数组,其中每行代表不同公司,每列代表其每天的股价,要求你找出每家公司的均值和标准差,见下图:

        下面的代码展示了如何使用 NumPy 库来求数组的均值、方差和标准差:

import numpy as np

# 数据:二维 NumPy 数组
x = np.array([[1, 3, 5],
              [1, 1, 1],
              [0, 2, 4]])

# 求数组的均值
print(np.average(x))
# 2.0

# 求数组的方差
print(np.var(x))
# 2.4444444444444446

# 求数组的标准差
print(np.std(x))
# 1.5634719199411433

'''
二维 NumPy 数组在执行这些函数时,会先把数组转换成一维的,然后再进行运算。
'''

        下面代码展示了具体怎样沿轴向计算均值、方差和标准差。目标是在二维数组中对每个股票计算这些指标。

 

import numpy as np

# 数据:每一行代表不同公司每天的股价
x = np.array([
    [8, 9, 11, 12],
    [1, 2, 2, 1],
    [2, 8, 9, 9],
    [9, 6, 6, 3],
    [3, 3, 3, 3],
])

# 一行流:沿轴1(即每行) 计算每家公司股票的均值、方差和标准差
avg, var, std = np.average(x, axis=1), np.var(x, axis=1), np.std(x, axis=1)
'''
参数 axis 指定沿着那条轴的方向计算均值、方差和标准差。axis=1 表示会把每行聚合为一个单独的值。
'''

# 结果
print('均值:' + str(avg))
print('方差:' + str(var))
print('标准差:' + str(std))
'''
均值:[10.   1.5  7.   6.   3. ]
方差:[2.5  0.25 8.5  4.5  0.  ]
标准差:[1.58113883 0.5        2.91547595 2.12132034 0.        ]
'''

10 支持向量机(SVM)分类

        支持向量机具有强大的分类能力,即使在高维空间中也是如此。即使维度(特征)比数据项还多,支持向量机仍能工作。对对于分类算法来说是非同寻常的,原因四所谓的维度诅咒——随着维度的增加,数据变得及其稀疏,这使得算法很难从数据集中寻找模式。

        分类算法的工作原理:使用训练数据找到一个决策边界,将一类数据与另一类数据划分开来。

        下图展示了一个一般化的分类器例子:

         上图展示了由一些用户组成的训练数据,根据逻辑和创造力两方面的技能,已经对这些用户进行了分类。有些人逻辑能力强,创造力相对弱——被分类到计算机科学家;有些人创造力强,逻辑能力相对弱——被分类到艺术家。

        为了对新用户进行分类,机器学习模型必须找到一个决策边界,以区分计算机科学家和艺术家。大致来说,应根据用相对于决策边界的位置对其进行分类。在这个例子中,把落入左边区域的用户分为计算机科学家,落入右边区域的永辉分为艺术家。

        在二维空间中,决策边界是一条直线(线性分类器),或者是一条(高阶)曲线(非线性分类器)。这里只探讨线性分类器。

        上图还展示了三种决策边界,它们都是有效的数据分隔线。在该例子中,不可能量化地确定哪个决策边界更好,因为它们对训练数据进行分类时都能提供完美的准确性。

        那么最佳的决策边界是什么呢?支持向量机为这个问题提供了一个漂亮而独特的答案——提供最大安全间隔的决策边界,可以认为是最佳决策边界。换句话说,SVM 可以最大化离决策边界最近的数据点与它的距离。这样做的目的是为了使那些接近决策边界的新数据的分类误差最小化。

        下图展示了一个支持向量机分类的例子:

         SVM 分类器会找到对应的支持向量,让支持向量之间的区域尽可能的厚。在该例子中,支持向量就是与决策边界相平行的两条虚线上的那几个数据点。这两条虚线之间的区域被称为间隔(margin),决策边界选用与间隔边界最远的、位于中间的直线,也就是最大化了间隔区域及支持向量与决策边界的距离,同时最小化了新数据点分类时的误差区域。这种思想在许多实际问题中都表现出了很高的分类准确度。

        下面看看如何使用 Python 来实现 SVM 分类:

from sklearn import svm
import numpy as np

# 数据:学生成绩(数学,语言,创造力) --> 学习专业
x = np.array([
    [9, 5, 6, 'computer science'],
    [10, 1, 2, 'computer science'],
    [1, 8, 1, 'literature'],
    [4, 9, 3, 'literature'],
    [0, 1, 10, 'art'],
    [5, 7, 9, 'art'],
])

# 一行流:建立 SVM 模型
svm = svm.SVC().fit(x[:, :-1], x[:, -1])
'''
因为数据是三维的,SVM 会使用(线性的)二维平面而不是一维直线对数据进行分隔。
该行代码,首先通过 svm.SVC 类(SVC 代表支持向量分类)创建出模型,然后调用 fit()
函数,基于数据 x 对其进行训练。
'''

# 结果
print(svm.predict([[3, 3, 6], [8, 1, 1]]))
# ['art' 'computer science']

11 随机森林分类

        如果算法预测准确率还不够,但需要不惜一切代价都要完成任务,则可以使用一个快糙猛的方法——元学习(也叫集成学习),它结合了多种机器学习算法的预测(或分类)方法,或许会在最后一刻给你一个满意的结果,

        不同的算法会有不同的优势,如神经网络分类器可以为复杂的问题输出极佳的结果,但正是因为具有在极细粒度下记忆数据模式的强大能力,它们也容易对数据进行过度拟合。在实际中,往往事先无法知道,究竟哪种机器学习算法的效果最好,而使用集成学习来进行分类,可以部分地克服这个问题。

        为什么呢?集成学习实际上创建的是由多种类型或多个事例的基本机器学习算法组合而成的元分类器。换句话说,训练多个模型,对一个单独的观测值进行分类时,你让所有的模型独立地对这个输入分类。对每一个给定输入,取返回频率最高的分类作为元预测。这就是集成学习算法的最终输出。

        随机森林是集成学习算法中的一种特殊类型,它专注于决策树学习,一座森林是由许多树组成的,同样,一个随机森林由许多决策树构成。每个决策树都是通过在训练阶段生成树的过程中注入随机性(如树的节点选择顺序)而得到的。这将导致各种各样的决策树。

        下图展示了一个训练好的随机森林是如何在下面的场景中进行预测的。给定条件为 Alice 的数学和语言技能很强。使用由三个决策树(构成了一个随机森林)的集成对 Alice 进行分类,每个决策树单独计算 Alice 的类别后,其中两个都把 Alice 分类为 CS(计算机科学家)。由于这个类被得到了最多的投票数,它将作为分类的最终结果被输出。

         下面的代码展示了在 Python 中如何实现随机森林分类算法:

from sklearn.ensemble import RandomForestClassifier
import numpy as np

# 数据:学社成绩(数学,语言,创造力) --> 学习专业
x = np.array([
    [9, 5, 6, 'computer science'],
    [5, 1, 5, 'computer science'],
    [8, 8, 8, 'computer science'],
    [1, 10, 7, 'literature'],
    [1, 8, 1, 'literature'],
    [5, 7, 9, 'art'],
    [1, 1, 6, 'art'],
])

# 一行流:创建随机森林分类器模型
forest = RandomForestClassifier(n_estimators=10).fit(x[:, :-1], x[:, -1])
'''
该行代码使用 RandomForestClassifier 类的构造函数来创建一个随机森林,通过参数 n_estimators
定义了决策树的输目。
接下来,调用 fit() 来填充前面构造函数生成的模型(一个空的森林),其中,输入训练数据由数组 x 中
除最后一列外的所有数据组成,训练数据的标注则定义在最后一列。
'''

# 结果:对新数据进行分类
print(forest.predict([
    [8, 6, 5],
    [3, 7, 9],
    [2, 2, 1],
]))
# ['computer science' 'literature' 'art']
'''
注意,结果是不确定的(多次执行的结果可能不一样),因为随机森林算法依赖随机数生成器,它在不同的时刻
会返回不同的随机数。你可以使用整数类型参数 random_state 使返回值变成确定的。例如
    RandomForestClassifier(n_estimators=10, random_state=1)
这样,每次创建一个新的随机森林分类器时,都会产生相同的输出结果,因为生成的随机数是相同的,都基于随
机种子 1。
'''

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值