lecture
website icon
廣義相加模型
卡方分析 相關分析 平均數檢定 變異數分析 回歸分析 共變數分析 廣義相加模型 時間序列分析 無母數統計 檢定力分析
×
website icon 資料管理 統計分析 相關資源 巨人肩膀 語法索引 關於作者

簡介

本部分介紹多項式回歸以及廣義相加模型,包含mgcv套件的用法。使用到的指令包含:

Facebook Icon Twitter Icon LinkedIn Icon LINE Icon

最小平方法擬合出一條回歸直線,讓我們可以從x來預測y。然而真實世界中,更多時候是資料分布不是線性關係,這時候如果用線性回歸強制擬合出一條直線來描述雙方關係,不僅勉強也影響預測力。這時候多項式就派上用場,甚至必要時可以使用廣義相加模型(Generalized Additive Model, GAM)。

為什麼不用線性回歸?

\(y=\alpha x + \beta\)是我們再熟悉不過的模型,但真實世界中更多是非線性相關。舉例來說,從中華民國統計資訊網的資料,我們可以畫出台灣1961至2020年超過半世紀的經濟成長率:

> okun<-read.csv("c:/Users/USER/downloads/okun.csv", header=T, sep=",")
> attach(okun)
> plot(x=year, y=growth, type="l", main="Taiwan's Economic Growth: 1961-2020", xlab="Year", ylab="Economic Growth %")
Taiwan's Economic Growth: 1961-2020 > linear<-lm(growth~year, data=okun)
> summary(linear)$r.squared
[1] 0.5211508

圖中可以看出經濟成長率的起伏很大,如果用一條回歸直線去描述半世紀以來的變動,回歸模型的解釋力只有0.52。面對這種情形,與其勉強用一條回歸直線,不如讓資料說真話,放棄線性模型另闢蹊徑。其中,多項式模型是一種解套方法。

多項式回歸

面對資料非線性的情況,當我們在擬合回歸模型的時候,刪除極端值是提高模型解釋力的一個方法,但如此不僅失真,實際上也不可能無限制刪除極端值,所以多項式回歸是一個可以提升模型配適度的方法。

我們知道一元一次方程式\(y=\alpha x + \beta \)在平面座標上是一條直線,而一元二次方程式\(y=\alpha x^2 +\beta x + c\)是一條拋物線,隨著方程式的次數越高,曲線波動越明顯。如下圖所示,三次方後已經是一條波形的曲線。

\(常數\)
\(一次方程式\)
\(二次方程式\)
\(三次方程式\)
\(四次方程式\)
\(五次方程式\)

我們可以利用多項式的特性,技巧性地在回歸模型中加入新的\(x^2\)變數,來擬合出一條接近資料分布的回歸模型,提高模型的解釋力。例如我們將原本的經濟成長率模型,加入\(x^2\)也就是年份的平方,讓回歸線變成一條拋物線。

> year_square<-year^2 #計算年份平方
> poly<-lm(growth~year+year_square, data=okun) #重新擬合二次回歸模型
> summary(poly) #輸出結果
Call:
lm(formula = growth ~ year + year_square, data = okun)

Residuals:
    Min      1Q  Median      3Q     Max
-7.1383 -1.0583  0.2147  1.3079  6.3122

Coefficients:
              Estimate Std. Error t value Pr(>|t|)
(Intercept) -5.031e+03  5.117e+03  -0.983    0.330
year         5.221e+00  5.142e+00   1.015    0.314
year_square -1.352e-03  1.292e-03  -1.046    0.300
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.683 on 57 degrees of freedom
Multiple R-squared:  0.5302,    Adjusted R-squared:  0.5137
F-statistic: 32.16 on 2 and 57 DF,  p-value: 4.468e-10

從結果可發現二次方程式回歸模型\(R^2=0.53\),與原本回歸模型\(R^2=0.52\)相較似乎改善一點,只不過自變數year變成不顯著。圖形上可以明顯看到本來的線性模型變成非線性:

> predict_growth<-fitted(poly) #計算回歸模型預測的經濟成長率
> plot(x=year, y=growth, main="Taiwan's Economic Growth: 1961-2020", xlab="Year", ylab="Economic Growth %", type="l") #繪製折線圖
> abline(lm(growth~year), col="blue", lwd=2) #加上線性回歸直線,藍色,寬度2
> lines(year, predict_growth, col="red", lwd=2) #加上二次曲線,紅色,寬度2
> legend("bottomleft", legend=c("Linear Function", "Polynomial Function"), col=c("blue", "red"), lty=1, lwd=2) #加上圖說
Polynomial vs. Linear

上面的例子是將year平方,創造出另一個year_square變數,其實R有更方便的做法可以進行多項式回歸,透過I()或poly()來達到相同的結果:

> poly<-lm(growth~year+I(year^2), data=okun)
> poly<-lm(growth~poly(year, degree=2, raw=TRUE), data=okun)

事實上隨著多項次的次方越高,模型的擬合度也就越好。例如當多項式來到\(x^3\)的時候,模型解釋力又再上升到\(R^2=0.55\)。不過因為多了\(x^2\),多項式回歸最大的問題是共線性,在模型上也比較難解釋。另外隨著次方越高,曲線在雙尾震盪的龍格現象(Runge Phenomenon)也會更加明顯。

> poly3<-lm(growth~poly(year, degree=3, raw=TRUE), data=okun)
> summary(poly3)$r.squared
[1] 0.5508988
> predict_growth2<-fitted(poly3)
> plot(x=year, y=growth, type="l", main="Taiwan's Economic Growth: 1961-2020", xlab="Year", ylab="Economic Growth %")
> abline(lm(growth~year), col="blue", lwd=2)
> lines(year, predict_growth, col="red", lwd=2)
> lines(year, predict_growth2, col="darkgreen", lwd=2)
> legend("bottomleft", legend=c("Degree=1", "Degree=2", "Degree=3"), col=c("blue", "red", "green"), lty=1, lwd=2)
Polynomial Degree=3

廣義相加模型

傳統回歸模型不適用於非線性的資料型態,多項式回歸又有侷限性,那麼到底有沒有方法可以擬合出一條曲線來涵蓋所有資料呢?答案是有的,就是廣義相加模型(Generalized Additive Model)。

我們熟悉的線性回歸方程式\(y=\alpha x + \beta\),等號右邊是估計參數\(\alpha\)與自變數\(x\),再加上一個常數項\(\beta\)。廣義相加模型等號右邊則是一連串平滑曲線函數,每一個自變數都被包含在\(s(x)\)裡面。可以想像廣義相加模型就是一個由數個光滑曲線所組成的模型,整個方程式會長成這樣:\(g(y)=s_{1}(x)+s_{2}(x)+\dots+s_{n}(x)\)。我們可以比較兩種模型的差異如下:

我們可以把廣義相加模型想像成一個由不同平滑曲線相加所構成的模型。例如\(g(y)=s_{1}(x)+s_{2}(x)+s_{3}(x)+s_{4}(x)\)可以想像成下圖:

s1(x) s2(x) s3(x) s4(x) g(y)

過去線性回歸的重點在於找出\(\alpha\)與\(\beta\),從上述可知廣義相加模型的重點已經不是\(\alpha\)、\(\beta\),而是找出\(s(x)\)也就是平滑曲線(smooth function)。

平滑曲線

為什麼要找出一條平滑曲線?就像上面經濟成長率的例子,折線圖雖然可以看出趨勢但卻不容易預測\(x\)與\(y\)之間的關係,我們通常會希望\(x\)與\(y\)的關係會符合某種模式,可以是一條直線(linear),或是一條曲線(curve)。所以怎麼樣能讓折線變得「平滑」(smooth)一點,通常有下列幾種方法。

