1 데이터

data(biometric, package = "dmtr")
Table 1.1: 나이, 키, 몸무게 데이터
age height weight
21 170 60
47 167 65
36 173 67
15 165 54
54 168 73
25 177 71
32 169 68
18 172 62
43 171 66
28 175 68

2 PLS 모형

2.1 하나의 종속변수에 대한 PLS 회귀분석

회귀분석에서와 같이 하나의 종속변수에 영향을 주는 \(k\)개의 독립변수가 있다고 하자. 모든 변수는 평균조정되었다고 간주한다. 본 장에서 다루고자 하는 부분최소자승법(partial least squares: PLS)는 앞에서 다룬 주성분 회귀분석(PCR)과 유사하나, 도출되는 새로운 잠재변수들이 다르다.

종속변수가 하나만 존재하는 경우에는 데이터 행렬 \(\mathbf{X}\)와 종속변수벡터 \(\mathbf{y}\)가 동일한 잠재변수로 설명된다고 가정할 수 있다. \((n \times k)\) 데이터 행렬 \(\mathbf{X}\)와 종속변수벡터 \(\mathbf{y}\)에 대하여 동시에 \(A\)개의 잠재변수벡터 \(\mathbf{t}_1, \ldots, \mathbf{t}_A\)로 설명하는 모형을 아래와 같이 기술해보자.

\[\begin{eqnarray} \mathbf{X} &=& \mathbf{t}_1 \mathbf{p}_1^\top + \mathbf{t}_2 \mathbf{p}_2^\top + \cdots + \mathbf{t}_A \mathbf{p}_A^\top + \mathbf{E} \tag{2.1}\\ \mathbf{y} &=& \mathbf{t}_1 b_1 + \mathbf{t}_2 b_2 + \cdots + \mathbf{t}_A b_A + \mathbf{f} \tag{2.2} \end{eqnarray}\]

여기서 계수벡터 \(\mathbf{p}_a\)\(\mathbf{X}\)에 해당하는 로딩(loading)을, 그리고 계수 \(b_a\)\(\mathbf{y}\)에 해당하는 로딩을 나타내며, \(\mathbf{E}\)\(\mathbf{f}\)는 각 모형에 해당하는 오차항(행렬 또는 벡터)이다.

위 모형을 (\(n \times A\)) 잠재변수 행렬 \(\mathbf{T} = \left[\mathbf{t}_1 \, \cdots \, \mathbf{t}_A \right]\)\((k \times A)\) 로딩행렬 \(\mathbf{P} = \left[\mathbf{p}_1 \, \cdots \, \mathbf{p}_A \right]\), 그리고 로딩벡터 \(\mathbf{b} = \left[b_1 \, \cdots \, b_A \right]^\top\)을 이용하여 아래와 같이 행렬식으로 나타낼 수 있다.

\[\begin{eqnarray} \mathbf{X} &=& \mathbf{T}\mathbf{P}^\top + \mathbf{E} \tag{2.3}\\ \mathbf{y} &=& \mathbf{T}\mathbf{b} + \mathbf{f} \tag{2.4} \end{eqnarray}\]

