Multivariate Bayesian variable selection regression

EM update for prior variance when prior cannot be directly inverted

The EM approach previously shown for estimating prior variance scalar has a problem: its current form does not work when input prior matrices has non-invertable component. I used generalized inverse in the code but it does not work.

Illustration

In [1]:
devtools::load_all('~/GIT/software/mvsusieR')
set.seed(1)
dat = mvsusie_sim1(n=50,p=50,r=2,s=2)
L = 10
Loading mvsusieR

Loading required package: mashr

Loading required package: ashr

In [2]:
V = matrix(1,2,2)
In [31]:
fit = mvsusie(dat$X,dat$y,L=L,prior_variance=V, 
              compute_objective=T, estimate_residual_variance=F, 
              estimate_prior_variance=T, estimate_prior_method = 'EM',
              track_fit=TRUE)
Loading mvsusieR

Warning message in m$get_objective(dump = TRUE):
“Objective is not non-decreasing”
In [32]:
fit$elbo
  1. -198.184041078147
  2. -185.479563171209
  3. -184.742826646249
  4. -184.733247523579
  5. -184.818631124017
In [33]:
fit$prior_history
    1. 0.141620258016417
    2. 0.0384484845390587
    3. 0.0297015819612639
    4. 0.0259265482236402
    5. 0.0236115241792811
    6. 0.0220150613190628
    7. 0.0208337899440703
    8. 0.0199177188521961
    9. 0.0191833192222777
    10. 0.0185799456061651
    1. 0.0771121247820147
    2. 0.0108390444862534
    3. 0.00955512435505676
    4. 0.00892832819150582
    5. 0.0085078320591506
    6. 0.00819617701612607
    7. 0.00795044115497997
    8. 0.00774836359342628
    9. 0.00757715003223193
    10. 0.00742886307410774
    1. 0.0447675232773647
    2. 0.00489783537940313
    3. 0.00437868815242494
    4. 0.00412612911362338
    5. 0.00395765345810459
    6. 0.00383377089008186
    7. 0.00373694167914759
    8. 0.00365801488941584
    9. 0.00359171063749159
    10. 0.00353474536542637
    1. 0.0293488672433036
    2. 0.00239954434417339
    3. 0.00215132739221103
    4. 0.0020311936280246
    5. 0.00195138990593238
    6. 0.00189297774606851
    7. 0.00184754163082143
    8. 0.00181068980553826
    9. 0.00177988837793423
    10. 0.00175356185234544
    1. 0.0203409831109546
    2. 0.00119783359927382
    3. 0.00107423521711347
    4. 0.00101456947462631
    5. 0.000975006523799342
    6. 0.000946100553396247
    7. 0.00092365629260936
    8. 0.00090548519134443
    9. 0.000890324964559441
    10. 0.000877391026596279

It does not work at all -- in the end all esimates of V are very small and no signal is captured.

A workaround implemented here avoids inverse on input prior when it is not invertable. The disadvantage here is that some quantities have to be recomputed which could otherwise be avoided using original direct inverse implementation.

Verify the workaround agrees with direct method when prior is invertable

But before using this approach we need to establish that the result is the same as the original implementation when prior is invertable. To achieve this I use a diagonal prior matrix which is invertable. But to trigger the workaround code, I manually mess up this line to take a non-existing function for inverse, so I can run the workaround even when prior is invertable.

In [12]:
V = diag(2)
In [ ]:
# This is the direct inverse version
devtools::load_all('~/GIT/software/mvsusieR')
fit1 = mvsusie(dat$X,dat$y,L=L,prior_variance=V, 
              compute_objective=T, estimate_residual_variance=F, 
              estimate_prior_variance=T, estimate_prior_method = 'EM',
              track_fit=TRUE)
In [15]:
fit1$V
  1. 0.340567896558995
  2. 0
  3. 0
  4. 0
  5. 0
  6. 0
  7. 0
  8. 0.34528976531648
  9. 0
  10. 0
In [ ]:
# This is the indirect inverse version
# after manually messed up the version for direct inverse
devtools::load_all('~/GIT/software/mvsusieR')
fit2 = mvsusie(dat$X,dat$y,L=L,prior_variance=V, 
              compute_objective=T, estimate_residual_variance=F, 
              estimate_prior_variance=T, estimate_prior_method = 'EM',
              track_fit=TRUE)
In [18]:
fit2$V
  1. 0.340567896558995
  2. 0
  3. 0
  4. 0
  5. 0
  6. 0
  7. 0
  8. 0.34528976531648
  9. 0
  10. 0

Let's try one more with the original simulated prior,

In [ ]:
# This is the direct inverse version
devtools::load_all('~/GIT/software/mvsusieR')
fit1 = mvsusie(dat$X,dat$y,L=L,prior_variance=dat$V, 
              compute_objective=T, estimate_residual_variance=F, 
              estimate_prior_variance=T, estimate_prior_method = 'EM',
              track_fit=TRUE)
In [20]:
fit1$V
  1. 0.716272294674903
  2. 0
  3. 0
  4. 0
  5. 0
  6. 0
  7. 0
  8. 0
  9. 0.694622119578786
  10. 0