> loess<-loess(growth~year, data=okun, span=0.6) #局部回歸模型
> fit_loess<-fitted(loess) #估計局部回歸模型的經濟成長率
> library(splines) #載入splines套件
> cubic<-lm(growth~bs(year, knots=6, degree=3), data=okun) #三次樣條模型
> fit_cubic<-fitted(cubic) #估計三次樣條模型的經濟成長率
> library(TTR) #載入TTR套件
> sma<-SMA(okun[,"growth"], n=10) #計算10年移動平均
> ema<-EMA(okun[,"growth"], ratio=0.3) #計算指數移動平均
> par(mfrow=c(2,2)) #設定圖表輸出版型為2x2
> plot(x=okun$year, y=okun$growth, type="l", main="Economic Growth", xlab="Year", ylab="Economic Growth %") #繪製折線圖
> lines(year, sma, col="red", lwd=2) #加上移動平均趨勢線
> legend("bottomleft", legend=c("SMA"), col=c("red"), lty=1, lwd=2) #圖例
> plot(x=okun$year, y=okun$growth, type="l", main="Economic Growth", xlab="Year", ylab="Economic Growth %") #繪製折線圖
> lines(year, ema, col="blue", lwd=2) #加上指數移動平均趨勢線
> legend("bottomleft", legend=c("EMA"), col=c("blue"), lty=1, lwd=2) #圖例
> plot(x=okun$year, y=okun$growth, type="l", main="Economic Growth", xlab="Year", ylab="Economic Growth %") #繪製折線圖
> lines(year, fit_loess, col="purple", lwd=2) #加上局部回歸趨勢線
> legend("bottomleft", legend=c("Loess"), col=c("purple"), lty=1, lwd=2) #圖例
> plot(x=okun$year, y=okun$growth, type="l", main="Economic Growth", xlab="Year", ylab="Economic Growth %") #繪製折線圖
> lines(year, fit_cubic, col="darkgreen", lwd=2) #加上三次樣條趨勢線
> legend("bottomleft", legend=c("Cubic Spline"), col=c("darkgreen"), lty=1, lwd=2) #圖例
Loess vs. Cubic Spline

模型試作

瞭解廣義相加模型的概念後,我們以奧肯法則(Okun's Law)實際例子來驗證廣義相加模型。奧肯法則是美國經濟學家Arthur Okun在1962年提出失業率與經濟成長率的反向關係。如果適用到台灣是不是也是如此呢?下圖呈現半世紀以來台灣失業率與經濟成長率的變動,可以看出兩者大抵呈現反向關係。

> plot(x=unemployment, y=growth, main="Taiwan's Okun Law", xlab="Unemployment %", ylab="Economic Growth %")
Taiwan's Okun Law

我們試著以廣義相加模型來擬合兩項變數。mgcv套件是R執行廣義相加模型最重要的套件,載入mgcv後以gam()重新擬合兩項變數:

> library(mgcv) #載入mgcv套件
> okun_new<-okun[complete.cases(okun),]
> attach(okun_new)
> gam<-gam(growth~s(unemployment), data=okun_new) #廣義相加模型
> summary(gam)
Family: gaussian
Link function: identity

Formula:
growth ~ s(unemployment)

Parametric coefficients:
            Estimate Std. Error t value Pr(>|t|)
(Intercept)   5.8447     0.3786   15.44   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Approximate significance of smooth terms:
                  edf Ref.df     F  p-value
s(unemployment) 5.202  6.296 5.773 0.000209 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
R-sq.(adj) =   0.45   Deviance explained = 51.8%
GCV = 7.2019  Scale est. = 6.1632    n = 43

GAM的輸出報表大致分為五個部分。

  1. 連結函數 (link function)
  2. 模型的連結函數。現在這個模型是高斯分配(gaussian),但如果應變數是類別變數,GAM其實也可以改為其他類型的分配。

  3. 方程式 (Formula)
  4. 說明模型之間的變數關係,也就是方程式長什麼樣子。

  5. 參數 (parametric coefficients)
  6. 這是我們熟悉的部分,和過去回歸模型的內容一樣,可以看到常數項是5.8447。

  7. 平滑曲線 (smooth terms)
  8. GAM最重要的部分。可以看到smooth term已經沒有預測參數,而是以effective degrees of freedom(edf)來代替。edf是GAM的統計量,反應的是非線性的程度,通常edf越大代表模型越複雜,非線性的情況越明顯;反之當edf=1的時候,代表兩者是線性相關,也就是跟一般的回歸模型一樣。

  9. 解釋力
  10. 最後一部分是模型解釋力,可以看到\(R^2=0.45\)。

plot.gam()輸出廣義相加模型的平滑曲線如下:

> plot.gam(gam, residuals=TRUE, pch=1, shade=TRUE, shade.col="lightblue")
GAM for Taiwan's Okun Law

gam.check()輸出模型檢定報表。報表第一段說明檢定的方法是「廣義交叉驗證」(Generalised Cross-Validation, GCV),GCV是用來選擇平滑參數\(\lambda\)的指標,與AIC一樣GCV的數值越小表示模型越佳。gam()會自動透過最小誤差或最大概似法,替每個模型找到適合的平滑參數。GCV的原理是誤差極小化來決定平滑參數,這也是gam()預設的方法。但多數人認為GCV模型有平滑不足(undersmoothing)的問題,因此更推薦使用有限最大概似法(restricted maximum likelihood, REML)來決定平滑參數。

報表第二段是基底函數(Basis Functions)K的檢驗結果。實務上K決定了模型自由度(degree of freedom)的上限,也就是K值越大,模型的自由度越大。所以當K越大,也就是基底函數越多,越能正確勾勒出平滑曲線。不過就像前面提到的,gam()在透過GCV或REML找到適合的平滑參數,已經決定模型的自由度,所以一般情況下K不太需要調整。但是當基底函數太少的時候,模型的平滑度通常不會太好,震盪會比較明顯,特別是一旦自變數增加的時候,可能需要手動調整基底函數,直到模型的p值不顯著為止。

在我們的模型中,k=9,p值不顯著,顯示模型通過統計檢驗。gam.check()也會同時輸出四張殘差檢定圖,這部分與線性回歸模型相同。

> gam.check(gam)

Method: GCV   Optimizer: magic
Smoothing parameter selection converged after 5 iterations.
The RMS GCV score gradient at convergence was 0.0003435376 .
The Hessian was positive definite.
Model rank =  10 / 10

Basis dimension (k) checking results. Low p-value (k-index<1) may
indicate that k is too low, especially if edf is close to k'.

                 k' edf k-index p-value
s(unemployment) 9.0 5.2    1.42    0.99
GAM Model Diagnosis

廣義相加模型的優勢在於面對非線性的資料,比傳統線性模型有更佳的配適度與解釋例。不同模型比較中可以看見廣義相加模型的優勢,無論是在\(R^2\)或AIC(Akaike Information Criterion)都有較好的表現:

> linear<-lm(growth~unemployment, data=okun)
> poly<-lm(growth~poly(unemployment, degree=2, raw=TRUE), data=okun)
> cubic<-lm(growth~bs(unemployment), data=okun)
> summary(linear)$r.squared
[1] 0.37149
> summary(poly)$r.squared
[1] 0.3743529
> summary(cubic)$r.squared
[1] 0.3837308
> summary(gam)$r.sq
[1] 0.450145
> AIC(linear)
[1] 210.9652
> AIC(poly)
[1] 212.7689
> AIC(cubic)
[1] 214.1195
> AIC(gam)
[1] 207.9345
Model \(R^2\) AIC
Linear 0.37 210.97
Polynomial 0.37 212.77
Cubic 0.38 214.12
GAM 0.45 207.93

除了比較\(R^2\)與AIC外,其實也可以ANOVA來比較模型差異:

> anova(linear, gam, test="Chisq")
Analysis of Variance Table

Model 1: growth ~ unemployment
Model 2: growth ~ s(unemployment)
  Res.Df    RSS    Df Sum of Sq Pr(>Chi)
1 41.000 295.88
2 36.798 226.79 4.202    69.089  0.02812 *
> anova(poly, gam, test="Chisq")
Analysis of Variance Table

Model 1: growth ~ poly(unemployment, degree = 2, raw = TRUE)
Model 2: growth ~ s(unemployment)
  Res.Df    RSS    Df Sum of Sq Pr(>Chi)
1 40.000 294.53
2 36.798 226.79 3.202    67.741  0.01411 *
> anova(cubic, gam, test="Chisq")
Analysis of Variance Table

Model 1: growth ~ bs(unemployment)
Model 2: growth ~ s(unemployment)
  Res.Df    RSS    Df Sum of Sq Pr(>Chi)
1 39.000 290.12
2 36.798 226.79 2.202    63.326  0.00741 **

最後我們直接以視覺化來比較不同模型擬合的結果:

> linear<-lm(growth~unemployment, data=okun_new)
> poly<-lm(growth~poly(unemployment, degree=2, raw=TRUE), data=okun_new)
> cubic<-lm(growth~bs(unemployment), data=okun_new)
> fit_poly<-fitted(poly)
> fit_cubic<-fitted(cubic)
> par(mfrow=c(2,2))
> plot(x=unemployment, y=growth, main="Linear", xlab="Unemployment %", ylab="Economic Growth %")
> abline(lm(growth~unemployment))
> plot(x=unemployment, y=growth, main="Polynomial", xlab="Unemployment %", ylab="Economic Growth %")
> lines(unemployment, fit_poly)
> plot(x=unemployment, y=growth, main="Cubic Spline", xlab="Unemployment %", ylab="Economic Growth %")
> lines(unemployment, fit_cubic)
> plot.gam(gam, residuals=TRUE, main="GAM")
4 Models Comparison

與線性回歸相同,predict.gam()可以用來估計未來的情況。假設失業率為4.80、3.91、3.73、3.71、3.76,估計經濟成長率為:

> var.unemployment<-data.frame(unemployment=c(4.80,3.91,3.73,3.71,3.76))
> predict.gam(gam, newdata=var.unemployment)
       1        2        3        4        5
4.561867 3.490964 3.641897 3.669994 3.604767

交互作用

廣義相加模型與多元回歸一樣,也可以包含許多\(s(x)\),甚至是\(s(x_{1}, x_{2})\)交互作用項。根據經濟學法則GDP = C + I + G + NX,我們以儲蓄率與投資率為例重新建立經濟成長率模型,並增加儲蓄率與投資率的交互作用項。

> gam2<-gam(growth~s(save)+s(investment)+s(save, investment), data=okun, method="REML")
> summary(gam2)
Family: gaussian
Link function: identity

Formula:
growth ~ s(save) + s(investment) + s(save, investment)

Parametric coefficients:
            Estimate Std. Error t value Pr(>|t|)
(Intercept)   7.1472     0.3177    22.5   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Approximate significance of smooth terms:
                      edf Ref.df      F  p-value
s(save)            5.214  6.183 2.866  0.0158 *
s(investment)      3.188  3.844 2.897  0.0368 *
s(save,investment) 2.107 27.000 0.112  0.0729 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

R-sq.(adj) =  0.591   Deviance explained = 66.4%
-REML = 147.97  Scale est. = 6.0548    n = 60

plot()輸出模型擬合結果,因為有三個自變數,因此共會輸出三張圖表。左上是儲蓄率的平滑曲線,右上是投資率的平滑曲線,下圖則是以等高線圖(contour)來呈現兩者的交互作用。從圖形來看,儲蓄率對經濟成長率的影響是先下降再上升,而投資率則是先上升後下降。至於交互作用項從等高線圖難以判別,我們可以改用3D圖來觀察。

> layout(matrix(c(1,2,3,3), 2, 2, byrow=TRUE))
> plot(gam2)
Interacton Plot with Contour

在plot()中加入scheme=1參數,可輸出交互作用3D圖。圖中顯示當儲蓄率高,投資率低的時候,對經濟成長率有負面影響;儲蓄率適中,投資率高對經濟成長率是最佳情況。

> plot(gam2, scheme=1)
3D Interacton Plot

除了plot()之外vis.gam()則可以繪製兩變數之間的透視圖:

> vis.gam(gam2, view=c("save", "investment"), plot.type="persp")
Perspective Plot

\(s(x_{1}, x_{2})\)當作交互作用項的時候,整個平滑曲線只有一個\(\lambda\)參數,當\(x_{1}\)與\(x_{2}\)單位相同的時候,共用\(\lambda\)參數並無不可,不過如果兩個變數單位不同的時候,共用同一個\(\lambda\)參數就顯得有些奇怪了。這時候就可以引進tensor來建構模型。用下面的方程式更能瞭解兩種交互作用模型的差異:

\[\text{原始模型: }y=s(x_{1})+s(x_{2})+s(x_{1}, x_{2}) \text{ with } \lambda_{1}, \lambda_{2}, \lambda_{3}\]

\[\text{Tensor模型: }y=s(x_{1})+s(x_{2})+ti(x_{1}, x_{2}) \text{ with } \lambda_{1}, \lambda_{2}, \lambda_{3}, \lambda_{4}\]