2.1.1 NIPALS 알고리즘

  • [단계 0] 반복알고리즘 수행을 위한 초기화를 한다. \(a \leftarrow 1\), \(\mathbf{X}_a \leftarrow \mathbf{X}\), \(\mathbf{y}_a \leftarrow \mathbf{y}\).
  • [단계 1] \(\mathbf{X}_a\)을 다중종속변수 행렬으로, \(\mathbf{y}_a\)를 독립변수 벡터로 하는 회귀모형으로부터 기울기 \(\mathbf{w}_a = [w_{a1} \, \cdots \, w_{ak}]^\top\)를 산출한다. \[\mathbf{w}_a \leftarrow \left. \mathbf{X}_a^\top \mathbf{y}_a \middle/ \mathbf{y}_a^\top \mathbf{y}_a \right. \]
  • [단계 2] 기울기 벡터 \(\mathbf{w}_a\)의 크기가 1이 되도록 한다. \[\left. \mathbf{w}_a \leftarrow \mathbf{w}_a \middle/ \sqrt{\mathbf{w}_a^\top \mathbf{w}_a} \right.\]
  • [단계 3] 잠재변수 \(\mathbf{t}_a\)를 행렬 \(\mathbf{X}_a\)의 각 열의 가중평균으로 구한다. 이 때, 가중치는 기울기 벡터 \(\mathbf{w}_a\)를 이용한다. \[\mathbf{t}_a \leftarrow \mathbf{X}_a \mathbf{w}_a\]
  • [단계 4](2.1)와 같이 \(\mathbf{X}_a\)을 다중종속변수 행렬으로, \(\mathbf{t}_a\)를 독립변수 벡터로 하는 회귀모형으로부터 로딩벡터 \(\mathbf{p}_a\)를 구한다. \[\mathbf{p}_a \leftarrow \left. \mathbf{X}_a^\top \mathbf{t}_a \middle/ \mathbf{t}_a^\top \mathbf{t}_a \right.\]
  • [단계 5] 로딩벡터 \(\mathbf{p}_a\)의 크기를 1로 조정하고, 잠재변수 벡터 \(\mathbf{t}_a\)와 기울기 벡터 \(\mathbf{w}_a\)의 크기를 그에 따라 보정한다. \[d \leftarrow \sqrt{\mathbf{p}_a^\top \mathbf{p}_a}, \, \mathbf{t}_a \leftarrow \mathbf{t}_a d, \, \mathbf{w}_a \leftarrow \mathbf{w}_a d, \, \mathbf{p}_a \leftarrow \frac{1}{d} \mathbf{p}_a \]
  • [단계 6](2.2)와 같이 잠재변수 \(\mathbf{t}_a\)를 종속변수 \(\mathbf{y}_a\)에 회귀시킬 때 계수 \(b_a\)를 산출한다. \[b_a \leftarrow \left. \mathbf{y}_a^\top \mathbf{t}_a \middle/ \mathbf{t}_a^\top \mathbf{t}_a \right. \]
  • [단계 7] 독립변수 행렬 \(\mathbf{X}_a\)와 종속변수벡터 \(\mathbf{y}_a\)로부터 새로 얻어진 잠재변수 벡터 \(\mathbf{t}_a\)가 설명하는 부분을 제거하고 나머지 변동만을 담은 독립변수 행렬 \(\mathbf{X}_{a + 1}\)과 종속변수벡터 \(\mathbf{y}_{a + 1}\)을 구한다. \[\mathbf{X}_{a + 1} \leftarrow \mathbf{X}_a - \mathbf{t}_a \mathbf{p}_a^\top, \, \mathbf{y}_{a + 1} \leftarrow \mathbf{y}_a - \mathbf{t}_a b_a\]
  • [단계 8] \(a \leftarrow a + 1\)로 업데이트하고, [단계 1]로 돌아간다. [단계 1] - [단계 8]의 과정을 \(A\)개의 잠재변수를 얻을 때까지 반복한다.

위 반복 알고리즘은 함수는 nipals_plsr()로 구현되어 있다.

y <- biometric %>% select(weight) %>% scale()
X <- biometric %>% select(age, height) %>% scale()
plsr_fit <- nipals_plsr(X, y, ncomp = 2L)
plsr_fit
#> $T
#>              LV1        LV2
#>  [1,] -0.7858087  0.3080356
#>  [2,]  0.3229184 -1.4835503
#>  [3,]  0.6295298  0.3450656
#>  [4,] -1.9750173 -0.5667679
#>  [5,]  0.9178231 -1.5567950
#>  [6,]  0.6082079  1.7210543
#>  [7,] -0.2725870 -0.3890125
#>  [8,] -0.6425632  0.8890449
#>  [9,]  0.7325346 -0.4071198
#> [10,]  0.4649624  1.1400451
#> 
#> $W
#>              LV1        LV2
#> age    0.7992341 -0.6028787
#> height 0.6039375  0.7978329
#> 
#> $P
#>              LV1        LV2
#> age    0.8321183 -0.6028787
#> height 0.5545982  0.7978329
#> 
#> $b
#>       LV1       LV2 
#> 0.9937095 0.0417354 
#> 
#> $ncomp
#> [1] 2

회귀계수 b와 잠재변수 스코어 행렬 T, 그리고 종속변수에 적용된 평균 및 분산조정 수치를 이용하여, 아래와 같이 분산조정 이전의 종속변수에 대한 회귀식을 추정할 수 있다.

