[最优化技术] 3-4 最速下降法(梯度法)

3-4 最速下降法

最速下降法

简述

\(\qquad\)最速下降法(Gradient Descent Method)又称梯度法 ,是最早提出且最为经典的多元函数无约束优化方法之一。该方法形式直观、原理简单,是其他无约数和约数优化方法的理论基础。由第二章 优化方法的数学基础可知,梯度方向是函数值增加最快的方向,而负梯度方向是函数值下降最快的方向。

\(\qquad\)该方法的基本思想是:从某点 \(\mathbf{x}\) 出发,取该点的负梯度方向作为搜索方向 \(\mathbf{d}\),使函数值在该点附近下降最快,直到满足优化设计目标。

原理

\(\qquad\)对于多元函数 \(f(X)\),在某一点 \(X^{(k)}\) 处,其负梯度方向 \(-\nabla f(X^{(k)})\) 是该点邻域内函数值下降最快的方向。

搜索方向

\S\^{(k)} = -\\nabla f(X\^{(k)}) \\

迭代公式

\X\^{(k+1)} = X\^{(k)} + \\alpha\^{(k)} \\cdot S\^{(k)} \\

其中:

  • \(X^{(k)}\) 为第 \(k\) 次迭代的点
  • \(S^{(k)}\) 为第 \(k\) 次迭代的搜索方向
  • \(\alpha^{(k)}\) 为第 \(k\) 次迭代的步长(通过一维搜索确定)

几何性质

负梯度方向与等值线的关系:

  1. 负梯度方向与当前点的等值线垂直
  2. 负梯度方向与下一搜索点处的等值线相切

因此,相邻两次搜索方向相互垂直 ,梯度下降法的路径呈现锯齿形(zigzag)。

收敛特性:

  1. 越靠近极值点,前进速度越慢
  2. 缺点:收敛速度较慢(线性收敛)

特殊情况

若等高线为同心圆,即函数形如:

\F(X) = C(x_1\^2 + x_2\^2) + \\text{线性项} \\

则可以从任一点沿负梯度方向一步到达极值点

具体步骤

对于给定初始点 \(X^{(0)}\) 和收敛精度 \(\varepsilon\):

  • ① 确定初始点 \(X^{(0)}\),收敛精度 \(\varepsilon\),令 \(k=0\);
  • ② 计算梯度 \(\nabla f(X^{(k)})\);
  • 一维搜索 :求最优步长 \(\alpha^{(k)}\),使 \(f(X^{(k)} + \alpha^{(k)} \cdot S^{(k)})\) 最小;
  • ④ 更新迭代点:\(X^{(k+1)} = X^{(k)} + \alpha^{(k)} \cdot S^{(k)}\),\(k=k+1\);
  • ⑤ 重复②~④步,直到:\(\|\nabla f(X^{(k)})\| \le \varepsilon\)。

实例计算

这里我们举个例子,手把手带大家使用最速下降法(梯度法),一步步来计算一个二元二次凸函数的最优解。

题目

\f(X) = x_1\^2 + 2x_2\^2 - 4x_1 - 2x_1 x_2,\\;\\; X\^{(0)} = \[1,1^T,\;\varepsilon = 0.01 \]

解答

首先计算 \(f(X)\) 的梯度函数:

\\\nabla f(X) = \\left(\\begin{array}{c} 2x_1 - 2x_2 -4 \\\\ -2x_1 + 4x_2 \\end{array}\\right) \\

代入 \(X^{(0)} = 1,1^T\),得:

\\\nabla f(X\^{(0)}) = \\left(\\begin{array}{c} 2(1) - 2(1) -4 \\\\ -2(1) + 4(1) \\end{array}\\right) = \\left(\\begin{array}{c} -4 \\\\ 2 \\end{array}\\right) \\

计算梯度的模长:

\\\\|\\nabla f(X\^{(0)})\\\| = \\sqrt{(-4)\^2 + 2\^2} = \\sqrt{20} = 2\\sqrt{5} \\approx 4.47 \> \\varepsilon \\

不满足收敛条件,迭代继续。

第 1 次迭代 (\(k=0\)):

  1. 确定搜索方向:

\S\^{(0)} = -\\nabla f(X\^{(0)}) = \\left(\\begin{array}{c} 4 \\\\ -2 \\end{array}\\right) \\

  1. 构造新迭代点:

