大白话 5 分钟带你走进人工智能 - 第九节梯度下降之函数最优化算法和梯度下降代码过程解析 (4)

第九节梯度下降之函数最优化算法 (4)

上一节中我们介绍了梯度下降的两种方式,批量梯度下降和随机梯度下降的两种方式,介绍了其具体的梯度下降的方式。本节的话,我们介绍一种函数最优化的算法。以后一听到函数最优化算法,就理解它就是一个工具,交给它一个函数,我给你找到它的最小值在哪。只有函数有最小值,才会有这种梯度下降的方式。

所以我们引入 局部最优解和全局最优解 。虽然对于之前讲的线性回归,以及未来讲的逻辑回归来讲,它只有一个最小值,但是对于深度神经网络来说,它是有很多个极小值的。那么有这么多的极小值,对于每一个极小值,我们都叫一个 local 最小值,局部最小值。而在 最深的谷底叫做全局最小值。

那么 梯度下降能找到全局最优解吗?不一定。对于凸函数来说,通过梯度下降法只能找到一个最优解。即全局最优解,如果函数并不是凸函数,那么函数定义域上存在多个最优解,使用梯度下降算法找到的最优解并不能保证是全局最优解,这样的解就有可能是局部最优解 。这个是没有办法突破的一个问题,所以在非凸函数这种有多个局部最优解的函数中,使用任何咱们现在已知的这种梯度下降的这种方式,并且可以实践的,找全局最优解的方式要么就是计算复杂度太高,要么就是压根找不到。

那该怎么办呢,其实可以通过你 多随机几次 ,你看它能找到谷底,完全取决于它上来初始化的位置在哪了,所以通常会使用多次初始化的方式,比如我初始化五次,我得到五个局部最优解,你拿最小的局部最优解来用,相对来说你至少没找到最差的一个局部最优解。通过更合理的初始化方式,是一个让它更大概率的落在比较深的谷底,这个也是咱们在学深度学习的时候要学的一些内容,目的也是为了防止梯度弥散梯度消失。所以多次随机之后找到的局部最优解也不是一个不可以接受的这么一个结果。但对于这种梯度下降法,它是还有一个缺陷的,比如下图:

20190318203941223.png

你通常认为在一个凸函数的时候, 越平缓的地方离最优解越近 ,但是有一些函数它会有这个 plateau(高原)。比如青藏高原就是它明明很高,但是它又很平,这会儿梯度下降就会陷入一个坑里,走不动了,所以梯度下降,对于这种情况也是它的弱项。我们看下梯度下降的代码实现:


import numpy as np
from sklearn.linear_model import LinearRegression
#构造 100个x

X = 2 * np.random.rand(100, 1)
# 模拟出100个y
y = 4 + 3 * X + np.random.randn(100, 1)
# 实例化出一个 线性回归器 是个对象
lin_reg = LinearRegression()
lin_reg.fit(X, y)
print(lin_reg.intercept_, lin_reg.coef_)

X_new = np.array([[0], [2]])
print(lin_reg.predict(X_new))

解释下上面代码:X = 2 * np.random.rand(10000, 1) 构造一百个 X。y = 4 + 3 * X + np.random.randn(10000, 1) 在干什么?模拟出一百个 Y。咱们从 import ,sklearn.linear_model import LinearRegression 导入线性回归。所有的 sklearn 里边的这种回归分类的这种东西,这种工具基本都是这么用的。就是首先通过 LinearRegression 这个类创建出一个对象,相当于实力化出一个线性回归器,然后这些对象有它自己的属性,其中就有一个 fit,这是它的成员函数 fit,fit 源码如下:


def fit(self, X, y, sample_weight=None)

这个函数,需要你传进一个 X,传进一个 Y,还可以传一个 sample_weight 代表这条数据的重要性,你可以去把一些数据给设的重要一些,设的不重要一些,通常不传它。那么一个 X,传一个 Y,按理说你想你训练模型就丢给这个算法一个训练集,然后就可以了,我们 fit 完了之后,在这个对象里面会产生两个新的属性,一个叫 intercept, 代表截距, 一个叫 coefficient,代表系数 ,其中 intercept 是 B 或者 W0, coefficient 所有的 W。

问:对于咱们这个例子里面有几个 intercept? 对于任何例子中有几个 intercept?coefficient 有几个?

答:intercept 都是只有一个,coefficient 在这个例子里是一个,在其它的例子里面是你 X 有多少列就会有多少个 W,它会把所有的 coefficient 按照对应的 W 给列出来。我们先运行一下代码,得出结果是截距(intercept)是 3.72 和参数 (coefficient 是)3.2,跟我们原始的 4 和 3 差不太多是吧?因为我们只有一百个 X 而且还是一样的,那么接下来 当来一条新数据怎么预测? 实际上我们就是用 lin_reg, 就是我通过 LinearRegression 这个类创建的对象,并且训练完了之后,对象本身模型就存在这个对象里了。你调用它的 predict 函数再把新的数据丢进去,它就会把新的数据的预测结果给返回来,那么


