3. 机器学习中为什么需要梯度下降_机器学习入门系列之梯度下降

4847d064ddcd7cba6ec636a002b88684.gif

前言

在上一章中我们了解了线性回归求最小值的方法(损失函数最小化),直接对其求导,即正规方程方法。 但是在机器学习中,像线性回归这样可以直接用数学公式推导出最小值的算法是很少的,绝大多数的损失函数是很复杂的,所以我们来介绍一种更为通用,也是机器学习中很重要的一种优化方法,即梯度下降法梯度下降法是机器学习中的一种优化算法,并非机器学习算法。 梯度简单的可以理解为多元函数的导数,多元函数沿梯度方向函数值变化最快,所以我们只要沿着梯度的负方向搜索,就会找到函数的低谷。

梯度下降法

原理

场景假设: 梯度下降就是从群山的山顶中找一条最短的路走到山谷最低的地方 。 所以首先需要选择一个方向,其次要沿着这个方向每走一步都要选择最陡的方向(函数值变化最快),直至走到最低点。
由于在算法中,方向的选择都是随机选取,所以我们往往走到的点并非是最低点,如下所示: 418721dcc64494d7404f1d399481db13.png 如上图,假如我们是从右上方的红点开始下山,当到达第一个山谷后,我们会发现这个点比周围的都小,所以就是走到了山谷,但其实还有更低的山谷。 我们一般将下山的步长记为η (又称为学习率),当η 太小时,我们下山的速度就会慢(收敛速度慢),但是当η太大时,我们很容易错过最低点,如下图所示: ea1f020d55653b1d71a0c0aa5360b82d.png

为什么梯度方向就是函数变化最快的方向

在一元函数中,我们知道导数在几何意义上代表函数的切线斜率,物理意义上代表函数的变化率。
导数的公式如下: 27ed03cb92737870348b49f78c62e223.png 66e57dfc30f356eddb0f691ea4b10117.png 直白的来说,导数代表了在自变量变化趋于无穷小的时候,函数值的变化与自变量变化的比值代表了导数。 我们发现 导数越大,即斜率越大,函数变化率越大 。 将一元函数扩充为多元函数,我们会发现,平面上某一个点的切线可能不止一条,所以我们要对各个方向求导数,这时候某一个方向的导数我们称为方向导数。 此时我们把是函数变化最快的方向称为梯度,所以梯度的方向就是函数变化最快的方向。 细节公式可参考 为什么梯度反方向是函数下降最快的方向?

模拟实现

我们知道了梯度就是对多个参数进行求导,所以针对一元函数来说,自变量的迭代公式如下 : 64abfe56791058472983115048ce0925.png 我们来模拟实现一下:
首先我们构造一个损失函数 f3ebaa8b0d9918d04965a96e015877e1.png 有了函数,可以构造数据,我们来看一下:
import numpy as npimport matplotlib.pyplot as pltfrom scipy.misc import derivativedef lossFunction(x):    return (x-2.5)**2-1# 在-1到6的范围内构建140个点plot_x = np.linspace(-1,6,141)# plot_y 是对应的损失函数值plot_y = lossFunction(plot_x)plt.plot(plot_x,plot_y)plt.show()

25a8413a5601babf751f55f54582f534.png 然后我们对其求导,求导我们这里使用scipy库中的derivative方法
#求导示例derivative(lossFunction,3) #参数1是函数 参数2是变量#手动计算导数为 2x-5#ou:1
在开始计算的时候,我们需要注意以下几点:
  1. 我们在梯度下降的时候,每下降一步,函数值的变化程度就在缩小,直到我们下降到某一步的时候,与上一次的函数值变化小于一个很小很小的值时,我们便认为已经找到了最小值。

  2. 上面说到我们下山的时候会选择一个方向,这里也就是我们theta的初始值,一般我们这里是随机选取的,可以先置为0

  3. 最后我们下山的时候还需要一个步长,步长不能太大,也不能太小,可以先设置为0.1