\X\^{(1)} = X\^{(0)} + \\alpha\^{(0)} \\cdot S\^{(0)} = \\left(\\begin{array}{c} 1 \\\\ 1 \\end{array}\\right) + \\alpha\^{(0)} \\left(\\begin{array}{c} 4 \\\\ -2 \\end{array}\\right) = \\left(\\begin{array}{c} 4\\alpha\^{(0)} + 1 \\\\ -2\\alpha\^{(0)} + 1 \\end{array}\\right) \\

  1. 一维搜索求最优步长 \(\alpha^{(0)}\):
    将 \(X^{(1)}\) 代入原函数,构造关于 \(\alpha\) 的一元函数 \(\varphi(\alpha)\):

\\\begin{aligned} \\varphi(\\alpha) \&= f(X\^{(1)}) \\\\ \&= (4\\alpha + 1)\^2 + 2(-2\\alpha + 1)\^2 - 4(4\\alpha + 1) - 2(4\\alpha + 1)(-2\\alpha + 1) \\\\ \&= 16\\alpha\^2 + 8\\alpha + 1 + 2(4\\alpha\^2 - 4\\alpha + 1) - 16\\alpha - 4 - 2(-8\\alpha\^2 + 6\\alpha - 1) \\\\ \&= 40\\alpha\^2 - 20\\alpha - 3 \\end{aligned} \\

对 \(\varphi(\alpha)\) 求导并令其为 0(或使用前面学过的一维搜索方法):

\\\varphi'(\\alpha) = 80\\alpha - 20 = 0 \\implies \\alpha\^{(0)} = 0.25 \\

此时得到新的迭代点:

\X\^{(1)} = \\left(\\begin{array}{c} 4(0.25) + 1 \\\\ -2(0.25) + 1 \\end{array}\\right) = \\left(\\begin{array}{c} 2 \\\\ 0.5 \\end{array}\\right) \\

  1. 检验收敛条件:
    计算 \(X^{(1)}\) 处的梯度:

\\\nabla f(X\^{(1)}) = \\left(\\begin{array}{c} 2(2) - 2(0.5) - 4 \\\\ -2(2) + 4(0.5) \\end{array}\\right) = \\left(\\begin{array}{c} -1 \\\\ -2 \\end{array}\\right) \\

梯度的模长:

\\\\|\\nabla f(X\^{(1)})\\\| = \\sqrt{(-1)\^2 + (-2)\^2} = \\sqrt{5} \\approx 2.236 \> \\varepsilon \\

不满足收敛条件,迭代继续。

第 2 次迭代 (\(k=1\)):

  1. 确定搜索方向:

\S\^{(1)} = -\\nabla f(X\^{(1)}) = \\left(\\begin{array}{c} 1 \\\\ 2 \\end{array}\\right) \\

  1. 构造新迭代点:

\X\^{(2)} = X\^{(1)} + \\alpha\^{(1)} \\cdot S\^{(1)} = \\left(\\begin{array}{c} 2 \\\\ 0.5 \\end{array}\\right) + \\alpha\^{(1)} \\left(\\begin{array}{c} 1 \\\\ 2 \\end{array}\\right) = \\left(\\begin{array}{c} \\alpha\^{(1)} + 2 \\\\ 2\\alpha\^{(1)} + 0.5 \\end{array}\\right) \\

\(\qquad\cdots\)

\(\qquad\)后面以此类推,重复上述"一维搜索求步长 \(\to\) 更新迭代点 \(\to\) 计算梯度并判断收敛"的过程,直到梯度的模长小于等于 \(\varepsilon = 0.01\) 为止。

:由于最速下降法在接近极值点时收敛速度较慢,搜索路径呈现"锯齿形",实际手写计算可能需要较多轮迭代才能满足精度要求,此处仅展示前两步以说明计算流程。在实际编程中,计算机可以在毫秒级完成成百上千次迭代。

程序框图

代码示例

\(\qquad\)对于最速下降法,给出以下 C++ 函数仅供参考。
C++代码示例:

cpp 复制代码
// 计算向量范数
double norm(const vector<double>& v) {
	double sum = 0.0;
	for (double vi : v) {
		sum += vi * vi;
	}
	return sqrt(sum);
}