X_new = np.array([[0], [2]])

这里面传了几条数据进去?传的是一条还是两条?实际上是两条,虽然受限于咱们代码的书写方式,它二维数组就是矩阵,实际上它输入的是这么一个东西。

\begin{bmatrix} \[0\]\\ \[2\] \end{bmatrix}

两行一列的矩阵。代表是两条数据,而每条数据因为只有一个维度。对于 0,它的预测结果其实就是 0×3.2+3.72,预测结果应该是 3.72。而对于 2 的预测结果就应该是 2×3.21+3.72。

问:predict 快还是 fit 快?

答:应该是 fit 快,按理说,任何模型都会发生这两步,先 predict 再 fit. 当我再运行一遍模型时,模型结果又变了,为什么变了?因为你随机产生的训练集变了。加上如下代码, 放在构建 X 之前。


np.random.seed(1)

seed(1) 里面我随便写个数进去,这里写成 1,以后我每次执行这代码运算的都是不变。这个就是你在调参的时候都会把这个东西给定死,否则你不知道是你参数影响你模型变好了,还是训练过程中的一些随机过程影响你这个结果变好了。通常会愿意把它定死。

问:这个背后到底是用的解析解求的还是用的梯度下降求得呢?

答:我们知道梯度下降一般会有超参数,那么我们的代码里超参数又在哪?你发现你在 fit 的过程中,是不是只让你传了 X 和 Y,没有传其他参数的地方了?实际上在你创建对象的时候,会有一些超参数,在 lin_reg 里面它基本上没有让我们去设置额外的超参数,但是在其他的算法里面是有的。lin_reg 的源码:


def __init__(self, fit_intercept=True, normalize=False, copy_X=True,n_jobs=1):

fit_intercept,是否要把截距给加上? 如果你把它置为 false,你训练出来就一个 W,什么样的情况下会和现在情况等价?当把你训练集通通的加一个 W0 等于 1,这样你再设成 false 和 fit_intercept 直接设成 true 是一模一样的结果。

问:无论是解析解,还是梯度下降。重运算量的事物在哪?

答:肯定是 fit,predict 只是简单的一个相乘相加就完事了,这个是我们想要的机器学习架构,有一些算法是反过来的,它 fit 阶段就把它存下来就好了,predict 阶段才会去计算。比如 KNN 算法,它实际上是一个分类算法,来一个新的数据,它去比较跟你训练集上的每一条数据谁更像?离谁更像哪条数据,你想它 fit 阶段做了什么? 什么事也没干。直到你拿到这条新数据之后,你才能去逐条的跟每一个原来的过去的训练集里的数据去比较。所以它的大部分运送量是不是都在 predict 的阶段?所以实际上 predict 阶段非常的冗余,那么为什么我们不想要这种情况?想一下,你训练模型的架构,是有具备大规模算力的系统,是在你的中心节点上训练的,而客户端使用你的系统,使用你的 AI 的成果,实际上是使用你提前训练好的模型。比如国产手机前置 AI 双摄拍人更美功能,它使用的时候,实际上保存在手机本地的就是一组权重值,是你提前训练好的模型预存在你手机里的,而不是需要你先拍一千张自己的照片越拍越美。它是提前已经训练好冻结住的模型,它认为最合适了,预制到手机里是一组权重,那么手机要干的什么?其实就是一个类似相乘相加的工作,把它给都搞出来,所以我们希望我们的训练架构苦多累没关系,你让我用的时候能达到实时运算就可以了,要做到 尽量把大规模的运算往中间节点放,提前准备好模型。

截止目前为止,你已经对人工智能应用有一个相对的自己的概念了,比如前阵 AlphaGo 火爆所有媒体眼球的时候,大家想象中的就是计算机很厉害,它再在你下一步棋的时候,其实它已经把所有的可能性想象到了,这个是媒体的脑洞,咱们的算力根本不可能达到,让它把所有的步骤算出来,远远达不到,你想的容易觉得计算机运算的快,它运算的再快,随着指数级上升,这个东西很快它也就饱和了,能理解吗?AlphaGo 找到的也都是一系列的局部最优解,远没有那么暴力,咱们的计算机的算力远没有那么暴力,然后另外一个就是 AlphaGo 知不知道它在下围棋? 其实真的不知道。计算机永远是一个大的计算器,至少在当前通用型人工智能没有实现之前,咱们现在的人工智能,你会发现它是一个更高效的帮你算数的机器,而更智能的应用是从深度神经网络开始的,深度神经网络所谓的智能,也不是说它真的有思想了,而只是中间它的隐藏层能自动帮你组合出新的特征来,这新的特征什么意思?人类已经没法理解了,它就是一种纯粹的数值上的特征。

下一节中我们将讲解归一化的各种方式和必要性。