梯度下降法(Gradient Descent)
的作用是最小化一个损失函数。梯度上升则是最大化一个效用函数。
- 梯度
在微积分里面,对多元函数的参数求∂偏导数,把求得的各个参数的偏导数以向量的形式写出来,就是梯度。比如函数f(x,y), 分别对x,y求偏导数,求得的梯度向量就是(∂f/∂x, ∂f/∂y)T,简称grad f(x,y)或者▽f(x,y)。对于在点(x0,y0)的具体梯度向量就是(∂f/∂x0, ∂f/∂y0)T.或者▽f(x0,y0),如果是3个参数的向量梯度,就是(∂f/∂x, ∂f/∂y,∂f/∂z)T,以此类推。
那么这个梯度向量求出来有什么意义呢?他的意义从几何意义上讲,就是函数变化增加最快的地方。具体来说,对于函数f(x,y),在点(x0,y0),沿着梯度向量的方向就是(∂f/∂x0, ∂f/∂y0)T的方向是f(x,y)增加最快的地方。或者说,沿着梯度向量的方向,更加容易找到函数的最大值。反过来说,沿着梯度向量相反的方向,也就是 -(∂f/∂x0, ∂f/∂y0)T的方向,梯度减少最快,也就是更加容易找到函数的最小值。
导数可以代表J 增大的方向,如上图一点导数为负值,说明J增大的方向是沿着X轴负方向。此时需要向右移动
当导数为0时损失函数的最小。
由上图可知想要最快取到最优解,那么第一个点的取值较为重要,此时有点看运气。
线性回归中使用梯度下降法
小括号第一个值是准确的结果,第二个值是所训练模型的输出值。
模拟梯度下降法
"""模拟梯度下降法"""
import numpy as np
import matplotlib.pyplot as plt
plot_x = np.linspace(-1, 6, 141)
plot_y = (plot_x - 2.5) ** 2 - 1
plt.plot(plot_x, plot_y)
plt.show()
def dJ(theta):
"""计算导数值"""
return 2 * (theta - 2.5)
def J(theta):
"""计算损失函数的值"""
return (theta - 2.5) ** 2 - 1
theta = 0.0
theta_history = [theta]
def gradient_descent(initial_theta,eta,epsilon=1e-8):
"""模拟梯度下降法"""
theta=initial_theta
theta_history.append(initial_theta)
while True:
gradient = dJ(theta)
last_theat = theta
theta = theta - eta * gradient
theta_history.append(theta)
if abs(J(theta)-J(last_theat)) < epsilon:
break
def plot_theta_history():
plt.plot(plot_x,J(plot_x))
plt.plot(np.array(theta_history),J(np.array(theta_history)),color='r')
plt.show()
eta = 0.8
theta_history=[]
gradient_descent(0.,eta)
plot_theta_history()
如果参数过多,那么需要对每个参数求偏导,然后移动位置,使得损失函数的值尽可能的小。
将w和b对L的两个偏微分所得到的结果排成一vector,这个vector 就叫做gradient。那么倒三角L就是所有参数对L 的偏微分形成的vector。
例如有两个参数(w,b),下图的颜色代表Loss 的值,假设起始点从第一个红点开始,经过几次指向移动到loss值最小的地方。
偏微分计算的例子:
def fit_gd(self, X_train, y_train, eta=0.01, n_iters=1e4):
"""根据训练数据集X_train, y_train, 使用梯度下降法训练Linear Regression模型"""
assert X_train.shape[0] == y_train.shape[0], \
"the size of X_train must be equal to the size of y_train"
def J(theta, X_b, y):
"""求损失函数"""
try:
return np.sum((y - X_b.dot(theta)) ** 2) / len(y)
except:
return float('inf')
def dJ(theta, X_b, y):
"""求梯度"""
# res = np.empty(len(theta))
# res[0] = np.sum(X_b.dot(theta) - y)
# for i in range(1, len(theta)):
# res[i] = (X_b.dot(theta) - y).dot(X_b[:, i])
# return res * 2 / len(X_b)
return X_b.T.dot(X_b.dot(theta) - y) * 2. / len(X_b)
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