-
-
Notifications
You must be signed in to change notification settings - Fork 129
Expand file tree
/
Copy pathefficiency-tuning.qmd
More file actions
1603 lines (1352 loc) · 50.8 KB
/
Copy pathefficiency-tuning.qmd
File metadata and controls
1603 lines (1352 loc) · 50.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
pagetitle: Efficiency Tuning
---
# Efficiency Tuning {#efficiency-tuning.chapter}
This chapter provides a grab bag of techniques for optimizing Stan
code, including vectorization, sufficient statistics, and conjugacy.
At a coarse level, efficiency involves both the amount of time
required for a computation and the amount of memory required. For
practical applied statistical modeling, we are mainly concerned with
reducing wall time (how long a program takes as measured by a clock on
the wall) and keeping memory requirements within available bounds.
## What is efficiency?
The standard algorithm analyses in computer science measure efficiency
asymptotically as a function of problem size (such as data, number of
parameters, etc.) and typically do not consider constant additive
factors like startup times or multiplicative factors like speed of
operations. In practice, the constant factors are important; if run
time can be cut in half or more, that's a huge gain. This chapter
focuses on both the constant factors involved in efficiency (such as
using built-in matrix operations as opposed to naive loops) and on
asymptotic efficiency factors (such as using linear algorithms instead
of quadratic algorithms in loops).
## Efficiency for probabilistic models and algorithms
Stan programs express models which are intrinsically statistical in
nature. The algorithms applied to these models may or may not
themselves be probabilistic. For example, given an initial value for
parameters (which may itself be given deterministically or generated
randomly), Stan's optimization algorithm (L-BFGS) for penalized
maximum likelihood estimation is purely deterministic. Stan's
sampling algorithms are based on Markov chain Monte Carlo algorithms,
which are probabilistic by nature at every step. Stan's variational
inference algorithm (ADVI) is probabilistic despite being an
optimization algorithm; the randomization lies in a nested Monte Carlo
calculation for an expected gradient.
With probabilistic algorithms, there will be variation in run times
(and maybe memory usage) based on the randomization involved. For
example, by starting too far out in the tail, iterative algorithms
underneath the hood, such as the solvers for ordinary differential
equations, may take different numbers of steps. Ideally this
variation will be limited; when there is a lot of variation it can be
a sign that there is a problem with the model's parameterization in
a Stan program or with initialization.
A well-behaved Stan program will have low variance between runs with
different random initializations and differently seeded random number
generators. But sometimes an algorithm can get stuck in one part of
the posterior, typically due to high curvature. Such sticking almost
always indicates the need to reparameterize the model. Just throwing
away Markov chains with apparently poor behavior (slow, or stuck) can
lead to bias in posterior estimates. This problem with getting stuck
can often be overcome by lowering the initial step size to avoid
getting stuck during adaptation and increasing the target acceptance
rate in order to target a lower step size. This is because smaller
step sizes allow Stan's gradient-based algorithms to better follow the
curvature in the density or penalized maximum likelihood being fit.
## Statistical vs.\ computational efficiency
There is a difference between pure computational efficiency and
statistical efficiency for Stan programs fit with sampling-based
algorithms. Computational efficiency measures the amount of time or
memory required for a given step in a calculation, such as an
evaluation of a log posterior or penalized likelihood.
Statistical efficiency typically involves requiring fewer steps in
algorithms by making the statistical formulation of a model better
behaved. The typical way to do this is by applying a change of
variables (i.e., reparameterization) so that sampling algorithms mix
better or optimization algorithms require less adaptation.
## Model conditioning and curvature
Because Stan's algorithms rely on step-based gradient-based
approximations of the density
(or penalized maximum likelihood) being fitted, posterior curvature
not captured by this first-order approximation plays a central role in
determining the statistical efficiency of Stan's algorithms.
A second-order approximation to curvature is provided by the
Hessian, the matrix of second derivatives of the log density $\log
p(\theta)$ with respect to the parameter vector $\theta$, defined
as
$$
H(\theta) = \nabla \, \nabla \, \log p(\theta \mid y),
$$
so that
$$
H_{i, j}(\theta) = \frac{\partial^2 \log p(\theta \mid y)}
{\partial \theta_i \ \partial \theta_j}.
$$
For pure penalized maximum likelihood problems, the posterior log
density $\log p(\theta \mid y)$ is replaced by the penalized likelihood
function $\mathcal{L}(\theta) = \log p(y \mid \theta) - \lambda(\theta)$.
### Condition number and adaptation {-}
A good gauge of how difficult a problem the curvature presents is
given by the condition number of the Hessian matrix $H$, which is the
ratio of the largest to the smallest eigenvalue of $H$ (assuming the
Hessian is positive definite). This essentially measures the
difference between the flattest direction of movement and the most
curved. Typically, the step size of a gradient-based algorithm is
bounded by the most sharply curved direction. With better conditioned
log densities or penalized likelihood functions, it is easier for
Stan's adaptation, especially the diagonal adaptations that are used
as defaults.
### Unit scales without correlation {-}
Ideally, all parameters should be programmed so that they have unit
scale and so that posterior correlation is reduced; together, these
properties mean that there is no rotation or scaling required for
optimal performance of Stan's algorithms. For Hamiltonian Monte
Carlo, this implies a unit mass matrix, which requires no adaptation
as it is where the algorithm initializes.
### Varying curvature {-}
In all but very simple models (such as multivariate normals), the
Hessian will vary as $\theta$ varies (an extreme example is Neal's
funnel, as naturally arises in hierarchical models with little or no
data). The more the curvature varies, the harder it is for all of the
algorithms with fixed adaptation parameters to find adaptations that
cover the entire density well. Many of the variable transforms proposed are
aimed at improving the conditioning of the Hessian and/or making it
more consistent across the relevant portions of the density (or
penalized maximum likelihood function) being fit.
For all of Stan's algorithms, the curvature along the path from the
initial values of the parameters to the solution is relevant. For
penalized maximum likelihood and variational inference, the solution
of the iterative algorithm will be a single point, so this is all that
matters. For sampling, the relevant "solution" is the typical set,
which is the posterior volume where almost all draws from the
posterior lies; thus, the typical set contains almost all of the
posterior probability mass.
With sampling, the curvature may vary dramatically between the points
on the path from the initialization point to the typical set and
within the typical set. This is why adaptation needs to run long
enough to visit enough points in the typical set to get a good
first-order estimate of the curvature within the typical set. If
adaptation is not run long enough, sampling within the typical set
after adaptation will not be efficient. We generally recommend at
least one hundred iterations after the typical set is reached (and the
first effective draw is ready to be realized). Whether adaptation has
run long enough can be measured by comparing the adaptation parameters
derived from a set of diffuse initial parameter values.
### Reparameterizing with a change of variables {-}
Improving statistical efficiency is achieved by reparameterizing the
model so that the same result may be calculated using a density or
penalized maximum likelihood that is better conditioned. Again, see
the example of reparameterizing Neal's funnel for an example, and also
the examples in the [change of variables
chapter](reparameterization.qmd).
One has to be careful in using change-of-variables reparameterizations
when using maximum likelihood estimation, because they can change the
result if the Jacobian term is inadvertently included in the revised
likelihood model.
## Well-specified models
Model misspecification, which roughly speaking means using a model
that doesn't match the data, can be a major source of slow code. This
can be seen in cases where simulated data according to the model runs
robustly and efficiently, whereas the real data for which it was
intended runs slowly or may even have convergence and mixing issues.
While some of the techniques recommended in the remaining sections of
this chapter may mitigate the problem, the best remedy is a
better model specification.
Counterintuitively, more complicated models often run faster
than simpler models. One common pattern is with a group of parameters
with a wide fixed prior such as `normal(0, 1000)`). This can fit
slowly due to the mismatch between prior and posterior (the prior has
support for values in the hundreds or even thousands, whereas the
posterior may be concentrated near zero). In such cases, replacing
the fixed prior with a hierarchical prior such as `normal(mu,
sigma)`, where `mu` and `sigma` are new parameters with
their own hyperpriors, can be beneficial.
## Avoiding validation
Stan validates all of its data structure constraints. For example,
consider a transformed parameter defined to be a covariance matrix and
then used as a covariance parameter in the model block.
```stan
transformed parameters {
cov_matrix[K] Sigma;
// ...
} // first validation
model {
y ~ multi_normal(mu, Sigma); // second validation
// ...
}
```
Because `Sigma` is declared to be a covariance matrix, it will be
factored at the end of the transformed parameter block to ensure that
it is positive definite. The multivariate normal log density function
also validates that `Sigma` is positive definite. This test is
expensive, having cubic run time (i.e., $\mathcal{O}(N^3)$ for
$N \times N$ matrices), so it should not be done twice.
The test may be avoided by simply declaring `Sigma` to be a simple
unconstrained matrix.
```stan
transformed parameters {
matrix[K, K] Sigma;
// ...
}
model {
y ~ multi_normal(mu, Sigma); // only validation
}
```
Now the only validation is carried out by the multivariate normal.
## Reparameterization {#reparameterization.section}
Stan's sampler can be slow in sampling from distributions with
difficult posterior geometries. One way to speed up such models is
through reparameterization. In some cases, reparameterization can
dramatically increase effective sample size for the same number of
iterations or even make programs that would not converge well
behaved.
### Example: Neal's funnel {- #funnel.figure}
In this section, we discuss a general transform from a centered to a
non-centered parameterization [@papa-et-al:2007].^[This parameterization came to be known on our mailing lists as the "Matt trick" after Matt Hoffman, who independently came up with it while fitting hierarchical models in Stan.]
This reparameterization is helpful when there is not much data,
because it separates the hierarchical parameters and lower-level
parameters in the prior.
@Neal:2003 defines a distribution that exemplifies the difficulties of
sampling from some hierarchical models. Neal's example is fairly
extreme, but can be trivially reparameterized in such a way as to make
sampling straightforward. Neal's example has support for $y \in
\mathbb{R}$ and $x \in \mathbb{R}^9$ with density
$$
p(y,x) = \textsf{normal}(y \mid 0,3) \times \prod_{n=1}^9
\textsf{normal}(x_n \mid 0,\exp(y/2)).
$$
The probability contours are shaped like ten-dimensional funnels. The
funnel's neck is particularly sharp because of the exponential
function applied to $y$. A plot of the log marginal density of $y$
and the first dimension $x_1$ is shown in the following plot.
The funnel can be implemented directly in Stan as follows.
```stan
parameters {
real y;
vector[9] x;
}
model {
y ~ normal(0, 3);
x ~ normal(0, exp(y/2));
}
```
When the model is expressed this way, Stan has trouble sampling from
the neck of the funnel, where $y$ is small and thus $x$ is constrained
to be near 0. This is due to the fact that the density's scale
changes with $y$, so that a step size that works well in the body will
be too large for the neck, and a step size that works in the neck will be
inefficient in the body. This can be seen in the following plots.
::: {#fig-funnel layout="[55, 45]"}


Neal's funnel. (Left) The marginal density of Neal's funnel for the upper-level variable $y$ and
one lower-level variable $x_1$ (see the text for the formula). The blue region has log density
greater than -8, the yellow region density greater than -16, and the gray background a density less
than -16. (Right) 4000 draws are taken from a run of Stan's sampler with default settings.
Both plots are restricted to the shown window of $x_1$ and $y$ values; some draws fell outside of
the displayed area as would be expected given the density. The samples are consistent with the
marginal density $p(y) = \textsf{normal}(y \mid 0,3)$, which has mean 0 and standard deviation 3.
:::
In this particular instance, because the analytic form of the density
from which samples are drawn is known, the model can be converted to
the following more efficient form.
```stan
parameters {
real y_raw;
vector[9] x_raw;
}
transformed parameters {
real y = 3.0 * y_raw;
vector[9] x = exp(0.5 * y) * x_raw;
}
model {
y_raw ~ std_normal(); // implies y ~ normal(0, 3)
x_raw ~ std_normal(); // implies x ~ normal(0, exp(y/2))
}
```
In this second model, the parameters `x_raw` and `y_raw` are
sampled as independent standard normals, which is easy for Stan,
and then transformed into samples from the funnel.
When this transform is used in Stan code, a comment indicating what the
distribution for the parameter implies for the distribution of the transformed parameter
will improve readibility and maintainability.
As of Stan release v2.19.0, this program can be written using Stan's
[affinely transformed real type](https://mc-stan.org/docs/reference-manual/types.html#affine-transform.section).
The affine transform on the vector `x` is applied to each element of `x`.
```stan
parameters {
real<multiplier=3> y;
vector<multiplier=exp(0.5 * y)>[9] x;
}
model {
y ~ normal(0, 3);
x ~ normal(0, 0.5 * y);
}
```
### Reparameterizing the Cauchy {-}
Sampling from heavy tailed distributions such as the Cauchy is
difficult for Hamiltonian Monte Carlo, which operates within a
Euclidean geometry.
The practical problem is that tail of the Cauchy
requires a relatively large step size compared to the trunk. With a
small step size, the No-U-Turn sampler requires many steps when
starting in the tail of the distribution; with a large step size,
there will be too much rejection in the central portion of the
distribution. This problem may be mitigated by defining the
Cauchy-distributed variable as the transform of a uniformly
distributed variable using the Cauchy inverse cumulative distribution
function.
Suppose a random variable of interest $X$ has a Cauchy distribution
with location $\mu$ and scale $\tau$, so that $X \sim
\textsf{Cauchy}(\mu,\tau)$. The variable $X$ has a cumulative
distribution function $F_X:\mathbb{R} \rightarrow (0,1)$ defined by
$$
F_X(x) = \frac{1}{\pi} \arctan \left( \frac{x - \mu}{\tau} \right) +
\frac{1}{2}.
$$
The inverse of the cumulative distribution function,
$F_X^{-1}:(0,1) \rightarrow \mathbb{R}$, is thus
$$
F^{-1}_X(y) = \mu + \tau \tan \left( \pi \left( y - \frac{1}{2} \right) \right).
$$
Thus if the random variable $Y$ has a unit uniform distribution, $Y
\sim \textsf{uniform}(0,1)$, then $F^{-1}_X(Y)$ has a Cauchy
distribution with location $\mu$ and scale $\tau$, i.e., $F^{-1}_X(Y) \sim
\textsf{Cauchy}(\mu,\tau)$.
Consider a Stan program involving a Cauchy-distributed parameter
`beta`.
```stan
parameters {
real beta;
// ...
}
model {
beta ~ cauchy(mu, tau);
// ...
}
```
This declaration of `beta` as a parameter may be replaced with a
transformed parameter `beta` defined in terms of a
uniform-distributed parameter `beta_unif`.
```stan
parameters {
real<lower=-pi() / 2, upper=pi() / 2> beta_unif;
// ...
}
transformed parameters {
real beta;
beta = mu + tau * tan(beta_unif); // beta ~ cauchy(mu, tau)
}
model {
beta_unif ~ uniform(-pi() / 2, pi() / 2); // not necessary
// ...
}
```
It is more convenient in Stan to transform a uniform variable on
$(-\pi/2, \pi/2)$ than one on $(0,1)$. The Cauchy location and scale
parameters, `mu` and `tau`, may be defined as data or may
themselves be parameters. The variable `beta` could also be
defined as a local variable if it does not need to be included in the
sampler's output.
The uniform distribution on `beta_unif` is defined explicitly in
the model block, but it could be safely removed from the program
without changing sampling behavior. This is because $\log
\textsf{uniform}(\beta_{\textsf{unif}} \mid -\pi/2,\pi/2) =
-\log \pi$ is a constant and Stan only
needs the total log probability up to an additive constant. Stan will spend
some time checking that that `beta_unif` is between
`-pi() / 2` and `pi() / 2`, but this condition is guaranteed by
the constraints in the declaration of `beta_unif`.
### Reparameterizing a Student-t distribution {-}
One thing that sometimes works when you're having trouble with the
heavy-tailedness of Student-t distributions is to use the
gamma-mixture representation, which says that you can generate a
Student-t distributed variable $\beta$,
$$
\beta \sim \textsf{Student-t}(\nu, 0, 1),
$$
by first generating a gamma-distributed precision (inverse variance)
$\tau$ according to
$$
\tau \sim \textsf{Gamma}(\nu/2, \nu/2),
$$
and then generating $\beta$ from the normal distribution,
$$
\beta \sim \textsf{normal}\left(0,\tau^{-\frac{1}{2}}\right).
$$
Because $\tau$ is precision, $\tau^{-\frac{1}{2}}$ is the scale
(standard deviation), which is the parameterization used by Stan.
The marginal distribution of $\beta$ when you integrate out $\tau$ is
$\textsf{Student-t}(\nu, 0, 1)$, i.e.,
$$
\textsf{Student-t}(\beta \mid \nu, 0, 1)
=
\int_0^{\infty}
\,
\textsf{normal}\left(\beta \middle| 0, \tau^{-0.5}\right)
\times
\textsf{Gamma}\left(\tau \middle| \nu/2, \nu/2\right)
\
\text{d} \tau.
$$
To go one step further, instead of defining a $\beta$ drawn from a
normal with precision $\tau$, define $\alpha$ to be drawn from a unit
normal,
$$
\alpha \sim \textsf{normal}(0,1)
$$
and rescale by defining
$$
\beta = \alpha \, \tau^{-\frac{1}{2}}.
$$
Now suppose $\mu = \beta x$ is the product of $\beta$ with a
regression predictor $x$. Then the reparameterization $\mu = \alpha
\tau^{-\frac{1}{2}} x$ has the same distribution, but in the original, direct
parameterization, $\beta$ has (potentially) heavy tails, whereas in
the second, neither $\tau$ nor $\alpha$ have heavy tails.
To translate into Stan notation, this reparameterization replaces
```stan
parameters {
real<lower=0> nu;
real beta;
// ...
}
model {
beta ~ student_t(nu, 0, 1);
// ...
}
```
with
```stan
parameters {
real<lower=0> nu;
real<lower=0> tau;
real alpha;
// ...
}
transformed parameters {
real beta;
beta = alpha / sqrt(tau);
// ...
}
model {
real half_nu;
half_nu = 0.5 * nu;
tau ~ gamma(half_nu, half_nu);
alpha ~ std_normal();
// ...
}
```
Although set to `0` here, in most cases, the lower bound for the
degrees of freedom parameter `nu` can be set to `1` or
higher; when `nu` is 1, the result is a Cauchy distribution with
fat tails and as `nu` approaches infinity, the Student-t
distribution approaches a normal distribution. Thus the parameter
`nu` characterizes the heaviness of the tails of the model.
### Hierarchical models and the non-centered parameterization {-}
Unfortunately, the usual situation in applied Bayesian modeling
involves complex geometries and interactions that are not known
analytically. Nevertheless, the non-centered parameterization
can still be effective for separating parameters.
#### Centered parameterization {-}
For example, a vectorized hierarchical model might draw a vector of
coefficients $\beta$ with definitions as follows. The so-called
centered parameterization is as follows.
```stan
parameters {
real mu_beta;
real<lower=0> sigma_beta;
vector[K] beta;
// ...
}
model {
beta ~ normal(mu_beta, sigma_beta);
// ...
}
```
Although not shown, a full model will have priors on both
`mu_beta` and `sigma_beta` along with data modeled based on
these coefficients. For instance, a standard binary logistic
regression with data matrix `x` and binary outcome vector
`y` would include a likelihood statement such as form
`y ~ bernoulli_logit(x * beta)`, leading to an analytically
intractable posterior.
A hierarchical model such as the above will suffer from the same kind
of inefficiencies as Neal's funnel, because the values of `beta`,
`mu_beta` and `sigma_beta` are highly correlated in the
posterior. The extremity of the correlation depends on the amount of
data, with Neal's funnel being the extreme with no data. In these
cases, the non-centered parameterization, discussed in the next
section, is preferable; when there is a lot of data, the centered
parameterization is more efficient. See
@Betancourt-Girolami:2013 for more information on the effects of
centering in hierarchical models fit with Hamiltonian Monte Carlo.
### Non-centered parameterization {-}
Sometimes the group-level effects do not constrain the hierarchical
distribution tightly. Examples arise when there are not many groups,
or when the inter-group variation is high. In such cases,
hierarchical models can be made much more efficient by shifting the
data's correlation with the parameters to the hyperparameters. Similar
to the funnel example, this will be much more efficient in terms of
effective sample size when there is not much data (see
@Betancourt-Girolami:2013), and in more extreme cases will be
necessary to achieve convergence.
```stan
parameters {
real mu_beta;
real<lower=0> sigma_beta;
vector[K] beta_raw;
// ...
}
transformed parameters {
vector[K] beta;
// implies: beta ~ normal(mu_beta, sigma_beta)
beta = mu_beta + sigma_beta * beta_raw;
}
model {
beta_raw ~ std_normal();
// ...
}
```
Any priors defined for `mu_beta` and `sigma_beta` remain as
defined in the original model.
Alternatively, Stan's
[affine transform](https://mc-stan.org/docs/reference-manual/types.html#affinely-transformed-real)
can be used to decouple `sigma` and `beta`:
```stan
parameters {
real mu_beta;
real<lower=0> sigma_beta;
vector<offset=mu_beta, multiplier=sigma_beta>[K] beta;
// ...
}
model {
beta ~ normal(mu_beta, sigma_beta);
// ...
}
```
Reparameterization of hierarchical models is not limited to the normal
distribution, although the normal distribution is the best candidate
for doing so. In general, any distribution of parameters in the
location-scale family is a good candidate for reparameterization. Let
$\beta = l + s\alpha$ where $l$ is a location parameter and $s$ is a
scale parameter. The parameter $l$ need not be the mean, $s$ need not
be the standard deviation, and neither the mean nor the standard
deviation need to exist. If $\alpha$ and $\beta$ are from the same
distributional family but $\alpha$ has location zero and unit scale,
while $\beta$ has location $l$ and scale $s$, then that distribution
is a location-scale distribution. Thus, if $\alpha$ were a parameter
and $\beta$ were a transformed parameter, then a prior distribution
from the location-scale family on $\alpha$ with location zero and unit
scale implies a prior distribution on $\beta$ with location $l$ and
scale $s$. Doing so would reduce the dependence between $\alpha$,
$l$, and $s$.
There are several univariate distributions in the location-scale
family, such as the Student t distribution, including its special
cases of the Cauchy distribution (with one degree of freedom) and the
normal distribution (with infinite degrees of freedom). As shown above,
if $\alpha$ is distributed standard normal, then $\beta$ is distributed
normal with mean $\mu = l$ and standard deviation $\sigma = s$. The
logistic, the double exponential, the generalized extreme value
distributions, and the stable distribution are also in the
location-scale family.
Also, if $z$ is distributed standard normal, then $z^2$ is distributed
chi-squared with one degree of freedom. By summing the squares of $K$
independent standard normal variates, one can obtain a single variate
that is distributed chi-squared with $K$ degrees of freedom. However,
for large $K$, the computational gains of this reparameterization may
be overwhelmed by the computational cost of specifying $K$ primitive
parameters just to obtain one transformed parameter to use in a model.
### Multivariate reparameterizations {-}
The benefits of reparameterization are not limited to univariate
distributions. A parameter with a multivariate normal prior distribution
is also an excellent candidate for reparameterization. Suppose you intend
the prior for $\beta$ to be multivariate normal with mean vector $\mu$
and covariance matrix $\Sigma$. Such a belief is reflected by the
following code.
```stan
data {
int<lower=2> K;
vector[K] mu;
cov_matrix[K] Sigma;
// ...
}
parameters {
vector[K] beta;
// ...
}
model {
beta ~ multi_normal(mu, Sigma);
// ...
}
```
In this case `mu` and `Sigma` are fixed data, but they could
be unknown parameters, in which case their priors would be unaffected
by a reparameterization of `beta`.
If $\alpha$ has the same dimensions as $\beta$ but the elements of
$\alpha$ are independently and identically distributed standard normal
such that $\beta = \mu + L\alpha$, where $LL^\top = \Sigma$, then
$\beta$ is distributed multivariate normal with mean vector $\mu$ and
covariance matrix $\Sigma$. One choice for $L$ is the Cholesky factor
of $\Sigma$. Thus, the model above could be reparameterized as follows.
```stan
data {
int<lower=2> K;
vector[K] mu;
cov_matrix[K] Sigma;
// ...
}
transformed data {
matrix[K, K] L;
L = cholesky_decompose(Sigma);
}
parameters {
vector[K] alpha;
// ...
}
transformed parameters {
vector[K] beta;
beta = mu + L * alpha;
}
model {
alpha ~ std_normal();
// implies: beta ~ multi_normal(mu, Sigma)
// ...
}
```
This reparameterization is more efficient for two reasons. First, it
reduces dependence among the elements of `alpha` and second, it
avoids the need to invert `Sigma` every time `multi_normal`
is evaluated.
The Cholesky factor is also useful when a covariance matrix is
decomposed into a correlation matrix that is multiplied from both
sides by a diagonal matrix of standard deviations, where either the
standard deviations or the correlations are unknown parameters. The
Cholesky factor of the covariance matrix is equal to the product of
a diagonal matrix of standard deviations and the Cholesky factor of
the correlation matrix. Furthermore, the product of a diagonal matrix
of standard deviations and a vector is equal to the elementwise
product between the standard deviations and that vector. Thus, if for
example the correlation matrix `Tau` were fixed data but the
vector of standard deviations `sigma` were unknown parameters,
then a reparameterization of `beta` in terms of `alpha`
could be implemented as follows.
```stan
data {
int<lower=2> K;
vector[K] mu;
corr_matrix[K] Tau;
// ...
}
transformed data {
matrix[K, K] L;
L = cholesky_decompose(Tau);
}
parameters {
vector[K] alpha;
vector<lower=0>[K] sigma;
// ...
}
transformed parameters {
vector[K] beta;
// This equals mu + diag_matrix(sigma) * L * alpha;
beta = mu + sigma .* (L * alpha);
}
model {
sigma ~ cauchy(0, 5);
alpha ~ std_normal();
// implies: beta ~ multi_normal(mu,
// diag_matrix(sigma) * L * L' * diag_matrix(sigma)))
// ...
}
```
This reparameterization of a multivariate normal distribution in
terms of standard normal variates can be extended to other multivariate
distributions that can be conceptualized as contaminations of the
multivariate normal, such as the multivariate Student t and the skew
multivariate normal distribution.
A Wishart distribution can also be reparameterized in terms of standard
normal variates and chi-squared variates. Let $L$ be the Cholesky factor
of a $K \times K$ positive definite scale matrix $S$ and let $\nu$ be
the degrees of freedom. If
$$
A = \begin{pmatrix}
\sqrt{c_{1}} & 0 & \cdots & 0 \\
z_{21} & \sqrt{c_{2}} & \ddots & \vdots \\
\vdots & \ddots & \ddots & 0 \\
z_{K1} & \cdots & z_{K\left(K-1\right)} & \sqrt{c_{K}}
\end{pmatrix},
$$
where each $c_i$ is distributed chi-squared with $\nu - i + 1$ degrees
of freedom and each $z_{ij}$ is distributed standard normal, then
$W = LAA^{\top}L^{\top}$ is distributed Wishart with scale matrix
$S = LL^{\top}$ and degrees of freedom $\nu$. Such a reparameterization
can be implemented by the following Stan code:
```stan
data {
int<lower=1> N;
int<lower=1> K;
int<lower=K + 2> nu
matrix[K, K] L; // Cholesky factor of scale matrix
vector[K] mu;
matrix[N, K] y;
// ...
}
parameters {
vector<lower=0>[K] c;
vector[0.5 * K * (K - 1)] z;
// ...
}
model {
matrix[K, K] A;
int count = 1;
for (j in 1:(K - 1)) {
for (i in (j + 1):K) {
A[i, j] = z[count];
count += 1;
}
for (i in 1:(j - 1)) {
A[i, j] = 0.0;
}
A[j, j] = sqrt(c[j]);
}
for (i in 1:(K - 1)) {
A[i, K] = 0;
}
A[K, K] = sqrt(c[K]);
for (i in 1:K) {
c[i] ~ chi_square(nu - i + 1);
}
z ~ std_normal();
// implies: L * A * A' * L' ~ wishart(nu, L * L')
y ~ multi_normal_cholesky(mu, L * A);
// ...
}
```
This reparameterization is more efficient for three reasons. First, it
reduces dependence among the elements of `z` and second, it
avoids the need to invert the covariance matrix, $W$ every time
`wishart` is evaluated. Third, if $W$ is to be used with a
multivariate normal distribution, you can pass $L A$ to the more
efficient `multi_normal_cholesky` function, rather than passing
$W$ to `multi_normal`.
If $W$ is distributed Wishart with scale matrix $S$ and degrees of
freedom $\nu$, then $W^{-1}$ is distributed inverse Wishart with inverse
scale matrix $S^{-1}$ and degrees of freedom $\nu$. Thus, the previous
result can be used to reparameterize the inverse Wishart distribution.
Since $W = L A A^{\top} L^{\top}$,
$W^{-1} = L^{{\top}^{-1}} A^{{\top}^{-1}} A^{-1} L^{-1}$, where all four
inverses exist, but
$L^{{-1}^{\top}} = L^{{\top}^{-1}}$ and $A^{{-1}^{\top}} = A^{{\top}^{-1}}$.
We can slightly modify the above Stan code for this case:
```stan
data {
int<lower=1> K;
int<lower=K + 2> nu
matrix[K, K] L; // Cholesky factor of scale matrix
// ...
}
transformed data {
matrix[K, K] eye;
matrix[K, K] L_inv;
for (j in 1:K) {
for (i in 1:K) {
eye[i, j] = 0.0;
}
eye[j, j] = 1.0;
}
L_inv = mdivide_left_tri_low(L, eye);
}
parameters {
vector<lower=0>[K] c;
vector[0.5 * K * (K - 1)] z;
// ...
}
model {
matrix[K, K] A;
matrix[K, K] A_inv_L_inv;
int count;
count = 1;
for (j in 1:(K - 1)) {
for (i in (j + 1):K) {
A[i, j] = z[count];
count += 1;
}
for (i in 1:(j - 1)) {
A[i, j] = 0.0;
}
A[j, j] = sqrt(c[j]);
}
for (i in 1:(K - 1)) {
A[i, K] = 0;
}
A[K, K] = sqrt(c[K]);
A_inv_L_inv = mdivide_left_tri_low(A, L_inv);
for (i in 1:K) {
c[i] ~ chi_square(nu - i + 1);
}
z ~ std_normal(); // implies: crossprod(A_inv_L_inv) ~
// inv_wishart(nu, L_inv' * L_inv)
// ...
}
```
Another candidate for reparameterization is the Dirichlet distribution
with all $K$ shape parameters equal. @ZyczkowskiSommers:2001 shows
that if $\theta_i$ is equal to the sum of $\beta$ independent squared
standard normal variates and $\rho_i = \frac{\theta_i}{\sum \theta_i}$,
then the $K$-vector $\rho$ is distributed Dirichlet with all shape
parameters equal to $\frac{\beta}{2}$. In particular, if $\beta = 2$,
then $\rho$ is uniformly distributed on the unit simplex. Thus, we can
make $\rho$ be a transformed parameter to reduce dependence, as in:
```stan
data {
int<lower=1> beta;
// ...
}
parameters {
array[K] vector[beta] z;
// ...
}
transformed parameters {
simplex[K] rho;
for (k in 1:K) {
rho[k] = dot_self(z[k]); // sum-of-squares
}
rho = rho / sum(rho);
}
model {
for (k in 1:K) {
z[k] ~ std_normal();
}
// implies: rho ~ dirichlet(0.5 * beta * ones)
// ...
}
```
## Vectorization
### Gradient bottleneck {-}
Stan spends the vast majority of its time computing the gradient of
the log probability function, making gradients the obvious target for
optimization. Stan's gradient calculations with algorithmic
differentiation require a template expression to be allocated
and constructed for each subexpression of a Stan program involving
parameters or transformed parameters.^[Stan uses its own arena-based allocation, so allocation and deallocation are faster than with a raw call to `new`.] This section defines optimization strategies based on vectorizing these subexpressions to reduce the work done during algorithmic differentiation.
### Vectorizing summations {-}
Because of the gradient bottleneck described in the previous section,
it is more efficient to collect a sequence of summands into a vector
or array and then apply the `sum()` operation than it is to
continually increment a variable by assignment and addition. For
example, consider the following code snippet, where `foo()` is
some operation that depends on `n`.
```stan
for (n in 1:N) {
total += foo(n,...);
}
```
This code has to create intermediate representations for each
of the `N` summands.
A faster alternative is to copy the values into a vector, then
apply the `sum()` operator, as in the following refactoring.
```stan
{
vector[N] summands;
for (n in 1:N) {
summands[n] = foo(n,...);
}
total = sum(summands);
}
```
Syntactically, the replacement is a statement block delineated
by curly brackets (`{`, `}`), starting with the definition
of the local variable `summands`.
Even though it involves extra work to allocate the `summands`
vector and copy `N` values into it, the savings in
differentiation more than make up for it. Perhaps surprisingly,