// 最速下降法 求多元函数最小值点(支持任意维度)
// func: 目标函数
// grad: 梯度函数
// x0: 初始点(向量)
// eps: 收敛精度
vector<double> gradient_descent(
    function<double(const vector<double>&)> func,
    function<vector<double>(const vector<double>&)> grad,
    const vector<double>& x0,
    double eps,
    int max_iter = 1000
) {
	vector<double> x = x0;

	cout << "\n迭代过程:" << endl;
	cout << "步骤\t点 X\t\t\t函数值 f(X)\t\t梯度范数" << endl;
	cout << "-----------------------------------------------------------------" << endl;

	for (int iter = 0; iter < max_iter; ++iter) {
		// 1. 计算当前点的梯度
		vector<double> g = grad(x);

		// 2. 计算梯度范数 ||∇f(X)||
		double grad_norm = norm(g);

		// 3. 检查收敛条件
		if (grad_norm < eps) {
			cout << iter << "\t";
			for (size_t i = 0; i < x.size(); ++i) {
				printf("%.6lf", x[i]);
				cout << (i == x.size() - 1 ? "" : ", ");
			}
			printf("\t%.6lf\t\t%.6lf (收敛)\n", func(x), grad_norm);
			break;
		}

		// 4. 确定搜索方向(负梯度方向) S^(k) = -∇f(X^(k))
		vector<double> d(g.size());
		for (size_t i = 0; i < g.size(); ++i)
			d[i] = -g[i];

		// 5. 定义一维搜索函数 phi(alpha) = f(x + alpha * d)
		auto phi = [&](double alpha) {
			vector<double> new_x(x.size());
			for (size_t i = 0; i < x.size(); ++i)
				new_x[i] = x[i] + alpha * d[i];
			return func(new_x);
		};

		// 6. 使用进退法确定一维搜索区间 (复用之前的函数)
		pair<double, double> bracket;
		try {
			bracket = bracket_minimum(phi, 0.0, 0.1);  // 从 alpha=0 开始,初始步长 0.1
		} catch (...) {
			// 如果进退法失败,使用默认区间
			printf("[Warning] bracket minimum failed! bracket = [0, 10]\n");
			bracket = make_pair(0.0, 10.0);
		}

		// 7. 使用黄金分割法确定最优步长 alpha*
		double alpha_star = golden_section(phi, bracket.first, bracket.second, eps);

		// 8. 更新迭代点 X^(k+1) = X^(k) + alpha* * S^(k)
		for (size_t i = 0; i < x.size(); ++i) {
			x[i] += alpha_star * d[i];
		}

		// 打印迭代信息
		cout << iter << "\t";
		for (size_t i = 0; i < x.size(); ++i) {
			printf("%.6lf", x[i]);
			cout << (i == x.size() - 1 ? "" : ", ");
		}
		printf("\t%.6lf\t\t%.6lf\n", func(x), grad_norm);
	}

	return x;
}

完整示例

\(\qquad\)结合一维搜索方法,下面给出一个完整的二维函数优化示例。
完整 C++ 代码实现:

cpp 复制代码
#include <iostream>
#include <vector>
#include <cmath>
#include <functional>
#include <iomanip>
#include <utility>

using namespace std;

// 进退法求初始区间
pair<double, double> bracket_minimum(function<double(double)> func, double a0, double h) {
	// 进退法最大搜索次数
	const int MAX_ITER = 1e4;
	if (h <= 0)
		throw invalid_argument("Step size h must be positive.");

	// 递增,向左搜索
	if (func(a0) <= func(a0 + h)) {
//		printf("[debug] left search.\n");
		double x0 = a0 + h, x1 = a0;
		h *= 2;
		double x2 = x1 - h;  // 步长加倍
		int iter = 0;
		while (func(x2) < func(x1)) {
			if (iter++ > MAX_ITER)
				throw runtime_error("Max iterations exceeded in left expansion");
//			printf("[debug] step %d: (%.2lf, %.2lf, %.2lf)\n", iter, x2, x1, x0);
			x0 = x1;
			x1 = x2;
			h *= 2;
			x2 = x1 - h; // 步长加倍
		}
//		printf("[debug] step %d: (%.2lf, %.2lf, %.2lf)\n", iter + 1, x2, x1, x0);
		return make_pair(x2, x0);
	}
	// 递减,向右搜索
	else {
//		printf("[debug] right search.\n");
		double x0 = a0;
		double x1 = a0 + h;
		h *= 2;
		double x2 = x1 + h;

		int iter = 0;
		while (func(x2) < func(x1)) {
			if (iter++ > MAX_ITER)
				throw runtime_error("Max iterations exceeded in right expansion");
//			printf("[debug] step %d: (%.2lf, %.2lf, %.2lf)\n", iter, x0, x1, x2);
			x0 = x1;
			x1 = x2;
			h *= 2;
			x2 = x1 + h; // 步长加倍
		}
//		printf("[debug] step %d: (%.2lf, %.2lf, %.2lf)\n", iter + 1, x0, x1, x2);
		return make_pair(x0, x2);
	}
}


