
最优化理论在人工智能领域占据着举足轻重的地位,无论是训练机器学习模型,还是让机器人规划最优路径,亦或是解决复杂的工程设计问题,都离不开最优化方法的有力支持。今天,我将带大家深入浅出地探索最优化理论的奇妙世界。资源绑定附上完整资源供读者参考学习!
你有没有想过,怎么让一个函数的输出值尽可能的小(或者尽可能的大)?或者说,在有限的资源下,怎样分配才能让收益最大化?这些都可以归结为最优化问题。比如,企业生产中,要在成本有限的情况下让利润最大化,这就是一个典型的最优化问题。
最优化问题一般可以描述为:在给定的约束条件下,找到一个变量的值,使得某个目标函数达到最小值(或者最大值)。这里的变量可以是一个数值、一个向量,甚至是一个函数。
可以想象一下三维空间中的一个曲面,这个曲面代表了目标函数的值。最优化问题就像是在这个曲面上寻找最低点或者最高点。约束条件则像是在这个空间中围起的一个区域,解必须在这个区域内找到。
如果变量的取值范围是有限的,我们可以尝试所有可能的值,然后找到使目标函数最优的那个。但是,这种方法在变量很多或者取值范围很大的时候就不实用了。
通过对目标函数求导,找到导数为零的点,这些点可能是极值点。然后进一步判断这些点是否是最优解。但是,很多复杂函数的导数很难求,甚至求不出来。
从一个初始猜测值开始,不断调整变量的值,逐步逼近最优解。梯度下降法和牛顿法都是迭代法。
import numpy as np
# 定义目标函数
def f(x):
return x**2 + 2*x + 1
# 求导函数
def df(x):
return 2*x + 2
# 解方程 df(x)=0
x_optimal = -2 / 2 # 解为 x = -1
print("最优解为 x =", x_optimal)
线性规划问题的目标函数和约束条件都是线性的。一个标准的线性规划问题可以表示为:
minimize(cTx)
subject toA x≤b,x≥0
其中,x 是决策变量,c 是目标函数的系数向量,A 是约束条件的系数矩阵,b 是约束条件的右侧向量。
在线性规划中,每一个不等式约束条件都定义了一个半空间,而所有约束条件的交集形成了一个凸多面体,称为可行域。目标函数则可以看作是一个超平面,随着目标函数值的变化,这个超平面会平移。最优解一定在这个可行域的顶点上。
单纯形法是一种求解线性规划问题的经典算法。它的基本思想是:从可行域的一个顶点出发,沿着使目标函数值改进的方向移动到相邻的顶点,直到找到最优解。
from scipy.optimize import linprog
# 目标函数系数(注意这里是求最小值,所以直接用)
c = [-1, 4]
# 不等式约束条件的系数矩阵和右侧向量
A = [[-3, 1], [1, 2]]
b = [6, 4]
# 变量的下界
x0_bounds = (0, None)
x1_bounds = (0, None)
# 求解线性规划问题
res = linprog(c, A_ub=A, b_ub=b, bounds=[x0_bounds, x1_bounds], method='highs')
print('结果:', res)
如果目标函数或者约束条件中包含非线性函数,那么这个问题就是一个非线性规划问题。非线性规划问题比线性规划问题要复杂得多,也更难以求解。
对于单变量无约束的最优化问题,常用的方法有:
import numpy as np
def golden_section_search(func, a, b, tol=1e-5, max_iter=100):
"""
使用黄金分割法求解单变量无约束最优化问题
参数:
func -- 目标函数
a, b -- 初始区间端点
tol -- 容忍误差
max_iter -- 最大迭代次数
返回:
x_opt -- 近似最优解
"""
gr = (5 ** 0.5 + 1) / 2 # 黄金比例
for i in range(max_iter):
c = b - (b - a) / gr
d = a + (b - a) / gr
if func(c) < func(d):
b = d
else:
a = c
if abs(b - a) < tol:
break
x_opt = (a + b) / 2
return x_opt
# 定义目标函数
def f(x):
return x ** 2 + 2 * x + 1
# 使用黄金分割法求解
x_opt = golden_section_search(f, -5, 5)
print("最优解为 x =", x_opt)
对于多变量无约束的最优化问题,常用的方法有:
import numpy as np
def gradient_descent(func, grad_func, x0, learning_rate=0.1, tol=1e-5, max_iter=1000):
"""
使用梯度下降法求解多变量无约束最优化问题
参数:
func -- 目标函数
grad_func -- 目标函数的梯度函数
x0 -- 初始点
learning_rate -- 学习率
tol -- 容忍误差
max_iter -- 最大迭代次数
返回:
x_opt -- 近似最优解
"""
x = x0.copy()
for i in range(max_iter):
grad = grad_func(x)
x_prev = x.copy()
x = x - learning_rate * grad
if np.linalg.norm(x - x_prev) < tol:
break
return x
# 定义目标函数和梯度函数
def f(x):
return x[0] ** 2 + x[1] ** 2 + 2 * x[0] + 4 * x[1] + 1
def grad_f(x):
return np.array([2 * x[0] + 2, 2 * x[1] + 4])
# 使用梯度下降法求解
x0 = np.array([5, 5])
x_opt = gradient_descent(f, grad_f, x0, learning_rate=0.1)
print("最优解为 x =", x_opt)
掌握梯度下降法的基本原理和实现方法,学会使用梯度下降法求解非线性规划问题。
用梯度下降法求 Rosenbrock 函数的最小值,并绘制迭代过程。
Rosenbrock 函数是一个经典的测试函数,用于验证优化算法的性能。其定义为:
f(x,y)=(a−x)2+b(y−x2)2
其中,a 和 b 是常数,通常取 a=1,b=100。这个函数的最小值在 (x,y)=(1,1) 处,最小值为 0。Rosenbrock 函数的形状像一个香蕉谷,使得许多优化算法在寻找最小值时容易陷入困境。
梯度下降法是一种迭代的优化算法,它利用目标函数的梯度信息来更新变量的值,逐步逼近最优解。在每次迭代中,变量的更新方向是梯度的反方向,因为梯度的方向是函数值增长最快的方向。
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def rosenbrock(x):
"""
Rosenbrock 函数
参数:
x -- 输入变量,形状为 (2,) 的 NumPy 数组
返回:
函数值
"""
a = 1
b = 100
return (a - x[0]) ** 2 + b * (x[1] - x[0] ** 2) ** 2
def grad_rosenbrock(x):
"""
Rosenbrock 函数的梯度
参数:
x -- 输入变量,形状为 (2,) 的 NumPy 数组
返回:
梯度向量
"""
a = 1
b = 100
grad_x0 = 2 * (x[0] - a) + 4 * b * x[0] * (x[0] ** 2 - x[1])
grad_x1 = 2 * b * (x[1] - x[0] ** 2)
return np.array([grad_x0, grad_x1])
def gradient_descent(rosenbrock, grad_rosenbrock, x0, learning_rate=0.001, tol=1e-5, max_iter=1000):
"""
使用梯度下降法求解 Rosenbrock 函数的最小值
参数:
rosenbrock -- Rosenbrock 函数
grad_rosenbrock -- Rosenbrock 函数的梯度函数
x0 -- 初始点,形状为 (2,) 的 NumPy 数组
learning_rate -- 学习率,默认为 0.001
tol -- 容忍误差,默认为 1e-5
max_iter -- 最大迭代次数,默认为 1000
返回:
x_opt -- 近似最优解,形状为 (2,) 的 NumPy 数组
f_values -- 每次迭代的目标函数值,形状为 (num_iter,) 的 NumPy 数组
x_values -- 每次迭代的变量值,形状为 (num_iter, 2) 的 NumPy 数组
"""
x = x0.copy()
f_values = []
x_values = []
for i in range(max_iter):
# 记录当前函数值和变量值
f_values.append(rosenbrock(x))
x_values.append(x.copy())
# 计算梯度
grad = grad_rosenbrock(x)
# 更新变量
x_prev = x.copy()
x = x - learning_rate * grad
# 检查是否收敛
if np.linalg.norm(x - x_prev) < tol:
break
# 将列表转换为 NumPy 数组
f_values = np.array(f_values)
x_values = np.array(x_values)
return x, f_values, x_values
# 初始化参数
x0 = np.array([-2, 2])
learning_rate = 0.001
tol = 1e-5
max_iter = 10000
# 梯度下降
x_opt, f_values, x_values = gradient_descent(rosenbrock, grad_rosenbrock, x0, learning_rate, tol, max_iter)
# 绘制迭代过程
plt.figure(figsize=(12, 5))
# 绘制函数值变化
plt.subplot(1, 2, 1)
plt.plot(f_values,color='r')
plt.xlabel('迭代次数')
plt.ylabel('目标函数值')
plt.title('目标函数值变化')
# 绘制变量值变化
plt.subplot(1, 2, 2)
plt.plot(x_values[:, 0], x_values[:, 1])
plt.xlabel('x')
plt.ylabel('y')
plt.title('变量值变化')
plt.tight_layout()
plt.show()
print("最优解为 x =", x_opt)
print("最小值为 f(x) =", rosenbrock(x_opt))
上图左图展示了梯度下降法在优化 Rosenbrock 函数过程中的目标函数值变化情况,随着迭代次数的增加,目标函数值逐渐减小并趋于稳定。右图展示了变量值在迭代过程中的变化轨迹,可以看到变量值逐步逼近最优解 (1, 1)。最终,算法成功找到了近似最优解,验证了梯度下降法在求解非线性规划问题中的有效性。
这一实验不仅加深了我对梯度下降法的理解,也让我对 Rosenbrock 函数的特性有了更直观的认识。通过实践操作,我更加熟练地掌握了如何运用优化算法解决实际问题,为今后在人工智能领域深入探索奠定了坚实基础。资源绑定附上完整资源供读者参考学习!

