跳转至正文
GZ Cloudhome Logo

共轭梯度法

发布于:2023 年 6 月 9 日

共轭梯度法(CG)是解决下式的多元线性系统的一个优化方法

Ax=b,\mathbf{A} \boldsymbol{x} = \boldsymbol{b},

其中,矩阵 A\mathbf{A} 需要满足对称正定条件。

共轭向量

如果两个向量 u\boldsymbol{u}v\boldsymbol{v} 满足

uTAv=0,\boldsymbol{u}^T \mathbf{A} \boldsymbol{v} = 0,

那么我们说 u\boldsymbol{u}v\boldsymbol{v}A\mathbf{A} 共轭的。

我们也可以基于此定义一个内积:

u,vA=uTAv.\langle \boldsymbol{u}, \boldsymbol{v} \rangle_\mathbf{A} = \boldsymbol{u}^T \mathbf{A} \boldsymbol{v}.

可以验证,它满足内积的条件。

闭式解

假设我们有一个向量集合 {p1,p2,,pn}\{ \boldsymbol{p}_1, \boldsymbol{p}_2, \dots, \boldsymbol{p}_n \},使得当 iji \neq j 时,

pi,pjA=0\langle \boldsymbol{p}_i, \boldsymbol{p}_j \rangle_\mathbf{A} = 0

因为 A\mathbf{A} 是正定的,{p1,p2,,pn}\{ \boldsymbol{p}_1, \boldsymbol{p}_2, \dots, \boldsymbol{p}_n \} 可以看作是 Rn\mathbb{R}^n 的基。假设前面线性系统的解是 x\boldsymbol{x}^*,那么 x\boldsymbol{x}^* 可以表示为

x=αipi.\boldsymbol{x}^* = \alpha_i \boldsymbol{p}^i.

因此,我们有

Ax=αiApi.\mathbf{A} \boldsymbol{x}^* = \alpha_i \mathbf{A} \boldsymbol{p}^i.

上式左乘 pk\boldsymbol{p}_k,得到

αipk,piA=αkpk,pkA=pkb.\alpha_i \langle \boldsymbol{p}_k, \boldsymbol{p}^i \rangle_\mathbf{A} = \alpha_k \langle \boldsymbol{p}_k, \boldsymbol{p}_k \rangle_\mathbf{A} = \boldsymbol{p}_k \boldsymbol{b}.

因此,αi\alpha_i 有闭式解

αi=piTbpi,piA\alpha_i = \frac{\boldsymbol{p}_i^T \boldsymbol{b}}{\langle \boldsymbol{p}_i, \boldsymbol{p}_i \rangle_\mathbf{A}}

迭带求解

实践中,基于闭式解求这个问题可能消耗过多的时间,我们往往希望能够避免这个问题。思路的关键是仅仅使用少数的 pk\boldsymbol{p}_k,来获得 x\boldsymbol{x}^* 的合理近似。这就带来了一个新的问题:什么样的 pk\boldsymbol{p}_k 是好的近似?

下面是一个无约束的二次规划(QP)问题:

minf(x)=12xTAxbTx.\min f(\boldsymbol{x}) = \frac{1}{2} \boldsymbol{x}^T \mathbf{A} \boldsymbol{x} - \boldsymbol{b}^T \boldsymbol{x}.

对变量求导,可以得到上面这个优化问题的解满足

Ax=b.\mathbf{A} \boldsymbol{x} = \boldsymbol{b}.

这个问题可以基于梯度下降法来求解,其中梯度为

Δx=f(x)=bAx.\Delta \boldsymbol{x} = -\nabla f (\boldsymbol{x}) = \boldsymbol{b} - \mathbf{A} \boldsymbol{x}.

在第一次迭代时,第一个基 p1\boldsymbol{p}_1 可以基于 p1=bAx0\boldsymbol{p}_1 = \boldsymbol{b} - \mathbf{A} \boldsymbol{x}_0 得到。然后基于 闭式解,我们可以求得和第一个基关联的 α1\alpha_1

这个迭代和解决无约束优化问题的梯度下降法的第一次迭代完全相同。在梯度下降法中,我们首先基于梯度计算得到下降的方向,然后用先搜索(line search)来寻找最佳的步长。这说明了 pk\boldsymbol{p}_k 的选择的正确性。得到 x1=x0α1f(x0)\boldsymbol{x}_1 = \boldsymbol{x}_0 - \alpha_1 \nabla f(\boldsymbol{x}_0) 之后,我们可以继续到后面的迭代。