// 黄金分割法 求最小值点(横坐标)
double golden_section(function<double(double)> func, double l, double r, double eps) {
	double a = l, b = r, aa, bb;
	int step = 0;
//	printf("[debug] step %d: (%.5lf, %.5lf)\n", step, a, b);
	while (b - a > eps) {			// 点距准则
		aa = b - 0.618 * (b - a);	// 黄金分割点
		bb = a + 0.618 * (b - a);	// 黄金分割点
		step++;
		if (func(aa) < func(bb))
			b = bb;
		else
			a = aa;
//		printf("[debug] step %d: (%.5lf, %.5lf)\n", step, a, b);
	}
	return (a + b) / 2;
}

// 计算向量范数
double norm(const vector<double>& v) {
	double sum = 0.0;
	for (double vi : v) {
		sum += vi * vi;
	}
	return sqrt(sum);
}

// 最速下降法 求多元函数最小值点(支持任意维度)
vector<double> gradient_descent(
    function<double(const vector<double>&)> func,
    function<vector<double>(const vector<double>&)> grad,
    const vector<double>& x0,
    double eps,
    int max_iter = 1000
) {
	vector<double> x = x0;

	cout << "\n迭代过程:" << endl;
	cout << "步骤\t点 X\t\t\t函数值 f(X)\t\t梯度范数" << endl;
	cout << "-----------------------------------------------------------------" << endl;

	for (int iter = 0; iter < max_iter; ++iter) {
		// 1. 计算当前点的梯度
		vector<double> g = grad(x);

		// 2. 计算梯度范数 ||∇f(X)||
		double grad_norm = norm(g);

		// 3. 检查收敛条件
		if (grad_norm < eps) {
			cout << iter << "\t";
			for (size_t i = 0; i < x.size(); ++i) {
				printf("%.6lf", x[i]);
				cout << (i == x.size() - 1 ? "" : ", ");
			}
			printf("\t%.6lf\t\t%.6lf (收敛)\n", func(x), grad_norm);
			break;
		}

		// 4. 确定搜索方向(负梯度方向) S^(k) = -∇f(X^(k))
		vector<double> d(g.size());
		for (size_t i = 0; i < g.size(); ++i)
			d[i] = -g[i];

		// 5. 定义一维搜索函数 phi(alpha) = f(x + alpha * d)
		auto phi = [&](double alpha) {
			vector<double> new_x(x.size());
			for (size_t i = 0; i < x.size(); ++i)
				new_x[i] = x[i] + alpha * d[i];
			return func(new_x);
		};

		// 6. 使用进退法确定一维搜索区间 (复用之前的函数)
		pair<double, double> bracket;
		try {
			bracket = bracket_minimum(phi, 0.0, 0.1);  // 从 alpha=0 开始,初始步长 0.1
		} catch (...) {
			// 如果进退法失败,使用默认区间
			printf("[Warning] bracket minimum failed! bracket = [0, 10]\n");
			bracket = make_pair(0.0, 10.0);
		}

		// 7. 使用黄金分割法确定最优步长 alpha*
		double alpha_star = golden_section(phi, bracket.first, bracket.second, eps);

		// 8. 更新迭代点 X^(k+1) = X^(k) + alpha* * S^(k)
		for (size_t i = 0; i < x.size(); ++i) {
			x[i] += alpha_star * d[i];
		}

		// 打印迭代信息
		cout << iter << "\t";
		for (size_t i = 0; i < x.size(); ++i) {
			printf("%.6lf", x[i]);
			cout << (i == x.size() - 1 ? "" : ", ");
		}
		printf("\t%.6lf\t\t%.6lf\n", func(x), grad_norm);
	}

	return x;
}


