\section{Implementation}\label{sec:impl}\zh{TODO}
In this section, we elaborate on how we collect data for training, the specific parameters we choose for the two orthogonal neural networks, and the software/hardware environment we use.

\paragraph{Data generation}
For each scene, we generate two datasets separately for training foveal and periphery networks. The foveal dataset is composed of $256\times256$ images rendered at different views with 20 deg field-of-view, while the periphery dataset uses 45 deg field-of-view to rendering.
% \nothing{
% %\paragraph{Input encoding}
% Similar to \cite{mildenhall2020nerf}, we also extend the input dimensions ($d=3$) to a higher dimensional ($d=60$) for training.
% \nothing{This is achieved via coding the input as:
% \begin{align}
% \iota (p) = (\sin(2^0 p), \cos(2^0 p), ..., \sin(2^{L-1}p, \cos(2^{L-1}p))
% \label{eqn:inputencode}
% \end{align}
% The function $\iota (\cdot)$ is applied to dimension of $\SpatialPt=(r_\SpatialPt,\theta_\SpatialPt, \phi_\SpatialPt)$.
% In our experiment we choose $L=10$.\qisun{What is $L$?}\dnc{It's an encoding parameter for input. maps 3D input to $R^{6L}$ space, as \autoref{eqn:inputencode} shows}
% }}
\paragraph{Optimized network parameters}
\warning{@Nianchen: fix all N values across the whole paper.}
Guided by the latency-quality joint optimization described in \Cref{sec:method:optimization} \new{and $64$ spheres depicting all the tested scenes}, we implemented the foveal network with \new{$\mlpLayerNum=4$, $\mlpChannelNum=512$ and $\sphereNum = 16$}. For the periphery network, the values are \new{$\mlpLayerNum=4$, $\mlpChannelNum=256$ and $\sphereNum = 32$}. These optimized parameters achieve a proper balance between quality and latency, as shown in \Cref{fig:results:comparison} and \Cref{tbl:ablation}.

% \paragraph{Ray marching bounding}
% \zh{@Dengnianchen: shall we remove the entire section?}

% We bound the ray marching within region that $\mathbf{\sphereRadius}$ are sampled in, thus has impact on $\sparseError$ in \Cref{eq:sparseError}. As shown in \Cref{fig:impl:bounding:off}, results with infinite upper bounding ($\sphereRadius\in[\sphereRadius_{near},+\infty)$ incur blurry and ghosting artifacts due to larger $\sparseError$. In contrast, limited bounding significantly improves the quality (\Cref{fig:impl:bounding:on}). %The bounding range, together with $\sphereNum$, $\mlpLayerNum$ and $\mlpChannelNum$, are determined by a spatial-temporal optimization, as detailed in \Cref{sec:method:optimization}.
% \input{TOG/fig_ray_bounding}

\paragraph{Environment}
The system was implemented through OpenGL framework using CUDA and accelerated by TensorRT with Intel(R) Xeon(R) Gold 6230R CPU @ 2.10GHz (256GB RAM) and one NVIDIA GTX 3090 graphics card.
\new{For each scene, we trained both the foveal network and the periphery network with 200 epochs.}


\section{Experiments}\label{sec:expts}

\begin{table}[h]
\centering
\small
 \setlength{\tabcolsep}{4pt}
\begin{tabular}{l l l l l l l l l }
\toprule
Dataset & \#folds/ & \#users & total & training set & \multicolumn{2}{c}{test set} & \multicolumn{2}{c}{\#features} \\
              & samples &              & \#items & \#pairs  & \#pairs & \#items
               & items & users \\
\midrule
Simulation a and b & 25 & 25 & 100 & 900 & 0 & 100 & 2 & 2 \\
Simulation c & 25 & 25 & 100 & 36--2304 & 0 & 100 & 2 & 2\\
\midrule
Sushi A-small & 25 & 100 & 10 & 500 & 2500 & 10 & 18 & 123 \\
Sushi A & 25 & 100 & 10 & 2000 & 2500 & 10 & 18 & 123 \\
Sushi B & 25 & 5000 & 100 & 50000 & 5000 & 100 &  18 & 123 \\
\midrule
UKPConvArgCrowdSample & 32 & 1442 & 1052 & 16398 & 529 & 33 & 32310 & 0
\\ \bottomrule
\end{tabular}
\caption{Summary of datasets showing average counts for the training and test sets
used in each fold/subsample.
%For simulations, we generate the subsamples of data independently,
%for Sushi we select subsamples independently from the dataset.
%Values for UKPConvArgCrowdSample are means per fold,
%where the test data in each fold corresponds to a single topic and stance.
The test sets all contain gold-standard rankings over items as well as
pairwise labels, except the simulations, which are not generated as
 we evaluate using the rankings only.
Numbers of features are given after categorical labels have been converted to one-hot encoding, counting
each category as a separate feature.
}
\label{tab:datasets}
\end{table}
Our experiments test key aspects of crowdGPPL:
 predicting consensus utilities and personal preferences from pairwise labels
 %predicting consensus utilities from noisy crowdsourced preferences
 % in a subjective task;
 and the scalability of our proposed SVI method.
In Section \ref{sec:exp_synth}, we use simulated data to test the robustness of crowdGPPL
to noise and unknown numbers of latent components.
Section \ref{sec:sushi}
compares different configurations of the model
against alternative methods
using the \emph{Sushi} datasets\footnote{\url{http://www.kamishima.net/sushi/}}~\citep{kamishima2003nantonac}.
Section \ref{sec:exp_scale} evaluates prediction performance and scalability of
 crowdGPPL %to predict both personal and consensus utilities
in a high-dimensional
NLP task
with sparse, noisy crowdsourced preferences
(\emph{UKPConvArgCrowdSample}\footnote{\url{https://github.com/ukplab/tacl2018-preference-convincing}}, ~\citet{simpson2018finding}).
%Using this latter dataset, we then analyse
%the scalability of our SVI approach.
Finally, Section \ref{sec:components} evaluates whether crowdGPPL ignores redundant
components.
%when the number of components, $C$,
%is larger than necessary.
The datasets are summarised in Table \ref{tab:datasets}.

%\paragraph{Method Comparison. }

% Ranking-SVM baseline -- only easy to compare in the single user case. My GPPL paper perhaps needs
% this adding in any follow up works.

% Houlsby tests: with/without user features (without is better with few users). + a hierarchical
% model, BI (multi task preference learning, Birlitiu et al), and the GPPL-joint model. None of
% these are done at scale, which we can do with our inference method --> *this is a new claim i.e.
% new empirical results*. They also test a per-user model.

As baselines, we compare crowdGPPL against
\emph{GPPL},
which we train on all users' preference labels to learn a single utility function,
%and a Gaussian process over the joint feature space of users and items
%(\emph{joint-GPPL}), as proposed by \citet{guo2010gaussian}.
and \emph{GPPL-per-user},
in which a separate GPPL instance is learned for each user with no collaborative
learning.
We also compare against the \emph{GPVU} model~\citep{khan2014scalable}
and
\emph{collabGP} ~\citep{houlsby2012collaborative}.
CollabGP contains parameters for each pairwise label and
 each user, so has a larger memory footprint than our SVI scheme,
which stores only the moments at the inducing points.
%CollabGP does not learn a consensus or scale parameters $s_c^{(v)}$ and $s_c^{(w)}$,
%and predicts pairwise labels directly rather than utilities that can be used for ranking.
%combines
%matrix factorisation without input features and a separate GP per user.
%Inference over the latent
%components does not benefit from any item or user features,
%and maximum likelihood steps are used to learn the parameters, rather than applying a fully-Bayesian
%treatment.
% While the original
%implementation used some maximum likelihood steps, we implement GPVU using SVI method
%to directly compare against crowdGPPL and GPPL-per-user.
% --not quite, as we emulate the ML steps without implementing them exactly.

We test \emph{crowdBT}~\citep{chen2013pairwise} as part of a method for
predicting consensus utilities from crowdsourced pairwise preferences.
CrowdBT models each worker's accuracy, assuming that
the differences between workers' labels are
due to random errors rather than subjective preferences.
Since crowdBT does not account for the item features,
it cannot predict utilities for items that were not part of the training set.
%and is hence purely an aggregation method.
We therefore treat the posterior mean utilities produced by crowdBT as training labels
for Gaussian process regression using SVI.
We set the observation noise variance of the GP equal to the crowdBT posterior variance
of the utilities
to propagate uncertainty from crowdBT to the GP.
This pipeline method, \emph{crowdBT--GP},
tests whether it is sufficient to treat annotator differences as noise,
in contrast to the crowdGPPL approach of modelling individual preferences.
%Uncertainty is propagated from crowdBT to the GP through the observation noise variance,
%hence labels that crowdBT has not learned confidently will have less effect on the GP.

We evaluate the methods using the following metrics:
\emph{accuracy (acc)}, which is the fraction of correct pairwise labels;
\emph{cross entropy error (CEE)} between the posterior probabilities over pairwise labels
and the true labels, which captures the quality of the pairwise posterior;
\emph{Kendall's $\tau$}, which evaluates the ranking obtained by sorting items by
predicted utility.

\subsection{Simulated Noisy Data}\label{sec:exp_synth}

\begin{figure}[t]
%\subfloat[Inferring preferences for a single user]{
%\label{fig:simA}
%\includegraphics[width=.35\columnwidth]{../../results/synth_3/single_user/tau_test}
%}
\subfloat[Consensus]{
\label{fig:simB}
\includegraphics[width=.322\columnwidth,clip=true,trim=15 4 13 0]{tau_test}
}
\subfloat[Personal preferences]{
\label{fig:simC}
\includegraphics[width=.306\columnwidth,clip=true,trim=30 5 14 0]{tau_test_personal}
}
\subfloat[Latent factors]{
\label{fig:simD}
\includegraphics[width=.324\columnwidth,clip=true,trim=8 0 13 0]{num_pairs_r}
}
\caption{Simulations: rank correlation between true and inferred utilities.
(a) \& (b) vary the level of noise in pairwise training labels, (c) varies the number of pairwise training labels.
}
\end{figure}
First, we evaluate whether crowdGPPL is able to model individual preferences
with varying amounts of labelling noise.
We set the number of latent components to $C=20$ and all Gamma hyperparameters for crowdGPPL, GPPL and GPPL-per-user
to $\alpha_0 = 1$, $\beta_0 = 100$.
We use Mat\'ern 3/2 kernels with the length-scale for each dimension of the feature vector, $d$,
chosen by a median heuristic:
\begin{flalign}
 l_{d,\mathrm{MH}} = \mathrm{median}( \{ ||x_{i,d} - x_{j,d}||,
 \forall i=1,..,N, \forall j=1,...,N\} ).
\end{flalign}
This is a computationally frugal way to choose the length-scales,
%which effectively normalizes the features and
that has been extensively used in various kernel methods (e.g., ~\citet{bors1996median,gretton2012optimal}).
The SVI hyperparameters were set to
 $\rho=0.9$, $P_i=1000$ and $\epsilon=1$.
 \citet{hoffman2013stochastic} found that higher values of
 $\rho$ gave better final results but slightly slower convergence, recommending
 $0.9$ as a good balance across several datasets,
 and did not find any effect from changing $\epsilon$.
We follow their recommendations and do not find it necessary to perform further
tuning in our experiments.
% While we do not find it necessary in our experiments,
% it is possible to tune $\rho$ and $\epsilon$ using, for example,
% grid search to trade-off between convergence time and maximising the lower bound of the log marginal
% likelihood, $\mathcal{L}$.
 Both $M$ and $P_i$ are constrained
 %y also found that increasing improves performance,
 %however this is likely to be constrained
 in practice by the computational resources available -- we investigate these further in
 Section \ref{sec:exp_scale}.

In simulation (a), to test consensus prediction,
we generate a $20\times 20$ grid of points
and split them into  50\% training and test sets.
For each gridpoint, we generate pairwise labels by drawing from the generative model of crowdGPPL
with $U=20$ users, $C=5$, each $s^{(v)}_c$ set
to random values between 0.1 and 10, and $s^{(w)}_c = 1, \forall c$.
We vary
%the inverse scale of the consensus function,
$s^{(t)}$ to control the noise in the consensus function.
We train and test crowdGPPL with $C=U$ and repeat the complete experiment
$25$ times, including generating new data.
%for each value of $s^{(t)}$.

Figure \ref{fig:simB} shows that crowdGPPL better recovers the
consensus ranking than the baselines, even as noise increases, as
GPPL's predictions are worsened by biased users who deviate
consistently from the consensus.
For GPPL-per-user, the consensus is simply
the mean of all users' predicted utilities,
so does not benefit from sharing information between users when training.
For simulation (b), we modify the previous setup
by fixing $s^{(t)} = 5$ and varying $s^{(v)}_c,\forall c$
to evaluate the methods'
ability to recover the personal preferences of simulated users.
The results in Figure \ref{fig:simC} show that crowdGPPL is able to make better
predictions when noise is below $0.3$.
% but its benefit disappears when
%the noise level increases further.

%In the final simulation, %we evaluate the effect of the quantity of training data
%with different numbers of latent factors in the generating model.
We hypothesise that crowdGPPL can recover latent components given sufficient training data.
%scenario that would require more training data.
In simulation (c), we generate data using the same setup as before,
but fix $s^{(t)} = s^{(v)}_c = s^{(w)} = 1,\forall c$
and vary the number of pairwise training labels
and the number of true components through
$C_{\mathrm{true}} \in \{ 1, 3, 10, 20\}$.
%We evaluate the correlation between inferred and true user components,
%we match inferred factors to true factors,
We match inferred components to the true components as follows:
compute Pearson correlations between each unmatched true component and
each unmatched inferred component;
select the pair with the highest correlation as a match;
repeat until all true components are matched.
In Figure \ref{fig:simD} we plot the mean correlation between matched pairs of components.
For all values of $C_{\mathrm{true}}$, increasing the
number of training labels beyond $700$ brings little improvement.
Performance is highest when $C_{\mathrm{true}} = 20$,
possibly because the predictive model has $C = 20$,
so is a closer match to the generating model.
However, %
crowdGPPL is able to recover latent components reasonably well for all
values of $C_{\mathrm{true}}$ given $>500$ labels, despite mismatches between $C$ and $C_{\mathrm{true}}$.
% Does this pose a question: does the mismatch affect the performance? If the correlations
% decrease, it may mean that the model is decomposing a factor into a sum of multiple factors,
% or it may just be unable to learn it. This would need a new experiment: for 700 training pairs,
% how does the accuracy of personalised predictions vary with the number of latent factors?
% I think this needs us to vary C and keep C_true=3, otherwise we don't know whether the
% performance differences are due to the mismatched no. factors or due to different underlying dataset, i.e. we need to keep the data the same to compare!

\subsection{Sushi Preferences}\label{sec:sushi}

\begin{table}
 \centering
 \small
 \setlength{\tabcolsep}{4pt}
 \begin{tabular}{l l l l@{\hskip 0.5cm} l l l@{\hskip 0.5cm} l l l}
\toprule
& \multicolumn{3}{c}{\textbf{Sushi-A-small}} & \multicolumn{3}{c}{\textbf{Sushi-A}} & \multicolumn{3}{c}{\textbf{Sushi-B}} \\
Method & Acc & CEE & $\tau$ & Acc & CEE & $\tau$ & Acc & CEE & $\tau$ \\
\midrule
crowdGPPL & \textbf{.71} & \textbf{.56} & .48
& .84 & .33 & .79
& .76 & .50 & . 54
 \\
crowdGPPL $\backslash $inducing & .70 & .60 & .45
& .84 & .34 & .78
& - & - & -
\\
crowdGPPL $\backslash  \bs u$ & .70 & .58 & .46 &
\textbf{.85} & \textbf{.31} & \textbf{.80}
& \textbf{.78} & .50 & .57
\\
crowdGPPL $\backslash  \bs u \backslash  \bs x$ & \textbf{.71} & .57 & \textbf{.49} &
\textbf{.85} & .33 & .80
& .77 & \textbf{.49} & .56
\\
crowdGPPL $\backslash \bs u,\backslash \bs t$
& .68 & .60 & .43
& .84 & .33 & .80
& .76 & .51 & .58
\\
\midrule
GPPL & .65 & .62 & .31
& .65 & .62 & .31
& .65 & .62 & .31
\\
GPPL-per-user & .67 & .64 & .42
& .83 & .40 & .79
& .75 & .60 & \textbf{.60}
\\
collabGP & .69 & .58 & n/a
& .83 & .35 & n/a
& .76 & \textbf{.49} & n/a
\\
collabGP$\backslash  \bs u$ & .69 & .59 & n/a & .84 & .33 & n/a & .76 & .50 & n/a
\\
GPVU & .70 & .67 & .43 & .72 & .67 & .42 & .73 & .59 & .52
%collabGP$\backslash u, C=50$ & .70 & .58 & n/a & .85 & .33 & n/a & \\
\\ \bottomrule
\end{tabular}
\caption{Predicting personal preferences on \emph{Sushi} datasets,
means over $25$ repeats.
%CrowdGPPL uses $C=20$ unless otherwise specified.
The standard deviations are $\leq 0.02$ for all accuracies,
$\leq 0.08$ for all CEE,
and $\leq 0.03$ for all $\tau$.
For Sushi-B, crowdGPPL, GPPL-per-user and collabGP had runtimes of $~30$ minutes on a 12 core, 2.6GHz CPU server; GPPL required only 1 minute.
 }
\label{tab:sushi}
\end{table}
%We use the \emph{Sushi} datasets
%to compare different variants of crowdGPPL against the baselines and rival methods.
The sushi datasets contain, for each user, a gold standard preference ranking
of $10$ types of sushi,
from which we generate gold-standard pairwise labels.
%These labels can be considered noise-free, since
%they are derived directly from the gold standard ranking.
To test performance with very few training pairs, we obtain \emph{Sushi-A-small}
by selecting $100$ users at random from the complete \emph{Sushi-A} dataset,
then selecting $5$ pairs for training and $25$ for testing per user.
For \emph{Sushi-A}, we select $100$ users at random from the complete dataset, then
split the data into training and test sets by randomly
selecting $20$ training $25$ test pairs per user.
For \emph{Sushi-B}, we use all $5000$ workers, and subsample $10$ training and $1$ test pair per user.

We compare standard crowdGPPL with four other variants:
\begin{itemize}
\item \emph{crowdGPPL$\backslash$inducing}: does
not use the sparse inducing point approximation and instead uses all the original
points in the training set;
\item \emph{crowdGPPL$\mathbf{\backslash \bs u}$}: ignores the user features;
\item  \emph{crowdGPPL$\mathbf{\backslash \bs u \backslash \bs x}$}: ignores both user and item features;
\item  \emph{crowdGPPL$\mathbf{\backslash \bs u \backslash \bs t}$}:
excludes the consensus function $\bs t$ from the model as well as the user
features.
\end{itemize}
For methods with $\backslash\bs u$, the user covariance matrix, $\bs L$,
is replaced by the identity matrix,
and for crowdGPPL$\mathbf{\backslash \bs u \backslash \bs x}$,
$\bs K$ is also replaced by the identity matrix.
As the user features do not contain detailed, personal information (only
region, age group, gender, etc.), they are not expected
to be sufficiently informative to predict personal preferences on their own.
Therefore, for crowdGPPL and crowdGPPL$\backslash$inducing,
 we compute $\bs L$ for 10 latent components using
 the Mat\'ern 3/2 kernel function
 and use the identity matrix for the remaining 10.
CollabGP is also tested with and without user features.
We set hyperparameters $C=20$,
$\epsilon=1$, $\rho=0.9$, $P_i=200$ for  \emph{Sushi-A-small} and \emph{Sushi-A},
 and $P_i=2000$ for \emph{Sushi-B},
without optimisation.
For the gamma hyperparameters,  a grid search over
$\{10^{-1},...,10^3\}$ on withheld user data from \emph{Sushi-A}
resulted in $\alpha_0=1, \beta_0=100$ for GPPL variants, and
$\alpha_0^{(t)}=1,\beta_0^{(t)}=100$,
$\alpha_0^{(v)}=1,\beta_0^{(v)}=10$ and
$\alpha_0^{(w)}=1,\beta_0^{(w)}=10$ for crowdGPPL variants.
%All other hyperparameters are the same
%as for Section \ref{sec:exp_synth}.
The complete process of subsampling, training and testing, was repeated $25$ times
for each dataset.

The results in Table \ref{tab:sushi}
illustrate the benefit of personalised models over single-user GPPL.
The inducing point approximation does not appear to harm performance of crowdGPPL, but
 including the user features tends to decrease its performance
compared to crowdGPPL$\backslash\bs u$ and crowdGPPL$\backslash\bs u\backslash\bs x$,
except on Sushi-A-small, where they may help with the small amount of training data.
Comparing crowdGPPL$\backslash\bs u$ with crowdGPPL$\backslash\bs u\backslash\bs t$, including the consensus function improves performance modestly.
The strong performance of GPPL-per-user
suggests that even 10 pairs per person were enough
to learn a reasonable model for \emph{Sushi-B}.
As expected, the more memory-intensive collabGP performs comparably well to crowdGPPL
on accuracy and CEE but does not provide a ranking function for computing Kendall's $\tau$.
GPVU does not perform as well as other personalised methods on Sushi-A and Sushi-B,
potentially due to its maximum likelihood inference steps.
% The inference method of collabGP did not present practical problems
% on the Sushi dataset on computer with 16GB RAM,
% and in fact produces relatively fast run times due to its more efficient implementation using a C library in place of
% Python.
The results show that crowdGPPL is competitive despite
the approximate SVI method,
so in the next experiment, we test the approach on a larger crowdsourced
dataset where
low memory consumption is required.

\subsection{Argument Convincingness}\label{sec:exp_scale}

% TODO: split the personalised results by no. training examples per worker
% and by model confidence. Does filtering predictions with low confidence estimates
% help? Do workers with more data get better predictions?

We evaluate consensus learning, personal preference learning and scalability
on an NLP task, namely, ranking arguments by \emph{convincingness}.
The task requires learning from crowdsourced data, but is not simply an aggregation task as it
requires learning a predictor for test documents that were not compared by the crowd.
The dataset, \emph{UKPConvArgCrowdSample}, was subsampled by \citet{simpson2018finding}
from raw data provided by \citet{habernal2016argument}, and
contains arguments written by users
of online debating forums,
with crowdsourced judgements of pairs of arguments
 indicating the most convincing argument.
%The task is to quantify how convincing each argument is
%by learning a model from pairwise preference labels obtained from crowdworkers
%on Amazon Mechanical Turk.
%Each argument is represented by $32,310$ numerical features and the
The data is divided into $32$ folds ($16$ topics, each with 2 opposing stances). For each fold, we train on $31$ folds and test on the remaining fold.
We extend
the task
%described in \citet{simpson2018finding}
to predicting both the consensus and personal preferences of individual crowd workers.
%thereby performing a cross-topic evaluation.
%test the ability of the preference learning methods to predict the consensus
 %by training on raw crowdsourced pairwise labels
%for $31$ topics, and testing against the gold pairwise labels and rankings for the
%remaining topic. This` process is repeated for all $32$ topics.
GPPL previously outperformed SVM and Bi-LSTM methods at consensus prediction for \emph{UKPConvArgCrowdSample}~\citep{simpson2018finding}.
%We compare GPPL with crowdGPPL and also test each method's
%ability to predict the raw crowdsourced labels, i.e. the individual preference labels
%supplied by each worker.
We hypothesise that a worker's view of convincingness
depends on their personal view of the subject
discussed, so crowdGPPL may outperform GPPL and
 crowdBT-GP on both consensus and personal preference prediction.
%We expect crowdGPPL to improve consensus prediction over crowdBT-GP,
%as it accounts for personal biases as well as noisy labels.
%For consensus prediction, crowdBT handles disagreements between workers by treating them
%as noisy labellers, but we expect that by accounting for
%for personal biases as well as noise, crowdGPPL may further improve consensus prediction.

The dataset contains $32,310$ linguistic and embedding features
for each document (we use mean GloVe embeddings for the words in each document, see \citet{simpson2018finding}). The high-dimensionality of the input feature vectors requires us to modify the length-scale heuristic for all GP methods,
%of \emph{UKPConvArgCrowdSample}.
as the distance between items grows with the number of dimensions,
which causes the covariance to shrink to very small values. We therefore use
$l_{d,\mathrm{scaledMH}} = 20\sqrt{D} \times l_{d,\mathrm{MH}}$,
where $D$ is the dimension of the input feature vectors,
and the scale was chosen by comparing the training set accuracy
with scales in $\{\sqrt{D}, 10\sqrt{D}, 20\sqrt{D}, 100\sqrt{D}\}$.
The hyperparameters are the same as Section \ref{sec:exp_synth}
except GPPL uses $\alpha_0 = 2$, $\beta_0 = 200$ and
crowdGPPL uses $\alpha^{(t)}_0=\alpha^{(v)}_0=2$, $\beta^{(t)}_0=\beta^{(t)}_0=200$,
$\alpha^{(w)}_0=1$, $\beta^{(w)}_0=10$.
We do not optimise $\alpha_0$, but choose $\beta_0$ by comparing
training set accuracy for GPPL with $\beta_0 \in \left\{2,200,20000\right\}$.
The best value of $\beta_0$ is also used for $\beta^{(t)}_0$ and $\beta^{(v)}_0$,
then training set accuracy of crowdGPPL is used to select
$\beta^{(w)}_0 \in \left\{1, 10, 100 \right\}$.
We set $C=50$, $M=500$, $P_i=200$, $\epsilon=10$, and $\rho=0.9$ without optimisation.

\begin{table}
 \centering
 \small
 \setlength{\tabcolsep}{4pt}
\begin{tabular}{ l l l l@{\hskip 1.0cm} l l l@{\hskip 1.0cm} l l l}
\hline
 & \multicolumn{3}{l}{Consensus} &
 \multicolumn{3}{l}{Personal: all workers} &\multicolumn{3}{l}{$>$50 training pairs} \\
 Method & Acc & CEE & $\tau$ & Acc & CEE & $\tau$ & Acc & CEE & $\tau$ \\
  \midrule
 %SVM & .70 & .58 & .31 & .63 & .66 & .31 \\
 %Bi-LSTM & .73 &  .55 & .21 & .64 & .64 & .21 \\
 GPPL  &
 .77 & .51 & .50 &
 .71 &  \textbf{.56} & .31 &
 .72 &  \textbf{.55} & .25 \\ % .71 & .56 & .32 \\ %
 crowdGPPL &
 \textbf{.79} & \textbf{.52} & \textbf{.53} &
 \textbf{.72} & .58 & \textbf{.33} &
 \textbf{.74} & \textbf{.55} & \textbf{.27}  \\ %.71 & .59 & .32 \\ % now showing workers with > 40 training examples
 crowdGPPL$\backslash \bs t$ & - & - & - &
.68 & .63 & .23 & \textbf{.74} & .57 & \textbf{.27}
 \\
 crowdBT-GP & .75 & .53 & .45 & .69 & .58 & .30 & .71 & .56 & .23
 \\ \bottomrule
\end{tabular}
%}
\caption{UKPConvArgCrowdSample: predicting consensus, personal preferences for all workers,
and personal preferences for workers with $>$50 pairs in the training set.
%Runtimes on a 12 core, 2.6GHz CPU server were approximately 3 minutes for GPPL and crowdBT-GP, 20 minutes for crowdGPPL.
}
% wilcoxon signed-rank test: crowdGPPL vs. GPPL --> medi. p =
\label{tab:convarg}
\end{table}
Table \ref{tab:convarg} shows that
crowdGPPL outperforms both GPPL and
crowdBT--GP
at predicting both the consensus and personal preferences
%pairwise labels
%shown by classification accuracy and cross entropy error,
%and the consensus ranking
(significant for Kendall's $\tau$ with $p<0.05$, Wilcoxon signed-rank test),
%shown by Kendall's $\tau$ rank correlation.
suggesting that there is a benefit
to modelling individual workers in subjective, crowdsourced tasks.
%For the personal preference predictions, crowdGPPL also outperforms
%GPPL.
We also compare against crowdGPPL without the consensus (crowdGPPL$\backslash \bs t$)
and find that including $\bs t$ in the model improves personalised
predictions. This is likely because many workers have few training pairs,
so the consensus helps to identify arguments that are commonly considered very poor or very
convincing.
Table \ref{tab:convarg} also shows that for
workers with more than 50 pairs in the training set,
accuracy and CEE improve for all methods but
$\tau$ decreases,
suggesting that some items may be ranked further away from their correct ranking
for these workers. It is possible that workers who were willing to complete more
annotations (on average 31 per fold)
deviate further from the consensus, and crowdGPPL does not fully capture
their preferences given the data available.

\begin{figure}[ht]
\centering
\subfloat[Varying $M$]{
\label{fig:M}
 \includegraphics[clip=true,trim=9 0 55 0,width=.28\columnwidth]{num_inducing_32310_features}
}
\subfloat[Varying $P_i$   \hspace{0.5cm}]{
\label{fig:P_i}
 \includegraphics[clip=true,trim=60 2 9 0,width=.275\columnwidth]{P_i_32310_features}
}\\
\subfloat[Varying $N$]{
\label{fig:Ntr}
\includegraphics[clip=true,trim=9 0 10 0,width=.33\columnwidth]{num_arguments}
}
\subfloat[Varying $P$ ]{
\label{fig:Npairs}
\includegraphics[clip=true,trim=26 0 10 0,width=.30\columnwidth]{num_pairs}
}
\caption{
    Wall-clock times for training+prediction of consensus utilities for arguments
    in the training folds of
    UKPConvArgCrowdSample. CrowdGPPL was run with $C=5$. In (b), (c) and (d),  $M=100$.
    Lines show means over 32 runs,
     bands indicate 1 standard deviation (mostly very little variation between folds).
}
\end{figure}
We examine the scalability of our SVI method by evaluating GPPL and crowd-GPPL with
different numbers of inducing points, $M$,
and different mini-batch sizes, $P_i$.
%Here, we fix $C=5$ and keep other model hyperparameters
%and experimental setup the same as for Table \ref{tab:convarg}.
Figure \ref{fig:M} shows the trade-off between
runtime and training set accuracy as an effect of choosing $M$.
Accuracy levels off as $M$ increases,
while runtime continues to increase rapidly in a polynomial fashion.
Using inducing points can therefore give a large improvement in runtimes
with a fairly small performance hit.
Figure \ref{fig:P_i} demonstrates that
smaller batch sizes do not negatively affect the accuracy,
although they increase runtimes as more iterations are required for convergence.
The runtimes flatten out as $P_i$ increases, so we recommend choosing $P_i\geq 200$
but small enough to complete an iteration rapidly with the computational resources available.
Figures \ref{fig:Ntr} and \ref{fig:Npairs} show runtimes as a
function of the number of items in the training set, $N$,
and the number of pairwise training labels, $P$, respectively (all other settings remain as in Figure \ref{fig:M}).
In both cases, the increases to runtime are small, despite the growing dataset size.

\subsection{Posterior Variance of Item Components}
\label{sec:components}

\begin{figure}[t]
\centering
\subfloat[\emph{UKPConvArgCrowdSample}]{
\includegraphics[trim=0 5 0 20,clip=true,width=.343\textwidth]{conv_factor_scales2}
\hspace{1cm}
}
\subfloat[\emph{Sushi-A}]{
\hspace{1cm}
\includegraphics[trim=10 0 0 17,clip=true,width=.33\textwidth]{sushi_factor_scales2}
}
\caption{
Latent component variances, $1/(s^{(v)}_c * s^{(w)}_c)$ in crowdGPPL, means over all runs.
}
\label{fig:latent_factor_variance}
\end{figure}
We investigate how many latent components were actively used by
 crowdGPPL on the \emph{UKPConvArgCrowdSample} and \emph{Sushi-A} datasets.
Figure \ref{fig:latent_factor_variance}
plots the posterior expectations of the inferred scales, $1/(s^{(v)}_c * s^{(w)}_c)$, for the latent item
 components.
 The plots show
that many factors have a relatively small variance and therefore do not contribute
to many of the model's predictions. This indicates that
our Bayesian approach will only make use of components that are supported by the data,
even if $C$ is larger than required.


\section{A contract for string}

This section outlines a partial specification of a \texttt{string} class.

\begin{lstlisting}
template <typename charT, typename traits = char_traits<charT>,
    typename Allocator = allocator<charT> >
class basic_string {
public:

  explicit basic_string(const Allocator & a)
    expects(
      data() != nullptr &&
      size() == 0
    );

  basic_string(const basic_string & str)
    ensures(valid_copy(str));

  basic_string(const basic_string && str)
    ensures(valid_copy(str) && str.valid_state());

  basic_string(const basic_strig & str,
               size_type pos, size_type n = npos,
               const Allocator & a = Allocator())
    expects<std::out_of_range>(pos <= str.size())
    ensures(valid_copy_n(str, n));

  basic_string(const charT * s, size_type n,
               const Allocator & a = Allocator())
    expects(s != nullptr)
    ensures(valid_copy_n_cstr(s,n));

  // ...

private:
  bool valid_copy(const basic_string & str) const {
    return data() != nullptr &&
      equal(data(), data() + size, str.data(), str.data() + str.size()) &&
      size() == str.size() &&
      capacity() >= size();
  }

  bool valid_copy_n(const basic_string & str, size_type n) const {
    auto rlen = min(n, str.size());
    return data() != nullptr &&
      equal(data(), data() + rlen, str.data(), str.data() + str.size()) &&
      size() == rlen &&
      capacity() >= size();
  }

  bool valid_copy_n_cstr(const charT * s, size_type n) const {
    return data() != nullptr &&
      equal(data(), data() + n, s, s+n) &&
      size() == n &&
      capacity() >= size();
  }

}
invariant(size() <= capacity());

\end{lstlisting}


\section{Codling Moth Generations}

All these functions are written by Giridhar.
% \item \func{
% \code{
% \vari{
% \texttt{
% \scriptsize{

\begin{enumerate}

\item \func{readbinarydata\_addmdy(filename, Nrecords, Nofvariables, ymd, ind)}
This function reads the binary data off the disk,
generates a matrix containing [\code{precip, Max temp., Min temp., Wind speed}]
adds the \code{ymd} to it and returns it.

\code{input:}
\begin{itemize}
\item \vari{filename} This is the full path to the file which includes the file name.
\item \vari{Nrecords}
\item \vari{Nofvariables}
\item \vari{ymd} Year, Month, Day values
\item \vari{ind}
\end{itemize}

\code{output:} [\code{precip, Max temp., Min temp., Wind speed, ymd}]

\hrule

Why not \code{readRDF}? why connection thing?

I do not know what the rest are yet. and I do not
know why \code{Nrecords} and {\code{Nofvariables}}
are not found inside the function by looking at dimension of
the file read off the disk! Are not they related?

\hrule

\pagebreak

%%%%%%%%%%%%%%%
\item \func{create\_ymdvalues(nYears, Years, leap.year)}

\code{input:}
\begin{itemize}
\item \vari{nYears}: Number of years. (Is not this just \code{length(\vari{Years})}?)
\item \vari{Years}
\item \vari{leap.year}: A binary vector indicating whether a year is a leap year or not.
\end{itemize}
\code{output:} \vari{ymd}, i.e. \code{Year, Month, Day}.
%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%
\item \func{add\_dd\_cumudd(metdata\_data.table, lower, upper)}

Computes the degree days (\code{dd}), cumulative degree days (\code{Cum\_dd}) and concatenates it to the \code{metdata\_data.table}

\code{input:}
\begin{itemize}
\item \vari{metdata\_data.table}: the data
\item \vari{lower}: lower threshold
\item \vari{upper}: upper threshold
\end{itemize}

\code{output:} \vari{[metdata\_data.table, dd, Cum\_dd]}
%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%
\item \func{CodlingMothRelPopulation(CodMothParams, metdata\_data.table)}

\code{input:}
\begin{itemize}
\item \vari{CodMothParams}

\item \vari{metdata\_data.table}
\end{itemize}

\code{output:}
It takes a data table, and parameters of the model such as cumulative
degree days, and then
based on them produces some random numbers from Weibull
distribution and then generates (predicts/models) number of eggs,
larva, pupa and adults, concatenates the new data to the loaded data
and spits it out.
%%%%%%%%%%%%%%%

\item \func{CodlingMothPercentPopulation( CodMothParams, metdata\_data.table )}

\code{input:}
\begin{itemize}
\item \vari{CodMothParams} The parameters of the model.
\item \vari{metdata\_data.table} The collected data
\end{itemize}

\code{output:} This reads the data off the disk, and adds some
new columns to it, containing percentages of stuff, it seems. (I have to find the source
that this code is based on. Where did he learn the model from?)

\subsection{Script}
This goes from line 210 to 1400 (in the clean version).
\end{enumerate}


\documentclass[12pt,onecolumn]{IEEEtran}
\usepackage{graphicx, graphics,epsfig}
\usepackage{color,cite}
\usepackage{amssymb, amsmath}
\usepackage{algorithm}
\usepackage{algorithmic}
\usepackage{url}
\usepackage{amsfonts}
\usepackage{latexsym}
\usepackage{tabularx}
\usepackage{fixltx2e}
%\usepackage{hyphenat}
%\usepackage{underscore}

\newcommand{\ab}{\allowbreak}
%\newcommand{\bus}{\allowbreak\_}
\newcommand{\bus}{\_\allowbreak}
\newcommand{\bas}{/\allowbreak}

\input{../macros/macros}

\title{Group Allocator}
\author{
{
  Sanjeev Mehrotra
}
}

\begin{document}

\maketitle

\section{Introduction}
A generic allocator attempts to match {\em requested resources}, $R$, to
an optimal (in some sense)
{\em allocatable resource location index}, $l_{opt} \in \{0, 1, \dots, L-1\}$
with the location having {\em allocatable resources}, $A_{l_{opt}}$ and
$N_i$ being the name of the $i$th resource location.
$A_l$ are the allocatable resources available at allocatable location index
$l$ and
$L$ is the number of possible allocatable resource locations.
If no location is found which satisfies the constraints for the requested
resources, it sets $l_{opt}=-1$.
Let $\mathcal{F} \subseteq \{N_0,N_1,\dots,N_{L-1}\}$ be the set of feasible
resource location names,
and let $s_l$ be a {\em score} at location $l \in \mathcal{F}$.
Then,
\bea
l_{opt} = \left\{
\ba{ll}
\argmax_{j \in \mathcal{F}} s_j & \mbox{if $\mathcal{F} \neq \emptyset$} \\
-1 & \mbox{if $\mathcal{F} = \emptyset$}
\ea
\right.
\eea
For example, in Figure~\ref{fig:req1},
the allocator matches the given requested resources, $R$,
to the allocatable resources at location $1$.

We represent the requested resources as $R$, where $R$ is a hash-map,
i.e. a list of $(K,V)$ key-value pairs,
with a key \texttt{request{\bus}name} taken from a set,
\texttt{requested{\bus}resources}.
That is $R[\texttt{request{\bus}name}]$ specifies the value needed
to satisfy the request, with
$\texttt{request{\bus}name} \in \texttt{requested{\bus}resources}$.
The exact function to used to determine the score and see if an
allocatable resource location can satisfy the requested resource
is completely arbitrary and can be dependent on the \texttt{request{\bus}name}
or the allocatable resource being used to satisfy the request.

We represent the allocatable resources at location $l$ as
$A_l$, where $A_l$ is a hash-map with a key
\texttt{alloc{\bus}name} taken from a set of available
resources at location $l$, given by \texttt{allocatable{\bus}resources{\bus}l}.

For a location $l$ to satisfy the request, for each requested resource,
$R[\texttt{request{\bus}name}]$, there must be an allocatable resource,
$A_l[\texttt{alloc{\bus}name}]$ which satisfies the request.
Let $M_l$ be this mapping from requested resource to allocatable resource.
That is $M_l[\texttt{request{\bus}name}]$ is a value in the
set \texttt{allocatable{\bus}resources{\bus}l}.
If no allocatable resource specifies the request, then
$M_l[\texttt{request{\bus}name}] = \texttt{nil}$.
Then, we can define the feasible set as the following.
\bea
& \mathcal{F} = \{
N_l | l \in \{0,1,\dots,L-1\}, \nn \\
& \hspace{.2in} M_l[\texttt{request{\bus}name}] \neq \texttt{nil}
& \forall \texttt{request{\bus}name} \in \texttt{requested{\bus}resources}
\}.
\eea
We note that $M_l[\texttt{request{\bus}name}]$ may have constraints, that
is a given requested resource may only be satisfied by certain allocatable
resources.
For example, a CPU request can only be satisfied by
an allocatable CPU resource, and not for example memory.

\begin{figure*}
\begin{center}
\includegraphics[height=7in,width=6in]{req1.pdf}
\caption{Showing requests mapping to resources at
given location\label{fig:req1}}
\end{center}
\end{figure*}

In the Kubernetes framework, such an allocator can be used to find the
node to schedule a pod on, or can simply be used to see if resources
fit on a given node by giving it one available resource location which
is the node being tested to start with.

\subsection{Example}
\label{sec:example}

As an example, $R$ is the set of requested resources as
in
\ttfamily
\begin{itemize}
\item[] cpu: 2
\item[] memory: 1GiB.
\end{itemize}
\normalfont
Here the set of
\texttt{requested{\bus}resources} = $\{\texttt{cpu},\texttt{memory}\}$,
and thus \texttt{resource{\bus}name} can either be \texttt{cpu} or \texttt{memory}.
For simplicity, we write this as
$R=$\texttt{map[cpu: 2 memory: 1GiB]}.

Similarly, $A_l$ is the set of allocatable resources at location $l$.
For example $A_1$ can be the set of allocatable resources at location $1$
given
by
\ttfamily
\begin{itemize}
\item[] cpu: 4
\item[] memory: 2GiB.
\end{itemize}
\normalfont
Here \texttt{allocatable{\bus}resources{\bus}1} = $\{\texttt{cpu},\texttt{memory}\}$,
and again \texttt{alloc{\bus}name} can either be \texttt{cpu} or \texttt{memory}.
We can write $A_1=$\texttt{map[cpu: 4 memory: 2GiB]}.

\section{Group allocator}

In the simple case as given in Sec.~\ref{sec:example},
the allocator can compare key by key to see if
the requested resources $R$ can fit onto
allocatable resources at location $l$, $A_l$. For example compare
requested \texttt{cpu} with allocatable \texttt{cpu} and similarly
for \texttt{memory}.
It does not make sense for a requested CPU resource to be satisfied
by an allocatable memory resource.
Thus, for any $l$, $M_l[\texttt{cpu}] = \texttt{cpu}$ if sufficient
CPU is available, otherwise $M_l[\texttt{cpu}]=\texttt{nil}$.
The same is true for memory.
Having only such resources makes the job of the allocator
fairly straightforward.
Thus for simple cases like this, the mapping, $M_l$ is not something
that is found by the allocator, but rather a static identity map.

However, this becomes infeasible for more complicated scenarios.
As an example, consider GPU resources, where a single location
can have multiple GPUs, each with differing memory
and similarly, the request can require a number of GPUs with
differing memory requirements.
Although it may seem difficult to express such constraints with simple
key-value pairs for the requested resources and allocatable resources,
it is fairly doable.
For example, one way to do this is to express requested resources and
allocatable resources for each GPU {\em separately}.
Then, we can write requested resources, $R$, as
\ttfamily
\begin{itemize}
\item[] cpu: 2
\item[] memory: 1GiB
\item[] gpu/0/cards: 1
\item[] gpu/0/memory: 1GiB
\item[] gpu/1/cards: 1
\item[] gpu/1/memory: 2GiB,
\end{itemize}
\normalfont
where
we have requested 1 GPU with minimum 1GiB memory and another with
minimum 2GiB memory.

Similarly, a resource location can advertise allocatable resources, $A_l$, as
\ttfamily
\begin{itemize}
\item[] cpu: 4
\item[] memory: 2GiB
\item[] gpu/dev0/cards: 1
\item[] gpu/dev0/memory: 1GiB
\item[] gpu/dev1/cards: 1
\item[] gpu/dev1/memory: 3GiB
\item[] gpu/dev2/cards: 1
\item[] gpu/dev2/memory: 2GiB
\item[] gpu/dev3/cards: 1
\item[] gpu/dev3/memory: 4GiB,
\end{itemize}
\normalfont
where we have 4 GPUs with differing memory on the node.

In such a case, a simple allocator cannot just look for a resource with
the same key as that being requested.
For example, it is clear that the request can be satisfied by
the resource location.
However, there is no key \texttt{gpu/0/cards} present in the resource
location even though it is a requested key.
This is the main need for what we define as the {\em group allocator}.

The job of the group allocator becomes the following.
\begin{itemize}
\item
Find the best allocatable resource location index(in some sense), $l_{opt}$,
amongst the feasible set, $\mathcal{F}$.
\item
Obtain the mapping from requested resource to allocatable resource
on for that best allocatable resource location, $M_{l_{opt}}$.
\end{itemize}
In the process of determining $l_{opt}$, the allocator may compute
a score for each location to find the best one.

The second item is needed as the map will not be the identity map anymore.
An example of such a mapping, $M_{l_{opt}}$, could be the following.
\texttt{
\begin{itemize}
\item[] cpu: cpu
\item[] memory: memory
\item[] gpu/0/cards: gpu/dev0/cards
\item[] gpu/0/memory: gpu/dev0/memory
\item[] gpu/1/cards: gpu/dev2/cards
\item[] gpu/1/memory: gpu/dev2/memory
\end{itemize}
}

\subsection{Partitioning}

The first task of the group allocator is partitioning.
Both the requested resources as well as the allocatable resources
of a given location are partitioned into ``base'' and ``group'' resources.
A base resource is one which can directly be compared
by looking at the key.
For example, a base requested resource requires a base allocatable resource
with the same key, for example, in the example above \texttt{cpu} and
\texttt{memory}.
That is for a base resource
\bea
M_l[\texttt{request{\bus}name}] = \texttt{request{\bus}name}.
\eea
A ``group'' resource on the other hand, may match a requested
resource to an allocatable resource with a differing key.

In our framework,
all requested resources which are of the format,
\bea
\texttt{request{\bus}name} =
\texttt{request{\bus}groupname\slash request{\bus}groupindex\slash request{\bus}basename}
\eea
are considered to be ``group'' requested resources by the allocator, where
the keys are in the sets,
\bea
\texttt{request{\bus}groupname} & \in & \texttt{request{\bus}subgroupnames} \\
\texttt{request{\bus}groupindex} & \in & \texttt{request{\bus}subgroupindices[request{\bus}groupname]} \\
\texttt{request{\bus}basename} & \in & \mbox{\footnotesize{\texttt{request{\bus}resourcebasenames[request{\bus}groupname][request{\bus}groupindex]}}}.
\eea

Similarly, all allocatable resource which are of the format,
\bea
\texttt{alloc{\bus}name} =
\texttt{alloc{\bus}groupname/alloc{\bus}groupindex/alloc{\bus}basename}
\eea
are ``group'' allocatable resources, with the keys in the sets
\bea
\texttt{alloc{\bus}groupname} & \in & \texttt{alloc{\bus}subgroupnames} \\
\texttt{alloc{\bus}groupindex} & \in & \texttt{alloc{\bus}subgroupindices[alloc{\bus}groupname]} \\
\texttt{alloc{\bus}basename} & \in & \mbox{\small{\texttt{alloc{\bus}resourcebasenames[alloc{\bus}groupname][alloc{\bus}groupindex]}}}.
\eea

Since for base resources,
$M_l[\texttt{request{\bus}name}] = \texttt{request{\bus}name}$,
it is sufficient to look at $A_l[$\texttt{req\-uest{\bus}name}$]$
and compare.
If \texttt{request{\bus}name} does not exist in the set
of \texttt{allocatable{\bus}resources}, then the resource request cannot be
satisfied.

To check if a group resource being requested can fit onto a location
with allocatable resources, group resources are first
organized into subgroups.
A new subgroup is formed for each unique combination
of $(\texttt{request{\bus}groupname, request{\bus}groupindex})$,
as
\bea
& R_G[\texttt{request{\bus}groupname}][\texttt{request{\bus}groupindex}]
[\texttt{request{\bus}basename}] = \nn \\
& \hspace{.2in} R[\texttt{request{\bus}groupname}\slash \texttt{request{\bus}groupindex} \slash
\texttt{request{\bus}basename}].
\eea
Similarly, the group allocatable resources at location $l$
which are of the format
$A_l$\ab[\texttt{alloc{\bus}groupname}]\ab[\texttt{alloc{\bus}groupindex}]
\ab[\texttt{alloc{\bus}basename}]
are partitioned, resulting in a new subgroup for each combination
of $(\texttt{alloc{\bus}groupname, alloc{\bus}groupindex})$,
\bea
& A_{lG}[\texttt{all\-oc{\bus}group\-name}][\texttt{all\-oc{\bus}group\-index}]
[\texttt{alloc{\bus}base\-name}] = \nn \\
& \hspace{.4in} A_l[\texttt{request{\bus}groupname}/\texttt{request{\bus}groupindex}/
\texttt{request{\bus}basename}].
\eea

\subsubsection{Example}

For example, in the example above, the requested resources
produces two requested
subgroups $R_G[$\texttt{gpu}$][0]$ and $R_G[$\texttt{gpu}$][1]$,
where $R_G[$\texttt{gpu}$][0]$ is given by
\ttfamily
\begin{itemize}
\item[] cards: 1
\item[] memory: 1GiB,
\end{itemize}
\normalfont
and $R_G[$\texttt{gpu}$][1]$ is given by
\ttfamily
\begin{itemize}
\item[] cards: 1
\item[] memory: 2GiB.
\end{itemize}
\normalfont

Similarly, the allocatable resources at the location $1$ produce
four allocatable subgroups, $A_{1G}$\ab[\texttt{gpu}]\ab[\texttt{dev0}], given by
\ttfamily
\begin{itemize}
\item[] cards: 1
\item[] memory: 1GiB,
\end{itemize}
\normalfont
$A_{1G}[\texttt{gpu}][\texttt{dev1}]$ given by,
\ttfamily
\begin{itemize}
\item[] cards: 1
\item[] memory: 2GiB,
\end{itemize}
\normalfont
$A_{1G}[\texttt{gpu}][\texttt{dev2}]$, given by
\ttfamily
\begin{itemize}
\item[] cards: 1
\item[] memory: 2GiB,
\end{itemize}
\normalfont
and $A_{1G}[\texttt{gpu}][\texttt{dev3}]$, given by
\ttfamily
\begin{itemize}
\item[] cards: 1
\item[] memory: 4GiB.
\end{itemize}
\normalfont

\subsection{Allocation}

Now, the group allocator is recursively run on each of the requested
subgroups,
$R_G[$\texttt{req\-uest{\bus}group\-name}$][$\texttt{req\-uest{\bus}group\-index}$]$.
For each requested subgroup, all allocatable subgroups where
\texttt{alloc{\bus}group\-name} = \texttt{request{\bus}group\-name} are
considered as possible resource locations.
The number of possible resource locations for
the allocation of a subgroup onto resource location $l$ is given by
\bea
L_{lG}[\texttt{request{\bus}groupname}] =
  \mbox{length}(A_{lG}[\texttt{request{\bus}groupname}]).
\eea
That is it is from the
set \texttt{alloc{\bus}subgroupindices[request{\bus}groupname]}.
Let's call this location
$n_{lG,opt}[$\texttt{req\-uest{\bus}group\-name}$]
  [$\texttt{req\-uest{\bus}group\-index}$]$.
For simplicity, we can simply denote it as $n_{lG,opt}$.

In addition, the recursive call to the allocator
returns the mapping for the subgroups $M_{lG}$.
The mapping returned by the recursive call to the group allocator for
the subgroups can be used to get back the mapping for the group
via the following,
\bea
M_l[\texttt{request{\bus}groupname/request{\bus}groupindex/request{\bus}basename}]
  = \nn \\
\hspace{.2in}
 \texttt{request{\bus}groupname}/ \nn \\
  \texttt{alloc{\bus}subgroupindices}[n_{lG,opt}]/ \nn \\
 M_{lG}[\texttt{request{\bus}groupname}][\texttt{request{\bus}groupindex}]
    [\texttt{request{\bus}basename}]
\label{eqn:mapp}
\eea
If any of the subgroup allocations fail at location $l$ (i.e., there
is no feasible location for the subgroup), then the
requested resources cannot fit at location $l$.

If all the subgroup allocation succeed, and if the base resource
allocations are deemed to be sufficient, then a score for location $l$
can be computed to be used by the allocator.

\subsubsection{Example}

For example, when considering the fit for location $1$ using the
allocatable resource $A_1$, for request $R$,
the group allocator does the following.
\begin{itemize}
\item
Compare the base resources \texttt{cpu} and \texttt{memory} to see if they
fit.
\item
Recursively call the group allocator to see if
$R_G[\texttt{gpu}][0]$ can fit onto any of the allocatable resources
subgroups,
$\{A_{1G}[\texttt{gpu}][\texttt{dev0}], A_{1G}[\texttt{gpu}][\texttt{dev1}],
A_{1G}[\texttt{gpu}][\texttt{dev2}], A_{1G}[\texttt{gpu}][\texttt{dev3}]\}$.
The group allocator returns $(fit, mapping)$.
Let $M_{1G}[\texttt{gpu}][0]$ be the mapping and
let $n_{1G}[\texttt{gpu}][0] \in \{-1, 0,1,2,3\}$ be the optimal location index.
-1 specifies that no location was found out of the four locations.
\item
Recursively call the group allocator to see if
$R_G[\texttt{gpu}][1]$ can fit onto any of the allocatable resources
subgroups,
$\{A_{1G}[\texttt{gpu}][\texttt{dev0}], A_{1G}[\texttt{gpu}][\texttt{dev1}],
A_{1G}[\texttt{gpu}][\texttt{dev2}], A_{1G}[\texttt{gpu}][\texttt{dev3}]\}$.
The group allocator returns $(fit, mapping)$.
Let $M_{1G}[\texttt{gpu}][1]$ be the mapping and
let $n_{1G}[\texttt{gpu}][1] \in \{-1, 0,1,2,3\}$ be the optimal location index.
-1 specifies that no location was found out of the four locations.
\item
If the base resources fit, and the recursive calls to the group
allocator return a fit (i.e. a location not equal to $-1$),
then the requested resources can fit.
\item
From Eqn.~\ref{eqn:mapp}, obtain the final mapping using the following.
\ttfamily
\begin{itemize}
\item[] cpu: cpu
\item[] memory: memory
\item[] gpu/0/cards: gpu/alloc{\bus}subgroupindices[$n_{1G,opt}$[gpu][0]]/cards
\item[] gpu/0/memory: gpu/alloc{\bus}subgroupindices[$n_{1G,opt}$[gpu][0]]/memory
\item[] gpu/1/cards: gpu/alloc{\bus}subgroupindices[$n_{1G,opt}$[gpu][1]]/cards
\item[] gpu/1/memory: gpu/alloc{\bus}subgroupindices[$n_{1G,opt}$[gpu][1]]/memory,
\end{itemize}
\normalfont
where $n_{1G,opt}[\texttt{gpu}][\texttt{request{\bus}groupindex}]$
is the location of the allocatable
subgroup with \texttt{alloc{\bus}name=gpu} returned
by the recursive call to the group allocator.
Note that
\texttt{alloc{\bus}sub\-group\-indices[$n_{1G,opt}$[gpu][*]]} will be from
the set $\{\texttt{dev0, dev1, dev2, dev3}\}$.
\item
If a fit is found, then a score is computed for location $1$.
\end{itemize}

For example, suppose $n_{1G,opt}[\texttt{gpu}][0]=0$
and $n_{1G,opt}[\texttt{gpu}][1]=2$,
then \texttt{alloc{\bus}sub\-group\-indices\allowbreak[$n_{1G,opt}$[gpu][0]] \allowbreak= \allowbreak dev0}
and \texttt{alloc{\bus}sub\-group\-indices[$n_{1G,opt}$[gpu][1]] = dev2}.
The recursive call to the group allocator returns
the mapping for both subgroups, as $M_{1G}$\ab[\texttt{gpu}]\ab[$0$] and
$M_{1G}$\ab[\texttt{gpu}]\ab[$1$], which are both given by,
\ttfamily
\begin{itemize}
\item[] cards : cards
\item[] memory : memory.
\end{itemize}
\normalfont
Then, using Eqn.~\ref{eqn:mapp}, we get the following mapping
\ttfamily
\begin{itemize}
\item[] cpu: cpu
\item[] memory: memory
\item[] gpu/0/cards: gpu/dev0/cards
\item[] gpu/0/memory: gpu/dev0/memory
\item[] gpu/1/cards: gpu/dev2/cards
\item[] gpu/1/memory: gpu/dev2/memory,
\end{itemize}
\normalfont

In the final mapping, for base resources,
$M[\texttt{resource}] = \texttt{resource}$.
For group resources,
the \texttt{request{\bus}groupindex} for the requested resource
is replaced by
\texttt{alloc{\bus}sub\-group\-indices[$n_{1G,opt}$\ab[req\-uest{\bus}group\-name]\ab[req\-uest{\bus}group\-index]]},
where $n_{1G,opt}$ is the best location for the subgroup
amongst the allocatable subgroups with the same groupname.

\subsection{Scorer}
\label{sec:score}

One component which can be used to find the best location
amongst a list of allocatable resource locations is a scorer.
For a location $l$,
it uses allocatable resources, $A_l$, and resources already being used
along with the requested resources, $R$, and requested resource mapping $M$
to find a score for the fit.
We define $s_l$ to be the score for the location $l$.
Let $U_l$ be the resources being used at location $l$.
It is a hash-map with the same keys used to index $A_l$, that is
it is indexed using \texttt{alloc{\bus}name}.

We define $s_l$ to be the sum of scores over all allocatable resources,
that is
\bea
& RL_l[\texttt{alloc{\bus}name}] = \{R[\texttt{request{\bus}name}]: M_l[\texttt{request{\bus}name}]=\texttt{alloc{\bus}name}\} \label{eqn:rlist} \\
& s_l = \sum\limits_{\texttt{alloc{\bus}name} \in \texttt{allocatable{\bus}resources{\bus}l}}
  S_{\texttt{alloc{\bus}name}}
\parbox[t]{3in}{
$(A_l[\texttt{alloc{\bus}name}]$, \\
$U_l[\texttt{alloc{\bus}name}]$, \\
$RL_l[\texttt{alloc{\bus}name}])$
}
\label{eqn:scorel}
\eea
that is the scorer function $S_{\texttt{alloc{\bus}name}}$ itself is a function
of the resource being allocated upon, and it is a function
of the allocatable resource $A_l$ for the given resource,
the amount of used resource $U_l$ for the given resource, and
the list of requests $R$ which map to using the given resource.
In addition to a score, the scorer can also tell whether a
sufficient amount of allocatable resource exists in order to satisfy
the request.
For example, the scorer could return -1 to denote that sufficient resource
does not exist.
In practice, the first step the scorer will do is
find a list of resources being mapped to a given allocatable resource, $RL$,
as in Eqn.~\ref{eqn:rlist}.
The list $RL$ is then passed to the scorer.

In addition to computing the score, an arbitrary function,
$\Phi_{\texttt{alloc{\bus}name}}$, can be
used to update the used resources for an allocatable resource,
$U_l[\texttt{alloc{\bus}name}]$, using
\bea
U'_l[\texttt{alloc{\bus}name}] =
  \Phi_{\texttt{alloc{\bus}name}}(A_l[\texttt{alloc{\bus}name}], U_l[\texttt{alloc{\bus}name}],
       RL_l[\texttt{alloc{\bus}name}]),
\eea
where $U'_l$ is used to denote the updated used resources.

Note that the scorer function, $S$, and used resource updater, $\Phi$,
can also be dependent upon the request
being made, and can also have additional state that may be needed.
As an example, in Kubernetes, pods consist of
both initialization containers and running containers.
Initialization containers are first run sequentially (one-by-one),
and once all initialization containers complete, then all running
containers are run in parallel.
Here, the scorer itself is dependent upon whether the request is
being made on behalf of a running container or an initialization container.
In addition, additional state such as the resource being requested
by the pod can be used as additional state for the scorer.
Thus in an actual implementation of the scorer function, additional
parameters may be passed in.

As an example, for linearly consumable resources, we can use the
following for the resource updater and scorer.
The resource updater and scorer must run in a particular
order.
When resources are being consumed, all running containers must be
updated and scored first, followed by initialization containers.
Let $P[\texttt{alloc{\bus}name}]$ be the resources being consumed by a pod.
Then, we first run the following for running containers.
\bea
P'_l[\texttt{alloc{\bus}name}] &=& P_l[\texttt{alloc{\bus}name}] + RL_l[\texttt{alloc{\bus}name}] \\
U'_l[\texttt{alloc{\bus}name}] &=& U_l[\texttt{alloc{\bus}name}] + RL_l[\texttt{alloc{\bus}name}] \\
s_l &=& \frac{U'_l[\texttt{alloc{\bus}name}]}{A_l[\texttt{alloc{\bus}name}]}
\eea
Then, for initialization containers, we do the following
\bea
P'_l[\texttt{alloc{\bus}name}] &=& \max(P_l[\texttt{alloc{\bus}name}], RL_l[\texttt{alloc{\bus}name}]) \\
U'_l[\texttt{alloc{\bus}name}] &=& U_l[\texttt{alloc{\bus}name}] +
  (P'_l[\texttt{alloc{\bus}name}]-P_l[\texttt{alloc{\bus}name}]) \\
s_l &=& \frac{U'_l[\texttt{alloc{\bus}name}]}{A_l[\texttt{alloc{\bus}name}]}
\eea
We see that for initialization containers, additional state regarding
the amount of resources being used by running containers for the pod
is used as an additional variable to modify the function $\Phi$.
If we put all equations together, we get the following for the scorer,
\bea
s_l &=& \frac{U_l[\texttt{alloc{\bus}name}] + \max(P_l[\texttt{alloc{\bus}name}], RL_l[\texttt{alloc{\bus}name}]) - P_l[\texttt{alloc{\bus}name}]}{A_l[\texttt{alloc{\bus}name}]},
\eea
and thus it is also a function of current pod usage.

When a pod is being removed from a node, then
the entire pod usage is first computed and then subtracted
from the node usage.

\section{Implementation in Kubernetes}

\sloppy
The group allocator is implemented in Kubernetes in the file
\texttt{plugin{\bas}pkg{\bas}scheduler{\bas}algorithm{\bas}
   predicates{\bas}grpallocate.go}.
All requested resources as well as allocatable resources on a node
with the prefix
\texttt{alpha.kubernetes.io{\bas}group-resource} are considered
to be ``group'' resources.
The type \texttt{GrpAllocator} is the main class which performs
the group allocator and stores state.

The group allocator's job is two-fold, one to find a suitable
location for the requested resources amongst the {\em resource locations}
with {\em allocatable resources} and two to find a mapping between
requested resources and allocatable resources on the resource location.

Initially, we consider there to be only one resource location,
which is the node group resources.
So the initial group allocator call attempts to find whether
the node is able to satisfy the requests and if it can satisfy the
requests, then it also outputs a mapping between requests and allocatable
resources on the node.

The variables \texttt{RequiredResource} and \texttt{AllocResource}
correspond to key-value pairs with keys being strings and value being
int64 corresponding to the required and allocatable resources at a given node
respectively.
These correspond to resources at the top-level group allocator.

The actual required resource and allocatable resource for a given group
are specified in
\texttt{GrpRequiredResource} and \texttt{GrpAllocResource} which
are string to string mappings.
Therefore,
\texttt{GrpRequiredResource map[string]string}
is a map whose keys specify the names of the requested resources.
And, \texttt{RequiredResources[GrpRequiredResource[resource{\bus}key]]} is the
amount of requested resource for with the name \texttt{resource{\bus}key}.
In addition, \texttt{GrpAllocResource map[string](map[string]string)}
is a map whose keys specify locations for resource locations.
And, \texttt{GrpAllocResource[resource{\bus}location{\bus}key]} specifies a list
whose keys specify the resources available at the allocatable location
\texttt{resource{\bus}location{\bus}key}.
And
\texttt{AllocResource[GrpAllocResource[resource{\bus}location{\bus}key][resource{\bus}key]]}
specifies the amount of allocatable resource at resource location
\texttt{resource{\bus}location{\bus}key} with the name \texttt{resource{\bus}key}.

For simplicity all variables in the group allocator are specified with
respect to the top level group.
The actual ones for a given group are simply found
by first looking up in
\texttt{GrpRequiredResource} or \texttt{GrpAllocResource} to find
the actual key for a given group.

The group allocator consists of several core functions.
\begin{itemize}
\item
\texttt{allocateGroup} allocates requested resources
amongst a list of resource locations.
It then picks a location with the best score amongst the list
of feasible locations, provided one exists.
\item
\texttt{allocateGroupAt} attempts to allocate the request to a particular
location and if it fits, it finds a score, along with a requested
resource to allocatable resource mapping.
\item
\texttt{resourceAvailable} checks if sufficient {\em base resources}
are available at the resource location.
\item
\texttt{allocateSubGroups} performs recursive calls to allocateGroup
for all subgroups, if any exist.
\item
\texttt{findScoreAndUpdate} performs scoring for the allocation and
updates used resources.
\end{itemize}

The scorer is a function with signature, \\
\texttt{
func(allocatableResource int64, usedByPod int64, usedByNode int64, requested []int64, initContainer bool) (found bool, score float64, usedByContainer int64, newUsedByPod, newUsedByNode int64)
} \\
It is specified for all allocatable resources at a given node.
A different function may be used to determine fit if desired.

\section{GPU Affinity Example}

Here we provide an example of using the group allocator for
allocating GPUs with affinity.
GPUs are often connected via a high speed peer-to-peer link, for example,
NVLink in NVIDIA GPUs with Pascal architecture as shown
in Fig.~\ref{fig:gpu}. GPU0 is connected via high-speed interconnect
to GPU1 and a lower speed interconnect to GPU2 and GPU3.

\begin{figure*}
\begin{center}
\includegraphics[width=6in]{gpu.pdf}
\caption{GPU with NVLink and corresponding GPU groups, showing
both high speed and lower speed interconnects.\label{fig:gpu}}
\end{center}
\end{figure*}

Then, one way to specify allocatable resources is the following.
\ttfamily
\begin{itemize}
\item[] Gpugrp1/0/Gpugrp0/0/gpu/dev0/cards: 1
\item[] Gpugrp1/0/Gpugrp0/0/gpu/dev1/cards: 1
\item[] Gpugrp1/0/Gpugrp0/1/gpu/dev2/cards: 1
\item[] Gpugrp1/0/Gpugrp0/1/gpu/dev3/cards: 1
\item[] Gpugrp1/1/Gpugrp0/2/gpu/dev4/cards: 1
\item[] Gpugrp1/1/Gpugrp0/2/gpu/dev5/cards: 1
\item[] Gpugrp1/1/Gpugrp0/3/gpu/dev6/cards: 1
\item[] Gpugrp1/1/Gpugrp0/3/gpu/dev7/cards: 1
\end{itemize}
\normalfont
That is, two GPUs with high-speed interconnect
are first grouped into Gpugrp0, with indices $\{0,1,2,3\}$.
Then, two successive groups of Gpugrp0 are grouped into Gpugrp1
which has the lower speed interconnect, with indices $\{0, 1\}$.

Requested resources can be similarly grouped depending on
which interconnects are required.
For example,
\ttfamily
\begin{itemize}
\item[] Gpugrp1/R0/Gpugrp0/RA/gpu/gpu0/cards: 1
\item[] Gpugrp1/R0/Gpugrp0/RA/gpu/gpu1/cards: 1
\item[] Gpugrp1/R1/Gpugrp0/RA/gpu/gpu2/cards: 1
\item[] Gpugrp1/R1/Gpugrp0/RA/gpu/gpu3/cards: 1
\item[] Gpugrp1/R1/Gpugrp0/RB/gpu/gpu4/cards: 1
\item[] Gpugrp1/R1/Gpugrp0/RB/gpu/gpu5/cards: 1
\end{itemize}
\normalfont
The top-level group allocator will make two recursive
calls to group allocate \texttt{request{\bus}groupname = Gpugrp1} with
\texttt{request{\bus}groupindex = 0} and
\texttt{request{\bus}groupindex = 1}.

The call to allocate \texttt{request{\bus}groupname = Gpugrp1} for
\texttt{request{\bus}groupindex = 0} will then
make a single recursive call to group allocate
\texttt{request{\bus}groupname = Gpugrp0} with
\texttt{request{\bus}groupindex = A}.
This in turn will make two recursive calls to group allocate
\texttt{request{\bus}groupname = gpu} with
\texttt{request{\bus}groupindex = dev0} and another
for
\texttt{request{\bus}groupname = gpu} with
\texttt{request{\bus}groupindex = dev1}.
Each of these will have the base resource of \texttt{cards}.

The call to allocate \texttt{request{\bus}groupname = Gpugrp1} for
\texttt{request{\bus}groupindex = 1} will then
make two recursive call to group allocate, one for
\texttt{request{\bus}groupname = Gpugrp0} with
\texttt{request{\bus}groupindex = A} and another for
\texttt{request{\bus}groupname = Gpugrp0} with
\texttt{request{\bus}groupindex = B}.
Each of these two will make two more recursive calls.

The final allocations to perform will be the following.
\ttfamily
\begin{verbatim}
<TopLevel>
  Gpugrp1/R0
    Gpugrp1/R0/Gpugrp0/RA
      Gpugrp1/R0/Gpugrp0/RA/gpu/gpu0
        Gpugrp1/R0/Gpugrp0/RA/gpu/gpu0/cards -- base resource
      Gpugrp1/R0/Gpugrp0/RA/gpu/gpu1
        Gpugrp1/R0/Gpugrp0/RA/gpu/gpu1/cards -- base resource
  Gpugrp1/R1
    Gpugrp1/R1/Gpugrp0/RA
      Gpugrp1/R1/Gpugrp0/RA/gpu/gpu2
        Gpugrp1/R1/Gpugrp0/RA/gpu/gpu2/cards -- base resource
      Gpugrp1/R1/Gpugrp0/RA/gpu/gpu3
        Gpugrp1/R1/Gpugrp0/RA/gpu/gpu3/cards -- base resource
    Gpugrp1/R1/Gpugrp0/RB
      Gpugrp1/R1/Gpugrp0/RB/gpu/gpu4
        Gpugrp1/R1/Gpugrp0/RB/gpu/gpu4/cards -- base resource
      Gpugrp1/R1/Gpugrp0/RB/gpu/gpu5
        Gpugrp1/R1/Gpugrp0/RB/gpu/gpu5/cards -- base resource
\end{verbatim}
\normalfont

Of course, each allocation will attempt to search for multiple locations
for the fit.
A diagram showing the call is shown in Fig.~\ref{fig:alloc}.
The green circles show group allocators.
The blue boxes show the allocatable locations being searched.
Each blue box will perform a search for all resources which
are considered as ``base resources'' for that level.
Each blue box may have further subgroups to search which will
be shown as descendants of the blue box.
If there are no group resources left (i.e. all resources are base
resources), then the box will have no descendants.
For example, the leaf of the tree at the top right of the figure is
requested subgroup \texttt{Grp1/R0/Grp0/RA/gpu/0} checking
if resources fit onto \texttt{Grp1/0/Grp0/0/gpu/dev0}.
The only requested resource is \texttt{cards} which is checked.
Each group allocator (green circle) picks the best of all locations.
That is it picks the best blue box which descends from the node.
In the figure, for simplicity, only the \texttt{Grp1/R0} branch is shown.
Another branch for \texttt{Grp1/R1} will also be present which is not
shown in the figure.

\begin{figure*}
\begin{center}
\includegraphics[width=6in]{alloc.pdf}
\caption{Figure showing the group allocators and the locations
being searched. For simplicity, ``Gpugrp'' has simply been written as ``Grp''.
The branch for \texttt{Grp1/R1} is not shown.
\label{fig:alloc}}
\end{center}
\end{figure*}

\section{Requested Resource Translation}

For simplicity, requests may specified in the lower groups without
specifying the higher group enumeration.
For example, if we need two GPUs within the same \texttt{Gpugrp0}, we can simply
specify the request as
\ttfamily
\begin{itemize}
\item[] Gpugrp0/RA/gpu/gpu0/cards: 1
\item[] Gpugrp0/RA/gpu/gpu1/cards: 1
\end{itemize}
\normalfont
If the resources being advertised also include a \texttt{Gpugrp1}, then
the requested resources will automatically get assigned
to a unique \texttt{Gpugrp1}, a different one for each differing
\texttt{Gpugrp0}.

\end{document}


\chapter{Speech Recognition with Hidden Markov Models}

\section{Phonetic Decision Trees for Context Dependent Models}
We review the article in \cite{young1994state}

\section{Statistical Methods For Speech Recognition}

A useful resource for understanding the statistical aspects of speech recognition is
the book \cite{frederick1997statistical} which reviews a large body of work
on building hidden Markov model speech systems.  I will review the various
chapters contained in the book.

\subsection{Triphones and Allophones}

Allophones such as the /t/ in ``top'' which is aspirated and the t in ``pot'' which is
not  show considerable variation depending on context.  Triphones are a method for dealing
with such context effects.  What do they buy you:
\begin{enumerate}
\item  Generate pronunciations of unseen words (potentially with novel spellings)
\item  Can handle coarticulation
\end{enumerate}

The general problem 60 phones translates to 216,000 triphones and there would not be
enough data to estimate all of these. The main approach to handling this problem
is to put triphones into equivalence classes.  The correct architecture for this is difficult
to work out.  Although we can normally think of the problem as using a free semi-group
examples such as ``Press show'' indicate there are occasions where a sound gets dropped
which indicates that this is in fact a semi-group with non-trivial relations.

Working out the equivalence classes is tricky the main way is to create a class for each phone
and put the phone in each context in that class. For triphones we may write each triphone
using symbols as $\psi_1 \alpha \psi_2$ where $\alpha$ is the phone, $\psi_1$ is the left context,
and $\psi_2$ is the right context.  A given equivalence class is assumed to follow a Gaussian
distribution and the distribution is split based on questions of the context, i.e., does $\psi_1$
belong to the set of Stops? or Consonants? You pick the best split for the currently observed triphones
and then this creates a decision tree that can then be used to generalize to unseen triphones.

\section{HTK}

\subsection{HVite for Forced Alignment}

In order to bootstrap we need to have state labels to classify the deep
neural network into the state labels we need a viterbi forced alignment
to get the state labels.  These are obtained in the HTK tools using
\texttt{HVite}.  The command we use is
\begin{verbatim}
HTK=/home/mark/Software/htk
HTKTools=$HTK/HTKTools

$HTKTools/HLEd -n trainTriphones -l '*' -i trainTri.mlf mktri.led trainMono.mlf

HVite -a -b h# -f -i forced_dev.mlf -H -I

$HTKTools/HLEd -i devTri.mlf mktri.led devMono.mlf
$HTKTools/HVite -b h# -f -a -T 1 -C hvite.config -H tri-nmix48-npass4/MMF -S dev.scp -I devTri.mlf -i fadevout.mlf dict tiedlist

$HTKTools/HVite -b h# -f -a -T 1 -C hvite.config -H tri-nmix48-npass4/MMF -S dev.scp -I devMono.mlf -i fadevout.mlf dict tiedlist
\end{verbatim}