c(
  attr(y, "scaled:center"),
  plsr_fit$b * attr(y, "scaled:scale") + 
    rowSums(solve(t(plsr_fit$T) %*% plsr_fit$T) %*% t(plsr_fit$T) * attr(y, "scaled:center"))
)
#>     weight        LV1        LV2 
#> 65.4000000  5.5069003  0.2312876

위 추정된 회귀식은 아래와 같이 잠재변수 스코어 행렬 T를 독립변수로 하여 평균조정 이전의 종속변수를 설명하는 회귀모형의 추정 결과이다.

T_A <- as_tibble(plsr_fit[["T"]])
reg_data <- biometric %>% select(weight) %>% bind_cols(T_A)
lm_fit <- fit_linear_regression(reg_data, weight, all_of(names(T_A)))
lm_fit
#> $betas
#> (Intercept)         LV1         LV2 
#>  65.4000020   5.5069325   0.2312937 
#> 
#> $hessian
#>               (Intercept)           LV1           LV2
#> (Intercept)  2.000000e+01 -1.340594e-11  1.398881e-11
#> LV1         -1.340594e-11  1.494215e+01 -6.557255e-12
#> LV2          1.398881e-11 -6.557255e-12  2.105785e+01
#> 
#> $mse
#> [1] 7.038464
#> 
#> $df
#> [1] 7
#> 
#> $se
#> (Intercept)         LV1         LV2 
#>   0.8389556   0.9706158   0.8176115 
#> 
#> $sst
#> [1] 276.4
#> 
#> $rsq
#> [1] 0.8217466
#> 
#> $rsqadj
#> [1] 0.770817
#> 
#> $n
#> [1] 10

위 과정을 함수 fit_plsr()로 아래와 같이 실행할 수 있다.

fit <- fit_plsr(biometric, weight, c(age, height), .ncomp = 2L)
ttest_linear_regression(fit)
#> # A tibble: 3 × 5
#>   term        estimate std_error t_statistic  p_value
#>   <chr>          <dbl>     <dbl>       <dbl>    <dbl>
#> 1 (Intercept)   65.4       0.839      78.0   1.50e-11
#> 2 LV1            5.51      0.971       5.67  7.56e- 4
#> 3 LV2            0.231     0.818       0.283 7.85e- 1

위 부분최소자승 회귀분석에 단계별 선택방법을 적용할 경우, 첫 번째 잠재변수만을 독립변수로 사용한 회귀모형이 최종적으로 선택되며, 이 때의 수정결정계수가 두 개의 주성분을 모두 이용한 모형보다 높음을 확인할 수 있다.

selected_lv <- select_variables_stepwise(reg_data, weight, names(T_A))
#> Inital variable:  LV1 , p-value =  0.0003124394  <  0.05 
#> Iteration  1 : forward selection - no additional variable gives statistically significant improvement of the fit.
fit_linear_regression(reg_data, weight, all_of(selected_lv))
#> $betas
#> (Intercept)         LV1 
#>   65.399974    5.506908 
#> 
#> $hessian
#>              (Intercept)          LV1
#> (Intercept) 2.000000e+01 1.057487e-11
#> LV1         1.057487e-11 1.494215e+01
#> 
#> $mse
#> [1] 6.229061
#> 
#> $df
#> [1] 8
#> 
#> $se
#> (Intercept)         LV1 
#>   0.7892440   0.9131028 
#> 
#> $sst
#> [1] 276.4
#> 
#> $rsq
#> [1] 0.8197088
#> 
#> $rsqadj
#> [1] 0.7971724
#> 
#> $n
#> [1] 10

2.2 다수의 종속변수의 경우

\(m\)개의 종속변수가 존재하여, 종속변수 데이터가 벡터가 아닌 (\(n \times m\)) 행렬

\[\mathbf{Y} = \left[ \mathbf{y}_1 \, \cdots \, \mathbf{y}_m \right]\]

으로 표현될 때, 각각의 종속변수에 대해 따로 잠재변수를 산출하기보다는, 여러 종속변수를 설명하는 공통의 잠재변수행렬 \(\mathbf{T}\)를 산출하는 것이 합리적이라 할 수 있다.

앞 절의 모형을 일반화하여 아래와 같은 모형을 가정한다.