// ==========================================================
// 示例:二元二次凸函数
// f(X) = x1^2 + 2*x2^2 - 4*x1 - 2*x1*x2
// ==========================================================
int main() {
	cout << "最速下降法求解多元函数极小值" << endl;
	cout << "目标函数: f(X) = x1^2 + 2*x2^2 - 4*x1 - 2*x1*x2" << endl;
	cout << "理论最优解: X* = [4.0, 2.0]^T, f(X*) = -8.0" << endl << endl;

	// 定义一个二元函数 f(x1, x2) = x1^2 + 2*x2^2 - 4*x1 - 2*x1*x2
	auto f = [](vector<double> x) {
		return x[0] * x[0] + 2 * x[1] * x[1] - 2 * x[0] * x[1] - 4 * x[0];
	};
	// 梯度函数 ∇f(X) = [2*x1 - 2*x2 - 4, -2*x1 + 4*x2]^T
	auto grad = [](vector<double> x) {
		vector<double> g(2);
		g[0] = 2 * x[0] - 2 * x[1] - 4; // ∂f/∂x1
		g[1] = 4 * x[1] - 2 * x[0];   // ∂f/∂x2
		return g;
	};

	double x1_init, x2_init, eps;
	cout << "输入初始点 x1: ";
	cin >> x1_init;
	cout << "输入初始点 x2: ";
	cin >> x2_init;
	cout << "输入精度 eps: ";		// 精度 ε
	cin >> eps;

	// 定义初始条件
	vector<double> x0 = {x1_init, x2_init};   // X^(0)

	// 执行最速下降法
	vector<double> result = gradient_descent(f, grad, x0, eps);

	// 输出最终结果
	cout << "\n最终结果:" << endl;
	cout << "最优解 X* = [";
	for (size_t i = 0; i < result.size(); ++i) {
		cout << result[i] << (i == result.size() - 1 ? "" : ", ");
	}
	cout << "]^T" << endl;
	cout << "目标函数值 f(X*) = " << f(result) << endl;

	vector<double> final_grad = grad(result);
	double final_norm = norm(final_grad);
	cout << "最终梯度范数 = " << final_norm << endl;

	return 0;
}

总结

\(\qquad\)最速下降法作为经典的优化算法,具有以下特点:

优点

  1. 理论明确、方法简单、概念清楚;
  2. 每迭代一次,除需进行一维搜索外,只需计算函数的一阶偏导数,计算量小;
  3. 对初始点要求不高;
  4. 存储需求小。

缺点

  1. 收敛速度较慢,线性收敛
  2. 靠近极值点时搜索路径呈锯齿形,效率降低
  3. 对于等高线为扁椭圆的情况收敛很慢

适用场景

  • 作为其他优化算法的基础
  • 对精度要求不高的初步优化
  • 与其他方法结合使用(如作为牛顿法的初始阶段)

\(\qquad\)**按最速下降法搜索并不等同于以最快的速度到达最优点。**因为"负梯度方向是函数值最速下降方向"仅是迭代点邻域内的一种局部性质,从局部上看,在一点附近函数的下降是快的,但从整体上看则走了许多弯路,因此最速下降法下降速度并不算快,所以从整个迭代过程来看,最速下降法不具有"最速下降"的性质。

\(\qquad\)因为最速下降法采用了函数的负梯度方向作为下一步的搜索方向,所以收敛速度较慢。越是接近极值点,收敛越慢 ,这是它的主要缺点。但是,应用最速下降法可以使目标函数在最初几步下降很快,所以它可与其他无约束优化方法配合使用。特别是一些更有效的方法都是在对它进行改进后,或在它的启发下获得的,因此最速下降法仍是许多无约束和有约束优化方法的基础。在接下来的文章中,我们将介绍收敛速度更快的优化方法,如牛顿法和共轭梯度法。


本学习笔记参考资料:

1 白清顺, 孙靖民, 梁迎春. 机械优化设计 第7版M. 北京: 机械工业出版社, 2024. ISBN: 978-7-111-75103-8

2 武汉理工大学《最优化技术B》课程课件,授课教师:颜彬老师