Regularização

Neste trabalho prático será aplicado o método Regularização, onde penalizamos o algoritmo de estimação dos betas (a função custo) mantendo, assim, as estimativas dos parâmetros próximas a zero ou nulas.

Existem várias definições de como regularizar os parâmetros a serem estimados ou penalizar a função custo, em geral, eles se diferem na forma de imposição deste custo. O caso mais comumente utilizado é penalizar o algoritmo de estimação da seguinte forma:

\[ \underset{\underline{\beta}}{min} \frac{1}{2n}\sum_{i = 1}^n (y_i - X\beta)^2 + \lambda \{ (1 - \alpha) ||\beta||_2^2/2 + \alpha ||\beta||_1 \} \]

onde o penalty é dado por \(\lambda \{ (1 - \alpha) ||\beta||_2^2/2 + \alpha ||\beta||_1 \}\). Neste trabalho o foco será na regularização por penalização Ridge, onde \(\alpha\) é igual a 0 e na regularização por penalização Lasso, onde \(\alpha\) é igual a 1. Em ambos os casos quanto maior o valor de \(\beta\) maior será sua penalização, ainda se tomarmos \(0 \leq \alpha \leq 1\) temos uma mescla das abordagens Ridge e Lasso (chamada de Elastic-Net). Quanto ao multiplicador \(\lambda\) far-se-á um estudo sobre seu impacto na estimativas dos parâmetros.

Felizmente temos a implementação do algoritmo de Regularização, da forma como apresentada, no software estatístico R. O método computacional esta disponível na biblioteca [glmnet], provida pela empresa Revolution Analytics, onde temos métodos para ajuste de modelos lineares generalizados, modelos de sobrevivência, entre outros sob penalizações definidas pelo usuário.

Conjunto de dados

Os dados considerados para aplicação do método é denominado por longley, disponível no pacote datasets. Este conjunto de dados refere-se a um estudo de macroeconomia onde temos 7 variáveis avaliadas em 16 anos consecutivos, de 1947 a 1962.

No R o conjunto de dados tem estrutura conforme output abaixo:

str(longley)
## 'data.frame':    16 obs. of  7 variables:
##  $ GNP.deflator: num  83 88.5 88.2 89.5 96.2 ...
##  $ GNP         : num  234 259 258 285 329 ...
##  $ Unemployed  : num  236 232 368 335 210 ...
##  $ Armed.Forces: num  159 146 162 165 310 ...
##  $ Population  : num  108 109 110 111 112 ...
##  $ Year        : int  1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 ...
##  $ Employed    : num  60.3 61.1 60.2 61.2 63.2 ...

O modelo considerado aqui será o modelo gaussiano considerando Employed como variável de interesse e todas as demais como explicativas. Conforme Abaixo

\[ Employed \mid X \sim Normal(\hat{\mu}, \hat{\sigma}^2) \\ \hat{\mu} = X\underline{\beta} \]

onde a matriz X compreende todas as 6 variáveis explicativas e portanto tem dimensão 16x7 (6 \(\beta\)’s associados as covariáveis e intercepto \(\beta_0\)). Portanto para o modelo descrito serão estimados 8 parâmetros (7 de regressão e o \sigma^2).

Abaixo ajustamos os modelos:

  • m0lm: Modelo gaussiano de regressão múltipla;
  • m0ridge: Modelo gaussiano de regressão múltipla com penalização Ridge;
  • m0lasso: Modelo gaussiano de regressão múltipla com penalização Lasso; e
  • m0elnet: Modelo gaussiano de regressão múltipla com penalização dada por \(\alpha = 0.5\)
##----------------------------------------------------------------------
## Preditor adotado
preditor <- Employed ~ .
data <- longley

##-------------------------------------------
## Via LM (Linear Models)
m0lm <- lm(preditor, data = data)

##-------------------------------------------
## Via Reguralização
library(glmnet)

## É ncessário informar as matrizes X e y
X <- model.matrix(preditor, data = data)
y <- longley$Employed

## Regularização Ridge
m0ridge <- cv.glmnet(x = X, y = y, family = "gaussian", alpha = 0,
                     grouped = FALSE)

## Regularização Ridge
m0lasso <- cv.glmnet(x = X, y = y, family = "gaussian", alpha = 1,
                     grouped = FALSE)

