For the record, a simple pure R implementation of R's glm algorithm, based on Fisher scoring (iteratively reweighted least squares), as explained in the other answer is given by:
glm_irls =
function(X, # design matrix
y, # response
weights = rep(1, ncol(X)), # prior observation weights
# e.g. total nr. of trials for proportions with
family=binomial
start = rep(1, ncol(X)), # coefficient starting values
offset = rep(0, nrow(X)), # model offset
family = gaussian(identity), # distribution & link function
maxit = 25,
tol = 1e-08)
{
beta = start
nobs = nrow(X)
nvars = ncol(X)
eval(family$initialize) # initializes n and mustart
eta = family$linkfun(mustart) # initialize η = g(µ)
mu = family$linkinv(eta) # predictions on response scale µ
for (i in 1:maxit)
{
var = family$variance(mu) # variance in function of the mean µ
gprime = family$mu.eta(eta) # derivative of link function w.r.t. η = d(g-1)/dη=dμ/dη
gradient = y - mu # gradient of log-likelihood with respect to η = ∂ℓ/∂η = deviance residual
z = eta - offset + gradient / gprime
# adjusted response
# = linearised version of log-likelihood function ℓ around η
W = weights * as.vector(gprime^2 / var)
# = working weights
betaold = beta
beta = solve(crossprod(X,W*X), crossprod(X,W*z))
# coefficient update based on quadratic approximation of log likelihood
# using weighted least square regression = as.matrix(coef(lm.wfit(x=X, y=z, w=W)), ncol=1)
eta = offset + X %*% beta # linear predictor, i.e. predictions on link scale
mu = family$linkinv(eta) # predictions on response scale µ = g-1(η)
if (sqrt(crossprod(beta-betaold)) < tol) break
}
return(list(coefficients=beta, iterations=i))
}
Example:
## Dobson (1990) Page 93: Randomized Controlled Trial :
y <- counts <- c(18,17,15,20,10,20,25,13,12)
outcome <- gl(3,1,9)
treatment <- gl(3,3)
X <- model.matrix(counts ~ outcome + treatment)
coef(glm.fit(x=X, y=y, family = poisson(log)))
(Intercept) outcome2 outcome3 treatment2 treatment3
3.044522e+00 -4.542553e-01 -2.929871e-01 -7.635479e-16 -9.532452e-16
coef(glm_irls(X=X, y=y, family=poisson(log)))
(Intercept) outcome2 outcome3 treatment2 treatment3
[1,] 3.044522 -0.4542553 -0.2929871 -3.151689e-16 -8.24099e-16
A good discussion of GLM fitting algorithms, including a comparison with Newton-Raphson (which uses the observed Hessian as opposed to the expected Hessian in the IRLS algorithm) and hybrid algorithms (which start with IRLS, as these are easier to initialize, but then finish with further optimization using Newton-Raphson) can be found in the book
"Generalized Linear Models and Extensions" by James W. Hardin & Joseph M. Hilbe.
A pure R implementation of Newton-Raphson to fit a GLM would be
glm_newton_raphson = function(X, # design matrix
y, # response
weights = rep(1, nrow(X)), # prior observation weights
start = rep(0, ncol(X)), # coefficient starting values
offset = rep(0, nrow(X)), # model offset
family = gaussian(identity), # distribution & link function
maxit = 25,
tol = 1e-08) {
beta = start
nobs = nrow(X)
nvars = ncol(X)
eval(family$initialize) # initializes n and mustart
eta = family$linkfun(mustart) # initialize η = g(µ)
mu = family$linkinv(eta) # predictions on response scale µ
for (i in 1:maxit) {
var = family$variance(mu) # variance as a function of the mean µ
gprime = family$mu.eta(eta) # derivative of the link function with respect to η = d(g^-1)/dη = dμ/dη
gradient = t(X) %*% ((y - mu) * gprime * weights) # gradient of the log-likelihood
W = diag(as.vector(weights*(gprime^2 / var))) # diagonal weight matrix
# Compute the Fisher information matrix = negative of observed Hessian matrix
XWX = t(X) %*% W %*% X
information = XWX # Fisher information matrix = negative of the observed Hessian (second derivatives of the log-likelihood)
betaold = beta
beta = beta + solve(information, gradient) # Newton-Raphson update step
eta = offset + X %*% beta # linear predictor, i.e., predictions on link scale
mu = family$linkinv(eta) # predictions on response scale µ = g^-1(η)
if (sqrt(crossprod(beta - betaold)) < tol) break
}
return(list(coefficients = beta, iterations = i))
}
Newton-Raphson has the disadvantage that it calculates the full observed Hessian matrix, which is slow & that it needs coefficient starting values, which IRLS / Fisher scoring doesn't need.
Newton's method
is a second order gradient descent method. $\endgroup$