Conjugate Gradient Methods

1. Introduction

CG方法一开始发明在上个世纪(1952年,Hestenes and Stiefel),作为直接求解的方法,一开始的动力是因为那时代的电脑完全没有我们现在的电脑强大。在更早的时候,甚至没有电脑存在,那种情况下使用基础的牛顿法求解一个几百个参数的系统,几乎是不可能的(因为我们要对一个100x100的矩阵求逆!),而在那个时代,几乎没有人会使用牛顿法。于是,聪明的数学家就发明这种共轭梯度方法(CG),CG的特点就是:

  • 不需要完整的将A矩阵的逆(A1A^{-1})表达出来。
  • 而需要的只是:能够衡量A和一个向量的点乘(evaluate A*x)。

现在的时代,我们的计算机已经非常非常强大了,100x100的系统对我们来说非常非常小,几个毫秒就可以求解完。最初提出CG所想要解决的问题已经不存在了,不过CG可以解决我们遇到的其他问题:

计算机内存不足的问题(因为不需要真正存储整个A矩阵或者A的逆)

  • 有一些情况下,A可能不能直接给到我们(比如处于公司保密等情况),但是他们能提供Ax的结果。这种情况下,CG也是很合适的。(当然,我们可以通过Ae,分别点乘所有的基单位向量恢复原本的A)
  • A可能具有特殊结构的情况,比如我们下面将看到的例子(A是一个块矩阵和一个稀疏矩阵的和A=Ablk+AspA = A_{blk}+A_{sp})这种情况下我们有Ax=Ablkx+AspxAx = A_{blk}x+A_{sp}x。我们不需要直接存储完整的A矩阵。

更快速的算法(因为CG是一个迭代的过程,也就是说我们可以提前停止)。之后我们会发现,A为n乘n矩阵的情况下,n次迭代的CG和Newton法是可以得到相同结果的。但是我们可以根据我们对精度的要求提前结束迭代(这个特性会由A矩阵的谱分析结果决定)。举个例子,假设有10610^{6}个变量,如果直接使用Newton法,需要O(n3)O(n^{3})的flop,大概为O(1018)O(10^{18}).但是由于A的矩阵特性,可能使用CG进行10次迭代就可以得到10810^{-8}的精度,每次CG的flop大约为O(n2)O(n^{2})(进行A矩阵和一个向量的点乘),那10次CG一共需要O(105)O(10^{5})量级的flop。O(105)O(10^{5})相对于O(1018)O(10^{18}),这个速度的提升几乎是不可想象的(当然如果我们知道A的稀疏特性,可以在求解Newton法的时候运用它,也可以得到类似的加速效果)。
在这里插入图片描述

2. Algorithms

2.1 Krylov sequence

下面主要讨论从Krylov sequence出发的理解。首先根据定义,定义一串Krylov sequence。
在这里插入图片描述

2.2 d和x序列

然后我们定义x和d的序列。
在这里插入图片描述

2.3 证明我们建立的d序列是一个共轭向量序列

在这里插入图片描述
之后的证明步骤略,见《最优化导论》10.3章,127页。

2.4 建立更优的alpha和beta参数序列

在这里插入图片描述

2.5 谱分析

更多细节见“convex optimization II”课程
在这里插入图片描述

2.6 CG特性

在这里插入图片描述
在这里插入图片描述

3. Matlab Implementation

https://gitee.com/gggliuye/cg_pcg/tree/master

3.1 CG

在这里插入图片描述

function [ x_res, vec_rhos, sqrt_vec_rhos ] = mine_cg( A, b, stop_threshold, num_iter )

