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法)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章