牛顿法示例:
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 定义目标函数
def f(x):
x1, x2 = x[0], x[1]
return x1 ** 2 + x1 * x2 + x2 ** 2 + 3 * x1
# 定义梯度函数(手动计算偏导数)
def gradient(x):
x1, x2 = x[0], x[1]
# 对 x1 的偏导数
df_dx1 = 2 * x1 + x2 + 3
# 对 x2 的偏导数
df_dx2 = x1 + 2 * x2
return np.array([df_dx1, df_dx2])
# 定义Hessian矩阵(手动计算二阶偏导数)
def hessian(x):
# Hessian矩阵的元素
d2f_dx1x1 = 2 # 对x1的二阶偏导数
d2f_dx1x2 = 1 # 对x1和x2的混合偏导数
d2f_dx2x2 = 2 # 对x2的二阶偏导数
return np.array([[d2f_dx1x1, d2f_dx1x2], [d2f_dx1x2, d2f_dx2x2]])
# 牛顿法(仅迭代一次)
def newton_method(initial_point):
x = initial_point.copy()
# 计算梯度
grad = gradient(x)
# 计算Hessian矩阵
hess = hessian(x)
# 计算牛顿步长(梯度的负方向乘以Hessian矩阵的逆)
# 牛顿步长:-H^{-1} * g
newton_step = -np.linalg.solve(hess, grad)
# 更新 x
x_new = x + newton_step
return x_new
# 参数设置
initial_point = np.array([1.0, 1.0]) # 初始点
# 执行牛顿法(仅迭代一次)
new_point = newton_method(initial_point)
# 绘制收敛轨迹
def plot_convergence_path(initial_point, new_point):
# 创建网格
x1 = np.linspace(-5, 5, 100)
x2 = np.linspace(-5, 5, 100)
X1, X2 = np.meshgrid(x1, x2)
Z = X1 ** 2 + X1 * X2 + X2 ** 2 + 3 * X1
# 绘制等高线图
plt.figure(figsize=(10, 8))
plt.contourf(X1, X2, Z, levels=30, alpha=0.5, cmap='viridis')
plt.colorbar(label='f(x1, x2)')
plt.contour(X1, X2, Z, levels=30, colors='k', alpha=0.3)
# 绘制初始点和新点
plt.scatter(initial_point[0], initial_point[1], color='r', s=100, label='初始点')
plt.scatter(new_point[0], new_point[1], color='g', s=100, label='新点')
# 绘制箭头表示移动方向
plt.arrow(initial_point[0], initial_point[1],
new_point[0] - initial_point[0], new_point[1] - initial_point[1],
head_width=0.2, head_length=0.3, fc='b', ec='b', label='牛顿步长')
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('牛顿法收敛路径',font='SimHei',fontsize=20)
plt.legend()
plt.grid(True)
plt.show()
# 绘制收敛轨迹
plot_convergence_path(initial_point, new_point)
# 输出结果
print(f"初始点: x = {initial_point}")
print(f"经过一次牛顿法迭代后的点: x = {new_point}")
print(f"初始点的目标函数值: f(x) = {f(initial_point)}")
print(f"新点的目标函数值: f(x) = {f(new_point)}")
梯度下降法:
import numpy as np
# 可视化迭代过程(可选)
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 定义目标函数
def f(x):
x1, x2 = x[0], x[1]
return x1 ** 2 - 2 * x1 * x2 + 4 * x2 ** 2 + x1 - 3 * x2
# 定义梯度函数(手动计算偏导数)
def gradient(x):
x1, x2 = x[0], x[1]
# 对 x1 的偏导数
df_dx1 = 2 * x1 - 2 * x2 + 1
# 对 x2 的偏导数
df_dx2 = -2 * x1 + 8 * x2 - 3
return np.array([df_dx1, df_dx2])
# 梯度下降法
def gradient_descent(initial_point, learning_rate, max_iterations, tolerance=1e-6):
x = initial_point.copy()
history = [x.copy()] # 记录每次迭代的点
for iteration in range(max_iterations):
grad = gradient(x) # 计算梯度
x_prev = x.copy()
x = x - learning_rate * grad # 更新 x
history.append(x.copy())
# 检查收敛条件(梯度的模长是否小于容忍度)
if np.linalg.norm(grad) < tolerance:
print(f"Converged after {iteration + 1} iterations.")
break
return x, history
# 参数设置
initial_point = np.array([1.0, 1.0]) # 初始点
learning_rate = 0.01 # 学习率
max_iterations = 1000 # 最大迭代次数
# 执行梯度下降
optimal_point, history = gradient_descent(initial_point, learning_rate, max_iterations)
# 输出结果
print(f"最优解: x = {optimal_point}")
print(f"函数f(x)最小值: {f(optimal_point)}")
# 绘制函数等高线
x1 = np.linspace(-2, 2, 100)
x2 = np.linspace(-2, 2, 100)
X1, X2 = np.meshgrid(x1, x2)
Z = X1 ** 2 - 2 * X1 * X2 + 4 * X2 ** 2 + X1 - 3 * X2
plt.contourf(X1, X2, Z, levels=30, alpha=0.5)
plt.colorbar(label='f(x1, x2)')
# 绘制迭代路径
history = np.array(history)
plt.plot(history[:, 0], history[:, 1], 'ro-', markersize=5)
plt.scatter(history[0, 0], history[0, 1], color='b', s=100, label='起始点')
plt.scatter(history[-1, 0], history[-1, 1], color='g', s=100, label='最优解')
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('梯度下降路径')
plt.legend()
plt.show()
以上就是本期关于最优化理论的全部内容啦!如果你在学习过程中有任何疑问或者想法,欢迎在评论区留言,大家一起交流探讨呀!