n = length(A);
x = zeros(n,1);
r = b;
stop_rho = stop_threshold * stop_threshold * (b'*b);
rho = r'*r;

vec_rhos = [];sqrt_vec_rhos = [];
vec_rhos = [vec_rhos, rho];
sqrt_vec_rhos = [sqrt_vec_rhos, sqrt(rho)];
p = 0;
for k = 1:num_iter
    % whether to stop
    if sqrt(rho) < sqrt(stop_rho)
        break;
    end
    
    if k == 1
        p = r;
    else
        p = r + (vec_rhos(k)/vec_rhos(k-1))*p;
    end
    
    w = A * p;
    alpha = vec_rhos(k)/(p'*w);
    x = x + alpha * p;
    r = r - alpha * w;
    rho = r'*r;
    vec_rhos = [vec_rhos, rho];
    sqrt_vec_rhos = [sqrt_vec_rhos, sqrt(rho)];
end

x_res = x;

end

3.2 PCG

在这里插入图片描述

function [ x_res, vec_rhos, sqrt_vec_rhos ] = mine_pcg( A, b, L, LT, stop_threshold, num_iter )

n = length(A);
x = zeros(n,1);
r = b;

stop_rho = stop_threshold * stop_threshold * (b' *b);
z = LT\(L\r);
rho = r'* z;

vec_rhos = [];sqrt_vec_rhos = [];
vec_rhos = [vec_rhos, rho];
sqrt_vec_rhos = [sqrt_vec_rhos, sqrt(r'*z)];
p = 0;
z = 0;
for k = 1:num_iter
    % whether to stop
    if sqrt(rho) < sqrt(stop_rho)
        break;
    end
    
    if k == 1
        p = LT\(L\r);
    else
        p = z + (vec_rhos(k)/vec_rhos(k-1))*p;
    end
    
    w = A * p;
    alpha = vec_rhos(k)/(p'*w);
    x = x + alpha * p;
    r = r - alpha * w;
    z = LT\(L\r);
    rho = r'* z;
    vec_rhos = [vec_rhos, rho];
    sqrt_vec_rhos = [sqrt_vec_rhos, sqrt(r'*r)];
end

x_res = x;

end


3.3 Test Result

%block preconditioning solution
clear all;
ex_blockprecond;

fprintf('\nStarting CG ...\n');
time_start = cputime;
[x,flag,relres,iter,resvec] = pcg(A,b,1e-4,200);
time_end = cputime;
fprintf('CG done. Status: %d\nTime taken: %e\n', flag,...
time_end - time_start);
figure; semilogy(resvec/norm(b), '.--'); hold on;
set(gca,'FontSize', 16, 'FontName', 'Times');
xlabel('cgiter'); ylabel('relres');

fprintf('\nStarting Mine CG ...\n');
time_start = cputime;
[x,res,resvec] = mine_cg(A,b,1e-4,200);
time_end = cputime;
fprintf('Mine CG done. Status: %d\nTime taken: %e\n', flag,...
time_end - time_start);
semilogy(resvec/norm(b), '.--'); hold on;
set(gca,'FontSize', 16, 'FontName', 'Times');
xlabel('cgiter'); ylabel('relres');

time_start = cputime;
L = chol(A_blk)';
time_end = cputime;
tchol = time_end - time_start;
fprintf('\nCholesky factorization of A blk. Time taken: %e\n', tchol);

fprintf('\nStarting Mine PCG ...\n');
time_start = cputime;
[x,res,resvec] = mine_pcg(A,b,L, L', 1e-4,200);
time_end = cputime;
fprintf('Mine CG done. Status: %d\nTime taken: %e\n', flag,...
time_end - time_start);
semilogy(resvec/norm(b), '.--'); hold on;
set(gca,'FontSize', 16, 'FontName', 'Times');
xlabel('cgiter'); ylabel('relres');

3.4 Data 1

在这里插入图片描述

%example: block precondioned pcg 
rand('state', 364);
m = 200; k = 10; n=m*k;
M = sparse(n, n);
for i = 1:k
    Asub = rand(m); Asub = 10*Asub*Asub';
    M(((i-1)*m+1):(i*m),((i-1)*m+1):(i*m)) = Asub;
end

density=5/(n);
A = -abs(sprandsym(n,density)); 
v = A*ones(n,1);
Sdiagonal = spdiags(v,0,n,n);
A = A - Sdiagonal + M;
%spy(A); 
clear M Sdiagonal Asub v

A_blk = sparse(n,n);
for i = 1:k
    A_blk(((i-1)*m+1):(i*m),((i-1)*m+1):(i*m)) = A(((i-1)*m+1):(i*m),((i-1)*m+1):(i*m));
end
b = 10*rand(n,1);

在这里插入图片描述

在这里插入图片描述

实现的效果和Matlab内置的CG算法完全一致。

3.5 Data 2 SLAM data

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3.6 summary

我实现了CG和PCG算法。

  • 在未知矩阵稀疏特性的情况下,使用CG和PCG可能可以得到更快更好的结果。(但是由于他们是heuristic,不能保证一定可以得到好的结果)
  • 如果已知稀疏特性,矩阵又不是特别大的情况下,直接使用稀疏特性处理,使用Newton法可以得到更好的结果。
  • 无论如何CG和PCG在处理非常非常大的问题的情况下,都有非常非常大的优势(在这里没有比较,但是CG和PCG可以处理极其巨大的问题,巨大到现代计算机都无法存储的Hessien矩阵,无法存储也就无法使用Newton法)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章