一、梯度下降法简介
梯度下降法是一种常用的优化算法,可以用于求解函数的最小值,尤其适用于机器学习中的参数优化问题。其基本思想是通过迭代地调整参数值,使得目标函数的值逐渐变小,直到满足一定的收敛条件。
二、梯度下降法原理
梯度下降法的核心在于求解函数的梯度向量,该向量指示了函数在某一点上升最快的方向,即梯度的反方向是函数下降最快的方向。因此,通过迭代地更新参数值,可以沿着梯度的反方向逐渐接近函数的最小值。
设$f(x)$是一个实数值函数,$x$是一个$n$维向量,则该函数在$x$点的梯度向量为:
$$nabla f(x) = begin{pmatrix} frac{partial f(x)}{partial x_1}\ frac{partial f(x)}{partial x_2}\ ...\ frac{partial f(x)}{partial x_n}end{pmatrix}$$
其中,$frac{partial f(x)}{partial x_i}$表示函数$f(x)$对第$i$个变量的偏导数。
梯度下降的更新公式为:
$$x_{t+1} = x_t - alpha nabla f(x_t)$$
其中,$x_t$表示第$t$次迭代的参数值,$alpha$表示步长,即每次迭代调整参数值的幅度。
三、梯度下降法步骤
1. 数据准备
在使用梯度下降法求解函数最小值之前,需要先准备好用于计算函数值和梯度的数据。具体来说,需要确定函数的形式以及函数参数的维度,以及使用哪些数据进行参数学习。
例如,假设我们要求解函数$f(x) = x_1^2 + x_2^2$的最小值,其中$x=(x_1,x_2)$,则可以定义如下的数据结构:
import numpy as np
from typing import Dict, Tuple
def prepare_data() -> Tuple[Dict[str, np.ndarray], np.ndarray]:
x = np.random.randn(100, 2)
y = np.square(x[:, 0]) + np.square(x[:, 1])
data = {"X": x, "y": y}
return data, y
2. 定义目标函数
在准备好数据后,需要定义目标函数,即需要优化的函数。在上述例子中,目标函数为$f(x) = x_1^2 + x_2^2$。需要注意的是,目标函数必须是可微的,才能使用梯度下降法进行优化。
def target_function(theta: np.ndarray, X: np.ndarray, y: np.ndarray) -> float:
diff = np.matmul(X, theta) - y
mse = np.sum(diff ** 2) / len(y)
return mse
3. 计算梯度
在定义好目标函数后,就可以计算函数的梯度向量了。对于一维函数,梯度向量即为函数的导数;对于多维函数,梯度向量为各个偏导数组成的向量。
def gradient(theta: np.ndarray, X: np.ndarray, y: np.ndarray) -> np.ndarray:
diff = np.matmul(X, theta) - y
grad = np.matmul(diff, X) / len(y)
return grad
4. 迭代优化
在完成数据准备、目标函数定义和梯度计算后,就可以开始利用梯度下降法进行迭代优化。具体地,需要设定初始参数值和迭代次数,然后不断计算梯度并更新参数值,直到满足一定的停止条件。
def gradient_descent(X: np.ndarray,
y: np.ndarray,
theta: np.ndarray,
alpha: float = 0.1,
max_iters: int = 1000,
eps: float = 1e-6) -> Tuple[np.ndarray, float, np.ndarray]:
loss_history = []
for i in range(max_iters):
grad = gradient(theta, X, y)
loss = target_function(theta, X, y)
loss_history.append(loss)
if np.linalg.norm(grad) < eps:
break
theta = theta - alpha * grad
return theta, loss_history[-1], np.array(loss_history)
5. 执行优化
最后,调用gradient_descent函数进行优化,得到最优的参数值。
data, y = prepare_data()
X = np.hstack((data["X"], np.ones((data["X"].shape[0], 1))))
theta = np.zeros(X.shape[1])
theta, loss, loss_history = gradient_descent(X, y, theta, alpha=0.1)