\[\begin{eqnarray} \mathbf{X} &=& \mathbf{T} \mathbf{P}^\top + \mathbf{E} \tag{2.5}\\ \mathbf{Y} &=& \mathbf{U} \mathbf{Q}^\top + \mathbf{F} \tag{2.6}\\ \mathbf{U} &=& \mathbf{T} \mathbf{B} + \mathbf{H} \tag{2.7} \end{eqnarray}\]

(2.5)의 모형은 앞서 하나의 종속변수의 경우에서 살펴본 모형식 (2.3)와 동일하다. 식 (2.6)에서 \((n \times A)\) 행렬 \(\mathbf{U}\)\(\mathbf{Y}\)를 설명하는 \(A\)개의 잠재변수를 나타내는 행렬이며, \((m \times A)\) 행렬 \(\mathbf{Q}\)는 종속변수행렬 \(\mathbf{Y}\)와 잠재변수행렬 \(\mathbf{U}\)간의 선형관계를 나타내는 로딩행렬이다. 또한 식 (2.7)는 잠재변수행렬 \(\mathbf{T}\)\(\mathbf{U}\)간의 선형관계를 나타내는데, 특히 \(\mathbf{B}\)\((A \times A)\) 대각행렬(diagonal matrix)로써, \(\mathbf{U}\)\(\mathbf{T}\)간에는 서로 대응하는 열 간에만 관계가 성립하며, 그 관계는 아래와 같다.

\[\begin{equation*} \mathbf{u}_a = b_a \mathbf{t}_a + \mathbf{h}_a, \, a = 1, \cdots, A \end{equation*}\]

이 때, \(b_a\)는 행렬 \(\mathbf{B}\)\(a\)번째 대각 원소를 나타낸다.

\[\mathbf{B} = \left[ \begin{array}{c c c c} b_{1} & 0 & \cdots & 0\\ 0 & b_{2} & & 0\\ \vdots & & \ddots & \vdots \\ 0 & 0 & \cdots & b_{A} \end{array} \right] \]

행렬 \(\mathbf{E}\), \(\mathbf{F}\), \(\mathbf{H}\)는 오차항에 해당하는 행렬이다.

2.2.1 NIPALS 알고리즘