## Regularização Elastic Net (com alpha = 0.5)
m0elnet <- cv.glmnet(x = X, y = y, family = "gaussian", alpha = 0.5,
                     grouped = FALSE)

Os objetos m0ridge, m0lasso e m0net tem classe cv.glmnet e para estas classes o pacote dispõe de vários métodos. Utilizaremos alguns deles para verificar o ajuste do modelos modelos. Note que utilzamos a função cv.glmnet o cv vem das iniciais Cross Validation, utilizando esta função o próprio algoritmo programada já sugere a melhor opção para \(\lambda\) a ser utilizada, segundo o critério implementado (Erro quadrático médio da validação cruzada).

Escolhendo a penalização adequada

Essas funções de estimação providas no pacote glmnet ajustam, por padrão, modelos com diferentes \(\lambda\)’s na penalização. É utilizado uma sequência de 100 valores escolhidos com base na escala da variável resposta e no método utilizado ($). Nos objetos de classe cv.glmnet temos a função plot que apresenta o critério utilizado para escolha do \(\lambda\).

par(mfrow = c(1, 3))
plot(m0ridge); title("Ridge", line = 2.5)
plot(m0lasso); title("Lasso", line = 2.5)
plot(m0elnet); title("alpha = 0.5", line = 2.5)

Com base nestes gráficos nota-se que o valor de \(\lambda\) sugerido, que minimiza o erro de validação cruzada é 0,3671, 0,0041, 0,0082 para os modelos com penalização Ridge, Lasso, e Elastic Net (\(\lambda = 0.5\)), ainda é apresentado (segunda linha vertical) o maior valor de \(\lambda\) que está no intervalo de um erro padrão do erro quadrático médio de validação que resultou nos valores 0,6414, 0,022, 0,0401.

Comparação de ajustes

Abaixo como comparação apresentamos os parâmetros estimados em ambos os métodos.

Primeiro apresentamos abaixo a comparação dos coeficientes estimados utilizando o \(\lambda\) que minimiza o erro de validação cruzada e o \(\lambda\) máximo pertencente ao intervalo de um erro padrão do erro de validação cruzada.

## cbind(coef(m0lm), coef(m0ridge), coef(m0lasso), coef(m0elnet))

coefRidge <- cbind("RIDGE-MIN" = coef(m0ridge, s = "lambda.min")[-2],
                   "RIDGE-1SE" = coef(m0ridge, s = "lambda.1se")[-2])
coefLasso <- cbind("LASSO-MIN" = coef(m0lasso, s = "lambda.min")[-2],
                   "LASSO-1SE" = coef(m0lasso, s = "lambda.1se")[-2])
coefElnet <- cbind("ELNET-MIN" = coef(m0elnet, s = "lambda.min")[-2],
                   "ELNET-1SE" = coef(m0elnet, s = "lambda.1se")[-2])

compareCoefs <- cbind(coefRidge, coefLasso, coefElnet)
rownames(compareCoefs) <- coef(m0elnet)@Dimnames[[1]][-2]

knitr::kable(compareCoefs, align = rep("c", 6), digits = 3)
RIDGE-MIN RIDGE-1SE LASSO-MIN LASSO-1SE ELNET-MIN ELNET-1SE
(Intercept) -363.081 -324.641 -1965.218 -1688.492 -1640.815 -761.290
GNP.deflator 0.083 0.079 0.000 0.000 0.025 0.046
GNP 0.011 0.010 0.000 0.001 0.000 0.016
Unemployed -0.007 -0.005 -0.014 -0.013 -0.014 -0.010
Armed.Forces -0.001 0.000 -0.008 -0.006 -0.007 -0.005
Population 0.120 0.117 -0.065 0.000 0.000 0.000
Year 0.207 0.187 1.046 0.900 0.875 0.419

Note na tabela que os valores, primeiramente olhando as colunas duas a duas (se foi pego o \(\lambda\) mínimo - -MIN ou o maior dentro do intervalo de 1 erro padrão - -1SE), dentre essas duas alternativas não vemos uma diferença muito discrepante das estimativas, porém perceba que sempre em -1SE temos uma estimativa mais próxima de zero do que a fornecida por -MIN, pois nestes casos a penalização é maior. Escolheremos o maior \(\lambda\), pois desejamos uma maior penalização. Agora comparando entre procedimentos de penalização RIDGE, LASSO e ELNET (com \(\lambda\) = 0,5) notamos maior diferença nas estimativas, note que no caso RIDGE as estimativas chegam próxima a zero, porém não são nulas, já nos casos LASSO e ELNET a nulidade já ocorre para alguns parâmetros.

