核心点:对常见机器学习中的最优化算法全面总结! 大家伙儿好!今儿来聊聊关于机器学习中的最优化算法!咱们常用且常见的机器学习中最优化算法有一阶优化算法、二阶优化算法、自适应学习率优化算法、基于线性规划的优化算法、具有约束条件的优化算法、具有全局收敛性的优化算法等。大家伙如果觉得还不错!可以点赞、转发安排起来,让更多的朋友看到。一阶优化算法梯度下降法(Gradient Descent)梯度下降法是一种常用的优化算法,用于求解函数的最小值或最大值。它通过迭代地更新参数的方式来逐步接近最优解。假设我们要最小化一个可微函数f(x) ,其中x 是参数向量。梯度下降法的目标是找到使得f(x) 达到最小值的x 。这个过程可以通过以下公式进行描述:其中,表示第n次迭代的参数值,表示函数f在处的梯度(即导数),称为学习率或步长,用于控制每次迭代的步幅。具体而言,梯度下降法从任意初始点开始,根据当前位置的梯度方向(即函数下降最快的方向)以及学习率确定下一个位置,并不断迭代直到满足停止条件。当函数存在多个局部最小值时,梯度下降法可能会收敛到其中一个局部最优解。import numpy as np import matplotlib.pyplot as plt
def gradient_descent(f, df, x0, learning_rate, num_iterations): x = x0 x_history = [x] for _ in range(num_iterations): gradient = df(x) x -= learning_rate * gradient x_history.append(x) return np.array(x_history)
# 定义函数f(x) def f(x): return x**2 + 10*np.sin(x)
# 定义函数f(x)的导数df(x) def df(x): return 2*x + 10*np.cos(x)
# 设置初始参数值和学习率 x0 = -5 learning_rate = 0.1 num_iterations = 100
# 运行梯度下降算法 x_history = gradient_descent(f, df, x0, learning_rate, num_iterations)
# 绘制函数曲线和梯度下降路径 x_range = np.linspace(-10, 10, 100) plt.plot(x_range, f(x_range), label='f(x)') plt.scatter(x_history, f(np.array(x_history)), c='red', label='Gradient Descent') plt.legend() plt.xlabel('x') plt.ylabel('f(x)') plt.title('Gradient Descent') plt.show()
这段代码会绘制函数f(x)的曲线以及梯度下降过程中的路径,以直观地展示梯度下降算法的工作原理。随机梯度下降法(Stochastic Gradient Descent,SGD)随机梯度下降法常用于训练机器学习模型。它的主要思想是通过迭代更新模型参数来最小化损失函数。相比于传统的梯度下降法,SGD在每一次迭代中仅使用一个样本数据进行参数更新,因此计算速度更快。其中,表示模型参数,表示学习率,表示损失函数关于参数的梯度,表示第个训练样本。- 重复步骤2-4,直到达到停止条件(例如达到指定的迭代次数或损失函数下降到一定程度)。
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D
# 定义损失函数 def loss_function(x, y): return (x - 1) ** 2 + (y - 2) ** 2
# 定义梯度函数 def gradient(x, y): return np.array([2 * (x - 1), 2 * (y - 2)])
# 生成数据 x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) Z = loss_function(X, Y)
# 绘制3D图形 fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.plot_surface(X, Y, Z, cmap='viridis')
# SGD迭代更新参数 eta = 0.1 theta = np.array([0, 0]) num_iterations = 100 for i in range(num_iterations): gradient_value = gradient(theta[0], theta[1]) theta = theta - eta * gradient_value ax.scatter(theta[0], theta[1], loss_function(theta[0], theta[1]), color='red')
plt.show()
上面,我们定义了一个简单的二维损失函数和对应的梯度函数,并使用matplotlib库绘制了该函数的三维图形。然后,我们使用SGD迭代更新参数,并在图中标示出每次迭代后的参数值。通过观察这些点的变化,可以更直观地理解SGD在参数空间中的搜索过程。小批量梯度下降法(Mini-batch Gradient Descent)小批量梯度下降法是介于随机梯度下降法(SGD)和批量梯度下降法(BGD)之间的一种折中方法。它将训练样本划分为多个批次(mini-batches),每个批次包含若干个样本。与SGD每次只使用一个样本进行参数更新相比,MBGD每次使用一个批次的样本进行参数更新。计算效率: 与BGD相比,MBGD采用小批量样本进行参数更新,可以减少计算时间。尤其在大规模数据集上,MBGD通常比BGD更快。而与SGD相比,MBGD每次更新的样本数量更多,可以充分利用矩阵运算的并行性,加速参数更新过程。 稳定性: MBGD相对于SGD具有更好的稳定性。由于MBGD每次使用多个样本进行参数更新,因此其参数更新方向相对于单个样本更加准确,避免了SGD中参数更新的高度不稳定问题。这使得MBGD在训练过程中更容易收敛到更好的局部最优解。 鲁棒性: MBGD相对于SGD对噪声数据具有更好的鲁棒性。由于MBGD每次使用多个样本进行参数更新,它对于单个样本中的噪声信息会有所平均化,从而减少了单个样本对模型的影响。 泛化能力: MBGD相对于SGD和BGD在一定程度上具有更好的泛化能力。与SGD相比,MBGD使用了更多的样本进行参数更新,可以更充分地表征数据集的特征;与BGD相比,MBGD采用了部分样本进行参数更新,避免了过度拟合的问题。 需要注意的是,MBGD的选择要根据具体问题的需求来确定。较小的批次大小可能会导致更快的收敛速度,但也可能陷入局部最优解。较大的批次大小可能导致计算变慢,但可能会获得更稳定的解。因此,在实践中,我们通常需要根据数据集的规模、模型的复杂度和计算资源的限制等因素来选择适当的批次大小。综上所述,MBGD是在SGD和BGD之间取得折中的一种优化算法,它在计算效率、稳定性、鲁棒性和泛化能力等方面都具有一定的优势。二阶优化算法牛顿法(Newton's Method)牛顿法用于求解无约束优化问题。它通过利用二阶导数信息来近似地更新参数,从而更快地收敛到最优解。其中,表示待优化的参数,表示目标函数,表示目标函数关于参数的梯度,表示目标函数关于参数的黑塞矩阵。- 重复步骤2-4,直到达到停止条件(例如达到指定的迭代次数或梯度下降到一定程度)。
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D
# 定义目标函数 def objective_function(x, y): return x**2 + 2*y**2
# 定义梯度函数 def gradient(x, y): return np.array([2*x, 4*y])
# 定义黑塞矩阵 def hessian_matrix(): return np.array([[2, 0], [0, 4]])
# 生成数据 x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) Z = objective_function(X, Y)
# 绘制3D图形 fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.plot_surface(X, Y, Z, cmap='viridis')
# 牛顿法迭代更新参数 theta = np.array([0, 0]) num_iterations = 10 for i in range(num_iterations): gradient_value = gradient(theta[0], theta[1]) hessian_inverse = np.linalg.inv(hessian_matrix()) theta = theta - np.dot(hessian_inverse, gradient_value) ax.scatter(theta[0], theta[1], objective_function(theta[0], theta[1]), color='red')
plt.show()
在上述示例中,我们定义了一个简单的二维目标函数以及对应的梯度函数和黑塞矩阵。然后,我们使用matplotlib库绘制了该函数的三维图形。接着,我们使用牛顿法迭代更新参数,并在图中标示出每次迭代后的参数值。通过观察这些点的变化,可以更直观地理解牛顿法在参数空间中的搜索过程。需要注意的是,牛顿法的收敛性和稳定性较好,但在某些情况下可能会受到黑塞矩阵的奇异性和计算复杂度的限制。因此,在实践中可能会使用牛顿法的一些改进版本,例如拟牛顿法(Quasi-Newton Methods),来克服这些问题。拟牛顿法(Quasi-Newton Methods),如BFGS、L-BFGS拟牛顿法用于求解无约束非线性优化问题。它通过逐步近似目标函数的二阶导数信息来更新搜索方向和步长,从而实现快速收敛。拟牛顿法的核心思想是使用一个正定矩阵来近似目标函数的Hessian矩阵(二阶导数矩阵),从而避免了直接计算Hessian矩阵的复杂性。其中最经典的拟牛顿法包括DFP(Davidon-Fletcher-Powell)和BFGS(Broyden-Fletcher-Goldfarb-Shanno)。DFP算法DFP算法的思想是通过迭代更新一个正定矩阵来近似Hessian矩阵。其迭代公式如下:其中,表示第k次迭代的自变量,表示第k次迭代的梯度,表示第k次迭代的近似Hessian矩阵。BFGS算法BFGS算法同样是通过迭代更新一个正定矩阵来近似Hessian矩阵。其迭代公式如下:其中,表示第k次迭代的自变量,表示第k次迭代的梯度,表示第k次迭代的近似Hessian矩阵。以下是一个简单的示例代码,在二维平面上绘制目标函数的等高线和拟牛顿法的迭代路径:import numpy as np import plotly.graph_objects as go
# 定义目标函数 def f(x, y): return x**2 + y**2
# 定义梯度函数 def gradient(x, y): return np.array([2*x, 2*y])
# 拟牛顿法迭代更新参数 def update_parameter(parameter, approx_hessian, gradient_value): parameter_delta = -np.dot(approx_hessian, gradient_value) new_parameter = parameter + parameter_delta gradient_delta = gradient(new_parameter[0], new_parameter[1]) - gradient_value rho = 1 / np.dot(gradient_delta, parameter_delta) outer_product = np.outer(parameter_delta, parameter_delta) approx_hessian += (rho * outer_product - np.dot(approx_hessian, outer_product) - np.dot(outer_product, approx_hessian)) / rho**2 return new_parameter
# 初始化参数和近似黑塞矩阵 parameter = np.array([8, 8]) approx_hessian = np.eye(2)
# 进行拟牛顿法迭代 num_iterations = 20 path = [parameter] for i in range(num_iterations): gradient_value = gradient(parameter[0], parameter[1]) parameter = update_parameter(parameter, approx_hessian, gradient_value) path.append(parameter)
# 生成等高线数据 x = np.linspace(-10, 10, 100) y = np.linspace(-10, 10, 100) X, Y = np.meshgrid(x, y) Z = f(X, Y)
# 绘制等高线和迭代路径 fig = go.Figure(data=[ go.Contour(x=x, y=y, z=Z, contours=dict(coloring='lines')), go.Scatter3d(x=[p[0] for p in path], y=[p[1] for p in path], z=[f(p[0], p[1]) for p in path], mode='markers', marker=dict(size=5)) ])
fig.show()
在上述代码示例中,我们首先定义了一个目标函数f(x, y) ,它是一个简单的二次函数。然后,我们生成一组网格点,并计算对应的函数值,以便绘制等高线图。接下来,我们使用拟牛顿法进行迭代更新参数的过程。在每次迭代中,我们通过调用update_parameter 函数来更新参数和近似黑塞矩阵。该函数实现了DFP或BFGS算法中的公式,根据选择的算法进行参数的更新。在迭代过程中,我们将每次迭代得到的参数保存到path 数组中。这样,我们就可以使用go.Scatter3d 绘制拟牛顿法的迭代路径。go.Scatter3d 会将参数在三维空间中的路径以散点的形式呈现出来。最后,我们使用go.Figure 创建一个图形对象,并将等高线图和迭代路径图添加到其中。通过调用fig.show() ,可以在浏览器中显示生成的图形。共轭梯度法(Conjugate Gradient)共轭梯度法是一种常用的无约束优化算法,特别适用于解决大规模线性方程组的求解问题。它利用梯度和残差的共轭性质,通过迭代寻找最优解。共轭梯度法的基本思想是在每次迭代中选择一个共轭方向进行搜索,并通过最小化目标函数沿该方向的步长来更新参数。具体而言,假设我们要求解一个二次型目标函数:其中,表示自变量,表示对称正定矩阵,表示常数向量。其中,表示第次迭代的自变量,表示第次迭代的共轭方向,表示第次迭代的步长,表示第次迭代的残差,为系数。共轭梯度法的步骤
自适应学习率优化算法ADAM(Adaptive Moment Estimation)ADAM 是一种自适应学习率优化算法,结合了RMSprop和Momentum的优点。在深度学习中广泛应用,能够有效地调整学习率,加速模型收敛,并且具有较好的泛化性能。ADAM算法的核心思想是根据梯度的一阶矩估计和二阶矩估计自适应地调整每个参数的学习率。下面是ADAM算法的公式: 其中,和分别表示第t次迭代的一阶矩估计和二阶矩估计。和是用于控制历史梯度对估计的影响程度的超参数。是学习率,是一个很小的常数,用于保证分母不为零。使用Python可以利用plotly库进行三维绘图,可以展示优化过程中损失函数在参数空间上的变化情况,以便更直观地理解ADAM算法的工作原理。import plotly.graph_objects as go
# 定义损失函数 def loss_function(x, y): return x**2 + y**2
# 设置参数空间 theta_range = np.linspace(-5, 5, 100) X, Y = np.meshgrid(theta_range, theta_range) Z = loss_function(X, Y)
# 绘制三维图像 fig = go.Figure(data=[go.Surface(z=Z, x=X, y=Y)]) fig.update_layout(title='Loss Function', scene=dict( xaxis_title='Theta1', yaxis_title='Theta2', zaxis_title='Loss')) fig.show()
这段代码可以绘制出一个损失函数在参数空间中的曲面图,从而可视化ADAM算法优化过程中参数收敛的情况。RMSProp(Root Mean Square Propagation)RMSProp 是一种自适应学习率优化算法,用于调整神经网络中每个参数的学习率。它通过计算梯度历史信息的均方根来自适应地调整学习率大小,从而加快模型收敛速度。 其中,表示第t次迭代的二阶矩估计,是一个用于控制历史梯度对估计影响程度的超参数。是学习率,是一个很小的常数,用于保证分母不为零。AdaGradAdaGrad 是一种自适应学习率优化算法,用于调整神经网络中每个参数的学习率。它根据梯度的历史信息来自适应地调整学习率的大小,使得对于经常出现的稀疏特征,其学习率较小,而对于不经常出现的特征,其学习率较大。 其中,表示第t次迭代的历史梯度累积。是学习率,是一个很小的常数,用于保证分母不为零。基于线性规划的优化算法线性规划(Linear Programming)线性规划旨在找到一个线性目标函数的最小值或最大值,并满足一组线性等式和不等式约束条件。其中, 是待求解的决策变量向量, 是目标函数的系数向量, 和 是不等式约束条件的系数矩阵和向量,和 是等式约束条件的系数矩阵和向量。通过Python和plotly库,我们可以绘制线性规划问题的可行域(feasible region)和目标函数在可行域上的等高线图,从而直观地理解线性规划的工作原理。以下是一个示例代码,假设我们要最小化目标函数,并且满足以下约束条件:import numpy as np import plotly.graph_objects as go
# 定义目标函数和约束条件 def objective_function(x, y): return 3 * x + 4 * y
def constraint1(x, y): return x + y - 6
def constraint2(x, y): return 2 * x + y - 8
# 设置参数空间 x_range = np.linspace(0, 10, 100) y_range = np.linspace(0, 10, 100) X, Y = np.meshgrid(x_range, y_range) Z = objective_function(X, Y)
# 绘制等高线图 fig = go.Figure(data=[go.Contour(z=Z, x=X, y=Y)]) fig.update_layout(title='Objective Function', xaxis_title='x', yaxis_title='y') fig.show()
通过运行以上代码,我们可以绘制出目标函数在参数空间中的等高线图。在这个例子中,我们还有两个不等式约束条件:和。这些约束条件将给出一个可行域,我们可以根据等高线图中的轮廓线来观察目标函数在该可行域上的变化情况。使用线性规划算法,我们可以找到目标函数在约束条件下的最小(或最大)值,并确定使其最小(或最大)的决策变量值。整数规划(Integer Programming)整数规划是线性规划的一种扩展形式,在整数规划中,决策变量被限制为整数值。与线性规划相比,整数规划更加复杂且计算上更具挑战性。其中,是待求解的决策变量向量,是目标函数的系数向量,和是不等式约束条件的系数矩阵和向量,和是等式约束条件的系数矩阵和向量。整数规划问题通常比线性规划问题更难求解,因为整数变量的离散性质使得问题空间更大。求解整数规划问题的方法包括分支定界法(branch and bound)、割平面法(cutting plane method)、混合整数线性规划(mixed integer linear programming, MILP)等。具有约束条件的优化算法条件梯度法(Conditional Gradient Method),也称为Frank-Wolfe算法条件梯度法(Method of Constrained Gradients)是一种用于求解具有约束条件的优化问题的数值方法。它结合了梯度下降法和拉格朗日乘子法,通过在梯度方向上进行搜索来找到满足约束条件的最优解。条件梯度法的基本思想是在每个迭代步骤中,使用拉格朗日乘子法构造一个逼近的目标函数,并沿着该目标函数的负梯度方向更新变量。这样可以逐步接近原始问题的最优解,并同时满足约束条件。4、使用拉格朗日乘子法更新拉格朗日乘子:,其中是步长。5、如果满足终止条件(例如达到最大迭代次数或目标函数的收敛性),则停止迭代;否则返回第2步。通过Python和plotly库,我们可以绘制优化问题的目标函数在参数空间中的等高线图,并可视化约束条件。import numpy as np import matplotlib.pyplot as plt import plotly.graph_objects as go
# 定义目标函数和约束条件 def objective_function(x, y): return x**2 + y**2
def constraint(x, y): return x - y
# 设置参数空间 x = np.linspace(-10, 10, 100) y = np.linspace(-10, 10, 100) X, Y = np.meshgrid(x, y) Z = objective_function(X, Y)
# 绘制等高线图 plt.contourf(X, Y, Z, levels=30, cmap='viridis') plt.colorbar(label='Objective Function')
# 绘制约束条件 plt.contour(X, Y, constraint(X, Y), levels=[0], colors='r')
# 执行条件梯度法迭代过程 x_init = -5.0 y_init = 5.0 alpha = 0.1 # 步长
for _ in range(100): x_grad = 2 * x_init y_grad = 2 * y_init x_init -= alpha * x_grad y_init -= alpha * y_grad plt.plot(x_init, y_init, 'bo', markersize=3)
plt.xlabel('x') plt.ylabel('y') plt.title('Constrained Optimization with Method of Constrained Gradients')
plt.show()
这段代码使用numpy 库定义目标函数和约束条件,并使用matplotlib 库绘制出目标函数的等高线图。通过执行条件梯度法的迭代过程,我们可以在参数空间中看到优化过程的轨迹。基于投影的优化算法,如投影梯度下降法(Projected Gradient Descent)基于投影的优化算法(Projected Gradient Algorithm)是一种用于求解具有约束条件的优化问题的方法。它通过在每个迭代步骤中将更新后的变量投影到可行域内来满足约束条件。基于投影的优化算法的基本思想是在每个迭代步骤中,计算目标函数的梯度,并根据梯度方向更新变量。然后将更新后的变量投影到可行域内,以满足约束条件。这样可以逐步接近原始问题的最优解,并同时满足约束条件。对更新后的变量进行投影操作以满足约束条件:,其中表示投影操作。 如果满足终止条件(例如达到最大迭代次数或目标函数的收敛性),则停止迭代;否则返回第2步。
具有全局收敛性的优化算法全局优化算法,如随机搜索、模拟退火、遗传算法等具有全局收敛性的优化算法是用于求解全局最优解的优化算法。咱们这里用随机搜索来举例,是其中一种简单但非常直观的全局优化算法,它通过在搜索空间中随机采样候选解来寻找最优解。随机搜索是一种简单而直接的全局优化算法,它不依赖于梯度信息,而是通过在搜索空间中随机采样来探索可能的解。其基本流程如下:- 重复上述步骤直至满足终止条件(例如达到最大迭代次数)
随机搜索算法的优点是易于实现和理解,适用于没有显式表达式或梯度信息的目标函数。然而,由于其随机性,它可能需要更多的采样次数才能达到较好的结果。import numpy as np import plotly.graph_objects as go
# 定义目标函数 def objective_function(x, y): return np.sin(x) + np.cos(y)
# 生成网格点 x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) Z = objective_function(X, Y)
# 随机搜索 num_iterations = 100 best_solution = None best_fitness = float('inf')
for _ in range(num_iterations): # 随机采样候选解 candidate_solution = [np.random.uniform(-5, 5), np.random.uniform(-5, 5)] # 计算目标函数值 fitness = objective_function(candidate_solution[0], candidate_solution[1]) # 更新最优解 if fitness < best_fitness: best_solution = candidate_solution best_fitness = fitness
# 绘制三维图像 fig = go.Figure(data=[go.Surface(x=X, y=Y, z=Z)]) fig.add_trace(go.Scatter3d(x=[best_solution[0]], y=[best_solution[1]], z=[best_fitness], mode='markers', marker=dict(size=5, color='red'))) fig.update_layout(title='Objective Function with Best Solution from Random Search', autosize=False, width=700, height=500, scene=dict( xaxis_title='X', yaxis_title='Y', zaxis_title='Z' )) fig.show()
首先定义了目标函数,然后生成网格点用于绘制曲面图。在随机搜索中,通过随机采样候选解,并计算其对应的目标函数值。然后,更新最优解,如果发现更好的解。最后,将最优解以红色点的形式添加到三维图中。最后今天介绍了最优化算法有一阶优化算法、二阶优化算法、自适应学习率优化算法、基于线性规划的优化算法、具有约束条件的优化算法、具有全局收敛性的优化算法等。
|