다수의 종속변수가 존재하는 경우에도 NIPALS 알고리즘을 이용하여 모형을 추정한다. 이때는 각 잠재변수 \(\mathbf{t}_a\)를 추출할 때 추출한 잠재변수의 수렴 여부를 확인할 필요가 없었던 위 2.1.1절의 경우와는 달리, 각 잠재변수 \(\mathbf{t}_a\)\(\mathbf{u}_a\)를 추출하는 과정에서 반복적인(iterative) 기법으로 두 잠재변수 벡터들을 업데이트하며 수렴 여부를 확인하여야 한다.

  • [단계 0] 반복알고리즘 수행을 위한 초기화를 한다. \(a \leftarrow 1\), \(\mathbf{X}_a \leftarrow \mathbf{X}\), \(\mathbf{Y}_a \leftarrow \mathbf{Y}\).
  • [단계 1] 종속변수 행렬 \(\mathbf{Y}_a\)의 임의의 열 하나를 잠재변수 벡터 \(\mathbf{u}_a\)로 선정한다.
  • [단계 2] \(\mathbf{X}_a\)을 다중종속변수 행렬으로, 잠재변수 \(\mathbf{u}_a\)를 독립변수 벡터로 하는 회귀모형으로부터 기울기 \(\mathbf{w}_a = [w_{a1} \, \cdots \, w_{ak}]^\top\)를 산출한다. \[\mathbf{w}_a \leftarrow \left. \mathbf{X}_a^\top \mathbf{u}_a \middle/ \mathbf{u}_a^\top \mathbf{u}_a \right. \]
  • [단계 3] 기울기 벡터 \(\mathbf{w}_a\)의 크기가 1이 되도록 한다. \[\left. \mathbf{w}_a \leftarrow \mathbf{w}_a \middle/ \sqrt{\mathbf{w}_a^\top \mathbf{w}_a} \right.\]
  • [단계 4] 잠재변수 \(\mathbf{t}_a\)를 행렬 \(\mathbf{X}_a\)의 각 열의 가중평균으로 구한다. 이 때, 가중치는 기울기 벡터 \(\mathbf{w}_a\)를 이용한다. \[\mathbf{t}_a \leftarrow \mathbf{X}_a \mathbf{w}_a\]
  • [단계 5] \(\mathbf{Y}_a\)을 다중종속변수 행렬으로, 잠재변수 \(\mathbf{t}_a\)를 독립변수 벡터로 하는 회귀모형으로부터 기울기 (로딩벡터) \(\mathbf{q}_a = [q_{a1} \, \cdots \, q_{am}]^\top\)를 산출한다. \[\mathbf{q}_a \leftarrow \left. \mathbf{Y}_a^\top \mathbf{t}_a \middle/ \mathbf{t}_a^\top \mathbf{t}_a \right. \]
  • [단계 6] 기울기 벡터 \(\mathbf{q}_a\)의 크기가 1이 되도록 한다. \[\left. \mathbf{q}_a \leftarrow \mathbf{q}_a \middle/ \sqrt{\mathbf{q}_a^\top \mathbf{q}_a} \right.\]
  • [단계 7] 잠재변수 \(\mathbf{u}_a\)를 행렬 \(\mathbf{Y}_a\)의 각 열의 가중평균으로 구한다. 이 때, 가중치는 기울기 벡터 \(\mathbf{q}_a\)를 이용한다. \[\mathbf{u}_a \leftarrow \mathbf{Y}_a \mathbf{q}_a\]
  • [단계 8] (수렴 확인) [단계 2]에서 [단계 7]까지의 과정을 잠재변수 벡터 \(\mathbf{u}_a\)의 모든 원소값이 수렴할 때까지 반복한다. 수렴이 확인되면 [단계 9]로 진행한다.
  • [단계 9] \(\mathbf{t}_a\)\(\mathbf{X}_a\)에 회귀시켜, \(\mathbf{X}_a\)을 다중종속변수 행렬으로, \(\mathbf{t}_a\)를 독립변수 벡터로 하는 회귀모형으로부터 로딩벡터 \(\mathbf{p}_a\)를 구한다. \[\mathbf{p}_a \leftarrow \left. \mathbf{X}_a^\top \mathbf{t}_a \middle/ \mathbf{t}_a^\top \mathbf{t}_a \right.\]
  • [단계 10] 로딩벡터 \(\mathbf{p}_a\)의 크기를 1로 조정하고, 잠재변수 벡터 \(\mathbf{t}_a\)와 기울기 벡터 \(\mathbf{w}_a\)의 크기를 그에 따라 보정한다. \[d \leftarrow \sqrt{\mathbf{p}_a^\top \mathbf{p}_a}, \, \mathbf{t}_a \leftarrow \mathbf{t}_a d, \, \mathbf{w}_a \leftarrow \mathbf{w}_a d, \, \mathbf{p}_a \leftarrow \frac{1}{d} \mathbf{p}_a \]
  • [단계 11] 잠재변수벡터 \(\mathbf{u}_a\)\(\mathbf{t}_a\)간의 내부관계 계수 \(b_a\)를 산출한다. \[b_a \leftarrow \left. \mathbf{u}_a^\top \mathbf{t}_a \middle/ \mathbf{t}_a^\top \mathbf{t}_a \right. \]
  • [단계 12] 독립변수행렬 \(\mathbf{X}_a\)와 종속변수행렬 \(\mathbf{Y}_a\)로부터 새로 얻어진 잠재변수 벡터 \(\mathbf{t}_a\)가 설명하는 부분을 제거하고 나머지 변동만을 담은 독립변수행렬 \(\mathbf{X}_{a + 1}\)과 종속변수행렬 \(\mathbf{Y}_{a + 1}\)을 구한다. \[\mathbf{X}_{a + 1} \leftarrow \mathbf{X}_a - \mathbf{t}_a \mathbf{p}_a^\top, \, \mathbf{Y}_{a + 1} \leftarrow \mathbf{Y}_a - b_a \mathbf{t}_a \mathbf{q}_a^\top \]
  • [단계 13] \(a \leftarrow a + 1\)로 업데이트하고, [단계 1]로 돌아간다. [단계 1] - [단계 13]의 과정을 \(A\)개의 잠재변수를 얻을 때까지 반복한다.

위 반복 알고리즘은 함수는 nipals_plsr_n()으로 구현되어 있다.