深度学习之梯度下降算法
梯度下降是一种优化算法,用于最小化一个目标函数。在机器学习中,我们通常使用梯度下降来更新神经网络的权重,以最小化损失函数。以下是对梯度下降的详细解释:
梯度是一个向量,它指向函数在某一点上的最大增加方向。梯度下降的基本思想是,从当前点出发,沿着梯度的反方向前进一定距离,直到达到一个极小值点。这种方法可以有效地降低目标函数的值,并且在深度学习中被广泛应用。
在梯度下降的过程中,我们需要计算目标函数的梯度,即导数。对于一个多元函数,它的梯度是一个向量,每个元素表示函数在相应自变量方向的偏导数。梯度下降的更新公式如下:
其中,θi\theta_iθi表示第iii次迭代时的权重参数,α\alphaα表示学习率,∇f(θi)\nabla f(\theta_i)∇f(θi)表示目标函数在θi\theta_iθi处的梯度。学习率是一个超参数,控制每次迭代更新的步长。较小的学习率会使模型收敛速度较慢,但会更准确;较大的学习率会使模型收敛速度更快,但容易超过最优值。
下面是一个使用梯度下降更新权重的Python示例代码:
import numpy as np
def gradient_descent(X, y, theta, alpha, num_iters):
"""
实现梯度下降算法,更新权重参数
:param X: 训练数据矩阵,每一行为一个训练样本,每一列为一个特征
:param y: 目标变量,每一行为一个训练样本的标签
:param theta: 权重参数
:param alpha: 学习率
:param num_iters: 迭代次数
:return: 更新后的权重参数
"""
m = len(y) # 样本数量
for i in range(num_iters):
h = X.dot(theta) # 计算预测值
loss = h - y # 计算误差
gradient = X.T.dot(loss) / m # 计算梯度
theta -= alpha * gradient # 更新权重参数
return theta
在这个例子中,我们使用了一个矩阵X来表示训练数据,每一行为一个训练样本,每一列为一个特征。矩阵y表示目标变量,每一行矩阵y表示目标变量,每一行为一个训练样本的标签。theta是一个列向量,表示权重参数。在每次迭代中,我们计算预测值h,计算误差loss,计算梯度gradient,然后使用更新公式更新权重参数theta。最后,函数返回更新后的权重参数。
需要注意的是,上面的代码中使用了向量化操作来加速计算。这种方式比使用循环计算每个样本更高效。向量化是使用梯度下降的关键,因为它可以充分利用线性代数的优势,加速计算,提高效率。
梯度下降是机器学习中最基础、最常用的优化算法之一。对于理解深度学习、神经网络等高级技术非常重要。
收敛性
梯度下降算法的收敛性是指在迭代过程中,目标函数的值是否能够逐渐减小并收敛到最小值。如果算法收敛,我们说它是收敛的;如果算法不收敛,我们说它是发散的。
在梯度下降算法中,我们通过迭代来逐步更新权重参数,从而使目标函数的值逐渐减小。如果学习率设置合理,那么梯度下降算法是一种收敛的算法,可以在有限次迭代后找到最小值。
下面是一个简单的Python实现梯度下降的代码:
def gradient_descent(X, y, theta, alpha, iterations):
m = len(y)
for i in range(iterations):
h = X @ theta
error = h - y
gradient = X.T @ error / m
theta -= alpha * gradient
return theta
其中,X是一个m×(n+1)m \times (n+1)m×(n+1)的矩阵,每一行表示一个样本的特征值,其中第一列都是1;y是一个m×1m \times 1m×1的向量,表示每个样本的真实值;theta是一个(n+1)×1(n+1) \times 1(n+1)×1的列向量,表示权重参数;alpha是学习率;iterations是迭代次数。在每次迭代中,我们计算预测值hhh,计算误差error,计算梯度gradient,然后使用更新公式更新权重参数theta。最后,函数返回更新后的权重参数。
在实际应用中,我们可以通过设置迭代次数或者设定一个收敛阈值来控制算法的收敛速度和精度。通常情况下,梯度下降算法的收敛速度和精度都可以得到保证。但是需要注意,当目标函数是非凸函数时,梯度下降算法可能会收敛到局部最优解而不是全局最优解。此时需要使用其他算法来寻找全局最优解。
可扩展性
梯度下降算法在可扩展性方面非常优秀,它可以应用于大规模数据集和高维特征空间。这是因为梯度下降算法可以通过向量化来加速计算,而且它能够处理非线性模型。
向量化是指将一组操作转换成矩阵或向量的形式,以便在单个操作中处理整个数据集。在梯度下降算法中,向量化可以用来计算梯度和更新参数,这样可以充分利用线性代数的优势,加速计算,提高效率。具体来说,可以使用以下公式来更新参数:
theta = theta - alpha * gradient
其中,theta是参数向量,alpha是学习率,gradient是梯度向量。这个公式可以同时更新所有参数,而且可以使用矩阵运算来计算梯度和参数,进一步提高计算效率。
在Python中,可以使用NumPy库来进行向量化计算。下面是一个使用梯度下降算法进行线性回归的例子:
import numpy as np
# 数据集
X = np.array([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6]])
y = np.array([3, 4, 5, 6, 7])
# 初始化参数
theta = np.zeros((X.shape[1], 1))
alpha = 0.01
num_iters = 1000
# 计算梯度和更新参数
for i in range(num_iters):
h = np.dot(X, theta)
error = h - y
gradient = np.dot(X.T, error) / len(y)
theta = theta - alpha * gradient
# 打印最终权重参数
print(theta)
在这个例子中,我们使用了NumPy库来计算矩阵乘法、转置和平均值。这个算法可以很容易地扩展到更大的数据集和更复杂的模型。因此,梯度下降算法在可扩展性方面非常优秀,被广泛应用于机器学习和深度学习领域。
学习率难以设置
梯度下降算法的学习率是一个重要的超参数,控制了每次更新权重参数的步长。如果学习率过大,可能会导致算法无法收敛或者振荡,而如果学习率过小,算法收敛速度会非常慢。因此,学习率的设置非常重要,需要根据实际情况进行调整。
下面是一个简单的梯度下降算法的代码示例,其中包含了学习率的设置:
import numpy as np
# 定义目标函数
def target_function(x):
return x**2
# 定义目标函数的导数
def derivative_function(x):
return 2*x
# 梯度下降算法
def gradient_descent(derivative_func, initial_theta, learning_rate, num_iterations):
theta = initial_theta
for i in range(num_iterations):
gradient = derivative_func(theta)
theta = theta - learning_rate * gradient
return theta
# 设置初始值和学习率
initial_theta = 1.0
learning_rate = 0.1
num_iterations = 10
# 调用梯度下降算法求解最优解
optimal_theta = gradient_descent(derivative_function, initial_theta, learning_rate, num_iterations)
# 输出最优解
print("Optimal theta:", optimal_theta)
在上面的代码中,我们定义了一个简单的目标函数 f(x)=x2f(x)=x^2f(x)=x2,以及其导数函数 f′(x)=2xf'(x)=2xf′(x)=2x。然后我们调用梯度下降算法来求解该函数的最优解。在调用梯度下降算法时,我们需要指定初始值、学习率和迭代次数。在本例中,我们将学习率设置为0.1,这是一个相对较大的值。如果我们将学习率设置得过大,比如设置为1.0,那么算法可能会无法收敛,因为步长过大。如果我们将学习率设置得过小,比如设置为0.01,那么算法收敛速度会非常慢,需要更多的迭代次数才能达到最优解。
因此,学习率的设置需要根据具体的问题进行调整,需要进行多次实验来确定最佳的学习率。通常情况下,可以从一个较小的学习率开始,然后逐步增大学习率,直到达到最优解为止。
容易陷入局部最优解
梯度下降容易陷入局部最优解的原因是因为目标函数可能存在多个局部最优解,当梯度下降算法到达其中一个局部最优解时,算法会停止迭代,无法发现全局最优解。
下面以一个简单的例子来说明梯度下降容易陷入局部最优解。假设我们要最小化目标函数 f(x) = x^4 - 3x^3 + 2,它的导数为 f'(x) = 4x^3 - 9x^2。我们可以使用梯度下降算法来找到 f(x) 的最小值,其中学习率设为0.1,初始值为x=0。
x = 0
learning_rate = 0.1
num_iterations = 100
for i in range(num_iterations):
gradient = 4 * x**3 - 9 * x**2
x = x - learning_rate * gradient
print("Iteration {}: x = {}, f(x) = {}".format(i, x, x**4 - 3*x**3 + 2))
运行结果为:
Iteration 0: x = 0.9, f(x) = -0.7873999999999997
Iteration 1: x = 1.386, f(x) = -1.2602675485168826
Iteration 2: x = 1.684, f(x) = -1.462420417081807
Iteration 3: x = 1.815, f(x) = -1.5166931953999164
Iteration 4: x = 1.871, f(x) = -1.532520957389773
Iteration 5: x = 1.899, f(x) = -1.537546264025747
Iteration 6: x = 1.914, f(x) = -1.539232877486169
Iteration 7: x = 1.922, f(x) = -1.5398394385340413
Iteration 8: x = 1.926, f(x) = -1.5400554062558563
Iteration 9: x = 1.928, f(x) = -1.5401201553878237
...
Iteration 94: x = 1.935, f(x) = -1.5401567271486816
Iteration 95: x = 1.935, f(x) = -1.5401567271486816
Iteration 96: x = 1.935, f(x) = -1.5401567271486816
Iteration 97: x = 1.935, f(x) = -1.5401567271486816
Iteration 98: x = 1.935, f(x) = -1.5401567271486816
Iteration 99: x = 1.935, f(x) = -1.5401567271486816
可以看到,梯度下降算法在第95次迭代时就停止了,此时的最小值为 f(x) = -1.5401567271486816,但是这并不是全局最小值,全局最小值为 f(x) = 2。梯度下降算法在第95次迭代时就停止了,此时的最小值为 f(x) = -1.5401567271486816,但是这并不是全局最小值,全局最小值为 f(x) = 2。梯度下降算法停留在了局部最优解处,而没有发现全局最优解。
为了避免梯度下降算法陷入局部最优解,可以采用以下方法:
-
调整初始值。不同的初始值可能会导致不同的局部最优解,可以多试几个不同的初始值,选择最小化的结果作为最终结果。
-
调整学习率。如果学习率过大,梯度下降算法会跳过最小值,如果学习率过小,梯度下降算法会收敛过慢。可以根据实际情况,逐步调整学习率的大小,以便更好地找到全局最优解。
-
使用随机梯度下降算法(SGD)。与批量梯度下降算法不同,SGD每次只使用一个样本来更新模型,这样可以避免梯度下降算法陷入局部最优解。
-
使用其他优化算法。如动量梯度下降算法、自适应学习率算法(如Adagrad、Adam)等。
在实际使用梯度下降算法时,需要根据实际情况进行调参,以避免算法陷入局部最优解。
对初始值敏感
梯度下降算法对于初始值的选择非常敏感,不同的初始权重可能会导致不同的结果。这是因为梯度下降算法的优化过程是基于目标函数的局部梯度进行的,不同的初始权重可能导致不同的局部极值点。
以简单的线性回归问题为例,我们可以通过调整初始权重来观察不同的优化结果:
import numpy as np
import matplotlib.pyplot as plt
# 构造数据
np.random.seed(42)
x = np.linspace(0, 10, num=50)
y = 2 * x + 1 + np.random.normal(size=50)
# 梯度下降算法
def gradient_descent(x, y, alpha=0.01, iterations=1000, init_theta=0):
m = len(y)
theta = init_theta
for i in range(iterations):
h = x.dot(theta)
loss = h - y
gradient = x.T.dot(loss) / m
theta -= alpha * gradient
return theta
# 不同的初始权重
theta1 = gradient_descent(x.reshape(-1, 1), y, init_theta=0)
theta2 = gradient_descent(x.reshape(-1, 1), y, init_theta=1)
theta3 = gradient_descent(x.reshape(-1, 1), y, init_theta=5)
# 绘制结果
plt.scatter(x, y)
plt.plot(x, x*theta1, color='r', label=f'theta1={theta1[0]:.2f}')
plt.plot(x, x*theta2, color='g', label=f'theta2={theta2[0]:.2f}')
plt.plot(x, x*theta3, color='b', label=f'theta3={theta3[0]:.2f}')
plt.legend()
plt.show()
上面的代码中,我们通过调整init_theta参数,可以得到不同的初始权重。然后,我们使用梯度下降算法求解模型的权重参数,最终绘制出模型的拟合曲线。
对于梯度下降算法,我们需要仔细选择初始权重,以避免陷入局部最优解。通常情况下,我们可以使用随机初始化的方式来避免这个问题。
转载自:https://juejin.cn/post/7225072417532887100