OPTIMASI
~ Multivariat ~
Nb: Untuk segala bentuk diskusi, kritik dan saran mengenai materi silahkan hubungi admin!
Alamat | Sebagai Berikut : \(\downarrow\) |
dsciencelabs@outlook.com | |
https://www.instagram.com/dsciencelabs/ | |
RPubs | https://rpubs.com/dsciencelabs/ |
Github | https://github.com/dsciencelabs/ |
Telegram | @dsciencelabs |
Dalam ilmu matematika, permasalahan optimasi yang paling umum ditemukan adalah pemrograman nonlinier (Non Linear Programming). Dimana proses pemecahan masalah optimasi ini mencakup beberapa kendala atau fungsi tujuan nonlinier. Sehingga, pada bagian ini akan dibahas sub-bidang optimasi matematika yang berhubungan dengan masalah yang tidak linier. Untuk pemahaman lebih lanjut, perhatikan fungsi nonliear dua dimensi dan tiga dimensi sebagai berikut.
Fungsi Dua Dimensi
Pada gambar berikut, fungsi dua dimensi dibawah ini memperlihatkan daerah yang berwana biru adalah daerah penyelesain yang layak. Garis singgung dengan daerah fisibel merupakan solusi optimum yang diharapkan.
Masalah sederhana ini dapat didefinisikan oleh kendala:
\[ \begin{eqnarray} x_1 & \ge & 0 \\ x_2 & \ge & 0 \\ x_1^2 + x_2^2 & \ge & 1 \\ x_1^2 + x_2^2 & \le & 2 \end{eqnarray} \] dengan fungsi tujuan yang akan dimaksimalkan adalah
\[ f(x) = x_1 + x_2 , \text{dimana} \space x = (x_1, x_2). \]
Fungsi Tiga Dimensi
Gambar fungsi tiga dimensi dibawah ini memperlihatkan bahwa garis singgung pada suatu permukaan ruang terbatas diposisi tengah mewakili solusi optimal.
Masalah sederhana ini dapat didefinisikan oleh kendala:
\[ \begin{eqnarray} x_1^2 − x_2^2 + x_3^2 \le 2 \\ x_1^2 + x_2^2 + x_3^2 \le 10 \\ \end{eqnarray} \]
dengan fungsi tujuan yang akan dimaksimalkan
\[f(x) = x_1x_2 + x_2x_3, \text{dimana} \space x = (x_1, x_2, x_3).\]
Metode Nelder-Mead
Algoritma ini dapat digunakan untuk fungsi yang memiliki satu variabel hingga lebih dari satu peubah. Dalam hal ini, R telah menyiapkan fungsi optimasi dengan salah satu algoritmanya adalah Nelder-Mead:
- optimize atau optimise (satu peubah)
- optim (lebih dari satu peubah)
Untuk memulai pembahasan ini perlu ditujau ulang mengenai kasus optimasi dengan satu peubah sebagai berikut:
Contoh 1
Mencari nilai maksimum dari \(sin(x) + sin(2 * x) + cos(3 * x)\) dengan interval \([0,2\pi]\)**
<- function(x) sin(x) + sin(2 * x) + cos(3 * x)
f1 curve(f1, from = 0, to = 2 * pi)
Permasalahan tersebut adalah model optimasi satu peubah, sebaiknya
diselesaikan dengan fungsi optimize
yang sudah tertanam
dalam R untuk mendapatkan nilai minimum dari suatu fungsi dengan satu
peubah:
optimize(f1, interval = c(0, 2*pi)) # minimum lokal
## $minimum
## [1] 3.033129
##
## $objective
## [1] -1.054505
optimize(f1, interval = c(4, 2*pi)) # minimum global
## $minimum
## [1] 5.273383
##
## $objective
## [1] -2.741405
optimize(f1, interval = c(0, 2*pi), maximum = T) # maksimum lokal
## $maximum
## [1] 4.0598
##
## $objective
## [1] 1.096473
optimize(f1, interval = c(0, 1.5), maximum = T) # maksimum global
## $maximum
## [1] 0.3323289
##
## $objective
## [1] 1.485871
Permasalahan optimasi dengan fungsi satu variabel sudah terselesaikan dengan baik. Berikut ini akan dibahas bagaimana kita mencari nilai minimum dari fungsi lebih dari satu peubah.
Contoh 2:
Mencari nilai \(x_1\) dan \(x2\) yang pada fungsi \[f(x_1,x_2)=100(x_2−x_1^2)^2+(1−x_1)^2\]
<- function (x) {
f2 <- x[1]
x1 <- x[2]
x2 100*(x2 - x1^2)^2 + (1 - x1)^2
}
optim(c( -3 ,3) , f2)
## $par
## [1] 1.006766 1.013557
##
## $value
## [1] 4.582775e-05
##
## $counts
## function gradient
## 193 NA
##
## $convergence
## [1] 0
##
## $message
## NULL
Metode Steepest Descent
Metode Steepest Descent bekerja pada fungsi yang memiliki turunan tunggal. Ini paling sering digunakan dalam masalah yang melibatkan lebih dari 1 variabel. Ide penting dari penurunan paling curam adalah bahwa fungsi menurun paling cepat ke arah gradien negatif. Mari kita asumsikan kita memiliki fungsi berikut:
\[f(x)=f(x_1,x_2,\cdots, x_n)\]
Tujuannya adalah untuk menemukan nilai maksimum atau minimum (sesuai dengan tujuan kita).
Misalnya, jika kita mencoba meminimalkan fungsi \(f\) yang dimulai dari \(x_n\) dengan menggunakan pendekatan Stepest Descent, atau \(-f′(x_n)\). Pendekatan ini sering dikatakan sebagai arah yang ortogonal terhadap kontur \(f\) di titik \(x_n\) di mana \(f\) berubah paling cepat di \(x_n\).
Algoritma Steepest Descent
- Metode ini dimulai pada tebakan awal \(x\).
- Tebakan berikutnya dibuat dengan bergerak ke arah gradien negatif. Lokasi minimum sepanjang garis ini kemudian dapat ditemukan dengan menggunakan algoritma pencarian satu dimensi seperti metode Golden Section.
- Pembaruan ke-n kemudian
\[x_n=x_{n−1}−\alpha f'(x_{n−1})\]
di mana \(\alpha\) dipilih untuk meminimalkan fungsi satu dimensi:
\[g(\alpha)=f(x_{n−1}−\alpha f'(x_{n−1}))\]
Sehingga dalam hal ini, kita perlu mengasumsikan bahwa \(\alpha\) berada dalam suatu interval. Jadi dalam hal ini, kita ambil intervalnya menjadi \([0,h]\) dimana \(h\) adalah nilai yang harus kita pilih. Berikut ini adalah Algoritma Steepest Descent di dalam R.
<- function(f, fprime, start, h,
steepestdescent tol=1e-7, maxiter=100) {
<- start
x <- function(alpha) { f(x - alpha*fpx) }
g <- 0
niter while(niter < maxiter & sum(abs(fprime(x))) > tol) {
<- fprime(x)
fpx <- golden(g, 0, h)
alpha <- x - alpha*fpx
x <- niter + 1
niter
}if (niter == maxiter) {
print("Warning: Maximum number of iterations reached")
}c("Minimizer" = x)
}
Proses optimasi Steepest Descent secara visualisasi dapat dilihat pada grafik berikut, atau klik di sini untuk melihat contoh lain dari metode Steepest Descent ini, khususnya untuk fungsi kuadrat. selengkapnya
Steepest Descent Dua Dimensi
Berikut ini adalah contoh penerapan Steepest Descent untuk melakukan proses optimasi minimal dengan fungsi 2 variabel.
\[f(x)=f(x_1,x_2)={(2−x_1)^2\over 2x_2^2} + {(3−x_1)^2 \over 2x_2^2}+log(x_2)\]
Perhatikan bahwa \(x_2\) harus positif, jadi kita mungkin perlu melindungi dari nilai negatif \(x_2\).
Pertama: Kita membutuhkan fungsi untuk fungsi dan gradien
<- function(x) {
f1 2-x[1])^2/(2*x[2]^2) +(3-x[1])^2/(2*x[2]^2) + log(x[2])
(
}<- function(x) {
f1prime c(-(2-x[1])/x[2]^2 - (3-x[1])/x[2]^2,-(2-x[1])^2/x[2]^3 -(3-x[1])^2/x[2]^3 + 1/x[2])
}
Kedua: Lokasi minimum di sepanjang garis ini kemudian dapat ditemukan dengan menggunakan algoritma pencarian satu dimensi seperti metode Golden Section.
<- function (f, a, b, tol = 0.0000001)
golden
{<- 2 / (sqrt(5) + 1)
ratio <- b - ratio * (b - a)
x1 <- a + ratio * (b - a)
x2 <- f(x1)
f1 <- f(x2)
f2
while(abs(b - a) > tol) {
if (f2 > f1) {
<- x2
b <- x1
x2 <- f1
f2 <- b - ratio * (b - a)
x1 <- f(x1)
f1 else {
} <- x1
a <- x2
x1 <- f2
f1 <- a + ratio * (b - a)
x2 <- f(x2)
f2
}
}return((a + b) / 2)
}
Ketiga: Mari kita coba nilai awal x=(.1,.1).
steepestdescent(f1, f1prime, start=c(.1,.1), h=.1)
## [1] "Warning: Maximum number of iterations reached"
## Minimizer1 Minimizer2
## 2.4992967 0.7123675
Pada posisi ini kita belum menemukan nilai konvergennya, sehingga dilajutkan pada langkah berikutnya.
Keempat: Dalam hal ini kita kembali ke iterasa pertama dengan menggunakan hasil terbaru sebagai tebakan awal.
steepestdescent(f1, f1prime,start=c(2.4992967, 0.7123675), h=.1)
## Minimizer1 Minimizer2
## 2.5000000 0.7071068
steepestdescent(f1, f1prime, start=c(.1, .1), h=.1,maxiter=200)
## Minimizer1 Minimizer2
## 2.5000000 0.7071068
Selesai. Nilainya sudah konvergen.
Multivariat Normal
Kita dapat menggunakan Steepest Descent untuk menghitung perkiraan kemungkinan maksimum rata-rata dalam distibusi multivariate normal, yang diberikan sampel data. Namun, ketika data sangat berkorelasi, seperti pada contoh simulasi di bawah, permukaan kemungkinan log bisa menjadi sulit untuk dioptimalkan. Dalam kasus seperti itu, ridge yang sangat sempit berkembang dalam kemungkinan log yang dapat menyulitkan algoritma Steepest Descent untuk dinavigasi.
Pada contoh di bawah, kita sebenarnya menghitung kemungkinan log negatif karena algoritme dirancang untuk meminimalkan fungsi.
set.seed(2020-09-30)
<- c(1, 2)
mu <- rbind(c(1, .9), c(.9, 1))
S <- MASS::mvrnorm(500, mu, S)
x <- function(mu1, mu2) {
nloglike <- mvtnorm::dmvnorm(x, c(mu1, mu2), S, log = TRUE)
dmv -sum(dmv)
}<- Vectorize(nloglike, c("mu1", "mu2"))
nloglike <- 40
nx <- 40
ny <- seq(-5, 5, len = nx)
xg <- seq(-5, 6, len = ny)
yg <- expand.grid(xg, yg)
g <- nloglike(g[, 1], g[, 2])
nLL <- matrix(nLL, nx, ny)
z par(mar = c(4.5, 4.5, 1, 1))
contour(xg, yg, z, nlevels = 40, xlab = expression(mu[1]),
ylab = expression(mu[2]))
abline(h = 0, v = 0, lty = 2)
Perhatikan bahwa pada gambar di atas permukaan sangat membentang dan minimum (1,2) terletak di tengah lembah sempit. Untuk algoritma Steepest Descent kita akan mulai pada titik (−5,−2) dan melacak jalur algoritma.
library(dplyr, warn.conflicts = FALSE)
<- function(x) x / sqrt(sum(x^2))
norm <- solve(S)
Sinv <- function(mu, alpha = 1) {
step1 <- sweep(x, 2, mu, "-")
D <- colSums(D) %>% norm
score + alpha * drop(Sinv %*% score)
mu
}<- function(mu, n = 10, ...) {
steep <- vector("list", length = n)
results for(i in seq_len(n)) {
<- step1(mu, ...)
results[[i]] <- results[[i]]
mu
}
results
}<- do.call("rbind", steep(c(-5, -2), 8))
m <- rbind(c(-5, -2), m)
m
par(mar = c(4.5, 4.5, 1, 1))
contour(xg, yg, z, nlevels = 40, xlab = expression(mu[1]),
ylab = expression(mu[2]))
abline(h = 0, v = 0, lty = 2)
points(m, pch = 20, type = "b")
Kita dapat melihat bahwa jalur algoritma agak berliku karena melintasi lembah yang sempit. Dalam hal ini, kita telah memperbaiki panjang langkah dalam kasus ini, yang mungkin saja tidak optimal. Namun, kita sudah dapat melihat bahwa algoritma memiliki beberapa kesulitan dalam menavigasi permukaan karena arah penurunan paling curam tidak selalu mengarah ke arah minimum.
Metode Arah Newton
Mengingat perkiraan terbaik saat ini adalah \(x_n\), kita dapat memperkirakan \(f\) dengan polinomial kuadrat. Untuk beberapa kecil \(p\), \[f(x_n+P)\approx f(x_n)+p'f'(x_n)+\frac{1}{2}p'f''(x_n)p.\] Jika kita meminimalkan ruas kanan terhadap \(p\), kita peroleh
\[p_n=f''(x_n)^{-1}[-f'(x_n)]\] Yang dapat kita anggap sebagai arah penurunan paling curam “dipelintir” oleh invers dari matriks Hessian \(f''(x_n)^{-1}\). Metode Newton memiliki panjang langkah “alami” 1, sehingga prosedur pembaruannya adalah
\[x_{n+1}=x_n-f''(x_n)^{-1}f'(x_n)\].
Metode Newton membuat pendekatan kuadrat untuk menargetkan fungsi \(f\) pada setiap langkah algoritma. Ini mengikuti prinsip “transfer optimasi” yang disebutkan sebelumnya, di mana kita mengambil fungsi kompleks \(f\). ganti dengan fungsi yang lebih sederhana \(g\) yang lebih mudah untuk dioptimasi dan kemudian optimalkan fungsi yang lebih sederhana secara berulang-ulang hingga konvergen ke situasi tersebut.
Kita dapat memvisualisasikan bagaimana metode Newton membuat pendekatan kuadratnya ke fungsi target dengan mudah dalam satu dimensi.
curve(-dnorm(x), -2, 3, lwd = 2, ylim = c(-0.55, .1))
<- -1.2
xn abline(v = xn, lty = 2)
axis(3, xn, expression(x[n]))
<- function(x) {
g -dnorm(xn) + (x-xn)*xn*dnorm(xn)-0.5*(x-xn)^2*(dnorm(xn)-xn*(xn*dnorm(xn)))
}curve(g, -2, 3, add = TRUE, col = 4)
<- optimize(g, c(0, 3))
opabline(v = op$minimum, lty = 2)
axis(3, op$minimum, expression(x[n+1]))
Pada gambar di atas, iterasi berikutnya, \(x_{n+1}\) sebenarnya lebih jauh dari minimum dari iterasi sebelumnya \(x_n\). Pendekatan kuadrat yang dibuat oleh metode Newton ke \(f\) tidak dijamin baik di setiap titik fungsi.
Ini menunjukkan “fitur” penting dari metode Newton, yaitu tidak monoton. Iterasi berturut-turut yang dihasilkan metode Newton tidak dijamin sebagai perbaikan dalam arti bahwa setiap iterasi semakin mendekati kebenaran. Tradeoff di sini adalah bahwa sementara metode Newton sangat cepat (konvergensi kuadrat), kadang-kadang bisa tidak stabil. Algoritme monoton (seperti algoritme EM yang akan dibahas nanti) yang selalu menghasilkan peningkatan, lebih stabil tetapi umumnya bertemu pada kecepatan yang lebih lambat.
Namun, pada gambar berikutnya, kita dapat melihat bahwa solusi yang diberikan oleh aproksimasi berikutnya, \(x_{n+2}\), memang cukup mendekati nilai minimum yang sebenarnya.
curve(-dnorm(x), -2, 3, lwd = 2, ylim = c(-0.55, .1))
<- -1.2
xn <- optimize(g, c(0, 3))
op abline(v = op$minimum, lty = 2)
axis(3, op$minimum, expression(x[n+1]))
<- op$minimum
xn curve(g, -2, 3, add = TRUE, col = 4)
<- optimize(g, c(0, 3))
op abline(v = op$minimum, lty = 2)
axis(3, op$minimum, expression(x[n+2]))
Perlu dicatat bahwa dalam kejadian langka bahwa \(f\) sebenarnya adalah polinomial kuadrat, metode Newton akan konvergen dalam satu langkah karena pendekatan kuadrat yang dibuatnya ke \(f\) akan tepat.
Model Linier Umum
Model linier umum (Generalized Linear Model) adalah perluasan dari model linier sederhana yang memungkinkan obeservasi berdistribusi tidak ormal. Distribusi yang digunakan biasanya berasal dari keluarga eksponensial yang fungsi densitasnya memiliki beberapa karakteristik yang sama. Dengan GLM, kita biasanya menyajikannya sebagai \(y_i \sim p(y_i|\mu_i)\), di mana \(p\) adalah distribusi keluarga eksponensial, \(\mathbb{E}[y_i]=\mu_i\),
\[g(\mu_i)=x'_i\beta,\]
di mana \(g\) adalah fungsi nonlinier, dan \(Var(y_i)=V(\mu)\) dengan \(V\) adalah fungsi varians yang diketahui.
Berbeda dengan model linier sederhana, estimasi kemungkinan maksimum dari parameter vektor \(\beta\) tidak dapat diperoleh dalam bentuk tertutup, sehingga algoritma iteratif harus digunakan untuk mendapatkan estimasi. Algoritma klasik yang digunakan adalah algoritma penilaian Fisher. Algoritma ini menggunakan pendekatan linier untuk fungsi nonlinier \(g\), yang dapat ditulis sebagai:
\[g(y_i)\approx g(\mu_i)+(y_i-\mu_i)g'(\mu_i).\]
Notasi khas GLM mengacu pada \(z_i=g(\mu_i)+(y_i-\mu_i)g'(\mu_i)\) sebagai variabel respon. Algoritma penilaian Fisher kemudian bekerja sebagai berikut:
- Mulailah dengan \(\hat{\mu_i}\), beberapa nilai awal.
- Hitung \(z_i=g(\mu_i)+(y_i-\mu_i)g'(\mu_i)\).
- Mengingat \(n \times 1\) adalah vektor variabel respon \(z\) dan matriks prediktor \(n \times p\) \(X\) kita dapat menghitung regresi berbobot \(z\) pada \(X\) untuk mendapatkan \[\beta_n=(X'WX)^{-1}X'Wz\] di mana \(W\) adalah matriks diagonal dengan elemen diagonal \[w_{ii}=[g'(\mu_i)^2V(\mu_i)]^{-1}\].
- Mengingat \(\beta_n,\) kita dapat menghitung ulang \(\hat{\mu_i}=g^{-1}(x'_i\beta_n)\) dan pergi ke 2.
Perhatikan bahwa pada Langkah 3 di atas, bobot hanyalah kebalikan dari varians \(z_i\), yaitu.
\[\begin {align*} Var(z_i) & = Var(g(\mu)+(y_i-\mu_i)g'(\mu_i))\\ & =Var((y_i-\mu_i)g'(\mu_i))\\ & =V(\mu_i)g'(\mu_i)^2 \end{align*}\]
Secara alami, ketika melakukan regresi berbobot, kita akan menggunakan fungsi kebalikan dari variansinya.
Contoh: Regresi Poisson
Untuk regresi Poisson, kita memiliki \(y_i\sim Poisson (\mu_i)\) dimana \(g(\mu)=log \space \mu_i=x'_i\beta\) karena log adalah fungsi tautan kanonik untuk distribusi Poisson, kita juga memiliki \(g'(\mu_i)=\frac{1}{\mu_i}\) dan \(V(\mu_i)=\mu_i.\) Oleh karena itu, algoritma penskoran Fisher adalah 1. Inisialisasi \(\hat{\mu_i},\) mungkin menggunakan \(y_i+1\) (untuk menghindari nol). 2. Biarkan \(z_i=log \space \hat{\mu_i}+(y_i-\hat{\mu_i})\frac{1}{\hat{\mu_i}}\) 3. Regresi \(z\) pada \(X\) menggunakan bobot \[w_{ii}=[\frac{1}{\hat{\mu_i^2}}\hat{\mu_i}]^{-1}=\hat{\mu_i}.\]
Dengan menggunakan contoh regresi Poisson, kita dapat menarik hubungan antara algoritma penilaian Fisher yang biasa untuk pemasangan GLM dan metode Newton. Ingat bahwa jika \(l(\beta)\) adalah kemungkinan log sebagai fungsi dari parameter regresi \(\beta\), maka skema pembaruan Newton adalah \[\beta_{n+1}=\beta_n+l''(\beta_n)^{-1}[-l'(\beta_n)].\]
Kemungkinan log untuk model regresi Poisson dapat ditulis dalam bentuk vektor/matriks sebagai \[l(\beta)=y'X\beta- exp(X\beta)'1\] di mana eksponensial diambil berdasarkan komponen pada vektor \(X\beta\). Fungsi gradiennya adalah \[l'(\beta)=X'y-X'x(X\beta)=X'(y-\mu)\] dan Hessian adalah \[l''(\beta)=-X'WX\] di mana \(W\) adalah matriks diagonal dengan nilai \(w_{ii}=exp(x'_i\beta)\) pada diagonalnya. Iterasi Newton adalah
\[\begin {eqnarray*} \beta_{n+1}=\beta_n+(-X'WX)^{-1}(-X'(y-\mu))\\ =\beta_n+(X'WX)^{-1}XW(z-X\beta_n)\\ =(X'WX)^{-1}X'Wz+\beta_n-(X'WX)^{-1}X'WX\beta_n\\ =(X'WX)^{-1}X'Wz \end {eqnarray*}\]
Oleh karena itu iterasinya persis sama dengan algoritma penilaian Fisher dalam kasus ini. Secara umum, metode Newton dan penskoran Fisher akan bertepatan dengan model linier umum apa pun yang menggunakan keluarga eksponensial dengan fungsi tautan kanonik.
Arah Newton dengan R
Fungsi nlm()
dalam R mengimplementasikan metode Newton
untuk meminimalkan fungsi yang diberikan vektor yang menyatakan nilai.
Secara default, seseorang tidak perlu menyediakan fungsi gradien atau
Hessian; mereka akan diestimasi secara numerik oleh algoritma. Namun,
untuk tujuan meningkatkan akurasi algoritme, gradien dan Hessian dapat
diberikan sebagai atribut dari fungsi target.
Sebagai contoh, kita akan menggunakan fungsi nlm()
agar
sesuai dengan model regresi logistik sederhana untuk data biner. Model
ini menetapkan bahwa \(y_i \sim \space
Bernoulli(p_i)\) where \[log
\frac{p_i}{1-p_i}=\beta_0+x_i\beta_1\]
dan tujuannya adalah untuk memperkirakan \(\beta\) melalui kemungkinan maksimum. Mengingat distribusi Bernoulli yang diasumsikan, kita dapat menulis kemungkinan log untuk satu pengamatan sebagai:
\[\begin{eqnarray*} \log L(\beta) & = & \log\left\{\prod_{i=1}^n p_i^{y_i}(1-p_i)^{1-y_i}\right\}\\ & = & \sum_{i=1}^n y_i\log p_i + (1-y_i)\log(1-p_i)\\ & = & \sum_{i=1}^n y_i\log\frac{p_i}{1-p_i}+\log(1-p_i)\\ & = & \sum_{i=1}^n y_i(\beta_0 + x_i\beta_1) + \log\left(\frac{1}{1+e^{(\beta_0 + x_i\beta_1)}}\right)\\ & = & \sum_{i=1}^n y_i(\beta_0 + x_i\beta_1) -\log\left(1+e^{(\beta_0 + x_i\beta_1)}\right) \end{eqnarray*}\]
Jika kita mengambil baris terakhir dari turunan di atas dan mengambil satu elemen di dalam jumlah, kita memiliki
\[\ell_i(\beta)=y_i(\beta_0 + x_i\beta_1) -\log\left(1+e^{(\beta_0 + x_i\beta_1)}\right)\]
Kita akan membutuhkan gradien dan Hessian untuk \(\beta\). Karena jumlah dan turunannya dapat
dipertukarkan, kita kemudian dapat menjumlahkan masing-masing gradien
individu dan Hessian untuk mendapatkan gradien penuh dan Hessian untuk
seluruh sampel, sehingga \[\ell^\prime(\beta)
= \sum_{i=1}^n\ell_i^\prime(\beta)\] dan \[\ell^\prime(\beta) =
\sum_{i=1}^n\ell_i^{\prime\prime}(\beta)\] Sekarang, mengambil
gradien dan Hessian dari ekspresi di atas mungkin agak merepotkan.
Namun, R menyediakan cara otomatis untuk melakukan diferensiasi simbolik
sehingga pekerjaan manual dapat dihindari. Fungsi deriv()
menghitung gradien dan Hessian dari ekspresi secara simbolis sehingga
dapat digunakan dalam rutinitas minimalisasi. Itu tidak dapat menghitung
gradien ekspresi arbitrer, tetapi mendukung berbagai fungsi statistik
umum.
Contoh: Tren nilai-p Sepanjang Waktu
Paket tidypvals
yang ditulis oleh Jeff
Leek berisi kumpulan data yang diambil dari literatur yang
mengumpulkan nilai-p yang terkait dengan berbagai publikasi bersama
dengan beberapa informasi tentang publikasi tersebut (yaitu jurnal,
tahun, DOI). Satu pertanyaan yang muncul adalah apakah ada tren dari
waktu ke waktu dalam signifikansi statistik yang diklaim dari publikasi,
di mana “signifikansi statistik” didefinisikan memiliki nilai \(p\) kurang dari 0,05.
Paket tidypvals
tersedia dari GitHub dan dapat diinstal
menggunakan fungsi install_github()
dalam paket
remotes
.
::install_github("jtleek/tidypvals") remotes
Setelah terinstal, kami akan menggunakan kumpulan data
jager2014
. Secara khusus, kita tertarik untuk membuat
indikator apakah nilai \(p\) kurang
dari 0,05 dan melakukan regresi pada variabel tahun
.
# devtools::install_github('jtleek/tidypvals')
library(tidypvals)
library(dplyr)
<- mutate(tidypvals::jager2014,
jager pvalue = as.numeric(as.character(pvalue)),
y = ifelse(pvalue < 0.05 |(pvalue == 0.05 & operator == "lessthan"), 1, 0),
x = year - 2000) %>% tbl_df
jager
Perhatikan di sini bahwa kita telah mengurangi tahun 2000 dari
variabel tahun
sehingga \(x=0\) sesuai dengan
tahun==2000
.
Selanjutnya kita menghitung gradien dan Hessian dari kemungkinan log
negatif sehubungan dengan \(\beta_0\)
dan \(\beta_1\) menggunakan fungsi
deriv()
. Di bawah ini, kami menetapkan
function.arg=TRUE
dalam panggilan ke deriv()
karena kami ingin deriv()
mengembalikan fungsi
yang argumennya adalah b0
dan b1
.
<- deriv(~ -(y * (b0 + x * b1) - log(1 + exp(b0 + b1 * x))),
nll_one c("b0", "b1"), function.arg = TRUE, hessian = TRUE)
Berikut tampilan fungsinya.
nll_one
## function (b0, b1)
## {
## .expr6 <- exp(b0 + b1 * x)
## .expr7 <- 1 + .expr6
## .expr11 <- .expr6/.expr7
## .expr15 <- .expr7^2
## .expr18 <- .expr6 * x
## .expr19 <- .expr18/.expr7
## .value <- -(y * (b0 + x * b1) - log(.expr7))
## .grad <- array(0, c(length(.value), 2L), list(NULL, c("b0",
## "b1")))
## .hessian <- array(0, c(length(.value), 2L, 2L), list(NULL,
## c("b0", "b1"), c("b0", "b1")))
## .grad[, "b0"] <- -(y - .expr11)
## .hessian[, "b0", "b0"] <- .expr11 - .expr6 * .expr6/.expr15
## .hessian[, "b0", "b1"] <- .hessian[, "b1", "b0"] <- .expr19 -
## .expr6 * .expr18/.expr15
## .grad[, "b1"] <- -(y * x - .expr19)
## .hessian[, "b1", "b1"] <- .expr18 * x/.expr7 - .expr18 *
## .expr18/.expr15
## attr(.value, "gradient") <- .grad
## attr(.value, "hessian") <- .hessian
## .value
## }
Fungsi nll_one()
yang dihasilkan oleh
deriv()
mengevaluasi kemungkinan log negatif untuk setiap
titik data. Output dari nll_one()
akan memiliki atribut
"gradient"
dan "hessian"
yang masing-masing
mewakili gradien dan Hessian. Misalnya, dengan menggunakan data dari
kumpulan data jager
, kita dapat mengevaluasi kemungkinan
log negatif pada \(\beta_0=0,\beta_1=0.\)
<- jager$x
x <- jager$y
y str(nll_one(0, 0))
## num [1:15653] 0.693 0.693 0.693 0.693 0.693 ...
## - attr(*, "gradient")= num [1:15653, 1:2] -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : NULL
## .. ..$ : chr [1:2] "b0" "b1"
## - attr(*, "hessian")= num [1:15653, 1:2, 1:2] 0.25 0.25 0.25 0.25 0.25 0.25 0.25 0.25 0.25 0.25 ...
## ..- attr(*, "dimnames")=List of 3
## .. ..$ : NULL
## .. ..$ : chr [1:2] "b0" "b1"
## .. ..$ : chr [1:2] "b0" "b1"
Fungsi nll_one()
mengevaluasi kemungkinan log negatif
pada setiap titik data, tetapi tidak menjumlahkan poin seperti yang
diperlukan untuk mengevaluasi kemungkinan log negatif penuh. Oleh karena
itu, akan dituliskan fungsi terpisah yang dapat melakukan itu untuk
kemungkinan log negatif, gradien, dan Hessian.
<- function(b) {
nll <- nll_one(b[1], b[2])
v <- sum(v) ## log-likelihood
f <- colSums(attr(v, "gradient")) ## gradient vector
gr <- apply(attr(v, "hessian"), c(2, 3), sum) ## Hessian matrix
hess attributes(f) <- list(gradient = gr,
hessian = hess)
f }
Sekarang, kita dapat mengevaluasi kemungkinan log negatif penuh
dengan fungsi nll()
. Perhatikan bahwa nll()
mengambil satu vektor numerik sebagai input karena inilah yang
diharapkan oleh fungsi nlm()
.
nll(c(0, 0))
## [1] 10849.83
## attr(,"gradient")
## b0 b1
## -4586.5 -21854.5
## attr(,"hessian")
## b0 b1
## b0 3913.25 19618.25
## b1 19618.25 137733.75
Menggunakan \(\beta_0=0,\beta_1=0\)
sebagai nilai awal, kita dapat memanggil nlm()
untuk
meminimalkan kemungkinan log negatif.
<- nlm(nll, c(0, 0))
res res
## $minimum
## [1] 7956.976
##
## $estimate
## [1] 1.57032807 -0.04416515
##
## $gradient
## [1] -1.451746e-06 -2.782241e-06
##
## $code
## [1] 1
##
## $iterations
## [1] 4
Perhatikan terlebih dahulu pada output bahwa ada kode
dengan nilai 4
dan jumlah iterasinya adalah 100. Setiap
kali jumlah iterasi dalam algoritme pengoptimalan adalah bilangan bulat
yang bagus, kemungkinan besar ia memiliki beberapa batas iterasi yang
telah ditetapkan. Ini pada gilirannya biasanya berarti algoritme tidak
konvergen.
Dalam bantuan untuk nlm()
kita juga mempelajari bahwa
nilai kode
dari 4 berarti “batas iterasi terlampaui”, yang
umumnya tidak baik. Untungnya, solusinya sederhana: kita dapat
meningkatkan batas iterasi dan membiarkan algoritme berjalan lebih
lama.
<- nlm(nll, c(0, 0), iterlim = 1000)
res res
## $minimum
## [1] 7956.976
##
## $estimate
## [1] 1.57032807 -0.04416515
##
## $gradient
## [1] -1.451746e-06 -2.782241e-06
##
## $code
## [1] 1
##
## $iterations
## [1] 4
Dalam hal ini kita melihat bahwa jumlah iterasi yang digunakan adalah
260, yang jauh di bawah batas iterasi. Sekarang kita mendapatkan
kode
sama dengan 2
yang berarti bahwa “iterasi
berturut-turut dalam toleransi, iterasi saat ini mungkin solusi”. Itu
terdengar seperti kabar baik!
Terakhir, sebagian besar algoritme pengoptimalan memiliki opsi untuk
menskalakan nilai parameter Anda sehingga nilainya bervariasi secara
kasar pada skala yang sama. Jika fungsi target Anda memiliki parameter
yang bervariasi pada skala yang sangat berbeda, ini dapat menyebabkan
masalah praktis bagi komputer (ini bukan masalah bagi teori). Cara untuk
mengatasi hal ini di nlm()
adalah dengan menggunakan
argumen typsize
, yang merupakan vektor yang panjangnya sama
dengan vektor parameter yang menyediakan ukuran relatif parameter.
Di sini, saya memberikan typsize = c(1, 0.1)
, yang
menunjukkan kepada nlm()
bahwa parameter pertama, \(\beta_0\), kira-kira 10 kali lebih besar
dari parameter kedua, \(\beta_1\)
ketika fungsi target minimum.
<- nlm(nll, c(0, 0), iterlim = 1000,
res typsize = c(1, 0.1))
res
## $minimum
## [1] 7956.976
##
## $estimate
## [1] 1.57032807 -0.04416515
##
## $gradient
## [1] -1.451745e-06 -2.782238e-06
##
## $code
## [1] 1
##
## $iterations
## [1] 4
Dengan menjalankan nlm()
anda akan melihat bahwa
solusinya sama tetapi jumlah iterasi sebenarnya jauh lebih sedikit dari
sebelumnya (4 iterasi) yang berarti algoritme berjalan lebih cepat.
Secara umum, penskalaan vektor parameter dengan tepat (jika mungkin)
meningkatkan kinerja semua algoritme pengoptimalan dan menurut
pengalaman saya, hampir selalu merupakan ide yang bagus. Nilai spesifik
yang diberikan pada argumen typsize
tidak penting;
melainkan hubungan mereka satu sama lain (yaitu urutan besarnya) yang
penting.
Metode Quasi-Newton
Metode Quasi-Newton muncul dari keinginan untuk menggunakan sesuatu seperti metode Newton untuk kecepatannya tetapi tanpa harus menghitung matriks Hessian setiap saat. Idenya adalah jika iterasi Newton adalah \[\theta_{n+1}=\theta_n−f′′(\theta_n)−1f′(\theta_n)\] apakah ada matriks lain yang dapat kita gunakan untuk menggantikan keduanya \(f′′(\theta_n)\) atau \(f′′(\theta_n)−1\)? Yaitu dapatkah kita menggunakan iterasi yang direvisi, \[\theta_{n+1}=\theta_n−B^{−1}_n f′(θn)\] dimana \(B_n\) lebih sederhana untuk dihitung tetapi masih memungkinkan algoritme untuk menyatu dengan cepat.
Ini adalah masalah yang menantang karena \(f′′(θn)\) memberi kita banyak informasi tentang permukaan \(f\) di \(\theta_n\) dan membuang informasi ini mengakibatkan, ya, kehilangan informasi yang parah.
Ide dengan Quasi-Newton adalah menemukan solusi \(B_n\) untuk masalah
\[f′(\theta_n)−f′(\theta{n−1})=\theta_n(\theta_n−\theta_{n−1 })\].
Persamaan di atas kadang-kadang disebut sebagai persamaan garis potong. Perhatikan terlebih dahulu bahwa ini mengharuskan kita untuk menyimpan dua nilai, \(\theta_n\) dan \(\theta_{n−1}\). Juga, dalam satu dimensi, solusinya sepele: kita cukup membagi ruas kiri dengan \(\theta_n−\theta{n−1}\). Namun, di lebih dari satu dimensi, terdapat jumlah solusi yang tak terbatas dan kita memerlukan beberapa cara untuk membatasi masalah agar sampai pada jawaban yang masuk akal.
Kunci dari pendekatan Quasi-Newton, secara umum, adalah bahwa walaupun pada awalnya kita mungkin tidak memiliki banyak informasi tentang \(f\), dengan setiap iterasi kita memperoleh sedikit lebih banyak. Secara khusus, kita belajar lebih banyak tentang matriks Hessian melalui perbedaan berurutan dalam \(f′\). Oleh karena itu, dengan setiap iterasi, kami dapat memasukkan informasi yang baru diperoleh ini ke dalam estimasi matriks Hessian kami. Batasan yang ditempatkan pada matriks \(B_n\) adalah matriks tersebut simetris dan mendekati \(B_{n−1}\). Kendala ini dapat dipenuhi dengan memperbarui \(B_n\) melalui penambahan matriks peringkat satu.
Jika kita membiarkan \[y_n=f′(\theta_n)−f′(\theta_{n−1})\]
dan \(s_n=\theta_n−\theta_{n−1},\) maka persamaan garis potongnya adalah \(y_n=B_ns_n\). Satu prosedur pembaruan untuk \(B_n\)
\[B_n=B_{n−1}+{y_ny_n'\over y_n's_n}−{B_{n−1}s_ns_n'B_{n−1}'\over s_n'B_{n−1}s_n}\]
Prosedur pembaruan di atas dikembangkan oleh Broyden, Fletcher, Goldfarb, dan Shanno (BFGS). Pendekatan analog, yang memecahkan persamaan garis potong berikut, \(H_ny_n=s_n\) diusulkan oleh Davidon, Fletcher, dan Powell (DFP).
Perhatikan bahwa dalam kasus metode BFGS, kita sebenarnya menggunakan \(B_n^{−1}\) dalam pembaruan Newton. Namun, tidak perlu menyelesaikan \(B_n\) dan kemudian membalikkannya secara langsung. Kami dapat langsung memperbarui \(B_{n-1}^{−1}\) untuk menghasilkan \(B_n^{−1}\) melalui Sherman-Morrison memperbarui rumus. Rumus ini memungkinkan kita untuk menghasilkan matriks invers baru dengan menggunakan invers sebelumnya dan beberapa perkalian matriks.
Catatan: Carilah contoh penerapannya dengan menggunakan R
Metode Conjugate Gradient
Metode gradien konjugasi mewakili semacam pendekatan Stepest Descent “dengan putaran”. Dengan pendekatan Stepest Descent, kita memulai minimalisasi fungsi \(f\) mulai dari \(x_0\) dengan bergerak ke arah gradien negatif \(−f′(x_0)\). Pada langkah selanjutnya, perjalanan ke arah gradien negatif yang dievaluasi pada setiap titik berturut-turut hingga konvergen.
Pendekatan gradien konjugasi dimulai dengan cara yang sama, tetapi menyimpang dari Stepest Descent setelah langkah pertama. Pada langkah selanjutnya, arah perjalanan harus dikonjugasikan ke arah yang terakhir dilalui. Dua vektor \(u\) dan \(v\) konjugasi terhadap matriks \(A\) jika \(u′Av=0.\)
Sebelum kita melangkah lebih jauh, mari kita ambil contoh konkret. Misalkan kita ingin meminimalkan fungsi kuadrat \[f(x)=12x′Ax−x′b\] di mana \(x\) adalah vektor dimensi-p dan \(A\) adalah matriks simetris \(p×p\). Mulai dari titik \(x_0\), baik Stepest Descent maupun gradien konjugasi akan membawa kita ke arah \(p_0=−f′(x_0)=b−Ax_0,\) yang merupakan gradien negatif. Setelah kita pindah ke arah itu ke titik \(x_1\), arah berikutnya, \(p_1\) harus memenuhi \(p_0' Ap_1=0\). Jadi kita bisa mulai dengan arah Stepest Descent \(−f′(x_1)\) tapi kemudian kita harus memodifikasinya untuk membuatnya terkonjugasi ke \(p_0.\) Batasan \(p_0'Ap_1=0\) memungkinkan kita untuk menghitung kembali arah berikutnya , dimulai dengan \(−f′(x_1)\), karena kita memiliki \[p_0'A\biggl(−f′−{p_0'A(−f′)\over p_0'Ap_0}p_0\biggr)=0.\] Tanpa kehadiran matriks \(A,\) ini adalah proses sederhana Gram-Schmidt ortogonalisasi. Kita dapat melanjutkan proses ini, setiap kali mengambil arah Stepest Descent dan memodifikasinya untuk membuatnya konjugasi dengan arah sebelumnya. Proses gradien konjugasi (agak) menarik di sini karena untuk meminimalkan fungsi kuadratik dimensi-p akan konvergen dalam langkah \(p\).
Pada kenyataannya, kita tidak berurusan dengan fungsi kuadrat secara tepat sehingga algoritma yang dijelaskan di atas tidak layak. Namun, metode gradien konjugasi nonlinier menarik dari ide-ide ini dan mengembangkan algoritme yang masuk akal untuk menemukan minimum fungsi mulus arbitrer. Ini memiliki fitur yang hanya memerlukan penyimpanan dua vektor gradien, yang untuk masalah besar dengan banyak parameter, merupakan penghematan yang signifikan dalam penyimpanan versus algoritma tipe Newton yang memerlukan penyimpanan vektor gradien dan \(p×p\) matriks Hessian.
Algoritma gradien konjugasi nonlinier Fletcher-Reeves bekerja sebagai berikut. Dimulai dengan \(x_0,\)
- Misalkan \(p_0=−f′(x_0).\)
- Selesaikan \[min_α>0f(x_0+αp_0)\] untuk \(α\) dan tetapkan \(x_1=x_0+αp_0.\)
- Untuk \(n=1,2,\cdots\) andaikan \(r_n=−f′(x_n)\) dan \(r_{n−1}=−f′(x_{n−1}).\) Kemudian set \[β_n ={r_n'r_n \over r_{n−1}'r_{n−1}}.\]Terakhir, perbarui \(p_n=r_n+β_n∗p_{n−1},\) dan andaikan \[x_{n+1}=x_n+ p_n,\] di mana \(α⋆\) adalah solusi dari masalah \(min_α>0f(x_n+αp_n)\). Periksa konvergensi dan jika tidak konvergen, ulangi.
Mungkin lebih mudah untuk menggambarkan metode ini dengan sebuah ilustrasi. Di sini, kita menunjukkan kontur fungsi 2 dimensi.
<- deriv(~ x^2 + y^2 + a * x * y, c("x", "y"), function.arg = TRUE)
f <- 1
a <- 40
n <- seq(-3, 2, len = n)
xpts <- seq(-2, 3, len = n)
ypts <- expand.grid(x = xpts, y = ypts)
gr <- with(gr, f(x, y))
feval <- matrix(feval, nrow = n, ncol = n)
z
par(mar = c(5, 4, 1, 1))
contour(xpts, ypts, z, nlevels = 20)
<- -2.5 ## Initial point
x0 <- 1.2
y0 points(x0, y0, pch = 19, cex = 2)
Kita akan menggunakan sebagai titik awal titik (−2.5,1.2), seperti yang ditunjukkan pada gambar di atas. Dari gambar, jelas bahwa idealnya, kita dapat melakukan perjalanan ke arah yang akan membawa kita langsung ke fungsi minimum, yang ditunjukkan di sini.
par(mar = c(5, 4, 1, 1))
contour(xpts, ypts, z, nlevels = 20)
points(x0, y0, pch = 19, cex = 2)
arrows(x0, y0, 0, 0, lwd = 3, col = "grey")
Andai hidup begitu mudah? Gagasan di balik gradien konjugasi adalah konstruksi arah itu menggunakan serangkaian arah konjugasi. Pertama, kita mulai dengan arah penurunan paling curam. Di sini, kita mengekstrak gradien dan menemukan nilai optimal (yaitu ukuran langkah).
<- f(x0, y0)
f0 <- drop(-attr(f0, "gradient")) ## Get the gradient function
p0 <- function(alpha) {
f.sub <- f(x0 + alpha * p0[1], y0 + alpha * p0[2])
ff as.numeric(ff)
}<- optimize(f.sub, c(0, 4)) ## Compute the optimal alpha
op <- op$minimum alpha
Sekarang setelah kita menghitung gradien dan optimal, kita dapat mengambil langkah ke arah Steepest Descent.
<- x0 + alpha * p0[1]
x1 <- y0 + alpha * p0[2] y1
dan plot berikut dilampirkan kemajuan sampai saat ini.
par(mar = c(5, 4, 1, 1))
contour(xpts, ypts, z, nlevels = 20)
points(x0, y0, pch = 19, cex = 2)
arrows(x0, y0, 0, 0, lwd = 3, col = "grey")
arrows(x0, y0, x1, y1, lwd = 2)
Sekarang kita perlu menghitung arah perjalanan selanjutnya. Kita mulai dengan arah turun yang paling curam lagi. Gambar di bawah menunjukkan arah yang akan dituju (tanpa modifikasi apa pun untuk konjugasi).
<- f(x1, y1)
f1 <- drop(attr(f1, "gradient")) ## Get the gradient function
f1g <- -f1g ## Steepest descent direction
p1 <- optimize(f.sub, c(0, 4)) ## Compute the optimal alpha
op <- op$minimum
alpha <- x1 + alpha * p1[1] ## Find the next point
x2 <- y1 + alpha * p1[2] y2
Sekarang kita dapat memplot arah berikutnya yang dipilih dengan pendekatan Steepest Descent yang biasa.
par(mar = c(5, 4, 1, 1))
contour(xpts, ypts, z, nlevels = 20)
points(x0, y0, pch = 19, cex = 2)
arrows(x0, y0, 0, 0, lwd = 3, col = "grey")
arrows(x0, y0, x1, y1, lwd = 2)
arrows(x1, y1, x2, y2, col = "red", lwd = 2, lty = 2)
Namun, pendekatan gradien konjugasi menghitung arah yang sedikit berbeda untuk melakukan perjalanannya.
<- f(x1, y1)
f1 <- drop(attr(f1, "gradient"))
f1g <- drop(crossprod(f1g) / crossprod(p0)) ## Fletcher-Reeves
beta <- -f1g + beta * p0 ## Conjugate gradient direction
p1 <- function(alpha) {
f.sub <- f(x1 + alpha * p1[1], y1 + alpha * p1[2])
ff as.numeric(ff)
}<- optimize(f.sub, c(0, 4)) ## Compute the optimal alpha
op <- op$minimum
alpha <- x1 + alpha * p1[1] ## Find the next point
x2c <- y1 + alpha * p1[2] y2c
Akhirnya, kita dapat memplot arah yang diambil oleh metode gradien konjugasi.
par(mar = c(5, 4, 1, 1))
contour(xpts, ypts, z, nlevels = 20)
points(x0, y0, pch = 19, cex = 2)
arrows(x0, y0, 0, 0, lwd = 3, col = "grey")
arrows(x0, y0, x1, y1, lwd = 2)
arrows(x1, y1, x2, y2, col = "red", lwd = 2, lty = 2)
arrows(x1, y1, x2c, y2c, lwd = 2)
Dalam hal ini, karena fungsi target tepat kuadrat, proses konvergen pada
minimum tepat 2 langkah. Kita dapat melihat bahwa algoritme penurunan
paling curam akan mengambil lebih banyak langkah untuk menuju ke
minimum.
Metode Coordinate Descent
Ide di balik metode penurunan koordinat (Coordinate Descent), jika \(f\) adalah fungsi k-dimensi, kita dapat meminimalkan \(f\) dengan meminimumkan berturut-turut masing-masing dimensi individu \(f\) secara siklik, sambil mempertahankan nilai \(f\) dalam dimensi lain tetap. Pendekatan ini kadang-kadang disebut sebagai penurunan koordinat siklik. Keuntungan utama dari pendekatan ini adalah ia mengambil masalah k-dimensi yang kompleks dan mereduksinya menjadi kumpulan masalah satu-dimensi \(k\). Kerugiannya adalah konvergensi sering kali sangat lambat, terutama dalam masalah di mana \(f\) tidak berperilaku baik. Dalam statistik, versi populer dari algoritma ini dikenal sebagai backfitting dan digunakan untuk menyesuaikan model aditif umum.
Jika kita mengambil fungsi kuadrat sederhana, kita dapat melihat secara mendetail bagaimana mengoordinasikan pekerjaan penurunan. Mari kita gunakan fungsi \[f(x,y)=x^2+y^2+xy.\]
Kita dapat membuat plot kontur dari fungsi ini mendekati minimum.
<- function(x, y) {
f ^2 + y^2 + x * y
x
}<- 30
n <- seq(-1.5, 1.5, len = n)
xpts <- seq(-1.5, 1.5, len = n)
ypts <- expand.grid(x = xpts, y = ypts)
gr <- with(gr, matrix(f(x, y), nrow = n, ncol = n))
feval par(mar = c(5, 4, 1, 1))
contour(xpts, ypts, feval, nlevels = 20, xlab = "x", ylab = "y")
points(-1, -1, pch = 19, cex = 2)
abline(h = -1)
Mari kita ambil sebagai titik awal kita (−1,−1) dan mulai minimisasi kita sepanjang dimensi \(x.\) Kita dapat menggambar sebuah transek pada tingkat \(y=−1\) (dengan demikian mempertahankan \(y\) konstan) dan mencoba untuk menemukan minimum sepanjang transek tersebut. Karena \(f\) adalah fungsi kuadrat, fungsi satu dimensi yang diinduksi dengan memegang \(y=−1\) juga merupakan kuadrat.
<- f(xpts, y = -1)
feval plot(xpts, feval, type = "l", xlab = "x", ylab = "f(x | y = -1)")
Kita bisa meminimalkan fungsi satu dimensi ini dengan fungsi
optimize()
(atau kita bisa melakukannya dengan tangan jika
kita tidak malas).
<- function(x) {
fx f(x, y = -1)
}<- optimize(fx, c(-1.5, 1.5))
op op
## $minimum
## [1] 0.5
##
## $objective
## [1] 0.75
Memang, kita bisa melakukan ini secara analitik karena kita melihat
fungsi kuadrat sederhana. Namun secara umum, Anda memerlukan pengoptimal
satu dimensi (seperti fungsi optimize()
di R) untuk
menyelesaikan setiap iterasi penurunan koordinat. Ini menyelesaikan satu
iterasi dari algoritma penurunan koordinat dan titik awal baru kami
adalah (0,5,−1). Mari simpan nilai \(x\) baru ini dan lanjutkan ke iterasi
berikutnya, yang akan meminimalkan sepanjang arah \(y.\)
<- op$minimum
x1 <- with(gr, matrix(f(x, y), nrow = n, ncol = n))
feval par(mar = c(5, 4, 1, 1))
contour(xpts, ypts, feval, nlevels = 20, xlab = "x", ylab = "y")
points(-1, -1, pch = 1, cex = 2) ## Initial point
abline(h = -1, lty = 2)
points(x1, -1, pch = 19, cex = 2) ## After one step
abline(v = x1)
Transek yang digambar dengan menahan \(x=0,5\) ditunjukkan pada Gambar di atas. Fungsi satu dimensi yang sesuai dengan transek tersebut ditunjukkan di bawah ini (sekali lagi, fungsi kuadrat satu dimensi).
<- f(x = x1, ypts)
feval plot(xpts, feval, type = "l", xlab = "x",
ylab = sprintf("f(x = %.1f | y)", x1))
Meminimalkan fungsi satu dimensi ini, kita mendapatkan yang berikut.
<- function(y) {
fy f(x = x1, y)
}<- optimize(fy, c(-1.5, 1.5))
op op
## $minimum
## [1] -0.25
##
## $objective
## [1] 0.1875
Ini melengkapi iterasi lain dari algoritma penurunan koordinat dan kita dapat memplot kemajuan kita di bawah ini.
<- op$minimum
y1 <- with(gr, matrix(f(x, y), nrow = n, ncol = n))
feval par(mar = c(5, 4, 1, 1))
contour(xpts, ypts, feval, nlevels = 20, xlab = "x", ylab = "y")
points(-1, -1, pch = 1, cex = 2) ## Initial point
abline(h = -1, lty = 2)
points(x1, -1, pch = 1, cex = 2) ## After one step
abline(v = x1, lty = 2)
points(x1, y1, pch = 19, cex = 2) ## After two steps
abline(h = y1) ## New transect
Kita dapat melihat bahwa setelah dua iterasi kita sedikit lebih dekat ke minimum. Tapi kita masih punya cara, mengingat kita hanya bisa bergerak di sepanjang arah sumbu koordinat. Untuk fungsi kuadrat yang sesungguhnya, ini bukanlah cara yang efisien untuk mencari minimum, terutama ketika metode Newton akan menemukan minimum dalam satu langkah! Tentu saja, metode Newton dapat mencapai kinerja seperti itu karena menggunakan dua nilai informasi turunan. Pendekatan penurunan koordinat tidak menggunakan informasi turunan.
Dalam contoh di atas, koordinat \(x\) dan \(y\) berkorelasi sedang tetapi tidak secara dramatis. Secara umum, algoritma penurunan koordinat menunjukkan kinerja yang sangat buruk ketika koordinat berkorelasi kuat. Spesifik dari algoritma penurunan koordinat akan sangat bervariasi tergantung pada fungsi umum yang diminimalkan, tetapi algoritma penting adalah sebagai berikut. Diberikan sebuah fungsi \(f:R^p→R,\)
- Untuk \(j=1,\cdots,p,\) minimalkan \(f_j(x)=f(\cdots,x_{j−1},x,x_{j+1},\cdots)\) di mana \(x_1, \cdots,x_{j−1},x_{j+1},\cdots,x_p\) semuanya tetap pada nilainya saat ini. Untuk ini gunakan pengoptimal satu dimensi sederhana.
- Periksa konvergensi. Jika tidak konvergen, kembali ke 1.