theta = 0.0  #方向eta = 0.1    #步长epsilon = 1e-6  #阈值while True:    # 每一轮循环后,要求当前这个点的梯度是多少    gradient = derivative(lossFunction,theta)    last_theta = theta    # 移动点,沿梯度的反方向移动步长eta    theta = theta - eta * gradient    # 判断theta是否达到最小值    # 因为梯度在不断下降,因此新theta的损失函数在不断减小    # 看差值是否达到了要求    if(abs(lossFunction(theta) - lossFunction(last_theta)) < epsilon):        breakprint(theta)print(lossFunction(theta))#OUT#2.4987323493997717#-0.9999983930619557

这里已经找到了最小值,我们可以将其图形化,来看到是怎么找到的最小值。
def gradient_descent(initial_theta, eta, epsilon=1e-6):    theta = initial_theta    theta_history.append(theta)    while True:        # 每一轮循环后,要求当前这个点的梯度是多少        gradient = derivative(lossFunction,theta)        last_theta = theta        # 移动点,沿梯度的反方向移动步长eta        theta = theta - eta * gradient        theta_history.append(theta)        # 判断theta是否达到损失函数最小值的位置        if(abs(lossFunction(theta) - lossFunction(last_theta)) < epsilon):            breakdef plot_theta_history():    plt.plot(plot_x,plot_y)    plt.plot(np.array(theta_history), lossFunction(np.array(theta_history)), color='red', marker='o')    plt.show()eta=0.1theta_history = []gradient_descent(0., eta)plot_theta_history()print("梯度下降查找次数:",len(theta_history))
ccf993823f1cf908a0c7c78634b87bbe.png 我们可以调节η 的大小来控制查找次数,但是如果η 如果太大,会造成函数不收敛,甚至内存溢出。

线性回归中的梯度下降法

根据之前在线性回归中说到的( 机器学习入门系列之线性回归 ),线性函数中的损失函数是  8f692cf75999f5751c9265fc490b76f9.png 针对损失函数每一项求偏导,会得到如下结果: 3d2d0d41b44b36ecafa2deced92d62e1.png 需要对我们的目标函数做进一步处理,因为我们求得的梯度是m项的求和,这里梯度的大小就跟样本数量有关,样本数量很大时,梯度就很大。为了将梯度与样本数量无关,需要对求的梯度除以m 。 51429cbec44e8f6027524573145f605b.png 此时我们再看目标函数,就是 c008e5d7814fe902f60d62e2b9f446b0.png 我们会发现这个公式特别熟悉,对,就是机器学习入门系列之评价模型的好坏 这里说到的 均方误差MSE。 在实现前,我们需要对损失函数进行向量化,对第一项加上常数项X0后,可得到以下结果: 907abdbedcc39d00cd3f3b518cde0151.png

模拟实现