In [ ]:
# This is the indirect inverse version
# after manually messed up the version for direct inverse
devtools::load_all('~/GIT/software/mvsusieR')
fit2 = mvsusie(dat$X,dat$y,L=L,prior_variance=dat$V, 
              compute_objective=T, estimate_residual_variance=F, 
              estimate_prior_variance=T, estimate_prior_method = 'EM',
              track_fit=TRUE)
In [27]:
fit2$V
  1. 0.716272294674902
  2. 0
  3. 0
  4. 0
  5. 0
  6. 0
  7. 0
  8. 0
  9. 0.694622119578786
  10. 0

It seems to work fine.

Comparing the new EM implementation with optim

In [29]:
V = matrix(1,2,2)
devtools::load_all('~/GIT/software/mvsusieR')
fit3 = mvsusie(dat$X,dat$y,L=L,prior_variance=V, 
              compute_objective=T, estimate_residual_variance=F, 
              estimate_prior_variance=T, estimate_prior_method = 'EM',
              track_fit=TRUE)
Loading mvsusieR

In [31]:
fit3$elbo
  1. -198.184041078147
  2. -194.811837430279
  3. -192.20503293868
  4. -190.021708939355
  5. -188.320238563406
  6. -187.103638731872
  7. -186.331632679386
  8. -185.862226348472
  9. -185.560691185511
  10. -185.363248149642
  11. -185.096023084764
  12. -184.737041447362
  13. -184.727271890713
  14. -184.720012466739
  15. -184.714149748068
  16. -184.709469892352
  17. -184.705776000582
  18. -184.702865844814
  19. -184.700544658945
  20. -184.698631699655
  21. -184.696959796368
  22. -184.695369230784
  23. -184.683090900474
  24. -184.659037025738
  25. -184.640788540617
  26. -184.620039644917
  27. -184.518216279398
  28. -184.41845687288
  29. -184.352540818326
  30. -184.317001711188
  31. -184.300056883766
  32. -184.292464685719
  33. -184.289158612395
  34. -184.287736929165
  35. -184.287128914951
In [32]:
fit3$V
  1. 0
  2. 0
  3. 0
  4. 0
  5. 0
  6. 0.215164026486418
  7. 0
  8. 0
  9. 0
  10. 0

It seems to be working, and detects a signal with the 6th effect.

To compare I also run optim directly,

In [34]:
fit4 = mvsusie(dat$X,dat$y,L=L,prior_variance=V,
              compute_objective=T, estimate_residual_variance=F, 
              estimate_prior_variance=T, estimate_prior_method = 'optim',
              track_fit=TRUE)
In [35]:
fit4$elbo
  1. -184.286676928861
  2. -184.286676928861
In [36]:
fit4$V
  1. 0.222436918612558
  2. 0
  3. 0
  4. 0
  5. 0
  6. 0
  7. 0
  8. 0
  9. 0
  10. 0

It takes less iterations (though not much less computational time, if not more) and the ELBO achieved is slightly higher.

An additional comparison between EM and optim can be found here. optim seems better in that scenario.

Verify that MASH prior is also correctly implemented

When V is not invertable,

In [ ]:
m_init = MashInitializer$new(list(V), 1)
fit5 = mvsusie(dat$X,dat$y,L=L,prior_variance=m_init, 
              compute_objective=T, estimate_residual_variance=F, precompute_covariances=T,
              estimate_prior_variance=T, estimate_prior_method = 'EM',
              track_fit=TRUE)
In [5]:
fit5$V
  1. 0
  2. 0
  3. 0
  4. 0
  5. 0
  6. 0.215164026486418
  7. 0
  8. 0
  9. 0
  10. 0

When V is invertable,

In [27]:
m_init = MashInitializer$new(list(diag(2)), 1)
fit6 = mvsusie(dat$X,dat$y,L=L,prior_variance=m_init, 
              compute_objective=T, estimate_residual_variance=F, precompute_covariances=T,
              estimate_prior_variance=T, estimate_prior_method = 'EM',
              track_fit=TRUE)
In [8]:
fit6$V
  1. 0.340567896558995
  2. 0
  3. 0
  4. 0
  5. 0
  6. 0
  7. 0
  8. 0.34528976531648
  9. 0
  10. 0

This result agrees with above from BMR. Now I comment out the line to precompute inverse so it will use the trick around inverse implemented for MASH,

In [28]:
devtools::load_all('~/GIT/software/mvsusieR')
m_init = MashInitializer$new(list(diag(2)), 1)
fit7 = mvsusie(dat$X,dat$y,L=L,prior_variance=m_init,
              compute_objective=T, estimate_residual_variance=F, precompute_covariances=T,
              estimate_prior_variance=T, estimate_prior_method = 'EM',
              track_fit=TRUE)
Loading mvsusieR

NULL
In [29]:
fit7$V
  1. 0.340567896558995
  2. 0
  3. 0
  4. 0
  5. 0
  6. 0
  7. 0
  8. 0.34528976531648
  9. 0
  10. 0
In [ ]:


Copyright © 2016-2020 Gao Wang et al at Stephens Lab, University of Chicago