在后面的迭代中,CG 和梯度下降法开始不同。在梯度下降中,每次迭代都是选择梯度为下降方向;相反,在 CG 的第 kk 次迭代中,下降的方向并不简单地为 pk=bAxk1\boldsymbol{p}_k = \boldsymbol{b} - \mathbf{A} \boldsymbol{x}_{k-1},相反,我们需要各个方向之间 A\mathbf{A} 正交:

rk=bAxkpk=rki<krk,piApi,piApi.\begin{align*} \boldsymbol{r}_k &= \boldsymbol{b} - \mathbf{A} \boldsymbol{x}_{k} \\ \boldsymbol{p}_k &= \boldsymbol{r}_k - \sum_{i < k} \frac{\langle \boldsymbol{r}_k, \boldsymbol{p}_i \rangle_\mathbf{A}}{\langle \boldsymbol{p}_i, \boldsymbol{p}_i \rangle_\mathbf{A}}\boldsymbol{p}_i. \end{align*}

CG 法的一个特别的性质是它必定在第 nn 次迭代后收敛到 x\boldsymbol{x}^*

Line search 法和和 CG 法计算 αk\alpha_k 的等价性

在第 kk 步迭代中,使用线搜索来计算 αk\alpha_k 和基于 闭式解 中的公式计算 αk\alpha_k 的方法是等价的。考虑线搜索问题:

argminαk f(xk+αkpk),\underset{\alpha_k}{\arg \min} ~ f(\boldsymbol{x}_k + \alpha_{k} \boldsymbol{p}_{k}),

ff 关于 αk\alpha_k 求导可得

fαk=0αk=pkT(bAxk)pk,pkA\begin{align*} \frac{\partial f}{\partial \alpha_k} = 0 && \Rightarrow && \alpha_k = \frac{\boldsymbol{p}_{k}^T(\boldsymbol{b} - \mathbf{A} \boldsymbol{x}_{k})}{\langle \boldsymbol{p}_{k}, \boldsymbol{p}_{k} \rangle_\mathbf{A}} \end{align*}

注意,因为 pi\boldsymbol{p}_i 之间是 A\mathbf{A} 正交的,

pkTAxk=pkTAi=0kxi=pkTAx0.\boldsymbol{p}_k^T \mathbf{A} \boldsymbol{x}_{k} = \boldsymbol{p}_k^T \mathbf{A} \sum_{i=0}^{k} \boldsymbol{x}_i = \boldsymbol{p}_k^T \mathbf{A} \boldsymbol{x}_0.

因此,

αk=pkT(bAxk)pk,pkA=pkT(bAx0)pk,pkA,\alpha_k = \frac{\boldsymbol{p}_{k}^T(\boldsymbol{b} - \mathbf{A} \boldsymbol{x}_{k})}{\langle \boldsymbol{p}_{k}, \boldsymbol{p}_{k} \rangle_\mathbf{A}} = \frac{\boldsymbol{p}_{k}^T(\boldsymbol{b} - \mathbf{A} \boldsymbol{x}_{0})}{\langle \boldsymbol{p}_{k}, \boldsymbol{p}_{k} \rangle_\mathbf{A}},

这和 闭式解 中的公式一致。

下面是基于 Python 的简单的 CG 法的实现。

def conjugate_gradient(A, b):
    # solving the problem of Ax = b
    # A should be *positive symmetric*.
    n = A.shape[0]
    x = np.zeros_like(b)
    xs = []
    for i in range(n):
        r = b - A @ x
        p = r - sum([(r @ A @ x) / (x @ A @ x) * x for x in xs])
        alpha = (b @ p) / (p @ A @ p)
        xs.append(alpha * p)
        x = x + xs[-1]
    
    return x, xs

需要注意的是,上面的代码并不高效。利用其它的性质,我们可以写出 更高效的实现,其中使用了更高效的计算 pk\boldsymbol{p}_k 的方法。

Newton-CG 法

在解决非线性优化问题的牛顿法中,CG 可以用来解决下面的线性系统的近似:

HkΔxnt=f(x).\mathbf{H}_k \Delta \boldsymbol{x}_{\mathrm{nt}} = - \nabla f(\boldsymbol{x}).

使用 CG 法的牛顿法称为 Newton-CG 法。

和 PLS 的关系

可以证明,PLS 各个主元的系数和 CG 中的一致。