library(maxLik)
## Loading required package: miscTools
Наши данные подчиняются следующему закону: \( x_i=i \), \( y_i=4.2 x_i+\varepsilon_i \), \( \varepsilon_i \sim N(0,9) \), \( i=1..100 \).
Создаём искуственно такие данные:
n.obs <- 100
x <- 1:n.obs
eps <- rnorm(n.obs, mean = 0, sd = 3)
y <- 4.2 * x + eps
А теперь сделаем вид, что мы забыли, чему равны истинные значения \( \beta \) и \( \sigma^2 \) и оценим модель \( y_i=\beta x_i +\varepsilon_i \), где \( \varepsilon_i\sim N(0,\sigma^2) \):
Заметим, что параметр \( \beta \) произвольный, а параметр \( \sigma^2>0 \). Чтобы автоматически получать только положительные оценки параметра, часто используют простой трюк — перепараметризацию. То есть вводят новый параметр, равный логарифму неотрицательного параметра, \( a=\log(\sigma^2) \). Тогда \( \sigma^2 = \exp(a) \) будет автоматом положительным. Этот трюк позволяет избежать лишних ошибок при максимизации фукнции: мы гарантируем то, что алгоритм будет перебирать только положительные \( \sigma^2 \).
m_log_lik <- function(params, y.in, x.in) {
# это функция правдоподобия, она зависит от параметров и данных
beta <- params[1]
s2 <- exp(params[2])
# этот трюк полезен, чтобы параметр s2 был заведомо неотрицательным
res <- -0.5 * log(s2) * length(y.in) - 0.5/s2 * sum((y.in - beta * x.in)^2)
# R умеет только минимизировать функции, поэтому возьмем функцию с минусом
return(-res)
}
opt.res <- optim(fn = m_log_lik, par = c(3, 0), y.in = y, x.in = x, hessian = T)
# с(3,0) - это стартовая точка. Лучшее её выбирать поближе к глобальному
# экстремуму. Если даже примерно не ясно, где находится глобальный
# экстремум, то попробуйте несколько разных стартовых значений, чтобы не
# попасться в локальный. Мы выбрали от фонаря.
opt.res$hessian # гессиан в точке минимума
## [,1] [,2]
## [1,] 34843.5258 -0.8239
## [2,] -0.8239 49.6466
opt.res$par # точка минимума
## [1] 4.192 2.273
opt.res$value # минимум функции
## [1] 163.3
var.hat <- solve(opt.res$hessian) # оценка ковариационной матрицы
estimates <- opt.res$par # вектор оценок неизвестных параметров
se <- sqrt(diag(var.hat)) # вектор ст. ошибок, корни из диагонали ковариационной матрицы
ci.left <- estimates - 1.96 * se
ci.right <- estimates + 1.96 * se
coef.table <- data.frame(estimates, se, ci.left, ci.right)
rownames(coef.table) <- c("beta", "log(sigma^2)")
colnames(coef.table) <- c("Оценка", "Ст. ошибка", "Левая граница",
"Правая граница")
coef.table
## Оценка Ст. ошибка Левая граница Правая граница
## beta 4.192 0.005357 4.182 4.203
## log(sigma^2) 2.273 0.141924 1.995 2.551
Можно найти кучу примеров “MLE in R” в гугле
maxLiklog_lik <- function(params, y.in, x.in) {
# это функция правдоподобия, она зависит от параметров и данных
beta <- params[1]
s2 <- exp(params[2])
# этот трюк полезен, чтобы параметр s2 был заведомо неотрицательным
res <- -0.5 * log(s2) * length(y.in) - 0.5/s2 * sum((y.in - beta * x.in)^2)
return(res)
}
Оцениваем модель с помощью ML:
ml.results <- maxLik(log_lik, start = c(0, 0), y.in = y, x.in = x)
Выводим привычную табличку-отчёт:
summary(ml.results)
## --------------------------------------------
## Maximum Likelihood estimation
## Newton-Raphson maximisation, 13 iterations
## Return code 1: gradient close to zero
## Log-Likelihood: -163.3
## 2 free parameters
## Estimates:
## Estimate Std. error t value Pr(> t)
## [1,] 4.19211 0.00534 785 <2e-16 ***
## [2,] 2.26612 0.14139 16 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## --------------------------------------------
К сожалению, алгоритмы поиска экстремума еще далеки от совершенства. Есть функции, у которых человек легко найдёт экстремум устно, а популярные алгоритмы не смогут найти экстремум.
Например, \( f(x_1,x_2,x_3)=0.01\cdot (x_1-0.5)^2+ |x_1^2-x_2|+|x_1^2-x_3| \)
Если присмотреться, то легко заметить, что точка минимума \( x_1=0.5 \), \( x_2=0.25 \), \( x_3=0.25 \).
А что скажет компьютер?
bad.f <- function(x) {
return(0.01 * (x[1] - 0.5)^2 + abs(x[1]^2 - x[2]) + abs(x[1]^2 - x[3]))
}
res <- optim(fn = bad.f, par = c(0, 0, 0))
res
## $par
## [1] 0.058622 0.003437 0.003432
##
## $value
## [1] 0.001953
##
## $counts
## function gradient
## 501 NA
##
## $convergence
## [1] 1
##
## $message
## NULL
Можно попробовать другие алгоритмы, встроенные в функцию optim.
Замечания: