Команда glm
в R
(или функция GLM
из библиотеки statsmodel.api
в Python) может соответствовать модели GLM без необходимости знать все сложные уравнения и методы оптимизации, используемые «за кулисами». Но умение что-то кодировать вручную обычно является признаком глубокого понимания.
Итак, в этой статье мы собираемся установить GLM вручную в R
и сравнить его со встроенным glm
.
Уравнения вероятности
Уравнения правдоподобия относятся к первой производной логарифмического правдоподобия модели GLM. ОМЛ имеют общетеоретический анализ — в нем рассматривается общий случай экспоненциального семейства (точнее, экспоненциальной дисперсионной модели [ЭДМ], являющейся подмножеством экспоненциального семейства), который выглядит следующим образом:
Где тета — «естественный параметр», который является функцией среднего мю; b(theta) — логарифмическая статистическая сумма; среднее значение связано с линейным предиктором эта через некоторую функцию связи; а линейный предиктор — это скалярное произведение x на бета (интересующие нас коэффициенты). Итак, взяв производную логарифмической вероятности относительно бета, мы можем использовать цепное правило, чтобы получить:
Вывод дель-тета / дель-мю использует то, что называется отношением средней дисперсии. В двух словах - с помощью этого анализа общей формы можно показать, что первая производная функции b (тета) (относительно тета) дает среднее значение распределения. А вторая производная дает функцию V(mu), которая фиксирует взаимосвязь между средним значением и дисперсией.
Мы можем записать это в матричной записи:
Где V — диагональная матрица функции среднего отклонения V(mu), а D — диагональная матрица для выбранной функции связи del-mu / del-eta. Обратите внимание, что если мы выбираем функцию связи так, что тета = эта, две частные производные сокращаются, и поэтому матрицы также сокращаются. Это называется «канонической» функцией связи, и она упрощает вывод:
Подсчет очков Фишера
GLM использует метод оптимизации 2-го порядка, называемый подсчетом Фишера. Он очень похож на метод Ньютона-Рафсона, только вместо гессиана (второй производной) логарифмического правдоподобия мы берем его ожидаемое значение, равное обратная) Информационная матрица Фишера.
Можно показать, что информационная матрица Фишера для ОЛМ равна:
Где W — диагональная матрица с этими элементами по диагонали.
Внедрение оценки Фишера
Теперь у нас есть все необходимое для реализации алгоритма оценки Фишера, поэтому давайте сделаем это для регрессии Пуассона. Функция средней дисперсии для распределения Пуассона имеет вид V(mu)=mu. В регрессии Пуассона мы видим, что каноническая функция связи — это логарифмическая функция (ее обратная — экспоненциальная), поэтому давайте используем каноническую функцию связи в качестве нашей функции связи. Это упростит вывод и для матрицы информации Фишера:
Давайте сначала сгенерируем данные:
set.seed(247) n=100 x = runif(n,-3,3) lp = 3+0.4*x mu = exp(lp) y = rpois(n,mu)
Таким образом, истинные коэффициенты бета0 = 3, бета1 = 0,4. Давайте построим это:
library(ggplot2) # for plotting ggplot(data.frame(x=x, y=y)) + geom_point(aes(x=x,y=y), color="#2E9FDF", fill="#2E9FDF", alpha=0.3)
Таким образом, алгоритм следующий:
Или в коде:
library(pracma) # for matrix inversion set.seed(247) # let's create the design matrix X = cbind(rep(1,n), x) # start with random values for beta's beta_0 = c(rnorm(1),rnorm(1)) # max number of iteration for the Fisher-scoring algorithm max_iter = 100 # Do Fisher-scoring beta_old = beta_0 for (i in 1:max_iter) { mu = c(exp(X%*%beta_old)) V = diag(mu) beta_t = beta_old + inv(t(X)%*%V%*%X) %*% t(X) %*% (y-mu) # if the coefficients change is below some threshold, we stop nrm = norm(beta_t-beta_old, type="2") if (nrm < 0.05) break beta_old = beta_t } print(i) # [1] 21
Потребовалась 21 итерация, и мы получили довольно близкие результаты:
beta_t # [,1] # 2.9960474 # x 0.3926822
Давайте сравним это с glm
:
fit.poisson = glm(y~x, family="poisson") summary(fit.poisson) Call: glm(formula = y ~ x, family = "poisson") Deviance Residuals: Min 1Q Median 3Q Max -2.7848 -0.7181 -0.0102 0.5873 3.2298 Coefficients: Estimate Std. Error z value Pr(>|z|) (Intercept) 2.99601 0.02533 118.26 <2e-16 *** x 0.39270 0.01443 27.22 <2e-16 *** --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 (Dispersion parameter for poisson family taken to be 1) Null deviance: 959.35 on 99 degrees of freedom Residual deviance: 106.49 on 98 degrees of freedom AIC: 601.1 Number of Fisher Scoring iterations: 4
Здесь потребовалось всего 4 итерации, и мы получили почти идентичное решение — но не совсем идентичное. Почему это?
IRLS — Итерированные перевзвешенные наименьшие квадраты
R
использует вариант оценки Фишера с повторным взвешиванием наименьших квадратов (IRLS). Кроме того, R
проверяет не сходимость бета, а сходимость остаточного отклонения. И R
также использует другую начальную точку — она инициирует мю, а не бета (бета — это функция мю).
IRLS был известным алгоритмом в 70-х годах, поэтому Нелдер и Веддерберн, которые изобрели структуру GLM, хотели внедрить оценку Фишера, используя уже существующее программное обеспечение. Это потребовало еще немного вывода и небольшой хитрости. Обратите внимание на взаимосвязь между матрицами D и V в градиенте и матрицей W в (ожидаемо-отрицательном) гессиане:
Таким образом, мы можем записать оценку Фишера как:
Где мы использовали хитрость и умножили исходную бету на слагаемые, отменяющие друг друга. Z определяется как «рабочий отклик» (рабочие y), поэтому каждую итерацию оценки Фишера можно рассматривать как выполнение (обобщенного) метода наименьших квадратов для рабочего отклика.
Для модели Пуассона с канонической функцией зацепления имеем
Остаточное отклонение для модели Пуассона:
И начальными значениями для мю являются сами у + 0,1 (во избежание ошибок, поскольку у может быть 0, а мю должны быть строго положительными).
Давайте закодируем это:
set.seed(247) n=100 x = runif(n,-3,3) X = cbind(rep(1,n), x) beta_true = c(3,0.4) lp = X%*%beta_true mu = exp(lp) y = rpois(n,mu) # start with mu0 = y+0.1 # max number of iteration for the IRLS algorithm max_iter = 100 # Do IRLS mu_t = mu0 for (i in 1:max_iter) { zt = log(mu_t)+y/mu_t-1 # calculate the working response V = diag(mu_t) beta_hat = solve(t(X)%*%V%*%X) %*% t(X)%*%V%*% zt # GLS on the working response # calculate the residual deviance before and after deviance_t = 2*sum(y*log(y/mu_t)-y+mu_t) mu_t = c(exp(X%*%beta_hat)) # update mu deviance_t_1 = 2*sum(y*log(y/mu_t)-y+mu_t) # if the Deviance change is below some threshold, we stop change = abs(deviance_t_1-deviance_t)/(abs(deviance_t_1)+0.1) if (change < 1e-8) break } print(i) # [1] 4 beta_hat # [,1] # 2.9960065 # x 0.3927027 deviance_t_1 # [1] 106.4857
Теперь у нас получилось точно так же, как glm
!
Если вы хотите узнать больше, загляните на мой канал YouTube series и на мой онлайн-курс GLM.