Fast, general, and tested differentiable structured prediction in PyTorch
The literature on structured prediction for NLP describes a rich collection of distributions and algorithms over sequences, segmentations, alignments, and trees; however, these algorithms are difficult to utilize in deep learning frameworks. We introduce Torch-Struct, a library for structured prediction designed to take advantage of and integrate with vectorized, auto-differentiation based frameworks. Torch-Struct includes a broad collection of probabilistic structures accessed through a simple and flexible distribution-based API that connects to any deep learning model. The library utilizes batched, vectorized operations and exploits auto-differentiation to produce readable, fast, and testable code. Internally, we also include a number of general-purpose optimizations to provide cross-algorithm efficiency. Experiments show significant performance gains over fast baselines and case-studies demonstrate the benefits of the library. Torch-Struct is available at https://github.com/harvardnlp/pytorch-struct.READ FULL TEXT VIEW PDF
Fast, general, and tested differentiable structured prediction in PyTorch
DGMs for NLP. A roadmap.
Structured prediction is an area of machine learning focusing on representations of spaces with combinatorial structure, and algorithms for inference and parameter estimation over these structures. Core methods include both tractable exact approaches like dynamic programming and spanning tree algorithms as well as heuristic techniques such linear programming relaxations and greedy search.
|Name||Structure ()||Parts ()||Algorithm ()||LoC||T/S||Sample Reference|
|20||390k||Lafferty et al. (2001)|
|Factorial F-B||20||25k||Ghahramani and Jordan (1996)|
|50||13k||Needleman and Wunsch (1970)|
|Semi-Markov||Seg. Labels||Edges||Segmental F-B||30||87k||
Baum and Petrie (1966)
Sarawagi and Cohen (2005)
|Inside-Outside CKY||70||37k||Kasami (1966)|
|Simple CKY||Labeled Tree||Splits||0-th order CKY||30||118k||Kasami (1966)|
|Dependency||Proj. Tree||Arcs||Eisner Algorithm||40||28k||Eisner (2000)|
|Dependency (NP)||Non-Proj. Tree||Arcs||
Koo et al. (2007)
McDonald et al. (2005)
|60||-||Tillmann and Ney (2003)|
Structured prediction has played a key role in the history of natural language processing. Example methods include techniques for sequence labeling and segmentationLafferty et al. (2001); Sarawagi and Cohen (2005), discriminative dependency and constituency parsing Finkel et al. (2008); McDonald et al. (2005)
, unsupervised learning for labeling and alignmentVogel et al. (1996); Goldwater and Griffiths (2007), approximate translation decoding with beam search Tillmann and Ney (2003), among many others.
In recent years, research into deep structured prediction has studied how these approaches can be integrated with neural networks and pretrained models. One line of work has utilized structured prediction as the final final layer for deep modelsCollobert et al. (2011); Durrett and Klein (2015). Another has incorporated structured prediction within deep learning models, exploring novel models for latent-structure learning, unsupervised learning, or model control Johnson et al. (2016); Yogatama et al. (2016); Wiseman et al. (2018). We aspire to make both of these use-cases as easy to use as standard neural networks.
The practical challenge of employing structured prediction is that many required algorithms are difficult to implement efficiently and correctly. Most projects reimplement custom versions of standard algorithms or focus particularly on a single well-defined model class. This research style makes it difficult to combine and try out new approaches, a problem that has compounded with the complexity of research in deep structured prediction.
With this challenge in mind, we introduce Torch-Struct with three specific contributions:
Modularity: models are represented as distributions with a standard flexible API integrated into a deep learning framework.
Completeness: a broad array of classical algorithms are implemented and new models can easily be added in Python.
Efficiency: implementations target computational/memory efficiency for GPUs and the backend includes extensions for optimization.
In this system description, we first motivate the approach taken by the library, then present a technical description of the methods used, and finally present several example use cases.
Several software libraries target structured prediction. Optimization tools, such as SVM-struct Joachims (2008), focus on parameter estimation. Model libraries, such as CRFSuite Okazaki (2007) or CRF++ Kudo (2005), implement inference for a fixed set of popular models, such as linear-chain CRFs. General-purpose inference libraries, such as PyStruct Müller and Behnke (2014) or TurboParser Martins et al. (2010), utilize external solvers for (primarily MAP) inference such as integer linear programming solvers and ADMM. Probabilistic programming languages, for example languages that integrate with deep learning such as Pyro Bingham et al. (2019), allow for specification and inference over some discrete domains. Most ambitiously, inference libraries such as Dyna Eisner et al. (2004) allow for declarative specifications of dynamic programming algorithms to support inference for generic algorithms. Torch-Struct takes a different approach and integrates a library of optimized structured distributions into a vectorized deep learning system. We begin by motivating this approach with a case study.
While structured prediction is traditionally presented at the output layer, recent applications have deployed structured models broadly within neural networks (Johnson et al., 2016; Kim et al., 2017; Yogatama et al., 2016, inter alia). Torch-Struct aims to encourage this general use case.
To illustrate, we consider a latent tree model. ListOps Nangia and Bowman (2018) is a dataset of mathematical functions. Each data point consists of a prefix expression and its result , e.g.
Models such as a flat RNN will fail to capture the hierarchical structure of this task. However, if a model can induce an explicit latent , the parse tree of the expression, then the task is easy to learn by a tree-RNN model Yogatama et al. (2016); Havrylov et al. (2019).
A popular approach is a latent-tree RL model which we briefly summarize. The objective is to maximize the probability of the correct prediction under the expectation of a prior tree model, ,
Computing the expectation is intractable so policy gradient is used. First a tree is sampled , then the gradient with respect to is approximated as,
is a variance reduction baseline. A common choice is the self-critical baselineRennie et al. (2017),
Finally an entropy regularization term is added to the objective encourage exploration of different trees, .
Even in this brief overview, we can see how complex a latent structured learning problem can be. To compute these terms, we need 5 different properties of the tree model :
Score policy samples,
For structured models, each of these terms is non-trivial to compute. A goal of Torch-Struct is to make it seamless to deploy structured models for these complex settings. To demonstrate this, Torch-Struct includes an implementation of this latent-tree approach. With a minimal amount of user code, the implementation achieves near perfect accuracy on the ListOps dataset.
The library design of Torch-Struct follows the distributions API used by both TensorFlow and PyTorchDillon et al. (2017). For each structured model in the library, we define a conditional random field (CRF) distribution object. From a user’s standpoint, this object provides all necessary distributional properties. Given log-potentials (scores) output from a deep network , the user can request samples , probabilities , modes , or other distributional properties such as
. The library is agnostic to how these are utilized, and when possible, they allow for backpropagation to update the input network. The same distributional object can be used for standard output prediction as for more complex operations like attention or reinforcement learning.
Figure 2 demonstrates this API for a binary tree CRF over an ordered sequence, such as from the previous section. The distribution takes in log-potentials which score each possible span in the input. The distribution converts these to probabilities of a specific tree. This distribution can be queried for predicting over the set of trees, sampling a tree for model structure, or even computing entropy over all trees.
Table 1 shows all of the structures and distributions implemented in Torch-Struct. While each is internally implemented using different specialized algorithms and optimizations, from the user’s perspective they all utilize the same external distributional API, and pass a generic set of distributional tests.111The test suite for each distribution enumerates over all structures to ensure that properties hold. While this is intractable for large spaces, it can be done for small sets and was extremely useful for development. This approach hides the internal complexity of the inference procedure, while giving the user full access to the model.
We now describe the technical approach underlying the library. To establish notation first consider the implementation of a categorical distribution, Cat(), with one-hot categories with from a set and probabilities given by the softmax,
Define the log-partition as , i.e. log of the denominator, where is the log-sum-exp operator. Computing probabilities or sampling from this distribution, requires enumerating to compute the log-partition . A useful identity is that derivatives of yield category probabilities,
Other distributional properties can be similarly extracted from variants of the log-partition. For instance, define then222This is a subgradient identity, but we observe that libraries like PyTorch will default to this value.: .
Conditional random fields, CRF(), extend the softmax to combinatorial spaces where is exponentially sized. Each , is now represented as a binary vector over polynomial-sized set of parts, , i.e. . Similarly log-potentials are now defined over parts . For instance, in Figure 2 each span is a part and the vector is shown in the top-left figure. Define the probability of a structure as,
Computing probabilities or sampling from this distribution, requires computing the log-partition term . In general computing this term is now intractable, however for many core algorithms in NLP there are exist efficient combinatorial algorithms for this term (as enumerated in Table 1).
Derivatives of the log-partition again provide distributional properties. For instance, the marginal probabilities of parts are given by,
Similarly derivatives of correspond to whether a part appears in the argmax structure. .
While these gradient identities are well-known Eisner (2016), they are not commonly deployed. Computing CRF properties is typically done through two-step specialized algorithms, such as forward-backward, inside-outside, or similar variants such as viterbi-backpointers Jurafsky and Martin (2014). In our experiments, we found that using these identities with auto-differentiation on GPU was often faster, and much simpler, than custom two-pass approaches. Torch-Struct is thus designed around using gradients for distributional computations.
|Entropy ( )||See Li and Eisner (2009)|
|Exp.||See Li and Eisner (2009)|
|Sparsemax||See Mensch and Blondel (2018)|
Torch-Struct is a collection of generic algorithms for CRF inference. Each CRF distribution object, , is constructed by providing where the parts are specific to the type of distribution. Internally, each distribution is implemented through a single Python function for computing the log-partition function . From this function, the library uses auto-differentiation and the identities from the previous section, to define a complete distribution object. The core models implemented by the library are shown in Table 1.
To make the approach concrete, we consider the example of a linear-chain CRF.
The model has labels per node with a length
edges utilizing a first-order linear-chain (Markov) model. This model hasparts corresponding to edges in the chain, and thus requires . The log-partition function factors into two reduce computations,
Computing this function left-to-right using dynamic programming yield the standard forward algorithm for sequence models. As we have seen, the gradient with respect to produces marginals for each part, i.e. the probability of a specific labeled edge.
We can further extend the same function to support generic semiring dynamic programming Goodman (1999). A semiring is defined by a pair with commutative , distribution, and appropriate identities. The log-partition utilizes , but we can substitute alternatives.
For instance, utilizing the log-max semiring in the forward algorithm yields the max score. As we have seen, its gradient with respect to is the argmax sequence, negating the need for a separate argmax (Viterbi) algorithm. Some distributional properties cannot be computed directly through gradient identities but still use a forward-backward style compute structure. For instance, sampling requires first computing the log-partition term and then sampling each part, (forward filtering / backward sampling). We can compute this value by overriding each backpropagation operation for the to instead compute a sample.
Table 2 shows the set of semirings and backpropagation steps for computing different terms of interest. We note that many of the terms necessary in the case-study can be computed with variant semirings, negating the need for specialized algorithms.
Torch-Struct aims for computational and memory efficiency. Implemented naively, dynamic programming algorithms in Python are prohibitively slow. As such Torch-Struct provides key primitives to help batch and vectorize these algorithms to take advantage of GPU computation and to minimize the overhead of backpropagating through chart-based dynamic programmming. Figure 3 shows the impact of these optimizations on the core algorithms.
The commutative properties of semiring algorithms allow flexibility in the order in which we compute . Typical implementations of dynamic programming algorithms are serial in the length of the sequence. On parallel hardware, an appealing approach is a parallel scan ordering Särkkä and García-Fernández (2019), typically used for computing prefix sums. To compute,
in this manner we first pad the sequence lengthout to the nearest power of two, and then compute a balanced parallel tree over the parts, shown in Figure 4. Concretely each node layer would compute a semiring matrix multiplication, e.g. . Under this approach, we only need steps in Python and can use parallel GPU operations for the rest. Similar parallel approach can also be used for computing sequence alignment and semi-Markov models.
Computational complexity is even more of an issue for parsing algorithms, which cannot be as easily parallelized. The log-partition for parsing is computed with the Inside algorithm. This algorithm must compute each width from 1 through T in serial; however it is important to parallelize each inner step. Assuming we have computed all inside spans of width less than , computing the inside span of width requires computing for all ,
In order to vectorize this loop over , we reindex the chart. Instead of using a single chart , we split it into two parts: one right-facing and one left facing, . After this reindexing, the update can be written.
Unlike the original, this formula can easily be computed as a vectorized semiring dot product. This allows use to compute in one operation. Variants of this same approach can be used for all the parsing models employed.
The two previous optimizations reduce most of the cost to semiring matrix multiplication. In the specific case of the semiring these can be computed very efficiently using matrix multiplication, which is highly-tuned on GPU hardware. Unfortunately for other semirings, such as log and max, these operations are either slow or very memory inefficient. For instance, for matrices and of sized and , we can broadcast with
to a tensor of sizeand then reduce dim by at a huge memory cost. To avoid this issue, we implement custom CUDA kernels targeting fast and memory efficient tensor operations. For log, this corresponds to computing,
where . To optimize this operation on GPU we utilize the TVM language Chen et al. (2018) to layout the CUDA loops and tune it to hardware.
We present Torch-Struct, a library for deep structured prediction. The library achieves modularity through its adoption of a generic distributional API, completeness by utilizing CRFs and semirings to make it easy to add new algorithms, and efficiency through core optimizations to vectorize important dynamic programming steps. In addition to the problems discussed so far, Torch-Struct also includes several other example implementations including supervised dependency parsing with BERT, unsupervised tagging, structured attention, and connectionist temporal classification (CTC) for speech. The full library is available at https://github.com/harvardnlp/pytorch-struct.
In the future, we hope to support research and production applications employing structured models. We also believe the library provides a strong foundation for building generic tools for interpretablity, control, and visualization through its probabilistic API. Finally, we hope to explore further optimizations to make core algorithms competitive with highly-optimized neural network components.
We thank Yoon Kim, Xiang Lisa Li, Sebastian Gehrmann, Yuntian Deng, and Justin Chiu for discussion and feedback on the project. The project was supported by NSF CAREER 1845664, NSF 1901030, and research awards by Sony and AWS.
Statistical inference for probabilistic functions of finite state markov chains.The annals of mathematical statistics, 37(6):1554–1563.
Factorial hidden markov models.In Advances in Neural Information Processing Systems, pages 472–478.
Svmstruct: Support vector machine for complex outputs.
Self-critical sequence training for image captioning.In