Agora vamos a comparação com a tradicional metodologia já amplamente utilizada, a regressão gaussiana múltipla. Abaixo temos a comparação dos coeficientes estimatidas. Apresentamos somente as estimativas com penalização utilizando o maior \(\lambda\) sugerido.

coeflm <- cbind("LM" = coef(m0lm))
compareCoefs <- cbind(coeflm, compareCoefs[, seq(2, 6, 2)])
colnames(compareCoefs) <- c("LINEAR", "RIDGE", "LASSO", "ELNET")

knitr::kable(compareCoefs, align = rep("c", 4), digits = 3)
LINEAR RIDGE LASSO ELNET
(Intercept) -3482.259 -324.641 -1688.492 -761.290
GNP.deflator 0.015 0.079 0.000 0.046
GNP -0.036 0.010 0.001 0.016
Unemployed -0.020 -0.005 -0.013 -0.010
Armed.Forces -0.010 0.000 -0.006 -0.005
Population -0.051 0.117 0.000 0.000
Year 1.829 0.187 0.900 0.419

Os valores estimados são razoavelmente distintos, porém note que a regressão LASSO toma a nulidade do parâmetros relacionados as variáveis GNP.deflator, GNP e Population. Verificando o teste de significância para estes parâmetros no modelo linear gaussiano temos:

## summary(m0lm)

## Para usar kable
est = coef(summary(m0lm))[, "Estimate"]
sdt = coef(summary(m0lm))[, "Std. Error"]
tval = est/sdt
pval = 2*pt(abs(tval), df = m0lm$df.residual, lower = FALSE)

mysummary <- cbind(Estimate = est, Std.Error = sdt,
                   "t value" = tval, "P(>|t|)" = pval)

knitr::kable(mysummary, align = rep("c", 4), digits = 3)
Estimate Std.Error t value P(>|t|)
(Intercept) -3482.259 890.420 -3.911 0.004
GNP.deflator 0.015 0.085 0.177 0.863
GNP -0.036 0.033 -1.070 0.313
Unemployed -0.020 0.005 -4.136 0.003
Armed.Forces -0.010 0.002 -4.822 0.001
Population -0.051 0.226 -0.226 0.826
Year 1.829 0.455 4.016 0.003

Portanto, nota-se que a regressão por penalização LASSO fez, neste caso, o trabalho manual que seria de retirar estas as variáveis do modelo e reajustá-lo.

Informações da sessão R

cat(format(Sys.time(),
           format = "Atualizado em %d de %B de %Y.\n\n"))
## Atualizado em 08 de agosto de 2016.
sessionInfo()
## R version 3.3.1 (2016-06-21)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 14.04.5 LTS
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=pt_BR.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=pt_BR.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=pt_BR.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=pt_BR.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] parallel  stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
## [1] glmnet_2.0-5        foreach_1.4.3       Matrix_1.2-6       
## [4] mboost_2.6-0        stabs_0.5-1         knitr_1.12.3       
## [7] latticeExtra_0.6-28 RColorBrewer_1.1-2  lattice_0.20-33    
## 
## loaded via a namespace (and not attached):
##  [1] magrittr_1.5      MASS_7.3-45       splines_3.3.1    
##  [4] nnls_1.4          quadprog_1.5-5    multcomp_1.4-5   
##  [7] highr_0.5.1       stringr_1.0.0     tools_3.3.1      
## [10] grid_3.3.1        TH.data_1.0-7     iterators_1.0.8  
## [13] modeltools_0.2-21 htmltools_0.3     yaml_2.1.13      
## [16] survival_2.39-4   digest_0.6.9      party_1.0-25     
## [19] formatR_1.3       codetools_0.2-14  strucchange_1.5-1
## [22] evaluate_0.9      rmarkdown_0.9.6   coin_1.1-2       
## [25] sandwich_2.3-4    stringi_1.0-1     compiler_3.3.1   
## [28] stats4_3.3.1      mvtnorm_1.0-5     zoo_1.7-13


© Copyright 2016 Ribeiro Jr., E. E.