在这里我们用波士顿房价对线性回归用梯度下降法进行计算。
class LinearRegression:    def __init__(self):         #截距,即theta_0        self.intercept = None        #系数,即theta_i        self.coef = None        #系数向量,即theta        self.theta = None    def fit_normal(self, X_train, y_train):        #给截距(首列)增加向量,使其为恒为1        X_b = np.hstack([np.ones((len(X_train), 1)),X_train])        #根据正定方程解编写        self.theta = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y_train)                self.intercept = self.theta[0]        self.coef = self.theta[1:]        return self    def fit_gd(self, X_train, y_train, eta=0.01, n_iters=1e4):        """使用梯度下降法训练Linear Regression模型"""        def J(theta, X_b, y):            """损失函数"""            return np.sum((y - X_b.dot(theta)) ** 2) / len(y)        def dJ(theta, X_b, y):            """对损失函数求导"""            return X_b.T.dot(X_b.dot(theta) - y) * 2. / len(y)        def gradient_descent(X_b, y, initial_theta, eta, n_iters=1e4, epsilon=1e-8):            """求梯度"""            theta = initial_theta            cur_iter = 0            while cur_iter < n_iters:                gradient = dJ(theta, X_b, y)                last_theta = theta                theta = theta - eta * gradient                #如果迭代之差满足最小阈值,则跳出                if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):                    break                cur_iter += 1            return theta        X_b = np.hstack([np.ones((len(X_train), 1)), X_train])        initial_theta = np.zeros(X_b.shape[1])        self.theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters)        self.intercept_ = self.theta[0]        self.coef_ = self.theta[1:]        return self    def predict(self, X_predict):        X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict])        return X_b.dot(self.theta)         def score(self, X_test, y_test):        """根据测试数据集 X_test 和 y_test 计算当前模型的R Square值"""        y_predict = self.predict(X_test)        return 1 - (np.sum((y_test - y_predict)**2) / len(y_test))/np.var(y_test) import numpy as npfrom sklearn import datasetsfrom sklearn.model_selection import train_test_splitboston = datasets.load_boston()X = boston.datay = boston.targetX = X[y < 50.0]y = y[y < 50.0]X_train, X_test, y_train, y_test = train_test_split(X, y)        # 使用均值方差归一化对数据进行处理from sklearn.preprocessing import StandardScalerstandardScaler = StandardScaler()standardScaler.fit(X_train)X_train_standard = standardScaler.transform(X_train)# 使用梯度下降计算lin_reg1 = LinearRegression()lin_reg1.fit_gd(X_train_standard, y_train)X_test_standard = standardScaler.transform(X_test)lin_reg1.score(X_test_standard, y_test)

随机梯度下降法

上面的梯度下降法的计算,又称为批量梯度下降法,每一次都要对所有的样本进行计算。 这样在样本数据量特别大时,计算梯度本身也是比较耗时的。 所以引入随机梯度下降法。即每次从样本中取一个进行梯度计算,作为搜索方向。 图1为批量梯度下降法,图2是随机梯度下降法。在应用中,通常会允许损失一定的精度换取时间性能。 e8bab911f5bbae2cdae448c73956226d.png 4887bbacc862f37908cf205d61db4025.png 不过随机梯度下降法需要确定在学习时,学习率要递减,这样才不会错过最低点 所以我们一般设置学习率η 的函数为: 6e641c6d8c77350c9b07153a69e18a61.png 其中i_iters是循环次数,保证学习率是随着循环次数的增多而递减的,a和b是两个控制学习率递减的超参数。

Sklearn实现

在上面的代码中,我们设置了阈值和循环次数,所以只是考虑了部分样本,遗漏了一些样本。 在实际中,往往需要将所有的样本至少都看一遍,所以Sklearn内部实现和我们以上程序略有区别,特别是将n_iters参数用做是指对样本看几遍。
#Slklearn中 线性回归的随机梯度下降from sklearn.linear_model import SGDRegressorsgd_reg = SGDRegressor(n_iter=50)sgd_reg.fit(X_train_standard, y_train)sgd_reg.score(X_test_standard, y_test)# Out:0.7943781493094382
 

总结

  1. 梯度下降法是机器学习、深度学习中解决最优化问题使用最广最核心的方法梯度下降,是一种基于搜索的最优化方法,其作用是用来对原始模型的损失函数进行优化,找到使损失函数(局部)最小的参数。

  2. 梯度下降法又分为批量梯度下降法和随机梯度下降法

    批量梯度下降法:每一次都要对所有样本进行计算。优点:可以获取全局最优解,并且易于并行实现;缺点:当样本数据量很多时,性能慢

    随机梯度下降法:每次只对一个样本进行计算。优点:计算速度快;缺点:收敛性能不好。

  3. 线性回归中梯度下降法的好处:梯度下降法在数据量大,特征数多的时候,性能会比正规方程好很多

321c0d0f0d0040ded0403530aba2d46a.png

 往期推荐  ? 机器学习入门系列之KNN 机器学习入门系列之线性回归 机器学习入门系列之评价模型的好坏 机器学习入门系列之特征处理

a68136ee268b4054d8de66e2c8e521d9.png

58f59d39192b349cfad4b8b767fcd3d0.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值