library(tidyverse)
library(Matrix)
library(broom)
Fisher Scoring dan IRLS untuk GLM dengan variabel respon biner
Package
Fisher Scoring untuk GLM
Formula Fisher Scoring yang digunakan untuk mendapatkan penduga bagi koefisien GLM adalah sebagai berikut:
dengan
Algoritme Fisher Scoring dapat dituliskan sebagai berikut:
- Tentukan perkiraan awal nilai optimal
dan stopping criterion - Tentukan Score function
dan Expected Fisher Information . - Hitung
, sehingga diperoleh perkiraan nilai optimal - Lakukan Langkah 5 sampai stopping criterion terpenuhi
- Hitung
untuk iterasi - Saat stopping criterion terpenuhi maka
merupakan nilai penduga bagi parameter - Ragam dari
diperoleh dengan $Var(_{(p+1)})=^{-1} $saat stopping criterion terpenuhi
Iteratively reweighted least squares (IRLS) untuk GLM
IRLS untuk GLM merupakan hasil reformulasi dari Fisher Scoring untuk GLM. Berikut proses penurunanya
kemudiann dengan mensubtitusikan
maka diperoleh persamaan normal (normal equation) sebagai berikut:
Kemudian, solusi dari persamaan normal diatas adalah
jadi, formulasi untuk IRLS adalah
Algoritme Fisher Scoring dapat dituliskan sebagai berikut:
- Tentukan perkiraan awal nilai optimal
dan stopping criterion - Tentukan
dan . - Hitung
, sehingga diperoleh perkiraan nilai optimal - Lakukan Langkah 5 sampai stopping criterion terpenuhi
- Hitung
untuk iterasi - Saat stopping criterion terpenuhi maka
merupakan nilai penduga bagi parameter - Ragam dari
diperoleh dengan saat stopping criterion terpenuhi
Data
No | Variabel | Keterangan |
---|---|---|
1 | age | umur nasabah(numeric) |
2 | job | jenis pekerjaan nasabah(categorical:"admin.","unknown","unemployed","management","housemaid","entrepreneur","student","blue-collar","self-employed","retired","technician","services") |
3 | marital | status perkawinan nasabah (categorical: "married","divorced","single") |
4 | education | tingkat pendidikan(categorical: "unknown","secondary","primary","tertiary") |
5 | default | apakah nasabah memiliki kredit yang macet? (binary: "yes","no") |
6 | balance | saldo tahunan rata-rata dalam euros (numeric) |
7 | housing | apakah nasabah memiliki cicilan rumah? (binary: "yes","no") |
8 | loan | apakah nasabah memiliki pinjaman pribadi? (binary: "yes","no") |
9 | contact | perangkat telekomunikasi (categorical: "unknown","telephone","cellular") |
10 | duration | durasi panggilan terakhir, dalam detik (numeric) |
11 | pdays | banyaknya hari yang telah berlalu setelah klien terakhir kali dihubungi dari campaign sebelumnya (angka, -1 berarti klien tidak dihubungi sebelumnya) |
12 | previous | banyaknya komunikasi yang dilakukan sebelum campaign ini(numeric) |
13 | poutcome | hasil dari promosi campaign sebelumnya (categorical: "unknown","other","failure","success") |
14 | subscribed | response variable (desired target),apakah nasabah telah berlangganan deposito berjangka? (binary: "yes","no") |
Data bisa didownload di link berikut
Import Data
<- read.csv("bank_marketing.csv",stringsAsFactors = TRUE) bank_marketing
glimpse(bank_marketing)
Rows: 45,211
Columns: 14
$ subscribed <fct> no, no, no, no, no, no, no, no, no, no, no, no, no, no, no,…
$ age <int> 58, 44, 33, 47, 33, 35, 28, 42, 58, 43, 41, 29, 53, 58, 57,…
$ job <fct> management, technician, entrepreneur, blue-collar, unknown,…
$ marital <fct> married, single, married, married, single, married, single,…
$ education <fct> tertiary, secondary, secondary, unknown, unknown, tertiary,…
$ default <fct> no, no, no, no, no, no, no, yes, no, no, no, no, no, no, no…
$ balance <int> 2143, 29, 2, 1506, 1, 231, 447, 2, 121, 593, 270, 390, 6, 7…
$ housing <fct> yes, yes, yes, yes, no, yes, yes, yes, yes, yes, yes, yes, …
$ loan <fct> no, no, yes, no, no, no, yes, no, no, no, no, no, no, no, n…
$ contact <fct> unknown, unknown, unknown, unknown, unknown, unknown, unkno…
$ duration <int> 261, 151, 76, 92, 198, 139, 217, 380, 50, 55, 222, 137, 517…
$ pdays <int> -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,…
$ previous <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ poutcome <fct> unknown, unknown, unknown, unknown, unknown, unknown, unkno…
Fisher Scoring dan IRLS untuk Binomial GLM
Misal Binomial GLM didefinisikan sebagai
Untuk ilustrasi penggunaan Fisher-scoring dan IRLS kita akan menggunakan variabel prediktor balance
, age
, housing
dan marital
set.seed(2045)
<- bank_marketing %>%
bank_marketing_mini select(subscribed,balance,age,housing,marital) %>%
slice_sample(n=250,by = subscribed)
glimpse(bank_marketing_mini)
Rows: 500
Columns: 5
$ subscribed <fct> no, no, no, no, no, no, no, no, no, no, no, no, no, no, no,…
$ balance <int> 354, 5838, 39, -123, 1150, 263, -11, 1733, 14, 416, 2, 0, 4…
$ age <int> 29, 56, 55, 35, 33, 40, 51, 33, 50, 34, 26, 27, 32, 23, 45,…
$ housing <fct> yes, no, no, yes, yes, yes, no, yes, yes, no, yes, yes, yes…
$ marital <fct> single, married, married, single, single, single, married, …
Kemudian kita akan ekstrak matriks modelnya
<- model.matrix(subscribed~balance+age+housing+marital,data = bank_marketing_mini)
X #dimensi matrix X
dim(X)
[1] 500 6
dan kemudian untuk yes
sebagai 1 dan 0 adalah untuk kategori no
<- ifelse(bank_marketing_mini$subscribed=="yes",1,0)
y # ukuran variabel respon
length(y)
[1] 500
# banyaknya nilai 1 dan 0
table(y)
y
0 1
250 250
berikut overview dari matriks
1:10,] X[
(Intercept) balance age housingyes maritalmarried maritalsingle
1 1 354 29 1 0 1
2 1 5838 56 0 1 0
3 1 39 55 0 1 0
4 1 -123 35 1 0 1
5 1 1150 33 1 0 1
6 1 263 40 1 0 1
7 1 -11 51 0 1 0
8 1 1733 33 1 0 1
9 1 14 50 1 1 0
10 1 416 34 0 0 1
Binomial sebagai distribusi keluarga eksponensial
Selanjutnya kita akan mendefinisikan matriks-matriks lain yang dibutuhkan untuk fisher scoring dan IRLS. Pertama-tama kita ingat terlebih dahulu tentang distribusi binomail sebagai distribusi keluarga eksponensial.
Distribusi Binomial yang digunakan untuk GLM adalah
dan parameter dispersi
Kemudian,
dan
$$Var(Y) &= Var() a()
&= a()
&=
$$
Ingat bahwa koneksi (pemisalan)
Jika kita subtitusi ke dalam
jadi
jika kita ubah menjadi fungsi dari
Mendefiniskan matriks untuk Fisher Scoring dan IRLS
Kemudian kita akan dapatkan matriks
elemen-elemen diagonalnya sebagai berikut:
dan
sehingga
Jadi matriks
kita tuliskan matriks
<- function(m,pi){
W <- m * pi * ( 1- pi )
wi Diagonal(x=as.numeric(wi))
}
misalkan
W(m=c(1,1,1),pi=c(0.5,0.6,0.7))
3 x 3 diagonal matrix of class "ddiMatrix"
[,1] [,2] [,3]
[1,] 0.25 . .
[2,] . 0.24 .
[3,] . . 0.21
Selanjutnya kita akan dapatkan matriks
elemen-elemen diagonalnya sebagai berikut:
Jadi matriks
kita tuliskan matriks
<- function(pi){
D <- pi * ( 1- pi )
di Diagonal(x=as.numeric(di))
}
misalkan
D(pi=c(0.5,0.6,0.7))
3 x 3 diagonal matrix of class "ddiMatrix"
[,1] [,2] [,3]
[1,] 0.25 . .
[2,] . 0.24 .
[3,] . . 0.21
Selanjutnya kita akan dapatkan matriks
<- function(X,beta){
mu exp(X %*% beta) / ( 1+exp( X%*% beta) )
}
misalkan bank_marketing_mini
dan
# memeriksa dimensi
dim( mu(X=X,beta = c(0.230,0.001,0.002,-0.769,-0.166,0.109) ) )
[1] 500 1
# menampilkan 10 elemen pertama
mu(X=X,beta = c(0.230,0.001,0.002,-0.769,-0.166,0.109) )[1:10]
[1] 0.4955001 0.9975617 0.5530496 0.3815440 0.6869718 0.4782637 0.5386726
[8] 0.7972185 0.3564054 0.6948728
Fisher Scoring
matriks
kita tuliskan matriks
<- function(X,W,D,y,mu){
S <- chol2inv(chol(D))
inv_D crossprod( X, W %*% inv_D %*% (y-mu) )
}
misalkan
set.seed(2045)
<- W(m = 1,pi = rep(0.5,nrow(X)))
W_coba dim(W_coba)
[1] 500 500
1:5,1:5] W_coba[
5 x 5 diagonal matrix of class "ddiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] 0.25 . . . .
[2,] . 0.25 . . .
[3,] . . 0.25 . .
[4,] . . . 0.25 .
[5,] . . . . 0.25
<- D(pi = rep(0.5,nrow(X)))
D_coba dim(D_coba)
[1] 500 500
1:5,1:5] D_coba[
5 x 5 diagonal matrix of class "ddiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] 0.25 . . . .
[2,] . 0.25 . . .
[3,] . . 0.25 . .
[4,] . . . 0.25 .
[5,] . . . . 0.25
<- as.matrix( runif(n = nrow(X),min = 0,max = 1 ) )
mu_coba dim(mu_coba)
[1] 500 1
head(mu_coba)
[,1]
[1,] 0.77550659
[2,] 0.94264755
[3,] 0.47241345
[4,] 0.08665263
[5,] 0.63646300
[6,] 0.42453500
S(X = X,W = W_coba,D = D_coba,y = y,mu = mu_coba)
6 x 1 Matrix of class "dgeMatrix"
[,1]
(Intercept) -5.12588
balance 64379.67806
age -132.00826
housingyes -27.00537
maritalmarried -9.45959
maritalsingle 5.37219
matriks
kita tuliskan matriks
<- function(X,W){
I_fs crossprod(X,W %*% X)
}
set.seed(2045)
<- W(m = 1,pi = rep(0.5,nrow(X)))
W_coba dim(W_coba)
[1] 500 500
1:5,1:5] W_coba[
5 x 5 diagonal matrix of class "ddiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] 0.25 . . . .
[2,] . 0.25 . . .
[3,] . . 0.25 . .
[4,] . . . 0.25 .
[5,] . . . . 0.25
I_fs(X = X,W = W_coba)[1:5,1:5]
5 x 5 Matrix of class "dgeMatrix"
(Intercept) balance age housingyes maritalmarried
(Intercept) 125.0 1.901442e+05 5160.00 58.00 68.50
balance 190144.2 1.821748e+09 8400186.75 59549.75 101843.00
age 5160.0 8.400187e+06 230156.50 2279.50 3062.25
housingyes 58.0 5.954975e+04 2279.50 58.00 31.00
maritalmarried 68.5 1.018430e+05 3062.25 31.00 68.50
Supaya lebih mudah, kita kumpulkan semua definisi matriks yang sudah dituliskan ke program R dibawah ini
<- function(m,pi){
W <- m * pi * ( 1- pi )
wi Diagonal(x=as.numeric(wi))
}
<- function(pi){
D <- pi * ( 1- pi )
di Diagonal(x=as.numeric(di))
}
<- function(X,beta){
mu exp(X %*% beta) / ( 1+exp( X%*% beta) )
}
<- function(X,W,D,y,mu){
S <- chol2inv(chol(D))
inv_D crossprod( X, W %*% inv_D %*% (y-mu) )
}
<- function(X,W){
I_fs crossprod(X,W %*% X)
}
Selanjutnya kita akan mulai menuliskan program R untuk iterasi fisher scoring
<- function(beta_old,beta_new,tol) {
stop_criterion <- as.matrix(abs(beta_new-beta_old))
error <- all(error < tol)
status return(list(error=as.data.frame(t(error)),status=status))
}
stop_criterion(beta_old = c(0.5,-0.9),beta_new = c(0.51,-0.91),tol = 1e-5)
$error
V1 V2
1 0.01 0.01
$status
[1] FALSE
stop_criterion(beta_old = c(0.5,-0.9),beta_new = c(0.50001,-0.90001),tol = 1e-5)
$error
V1 V2
1 1e-05 1e-05
$status
[1] TRUE
stop_criterion(beta_old = c(0.5,-0.9),beta_new = c(0.5001,-0.90001),tol = 1e-5)
$error
V1 V2
1 1e-04 1e-05
$status
[1] FALSE
berikut adalah nilai awal dari subscribed
adalah respon distribusi bernouli.
<- rep(0,ncol(X))
beta_new names(beta_new) <- colnames(X)
beta_new
(Intercept) balance age housingyes maritalmarried
0 0 0 0 0
maritalsingle
0
<- 1000
max_iter <- 1
mi
<- 1e-15 tolerance
Ingat Formula Fisher Scoring yang digunakan adalah sebagai berikut:
for(i in 1:max_iter){
#menyimpan beta lama
<- beta_new
beta_old
<- mu(X = X,beta = beta_new)
mu_new <- W(m = mi,pi = mu_new)
W_new <- D(pi = mu_new)
D_new
<- chol2inv(chol( I_fs(X = X,W = W_new) ))
I_inv
#formulasi fisher scoring
<- beta_new +I_inv %*% S(X=X,W = W_new,D = D_new,y =y ,mu = mu_new)
beta_new
#menghitung stopping criterion
<- stop_criterion(
stop_criterion_iter beta_old = beta_old,
beta_new = beta_new,
tol = tolerance)
#iterasi berhenti jika sudah memenuhi stopping criterion
if(stop_criterion_iter$status) break
}
Saat Convergent maka kita mendapatkan
<- list(iter_convergence=i,coefficients=as.numeric(beta_new),var_beta=I_inv)
result_fs $iter_convergence result_fs
[1] 31
$var_beta result_fs
6 x 6 Matrix of class "dpoMatrix"
(Intercept) balance age housingyes
(Intercept) 2.743637e-01 -5.891964e-07 -4.050466e-03 -3.584211e-02
balance -5.891964e-07 1.308069e-09 -3.003060e-08 6.255880e-07
age -4.050466e-03 -3.003060e-08 8.377425e-05 3.146135e-04
housingyes -3.584211e-02 6.255880e-07 3.146135e-04 3.568298e-02
maritalmarried -7.623728e-02 -2.022522e-07 2.006810e-04 5.139815e-03
maritalsingle -1.230458e-01 -4.027429e-07 1.169965e-03 8.360438e-03
maritalmarried maritalsingle
(Intercept) -7.623728e-02 -1.230458e-01
balance -2.022522e-07 -4.027429e-07
age 2.006810e-04 1.169965e-03
housingyes 5.139815e-03 8.360438e-03
maritalmarried 8.060487e-02 6.751034e-02
maritalsingle 6.751034e-02 1.072151e-01
<- data.frame(variables=rownames(beta_new),
summary_fs Estimate=round(result_fs$coefficients,10),
Std_Err=round(sqrt(diag(as.matrix(result_fs$var_beta))),10),row.names = NULL) %>%
mutate(z=Estimate / Std_Err, p_value=round(2*pnorm(q=abs(z),lower.tail = F),3))
summary_fs
variables <chr> | Estimate <dbl> | Std_Err <dbl> | z <dbl> | p_value <dbl> |
---|---|---|---|---|
(Intercept) | 0.2300633445 | 0.5237974260 | 0.4392220 | 0.661 |
balance | 0.0000557833 | 0.0000361672 | 1.5423726 | 0.123 |
age | 0.0024653247 | 0.0091528273 | 0.2693512 | 0.788 |
housingyes | -0.7690447815 | 0.1888993831 | -4.0711874 | 0.000 |
maritalmarried | -0.1661114916 | 0.2839099667 | -0.5850851 | 0.558 |
maritalsingle | 0.1087506638 | 0.3274371898 | 0.3321268 | 0.740 |
IRLS
matriks
kita tuliskan matriks
<- function(X,beta,D,y,mu){
z <- chol2inv(chol(D))
inv_D %*% beta + inv_D %*% (y-mu)
X }
misalkan
set.seed(2045)
<- W(m = 1,pi = rep(0.5,nrow(X)))
W_coba dim(W_coba)
[1] 500 500
1:5,1:5] W_coba[
5 x 5 diagonal matrix of class "ddiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] 0.25 . . . .
[2,] . 0.25 . . .
[3,] . . 0.25 . .
[4,] . . . 0.25 .
[5,] . . . . 0.25
<- c(0.230,0.001,0.002,-0.769,-0.166,0.109)
beta_coba beta_coba
[1] 0.230 0.001 0.002 -0.769 -0.166 0.109
<- D(pi = rep(0.5,nrow(X)))
D_coba dim(mu_coba)
[1] 500 1
1:5,1:5] D_coba[
5 x 5 diagonal matrix of class "ddiMatrix"
[,1] [,2] [,3] [,4] [,5]
[1,] 0.25 . . . .
[2,] . 0.25 . . .
[3,] . . 0.25 . .
[4,] . . . 0.25 .
[5,] . . . . 0.25
<- as.matrix( runif(n = nrow(X),min = 0,max = 1 ) )
mu_coba dim(mu_coba)
[1] 500 1
head(mu_coba)
[,1]
[1,] 0.77550659
[2,] 0.94264755
[3,] 0.47241345
[4,] 0.08665263
[5,] 0.63646300
[6,] 0.42453500
<- z(X = X,beta = beta_coba,D = D_coba,y = y,mu = mu_coba)
z_coba dim(z_coba)
[1] 500 1
1:10] z_coba[
[1] -3.1200264 2.2434098 -1.6766538 -0.8296105 -1.7598520 -1.7851400
[7] -3.2987687 -1.1895575 -1.5743409 0.1808609
Supaya lebih mudah, kita kumpulkan semua definisi matriks yang sudah dituliskan ke program R dibawah ini
<- function(X,beta){
mu exp(X %*% beta) / ( 1+exp( X%*% beta) )
}
<- function(m,pi){
W <- m * pi * ( 1- pi )
wi Diagonal(x=as.numeric(wi))
}
<- function(pi){
D <- pi * ( 1- pi )
di Diagonal(x=as.numeric(di))
}
<- function(X,beta,D,y,mu){
z <- chol2inv(chol(D))
inv_D %*% beta + inv_D %*% (y-mu)
X }
Selanjutnya kita akan mulai menuliskan program R untuk iterasi IRLS
<- function(beta_old,beta_new,tol) {
stop_criterion <- as.matrix(abs(beta_new-beta_old))
error <- all(error < tol)
status return(list(error=as.data.frame(t(error)),status=status))
}
stop_criterion(beta_old = c(0.5,-0.9),beta_new = c(0.51,-0.91),tol = 1e-5)
$error
V1 V2
1 0.01 0.01
$status
[1] FALSE
stop_criterion(beta_old = c(0.5,-0.9),beta_new = c(0.50001,-0.90001),tol = 1e-5)
$error
V1 V2
1 1e-05 1e-05
$status
[1] TRUE
stop_criterion(beta_old = c(0.5,-0.9),beta_new = c(0.5001,-0.90001),tol = 1e-5)
$error
V1 V2
1 1e-04 1e-05
$status
[1] FALSE
berikut adalah nilai awal dari subscribed
adalah respon distribusi bernouli.
<- rep(0,ncol(X))
beta_new names(beta_new) <- colnames(X)
beta_new
(Intercept) balance age housingyes maritalmarried
0 0 0 0 0
maritalsingle
0
<- 1000
max_iter <- 1
mi
<- 1e-15 tolerance
Ingat Formula IRLS yang digunakan adalah sebagai berikut:
for(i in 1:max_iter){
#menyimpan beta lama
<- beta_new
beta_old
<- mu(X = X,beta = beta_new)
mu_new <- W(m = mi,pi = mu_new)
W_new <- D(pi = mu_new)
D_new
<- z(X = X,beta = beta_new,D = D_new,y = y,mu = mu_new)
z_new <- chol2inv(chol(crossprod(X,W_new %*% X)))
inv_XWX
#formulasi fisher scoring
<- inv_XWX %*% crossprod(X,W_new %*% z_new)
beta_new
#menghitung stopping criterion
<- stop_criterion(
stop_criterion_iter beta_old = beta_old,
beta_new = beta_new,
tol = tolerance)
#iterasi berhenti jika sudah memenuhi stopping criterion
if(stop_criterion_iter$status) break
}
Saat Convergent maka kita mendapatkan
kemudian standard error dari
<- list(iter_convergence=i,coefficients=as.numeric(beta_new),var_beta=inv_XWX)
result_irls $iter_convergence result_irls
[1] 33
$var_beta result_irls
6 x 6 Matrix of class "dpoMatrix"
(Intercept) balance age housingyes
(Intercept) 2.743637e-01 -5.891964e-07 -4.050466e-03 -3.584211e-02
balance -5.891964e-07 1.308069e-09 -3.003060e-08 6.255880e-07
age -4.050466e-03 -3.003060e-08 8.377425e-05 3.146135e-04
housingyes -3.584211e-02 6.255880e-07 3.146135e-04 3.568298e-02
maritalmarried -7.623728e-02 -2.022522e-07 2.006810e-04 5.139815e-03
maritalsingle -1.230458e-01 -4.027429e-07 1.169965e-03 8.360438e-03
maritalmarried maritalsingle
(Intercept) -7.623728e-02 -1.230458e-01
balance -2.022522e-07 -4.027429e-07
age 2.006810e-04 1.169965e-03
housingyes 5.139815e-03 8.360438e-03
maritalmarried 8.060487e-02 6.751034e-02
maritalsingle 6.751034e-02 1.072151e-01
<- data.frame(variables=rownames(beta_new),
summary_irls Estimate=round(result_irls$coefficients,10),
Std_Err=round(sqrt(diag(as.matrix(result_irls$var_beta))),10),row.names = NULL) %>%
mutate(z=Estimate / Std_Err, p_value=round(2*pnorm(q=abs(z),lower.tail = F),3))
summary_irls
variables <chr> | Estimate <dbl> | Std_Err <dbl> | z <dbl> | p_value <dbl> |
---|---|---|---|---|
(Intercept) | 0.2300633445 | 0.5237974260 | 0.4392220 | 0.661 |
balance | 0.0000557833 | 0.0000361672 | 1.5423726 | 0.123 |
age | 0.0024653247 | 0.0091528273 | 0.2693512 | 0.788 |
housingyes | -0.7690447815 | 0.1888993831 | -4.0711874 | 0.000 |
maritalmarried | -0.1661114916 | 0.2839099667 | -0.5850851 | 0.558 |
maritalsingle | 0.1087506638 | 0.3274371898 | 0.3321268 | 0.740 |
Default fungsi R
<- glm(formula = subscribed~.,
mod1 data = bank_marketing_mini,
family = binomial(link = "logit"))
tidy(mod1) %>%
mutate(across(where(is.numeric),function(x) round(x,7)))
term <chr> | estimate <dbl> | std.error <dbl> | statistic <dbl> | p.value <dbl> |
---|---|---|---|---|
(Intercept) | 0.2300633 | 0.5237974 | 0.4392220 | 0.6605007 |
balance | 0.0000558 | 0.0000362 | 1.5423725 | 0.1229831 |
age | 0.0024653 | 0.0091528 | 0.2693512 | 0.7876595 |
housingyes | -0.7690448 | 0.1888994 | -4.0711874 | 0.0000468 |
maritalmarried | -0.1661115 | 0.2839100 | -0.5850851 | 0.5584905 |
maritalsingle | 0.1087507 | 0.3274372 | 0.3321268 | 0.7397935 |