![4847d064ddcd7cba6ec636a002b88684.gif](https://img-blog.csdnimg.cn/img_convert/4847d064ddcd7cba6ec636a002b88684.gif)
前言
在上一章中我们了解了线性回归求最小值的方法(损失函数最小化),直接对其求导,即正规方程方法。 但是在机器学习中,像线性回归这样可以直接用数学公式推导出最小值的算法是很少的,绝大多数的损失函数是很复杂的,所以我们来介绍一种更为通用,也是机器学习中很重要的一种优化方法,即梯度下降法。 梯度下降法是机器学习中的一种优化算法,并非机器学习算法。 梯度简单的可以理解为多元函数的导数,多元函数沿梯度方向函数值变化最快,所以我们只要沿着梯度的负方向搜索,就会找到函数的低谷。梯度下降法
原理
场景假设: 梯度下降就是从群山的山顶中找一条最短的路走到山谷最低的地方 。 所以首先需要选择一个方向,其次要沿着这个方向每走一步都要选择最陡的方向(函数值变化最快),直至走到最低点。由于在算法中,方向的选择都是随机选取,所以我们往往走到的点并非是最低点,如下所示:
![418721dcc64494d7404f1d399481db13.png](https://img-blog.csdnimg.cn/img_convert/418721dcc64494d7404f1d399481db13.png)
![ea1f020d55653b1d71a0c0aa5360b82d.png](https://img-blog.csdnimg.cn/img_convert/ea1f020d55653b1d71a0c0aa5360b82d.png)
为什么梯度方向就是函数变化最快的方向
在一元函数中,我们知道导数在几何意义上代表函数的切线斜率,物理意义上代表函数的变化率。导数的公式如下:
![27ed03cb92737870348b49f78c62e223.png](https://img-blog.csdnimg.cn/img_convert/27ed03cb92737870348b49f78c62e223.png)
![66e57dfc30f356eddb0f691ea4b10117.png](https://img-blog.csdnimg.cn/img_convert/66e57dfc30f356eddb0f691ea4b10117.png)
模拟实现
我们知道了梯度就是对多个参数进行求导,所以针对一元函数来说,自变量的迭代公式如下 :![64abfe56791058472983115048ce0925.png](https://img-blog.csdnimg.cn/img_convert/64abfe56791058472983115048ce0925.png)
首先我们构造一个损失函数
![f3ebaa8b0d9918d04965a96e015877e1.png](https://img-blog.csdnimg.cn/img_convert/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](https://img-blog.csdnimg.cn/img_convert/25a8413a5601babf751f55f54582f534.png)
#求导示例
derivative(lossFunction,3) #参数1是函数 参数2是变量
#手动计算导数为 2x-5
#ou:1
在开始计算的时候,我们需要注意以下几点:
我们在梯度下降的时候,每下降一步,函数值的变化程度就在缩小,直到我们下降到某一步的时候,与上一次的函数值变化小于一个很小很小的值时,我们便认为已经找到了最小值。
上面说到我们下山的时候会选择一个方向,这里也就是我们theta的初始值,一般我们这里是随机选取的,可以先置为0
最后我们下山的时候还需要一个步长,步长不能太大,也不能太小,可以先设置为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](https://img-blog.csdnimg.cn/img_convert/ccf993823f1cf908a0c7c78634b87bbe.png)
线性回归中的梯度下降法
根据之前在线性回归中说到的( 机器学习入门系列之线性回归 ),线性函数中的损失函数是![8f692cf75999f5751c9265fc490b76f9.png](https://img-blog.csdnimg.cn/img_convert/8f692cf75999f5751c9265fc490b76f9.png)
![3d2d0d41b44b36ecafa2deced92d62e1.png](https://img-blog.csdnimg.cn/img_convert/3d2d0d41b44b36ecafa2deced92d62e1.png)
![51429cbec44e8f6027524573145f605b.png](https://img-blog.csdnimg.cn/img_convert/51429cbec44e8f6027524573145f605b.png)
![c008e5d7814fe902f60d62e2b9f446b0.png](https://img-blog.csdnimg.cn/img_convert/c008e5d7814fe902f60d62e2b9f446b0.png)
![907abdbedcc39d00cd3f3b518cde0151.png](https://img-blog.csdnimg.cn/img_convert/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](https://img-blog.csdnimg.cn/img_convert/e8bab911f5bbae2cdae448c73956226d.png)
![4887bbacc862f37908cf205d61db4025.png](https://img-blog.csdnimg.cn/img_convert/4887bbacc862f37908cf205d61db4025.png)
![6e641c6d8c77350c9b07153a69e18a61.png](https://img-blog.csdnimg.cn/img_convert/6e641c6d8c77350c9b07153a69e18a61.png)
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
总结
梯度下降法是机器学习、深度学习中解决最优化问题使用最广最核心的方法梯度下降,是一种基于搜索的最优化方法,其作用是用来对原始模型的损失函数进行优化,找到使损失函数(局部)最小的参数。
梯度下降法又分为批量梯度下降法和随机梯度下降法
批量梯度下降法:每一次都要对所有样本进行计算。优点:可以获取全局最优解,并且易于并行实现;缺点:当样本数据量很多时,性能慢
随机梯度下降法:每次只对一个样本进行计算。优点:计算速度快;缺点:收敛性能不好。
线性回归中梯度下降法的好处:梯度下降法在数据量大,特征数多的时候,性能会比正规方程好很多。