Cardinal B-Splines 曲線擬合

前言

前面介紹了spline的基函數,沒想到以前覺得很簡單的東西,能夠玩出這麼多花樣。我的初衷本不過是想了解一下spline迴歸的基本思想,沒想到陷進去了,索性弄得透徹點些吧

目標

這篇日誌主要是解釋一下Cardinal B-Splines的求導,後面給出一個spline的平滑計算實例。平滑不等於插值,所以spline平滑和spline的內插是不同的,前者不通過控制點,後者通過。我以前所理解的spline其實是插值,而不是平滑

Cardinal B-Splines

下面要介紹的是spline的一種特殊形式,也是一種簡單的形式,就是線段按照均勻分割,稱爲Cardinal B-Splines,其有一些特殊的性質,導致其應用比較廣泛。

首先,我們先了解一下離散卷積的概念
y(n)=i=h(ni)x(i)=h(n)x(n) y(n) = \sum_{i=-\infty }^{\infty } h(n-i)x(i) = h(n)*x(n)
y(n),x(n),h(x)y(n),x(n),h(x)分別對應一個LTI係數的輸出,輸入和響應。LTI系統對與一個單位衝擊是有一個連續的響應過程,所以,在連續輸入時,y的輸出可以看作是各個不同時延的幅度不同的衝擊不同的脈衝共同作用下的輸出。那麼,這和Cardinal B-Splines有什麼聯繫呢?
先回顧一下,spline的基函數遞推生成公式,
Ni,0(u)={1,uiu<ui+10,otherwiseNi,p(u)=uuiui+puiNi,p1(u)+ui+p+1uui+p+1ui+1Ni+1,p1(u) N_{i,0}(u) = \left\{\begin{matrix} 1,& u_i \leq u < u_{i+1} \\ 0, & otherwise \end{matrix}\right.\\ N_{i,p}(u) = \tfrac{u-u_i}{u_{i+p}-u_i} N_{i,p-1}(u) + \tfrac{u_{i+p+1}-u}{u_{i+p+1}-u_{i+1}}N_{i+1,p-1}(u)
很多文獻喜歡將Ni,pN_{i,p}記爲Bi,pB_{i,p},在此文中,兩者含義相同。假設分割區間長度爲h,那麼上式可以轉換爲
Ni,p(u)=uuiphNi,p1(u)+ui+p+1uphNi+1,p1(u) N_{i,p}(u) = \tfrac{u-u_i}{p*h} N_{i,p-1}(u) + \tfrac{u_{i+p+1}-u}{p*h}N_{i+1,p-1}(u)
我們令h(u)=uh(u) = u,我們可以看到N_{i,p}(u)是由兩組卷積構成的,這解釋了前面門函數往三角波,再往更復雜的基函數變換的過程。另外一個性質上,Ni,p1(u)N_{i,p-1}(u)Ni+1,p1(u)N_{i+1,p-1}(u),只差了一個時延h,看前面那篇文章的圖就很直觀了。
Ni,pN_{i,p}導數如下,證明涉過程及到高階差分方程,有點複雜,有興趣可以看參考文獻1。從結果看,spline應該能看成一種差分方程
dNi,p(u)du=pti+ptiNi,p1(u)pti+p+1ti+1Ni+1,p1(u)=1h(Ni,p1(u)Ni+1,p1(u))  . \frac{dN_{i,p}(u)}{du} = \frac{p}{t_{i+p}-t_i}N_{i,p-1}(u)- \frac{p}{t_{i+p+1}-t_{i+1}}N_{i+1,p-1}(u)=\frac{ 1}{h}(N_{i,p-1}(u)- N_{i+1,p-1}(u))\;.
Ni,pN_{i,p}的導數和自身有點像,這個結果以後會用到,先放放。

Cardinal B-Spline 曲線擬合

搞了這麼久,本來的目的是想做一個曲線Cardinal B-Spline,先用一下最簡單的曲線擬合。

基本原理

f(x)f(x)是已知函數,Ni,p(x)N_{i,p}(x)是生成的p次基函數
f(x)=i=0n1ciNi,p(x) f(x) = \sum_{i=0}^{n-1} c_i N_{i,p}(x)
cic_i爲待求係數,我們這裏先用最小二乘法估計
y,xRm×1,yi=f(xi),N=[N0,p,,Nn1,p],NRm×n,\bf y,x \in \mathbb{R}^{m\times 1},y_i = f(x_i),N =[N_{0,p},\dots,N_{n-1,p}],N \in \mathbb{R}^{m\times n},
上式可以寫爲
y=Ncc^=(NTN)1NTy \bf y = Nc \Rightarrow \hat{c} = (N^TN)^{-1}N^Ty
由了c\bf c和基函數,那麼,在給定範圍內的任意x都可以給出它的spline曲線上的值。
好了,現在終於可以試一下手了

代碼

代碼依舊很爛,懶得整,主要功能是生成正弦信號y=sin(x),x(02π)y =sin(x), x\in (0,2\pi),加入少量噪聲。分段節點knots 按兩種模式來設置,一種是均勻分割,第二種是按照p次重插入起始點,即(0,0,,2π,2π)(0,0,\dots,2\pi,2\pi),然後測試0-3次的B-spline,詳情見代碼

clear;
%Generating simulation data
n = 101;
max_x = 2*3.14159;
x = linspace(0,max_x,n);
knots = x(1:5:101);
y = sin(x)+(rand(1,n)-0.5)/3;
%i (1-4) test 1-4 order spline fit
%i (5-8) test 1-4 order spline fit with  multiplicity
figure('color','w');
fmt = "%d order spline"; 
for i = 1:1:8
    j = i;
    if j>=5
        fmt = "%d order spline with  multiplicity ";
         knots = [ones(1,i)*0,ones(1,i)*max_x];
        if j==5
            figure('color','w');
        end
        j = j-4;
    end
    N  = GenerateBasicFunctionCurves((j-1),x,knots);
    N = N';
    c = pinv(N)*y';
    yfit = N*c;
    subplot(2,2,j)
    plot(x,y,'ob');
    hold on;
    plot(x,yfit,'-r','Linewidth',1.5);
    str = sprintf(fmt,j);
    title(str);
    plotsetting
end
function v  = SplineBasisFunction(i,p,u,knots)
u_i  = knots(i);
u_i1 = knots(i+1);
if p==0
    if u>=u_i && u<u_i1
        v = 1;return;
    end
    v = 0;return;
end
u_ip1 = knots(i+p+1);u_ip = knots(i+p);
w1 = 0;w2 = 0;
if u_ip~=u_i
    w1=(u-u_i)/(u_ip-u_i);
end
if u_ip1~=u_i1
    w2=(u_ip1-u)/(u_ip1-u_i1);
end
v = w1*SplineBasisFunction(i,p-1,u,knots)+w2*SplineBasisFunction(i+1,p-1,u,knots);
end
function bf = GenerateBasicFunctionCurves(p,x,knots)
for i = 1:1:length(knots)-1-p
    for j = 1:1:length(x)
        bf(i,j) = SplineBasisFunction(i,p,x(j),knots);
    end
end
end
function plotsetting()
a=gca;
a.FontSize=20;
set(gca,'fontweight','bold');
set(gca,'Linewidth',2);
end

結果

均勻分割的問題在於,基函數總是會試圖去擬合在knot上的點,導致spline怎麼看都不太光滑。
在這裏插入圖片描述
多重插入節點
在這裏插入圖片描述
多重節點在階數少的時候,基函數表達能力比較弱,所以擬合效果也出所料地差,但是到了三階的時候明顯效果飛昇,平滑效果要好於前者。

參考文獻

  1. Carl de Boor (1978). A Practical Guide to Splines.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章