---
abstract: 'Neural network pruning is an important technique for creating efficient machine learning models that can run on edge devices. We propose a new, highly flexible approach to neural network pruning based on Gibbs distributions. We apply it with Hamiltonians that are based on weight magnitude, using the annealing capabilities of Gibbs distributions to smoothly move from regularization to adaptive pruning during an ordinary neural network training schedule. This method can be used for either unstructured or structured pruning, and we provide explicit formulations for both. We compare our proposed method to several established pruning methods on ResNet variants and find that it outperforms them for unstructured, kernel-wise, and filter-wise pruning.'
author:
- 
- 
bibliography:
- 'mybibfile.bib'
title: 'A Framework for Neural Network Pruning Using Gibbs Distributions\'
---

neural networks, neural network pruning, edge intelligence, machine learning

Introduction {#sec:introduction}
============

While machine learning research has recently focused on massive deep neural networks, practical deployment of these powerful models on edge devices remains a challenge. One approach to making machine learning for edge intelligence more feasible is neural network pruning, where connections in a neural network are removed to produce a smaller final model. Pruning can be used on its own to improve inference performance and compress models, or it can be combined with other techniques like quantization to produce even more efficient neural networks [@han2016deep].

Neural network pruning methods can be divided into structured and unstructured pruning. In unstructured pruning, any set of connections can be removed. Structured pruning instead requires that the pruned connections follow some structure, such as removing entire rows from weight matrices or entire filters from convolutional layers. Structured pruning can usually improve performance metrics like inference speed at lower sparsity levels than unstructured pruning, since the structure can be taken advantage of in optimization [@wen2016learning]. On the other hand, unstructured pruning can generally remove more connections without significantly decreasing accuracy, even finding architectures that outperform the original model [@frankle2019lottery].

We introduce a novel pruning method that can be used for both unstructured and structured pruning. Taking inspiration from statistical physics, we propose inducing a Gibbs distribution over the weights of a neural network and sampling from it during training to determine pruning masks. This procedure induces a learned network structure that is resilient to high degrees of pruning. Gibbs distributions are highly flexible in terms of network properties that they can express, and quadratic energy functions, such as Ising models, can capture parameter interactions and induce desired structure in pruning masks. They also naturally allow a temperature parameter to be used for annealing, gradually converging to a final pruning mask during training and improving network resilience to pruning.

Many existing pruning methods start with a trained network and use an iterative procedure that alternates between removing some connections and training for additional epochs to fine-tune the network [@han2016deep; @li2017pruning; @luo2017thinet]. We believe that it is preferable to prune the network during the original training phase, since this allows the network and the pruning mask to adapt to each other, in particular encouraging the network to take on a representation that is robust to pruning. We show that our method can take advantage of additional training epochs by stretching the annealing and learning rate schedules rather than adding a separate fine-tuning phase. We compare our proposed method to a number of structured and unstructured pruning methods from the literature, including methods that fine-tune trained networks and methods that prune while training [@han2016deep; @frankle2019lottery; @gomez2019learning; @li2017pruning; @luo2017thinet]. We generally find that our method outperforms them.

Sample code for our proposed method is available at <https://github.com/j201/gibbs-pruning>.

Proposed Method {#sec:method}
===============

A Gibbs distribution over a vector $\x$ has a density function $$p(\x) = \frac{1}{Z(\beta)}e^{-\beta H(\x)},
    \label{eq:gibbs}$$ where $H(\x)$ is the Hamiltonian function, or [*energy*]{}, $\beta$ is an inverse temperature parameter, and $Z(\beta)$ is a partition function that normalizes the distribution. Gibbs distributions are a very general family of probability distributions, giving considerable flexibility in defining pruning methods. For instance, every Markov random field corresponds to a Gibbs distribution, and vice versa. They also naturally permit annealing, by changing $\beta$ over the course of training.

In our proposed method, $\x$ represents a pruning mask for a single neural network layer, with $x_i \in \{-1,1\}$. Given the weights of the layer as a flattened vector $\w$, a weight $w_i$ is masked (treated as zero) during training if the corresponding $x_i$ is -1. $\x$ is sampled from $p(\x)$ at every training step and once again after training to determine the final mask. Note that weight values are not permanently modified during training, allowing a connection to be masked out in some iterations and active in others.

The goal of our method is to have both the neural network weights and pruning mask converge together from random initial states to a final effective representation over the course of training. Gibbs distributions enable this, since they can be annealed from more random behaviour at high temperatures and converge to a final steady state [@kirkpatrick1983optimization]. The most likely result of sampling — and the result we expect to converge to — corresponds to the state that minimizes the Hamiltonian. We select a Hamiltonian and annealing schedule that will converge to an effective pruned network.

Defining a pruning method within this framework requires:

-   A Hamiltonian where the minimum energy corresponds to a desired pruned structure, based on the network parameters;

-   A method for sampling from the distribution;

-   An approach for annealing $\beta$ during training.

The following sections discuss these elements in more detail.

Unstructured Pruning
--------------------

A widely used heuristic for pruning neural networks is pruning weights with low magnitudes [@han2016deep; @li2017pruning; @gale2019state]. We follow this approach in designing a Hamiltonian for unstructured pruning: we want the energy to be minimized when weights with low magnitude are pruned away.

Let $N$ be the number of weights in the layer and let $\v$ represent a vector with the elements of $\w$ sorted such that $v_i^2 \leq v_{i+1}^2$. Given a pruning rate $p$ between 0 and 1, select $Q(p) = v_i^2$, where $i = p(N-1)+1$. If $i$ is not an integer for the given value of $p$, we linearly interpolate between $Q(p^-)$ and $Q(p^+)$, where $p^-$ and $p^+$ are the nearest values below and above $p$ respectively for which $i$ is an integer: $$Q(p) = Q(p^-) + \frac{p - p^-}{p^+ - p^-}\left(Q(p^+) - Q(p^-)\right).$$ In effect, $Q(p)$ is the empirical $p$th quantile of the squared weight magnitudes.

We define the Hamiltonian for unstructured pruning as $$H(\x) = \sum_{i=1}^N \Big(Q(p) - w_i^2\Big)x_i.
    \label{eq:ham-unstruct}$$ The value of the Hamiltonian is minimized when the fraction $p$ of weights with lowest magnitude are masked, making that the state that annealing will converge towards.

In general, sampling from the Gibbs distribution is difficult because the partition function cannot be feasibly computed for large vectors. In the following proposition, we show that our proposed Hamiltonian for unstructured pruning factors in terms of elements of $x$. The elements are therefore independent and can easily be sampled individually.

The Gibbs distribution corresponding to the proposed Hamiltonian (\[eq:ham-unstruct\]) has a product form. \[prop:unstruct-factor\]

The partition function $Z(\beta)$ normalizes the distribution. With the proposed Hamiltonian, it has the form: $$\begin{aligned}
    Z(\beta) &= \sum_{\x\in\{-1,1\}^N} e^{-\beta \sum_{i=1}^N \left(Q(p) - w_i^2\right)x_i} \\
    &= \sum_{x_1\in\{-1,1\}} \sum_{x_2\in\{-1,1\}} ... \prod_{i=1}^N e^{-\beta \left(Q(p) - w_i^2\right)x_i} \\
    &= \prod_{i=1}^N \left(e^{-\beta \left(Q(p) - w_i^2\right)} + e^{\beta \left(Q(p) - w_i^2\right)} \right) \\
    &= \prod_{i=1}^N 2 \mbox{cosh} \Big(\beta\left(Q(p) - w_i^2\right)\Big).\end{aligned}$$ The Gibbs distribution therefore factors as: $$\begin{aligned}
p(\x) &= \frac{1}{\prod_{i=1}^N 2 \mbox{cosh} \Big(\beta\left(Q(p) - w_i^2\right)\Big)} e^{-\beta \sum_{i=1}^N \left(Q(p) - w_i^2\right)x_i} \\
    &= \prod_{i=1}^N \frac{1}{2 \mbox{cosh} \Big(\beta\left(Q(p) - w_i^2\right)\Big)} e^{-\beta \left(Q(p) - w_i^2\right)x_i}.\end{aligned}$$

Structured Pruning {#sec:structured}
------------------

For structured pruning, we prune blocks of weights together in a way that is advantageous for inference performance. We enforce this by adding a quadratic term to the Hamiltonian that encourages all elements of the pruning mask within given neighbourhoods to take the same value, where the neighbourhoods are chosen to correspond to the blocks of weights that are pruned together. For instance, to do filter-wise pruning on a convolutional network, we would define the neighbourhoods to correspond to the filters of the layer.

We define a neighbourhood as a set of indices corresponding to a subset of weights in the neural network layer. Let $\N_k$ represent the $k$th neighbourhood. Neighbourhoods are disjoint and the union of all neighbourhoods is the set of all weight indices, that is $\N_i \cap \N_j = \emptyset$ and $\bigcup_{k=1}^{M} \N_k = \{1,...,N\}$, where $M$ is the number of neighbourhoods. Let us also define a vector $\bar{\w}$ such that $$\bar{w}_k^2 = \frac{1}{|\N_k|}
\sum_{i\in \N_k}w_i^2.$$ We propose the Hamiltonian $$H(\x) = -c\sum_{k=1}^M\,\sum_{\substack{i,j \in \N_k\\i \neq j}}x_ix_j + \sum_{i=1}^N 
    \Big( \bar{Q}(p) - w_i^2\Big)x_i,
    \label{eq:ham-struct}$$ where $c$ is a hyperparameter controlling the relative influence of the structure term on the Hamiltonian, and $\bar{Q}(p)$ is an empirical quantile function over squared weight magnitudes as described previously, but calculated over the values in $\bar{\w}$ instead of $\w$. The state that minimizes the Hamiltonian when $c$ is large enough to enforce the mask structure therefore corresponds to pruning the fraction $p$ of neighbourhoods with lowest average weight magnitude. When $|\N_k| = 1\,\forall\,k$, the inner summation in the quadratic term does not sum over anything, so the quadratic term does not apply. Hence the unstructured pruning Hamiltonian (\[eq:ham-unstruct\]) is a special case of the Hamiltonian (\[eq:ham-struct\]).

The following proposition shows that the probability distribution still factors in terms of neighbourhoods, so we can sample each neighbourhood separately.

The Gibbs distribution corresponding to the Hamiltonian (\[eq:ham-struct\]) has a block product form.

Since the first term of the Hamiltonian (\[eq:ham-struct\]) is zero for elements from different neighbourhoods, the Hamiltonian can be expressed as: $$\begin{aligned}
    H(\x) &= \sum_{k=1}^M H_k(\x_k),\end{aligned}$$ where $H_k(\x_k)$ is the Hamiltonian (\[eq:ham-struct\]) only computed over the elements of the neighbourhood $\N_k$. Following the same approach as for Proposition \[prop:unstruct-factor\], the partition function can then be shown to factor as: $$\begin{aligned}
    Z(\beta) &= \prod_{k=1}^M\,\sum_{\x_k \in \{-1,1\}^{|\N_k|}} e^{-\beta H_k(\x_k)} \\
    &= \prod_{k=1}^M Z_k(\beta),\end{aligned}$$ and the overall distribution factors as: $$\begin{aligned}
p(\x) &= \prod_{k=1}^M\frac{1}{Z_k(\beta)} e^{-\beta H_k(\x_k)}.\end{aligned}$$

Sampling from the above distribution is more challenging than in the unstructured case. If neighbourhoods are small enough, the partition function for each can be computed and the distribution can be sampled from directly. But for large neighbourhoods, this is computationally infeasible, and so we use a Markov chain Monte Carlo (MCMC) method to generate samples. A sampling method that parallelizes well and is simple to implement on a GPU is preferable for neural network training to highly iterative methods like standard Gibbs sampling or the Wolff algorithm [@wolff1989collective]. We therefore propose using Chromatic Gibbs sampling [@gonzalez2011parallel].

Chromatic Gibbs sampling involves making a colouring over the elements in $\x$ such that any interacting elements $x_i$ and $x_j$ in the Hamiltonian have different colours. For our Hamiltonians, $x_i$ and $x_j$ interact if there is a term $\alpha x_ix_j$ with $\alpha \neq 0$. An iteration of the Markov chain then consists of sampling all elements of one colour simultaneously given the current values of all other elements.

To create a colouring, we arbitrarily divide each neighbourhood into two sets of elements and modify the Hamiltonian to remove all quadratic terms containing elements in the same set. In graphical terms, this transforms the Markov random field for each neighbourhood from a complete graph to a complete bipartite graph that can be coloured with two colours, as shown in Figure \[fig:chromatic\]. This modification still preserves a high degree of connectivity within each neighbourhood and so is still effective at encouraging elements in neighbourhoods to take the same value. For instance, to perform filter-wise pruning of a convolutional network, we can divide all connections into two groups based on their input channel. Pseudocode for this algorithm is shown in Algorithm \[fig:chromatic-algo\].

[latex@errorgobble]{}

$\X_0 \gets \X_{\text{init}\,:,:,0:C/2,:}$ $\X_1 \gets \X_{\text{init}\,:,:,C/2+1:C,:}$ $\X \gets \text{concatenate}(\X_0, \X_1)$

\[fig:chromatic-algo\]

Our Hamiltonians are designed so that we know which $\x$ value minimizes them and is therefore the mode of the distribution. To minimize burn-in time, we use this value to initialize the Markov chain, as recommended in [@brooks2011handbook].

Annealing
---------

We perform annealing by increasing $\beta$ from a low value to a high value while training. At high temperatures (low $\beta$), differences in the Hamiltonian do not affect sampling much, and so roughly 50% of weights are pruned randomly. This phase acts similarly to stochastic methods for regularizing neural networks [@labach2019survey], in particular dropconnect [@wan2013regularization], regularizing the network and conditioning it to be robust under weight pruning. Once annealed to a low temperature (high $\beta$), the Gibbs distribution converges towards the value of $\x$ that minimizes the Hamiltonian, which is our desired final pruning mask. A significant amount of later training time is therefore spent adapting to the particular structure of the pruning mask. The balance between time spent on adapting to random masks and time spent on adapting to the final mask can be controlled by the particular annealing schedule for $\beta$ used in training.

Experiments {#sec:experiments}
===========

We compare Gibbs pruning, our proposed method, to several pruning methods that are either well-established or have recently shown exceptional results. We evaluate performance on ResNet neural networks [@he2016deep]. Nearly all of the parameters of these networks are in convolutional layers, which already are much more sparse than dense layers of similar dimensions, and additionally employ weight sharing, making them more difficult to prune than dense layers. Pruning methods that are effective on networks like AlexNet [@krizhevsky2012imagenet] with large dense layers often do not show the same performance on networks like ResNet, making them a more challenging test.

In particular, we train ResNet-20 and ResNet-56 with linear projection [@he2016deep] on the CIFAR-10 [@krizhevsky2012cifar] dataset. The networks are trained for 200 epochs using the Adam optimizer [@kingma2014adam], with a learning rate initially set to $10^{-3}$ and reduced by a factor of 10 at epoch 80 and every 40 epochs thereafter. We use data augmentation during training, randomly shifting images horizontally and vertically by up to 10% and flipping images horizontally with 50% probability. This achieves baseline top-1 accuracies of 90.7% for ResNet-20 and 92.2% for ResNet-56. For all methods, we prune all convolutional layers except for the first one, following the recommendation in [@gale2019state].

For pruning methods that use additional epochs for fine-tuning, we use a training rate of $10^{-5}$ after the initial training phase. We also test changing the lengths of fine-tuning schedules, to evaluate tradeoffs between the number of additional training epochs and the final accuracy.

Unstructured Pruning
--------------------

[.45]{} ![Comparison of our proposed method to other unstructured pruning methods. All methods prune 90% of weights. Dotted lines are shown for comparison on methods that do not require additional training epochs. []{data-label="fig:resnetscatter"}](resnet20-scatter.pdf "fig:"){width="\textwidth"} \[fig:resnet20scatter\]

[.45]{} ![Comparison of our proposed method to other unstructured pruning methods. All methods prune 90% of weights. Dotted lines are shown for comparison on methods that do not require additional training epochs. []{data-label="fig:resnetscatter"}](resnet56-scatter.pdf "fig:"){width="\textwidth"} \[fig:resnet56scatter\]

We compare Gibbs pruning to four established unstructured pruning methods. The first is using $l_1$ regularization with a tuned penalty of 0.001 to induce sparsity during training before masking the fraction $p$ of weights in each layer with the lowest magnitude. The other pruning methods are more advanced approaches from the literature. We evaluate the method proposed in [@han2016deep] by Han et al. Once training is complete, a certain percentage of the weights with lowest magnitude are pruned and the network is fine-tuned. This procedure is then repeated several times. We test different training schedules, using pruning percentages of 10%, 20%, 30%, etc. up to 90%, fine-tuning for $n$ epochs after each step, leading to a total of $200+9n$ epochs. We also compare to iterative magnitude pruning (IMP), proposed by Frankle et al. [@frankle2019lottery] with rewinding [@frankle2019stabilizing]. This method trains the network several times, pruning gradually more each time and then rewinding the network weights to the values they had after the first 500 training steps. To test different training times, we vary the number of times that the network is trained with intermediate pruning rates between the initial rate of 0% and the final rate of 90%. Finally, we test targeted dropout, a method recently proposed by Gomez et al.[@gomez2019learning]. We use their most successful hyperparameter settings: $\alpha=0.75,\gamma=0.9$.

When using Gibbs pruning, we anneal $\beta$ according to a logarithmic schedule from 0.7 to 10000. With the baseline training schedule of 200 epochs, we anneal over the first 128 epochs. These values were chosen empirically to cover a range from an effective pruning rate of 50% to $p$.

To compare our proposed method to those that use additional training epochs, we also evaluate ’stretching’ the training and pruning procedure to take place over a longer training schedule. To do this, we run training for some multiple of 200 epochs, and stretch out the learning rate and $\beta$ annealing schedules by the same multiple. This follows our general approach of training and pruning at the same time, but gives the network longer to both converge to the final pruning mask and to fully adapt to it. In some highly stretched configurations, the pruning mask requires a higher final $\beta$ to converge—in these cases we increase the final beta to $10^6$.

We evaluate pruning 90% of weights in each pruned layer, which is a high degree of compression for the given networks that effectively shows performance differences between different methods. Results are shown in Figure \[fig:resnetscatter\]. Our proposed method outperforms the other evaluated methods for any number of training epochs. While it is effective at 200 epochs, stretching the learning and annealing schedules provides even better performance.

We evaluate Gibbs pruning using different pruning rates in Figure \[fig:p-acc\]. When additional training epochs are used, the resulting accuracy can come close to or exceed the baseline accuracy even with very high pruning rates. For instance, removing 95% of weights in pruned layers decreases accuracy by only 0.90% for ResNet-20 and 0.45% for ResNet-50. To the best of our knowledge, these are the best results reported for such a high compression rate on CIFAR-10 [@blalock2020state].

![Loss in accuracy compared to the baseline versus pruning rate using our proposed unstructured pruning method.[]{data-label="fig:p-acc"}](p-acc-plot.pdf){width="45.00000%"}

[0.4]{}

[width=]{}

       Method                            
  ----------------- ------ ------ ------ ------
                     50%    75%    90%    95%
     Random mask     89.3   87.2   83.2   79.8
   Random re-init.   88.6   86.3   83.4   79.4
    Gibbs pruning    89.9   89.0   87.5   85.2

  : ResNet-56

[0.4]{}

[width=]{}

       Method                            
  ----------------- ------ ------ ------ ------
                     50%    75%    90%    95%
     Random mask     91.2   90.0   87.3   84.6
   Random re-init.   90.3   89.1   86.7   84.5
    Gibbs pruning    91.2   90.8   89.8   88.5

  : ResNet-56

\[t:random\]

Random Masks and Re-initialization
----------------------------------

In [@liu2019rethinking], the authors show that results with many pruning methods can be replicated by training a network with the same structure as the pruned network, but with newly initialized weights. This calls into question the utility of training large networks and then pruning, as well as the relevance of weight values in pruning networks. To assess this phenomenon with our proposed method, we evaluate retraining pruned networks with randomly re-initialized weights. For comparison, we also test training networks from scratch using random pruning masks with the chosen sparsity.

Results are shown in Table \[t:random\]. We find that random re-initialization does not meet the performance of our proposed method, and is more similar to training with a random mask. These results suggests that Gibbs pruning acts less as a search over possible network architectures, and instead adapts more dynamically along with weight values throughout training, resulting in a final representation that is more tuned to particular weights than other methods are.

[.45]{} ![Comparison of our proposed method to other kernel-wise structured pruning methods. All methods prune 90% of weights. Dotted lines are shown for comparison on methods that do not require additional training epochs. []{data-label="fig:resnetkernelscatter"}](resnet20-kernel-scatter.pdf "fig:"){width="\textwidth"} \[fig:resnet20kernelscatter\]

[.45]{} ![Comparison of our proposed method to other kernel-wise structured pruning methods. All methods prune 90% of weights. Dotted lines are shown for comparison on methods that do not require additional training epochs. []{data-label="fig:resnetkernelscatter"}](resnet56-kernel-scatter.pdf "fig:"){width="\textwidth"} \[fig:resnet56kernelscatter\]

Structured Pruning {#structured-pruning}
------------------

We also evaluate performance for structured convolutional neural network pruning. We consider pruning individual kernels as well as entire filters of size $N\times N\times C$, where $C$ is the number of output channels for the layer being pruned. Generally speaking, kernel-wise pruning can achieve higher accuracies for the same number of parameters, but might be harder to optimize performance for compared to filter-wise pruning. In practice, the desired structure depends on the optimization capabilities of the hardware and software the neural network is implemented on.

We compare Gibbs pruning to three established structured pruning methods. One is targeted dropout [@gomez2019learning], implemented as previously described, but dropping entire kernels or filters. For filter-wise pruning, we set its hyperparameters to the most effective values described for unit dropout: $\alpha=0.9, \gamma=0.75$. We also evaluate iteratively pruning filters or kernels based on their $l_1$-norm and retraining, as proposed by Li et al. in [@li2017pruning]. Finally, we evaluate ThiNet pruning, as proposed by Luo et al. [@luo2017thinet]. This method greedily prunes channels to minimize changes in activations over an evaluation dataset. Pruning a channel is equivalent to pruning a filter in the previous layer, making channel-wise and filter-wise pruning comparable. This method does not prune $1\times1$ convolution layers, which we also omit for filter-wise pruning with our proposed method.

For kernel-wise Gibbs pruning, each neighbourhood is small enough that the partition function is practically computable, so we sample directly from the Gibbs distribution. For filter-wise pruning, we use chromatic Gibbs sampling as described in Section \[sec:structured\]. We run the Markov chain for 50 iterations, as further iterations did not improve performance in practice. For kernel-wise pruning, we set $c$ to 0.01 and anneal $\beta$ logarithmically from 0.7 to 10000. For filter-wise pruning, we set $c$ to 1 and anneal $\beta$ logarithmically from 0.003 to 1. As with unstructured pruning, these values were chosen empirically to cover a range from an effective pruning rate of 50% to $p$, while also converging to a mask that has the desired structure. To test different training lengths, we stretch the learning rate and annealing schedules as previously described.

Kernel-wise pruning results are shown in Figure \[fig:resnetkernelscatter\] and filter-wise pruning results are shown in Figure \[fig:resnetfilterscatter\]. Note that a lower pruning rate of 75% is used in evaluating filter-wise pruning methods, since filter-wise pruning at high rates results in much lower accuracy. In both types of structured pruning, Gibbs pruning outperforms other methods, and stretching the learning rate and annealing schedules proves to be effective in additionally improving accuracy.

[.45]{} ![Comparison of our proposed method to other filter-wise structured pruning methods. All methods prune 75% of weights. Dotted lines are shown for comparison on methods that do not require additional training epochs. []{data-label="fig:resnetfilterscatter"}](resnet20-filter-scatter.pdf "fig:"){width="\textwidth"} \[fig:resnet20filterscatter\]

[.45]{} ![Comparison of our proposed method to other filter-wise structured pruning methods. All methods prune 75% of weights. Dotted lines are shown for comparison on methods that do not require additional training epochs. []{data-label="fig:resnetfilterscatter"}](resnet56-filter-scatter.pdf "fig:"){width="\textwidth"} \[fig:resnet56filterscatter\]

Conclusion
==========

We introduce a novel method for neural network pruning based on Gibbs distributions that achieves high pruning rates with little reduction in accuracy. It can be used for either unstructured or structured pruning, and the general framework can be adapted to a wide range of pruning structures. Our results show the efficacy of simultaneously training and pruning a network rather than training and pruning as distinct steps at different times. Future work could explore other structures that can be expressed within this framework, such as limiting the number of non-zero values in convolutional kernels or structured pruning of recurrent neural network layers.

Acknowledgement
===============

The authors would like to thank Fujitsu Laboratories Ltd. and Fujitsu Consulting (Canada) Inc. for providing financial support for this project at the University of Toronto.
