Coder Social home page Coder Social logo

matchbox's Introduction

Matchbox

Matchbox enables deep learning researchers to write PyTorch code at the level of individual examples, then run it efficiently on minibatches. It does this using three components:

  • A MaskedBatch type, together with overloaded implementations of PyTorch methods and neural network layers, keeps track of padding and masking for variable-size data automatically. Use dir(matchbox.MaskedBatch) to see a list of supported methods.
  • A @batch decorator rewrites some Python control flow into a SIMT-like form that includes execution masking and synchronization primitives.
  • Convenience methods like batch_ones, split_dim, and causal_mask support common use cases in dynamic neural network code in a way that benefits from the more semantically meaningful shape information available with the MaskedBatch type. These are implemented both for batch and tensor objects, because all code written for Matchbox also works with plain Tensors at batch size one.

There is also a plugin for torchtext and a wrapper for testing that Matchbox results are numerically equivalent to a loop over unbatched examples. See the examples and test directories for details.

Installation and requirements

Matchbox is in early-release alpha. Use python setup.py install to install. Please file or upvote issues to request new operation implementations, or feel free to post one as a pull request. If Matchbox throws a NotImplementedError, that means that a particular feature of an operation could be supported but isn't yet.

Matchbox is developed on Python 3.6 and PyTorch 0.4. It contains compatibility code that is intended to support PyTorch 0.3, but not all features will work. Matchbox also requires gast, astor, and six. Python 2 support is not an immediate priority but we would welcome a PR.

Getting started

The first step to using Matchbox is to replace your import of torch.nn.functional with matchbox.functional:

import matchbox
import matchbox.functional as F
# now calls like `F.softmax` refer to Matchbox's implementations

This import also replaces methods on PyTorch Tensors with Matchbox versions and injects matchbox.functional functions into torch.nn modules.

Now you can write model code that applies to individual examples. If your code uses control flow, add the @matchbox.batch decorator to that function or class (unfortunately, this doesn't yet work in the interactive interpreter or in Jupyter notebooks):

from torch import nn
class RNN(nn.Module):
    def __init__(self, size):
        super().__init__()
        self.cell = nn.RNNCell(size, size)
    @matchbox.batch
    def forward(self, x):
        h = x.new_zeros(x.size(0), x.size(-1))
        for xt in x.unbind(1):
            h = self.cell(xt, h)
        return h

You can create input data to pass to this model in three ways. First, you can pass them ordinary PyTorch Tensors with batch size one. You can also pass MaskedBatch objects created manually, from lists of Tensors with batch size one (note that torch.rand should be wrapped in Variable on PyTorch 0.3):

import torch
from matchbox import MaskedBatch
from random import randint
b, t, c = 32, 10, 128
model = RNN(c)
x_unbatched = torch.rand(1, randint(1, t), c) # a single random example
x_manual_batch = MaskedBatch.fromlist(
    [torch.rand(1, randint(1, t), c) for i in range(b)], # list of examples
    (True, False)) # dimension 1 is dynamic and dimension 2 is static
h = model(x_unbatched)
h = model(x_manual_batch)

And we provide a torchtext Field class that produces MaskedBatch objects when a dataset is iterated:

from matchbox.data import MaskedBatchField
TEXT = MaskedBatchField(batch_first=True)
train, dev, test = datasets.IWSLT.splits(('.de', '.en'), (TEXT, TEXT))
TEXT.build_vocab(train, max_size=50000)
train_iter = data.BucketIterator(train, batch_size=32, device=-1)
for x_torchtext_batch in train_iter:
    h = model(x_torchtext_batch)
    # more training loop code

Credit

Matchbox is developed by James Bradbury at Salesforce Research. It also contains Python source-wrangling code modified from Patrick Maupin and Berker Peksag's AST observe-rewrite as well as Google Brain's Tangent, a source-to-source automatic differentiation package developed by Alex Wiltschko, Bart van Merrienboer and Dan Moldovan. The modified Tangent code is licensed under Apache 2 while the rest of the codebase is licensed under three-clause BSD; see LICENSE.BSD-3.txt and LICENSE.Apache-2.txt.

Limitations

Matchbox only works on code that uses native PyTorch operators. In particular, everything that could vary between examples in a batch needs to be a Tensor in order for code written for individual examples to work with Matchbox. Support for scalar tensors is significantly better in PyTorch 0.4. NumPy ops also need to be replaced with their native PyTorch equivalents.

Control flow support is limited. While some of these limitations will be lifted (e.g., support for continue within while is straightforward to add) some constructs are conceptually harder for Matchbox to support (e.g., return from within a for).

There’s also a long tail of less-common operations that haven’t been implemented (plus bigger gaps, like convolutions). We will be continuously adding support for additional ops but also welcome pull requests.

Implementation details (batch semantics)

MaskedBatch objects behave like PyTorch Tensors, but represent a collection ("batch") of Tensors that may be of different sizes in some of their dimensions. Most of the time, MaskedBatch objects adhere to Matchbox's "standard" semantics, but control flow constructions require a different "SIMT" semantics.

Standard

The dims attribute is a tuple with a bool for each non-batch dimension, representing whether that dimension is static (False) or dynamic (True).

The data attribute is a Tensor whose size is the batch size in the batch dimension, the size of all examples in static dimensions, and at least as large as the largest example in the batch in dynamic dimensions.

The mask attribute is a Tensor whose size is the batch size in the batch dimension, one in static dimensions, and at least as large as the largest example in the batch in dynamic dimensions. Each entry in the mask corresponds to one or more entries in the data array (singleton, i.e., static, dimensions are broadcasted), with a one in the mask denoting that the corresponding data entries represent valid, meaningful data and a zero denoting that they do not.

Data values corresponding to zeros in the mask are not required to be zero, and operations should propagate masked data if doing so would not affect non-masked parts of the output. Operations for which this is not the case should first multiply their input data by the corresponding masks.

SIMT

A one in the mask denotes that the corresponding data entries represent currently active data. A zero denotes that the corresponding data entries represent "dormant" data, which may be valid at a previous step of a loop (e.g., at a previous index along an external dimension that is being iterated over) or in another branch of a conditional. Currently, no dimensions in a SIMT batch may be dynamic, but support for this case will be added.

Future work

In addition to adding MaskedBatch support for more operations, we also plan a separate PackedBatch type that can pack its data tensor along its batch dimension and one dynamic dimension and store a separate tensor of offsets. This type will be natively compatible with cuDNN RNNs and saves memory relative to MaskedBatch, but will be slower for some operations.

matchbox's People

Contributors

goldsborough avatar jekbradbury avatar svc-scm avatar vlasenkov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

matchbox's Issues

inner() missing 1 required positional argument: 'batch2'

Hi! I'm trying to run a bidirectional GRU with masking. This fails with an error. Is it a bug?

The full script:

import torch
from torch import nn
import matchbox
from matchbox import MaskedBatch
import numpy as np

class BiGRU(nn.Module):
    
    def __init__(self, in_size, out_size):
        super().__init__()
        self.fcell = nn.GRUCell(in_size, out_size)
        self.rcell = nn.GRUCell(in_size, out_size)

    @matchbox.batch
    def forward(self, x, h0=None):
        hf = x.batch_zeros(self.fcell.hidden_size) if h0 is None else h0
        for xt in x.unbind(1):
            hf = self.fcell(xt, hf)
        hr = x.batch_zeros(self.rcell.hidden_size) if h0 is None else h0
        for xt in reversed(x.unbind(1)):
            hr = self.rcell(xt, hr)
        return hf, hr

model = BiGRU(4, 5)

x = MaskedBatch(
    data=torch.rand((2, 3, 4)),
    dims=(True, False),
    mask=torch.tensor([
        [1, 1, 1],
        [1, 1, 0],
    ], dtype=torch.float32)[:, :, np.newaxis]
) 

model(x)

Traceback:

Traceback (most recent call last):
  File "test.py", line 49, in <module>
    model(x)
  File ".../dl/lib/python3.6/site-packages/torch/nn/modules/module.py", line 491, in __call__
    result = self.forward(*input, **kwargs)
  File "/tmp/tmpnu655rxr/matchbox_618c.py", line 10, in forward
    matchbox.MaskedBatch, matchbox.TENSOR_TYPE)) else self.fcell(xt, hf
  File ".../dl/lib/python3.6/site-packages/torch/nn/modules/module.py", line 491, in __call__
    result = self.forward(*input, **kwargs)
  File ".../dl/lib/python3.6/site-packages/torch/nn/modules/rnn.py", line 763, in forward
    self.bias_ih, self.bias_hh,
  File ".../dl/lib/python3.6/site-packages/torch/nn/_functions/rnn.py", line 64, in GRUCell
    hy = newgate + inputgate * (hidden - newgate)
  File ".../matchbox/matchbox/functional/elementwise.py", line85, in inner
    return replacement(self, other)
  File ".../matchbox/matchbox/functional/elementwise.py", line90, in <lambda>
    TENSOR_TYPE.__sub__ = _inject_arith(TENSOR_TYPE.__sub__, lambda a, b: -b + a)
TypeError: inner() missing 1 required positional argument: 'batch2'

Cloned the repo from master. Commit hash: d8ec789.

Exception in RNN example from readme.md

Hi. I'm trying to replicate basic RNN example from readme, but it crashes with Attribute error. Stack trace:
Traceback (most recent call last): File "/home/gei/App/pycharm-2017.3.3/helpers/pydev/pydevd.py", line 1668, in <module> main() File "/home/gei/App/pycharm-2017.3.3/helpers/pydev/pydevd.py", line 1662, in main globals = debugger.run(setup['file'], None, None, is_module) File "/home/gei/App/pycharm-2017.3.3/helpers/pydev/pydevd.py", line 1072, in run pydev_imports.execfile(file, globals, locals) # execute the script File "/home/gei/App/pycharm-2017.3.3/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile exec(compile(contents+"\n", file, 'exec'), glob, loc) File "/home/gei/projects/dynamic-rnn-pytorch/tets.py", line 7, in <module> class RNN(nn.Module): File "/home/gei/projects/dynamic-rnn-pytorch/tets.py", line 12, in RNN @matchbox.batch File "/home/gei/miniconda3/envs/pytorch-dev/lib/python3.6/site-packages/matchbox-0.1.0-py3.6.egg/matchbox/macro.py", line 92, in batch node = LoopAccumulation().visit(node) File "/home/gei/miniconda3/envs/pytorch-dev/lib/python3.6/ast.py", line 253, in visit return visitor(node) File "/home/gei/miniconda3/envs/pytorch-dev/lib/python3.6/site-packages/matchbox-0.1.0-py3.6.egg/matchbox/macro.py", line 48, in visit_FunctionDef node.decorator_list = [d for d in node.decorator_list File "/home/gei/miniconda3/envs/pytorch-dev/lib/python3.6/site-packages/matchbox-0.1.0-py3.6.egg/matchbox/macro.py", line 49, in <listcomp> if d.id != 'batch'] AttributeError: 'Attribute' object has no attribute 'id'

Running GRUCell on GPU

Tried to forward a MaskedBatch through a GRUCell on GPU. Got the following:

Traceback (most recent call last):
  File "bi_gru_test.py", line 110, in <module>
    res = model(x)
  File ".../dl/lib/python3.6/site-packages/torch/nn/modules/module.py", line 491, in __call__
    result = self.forward(*input, **kwargs)
  File "/tmp/tmp7bd5sts_/matchbox_572f.py", line 9, in forward
    matchbox.MaskedBatch, matchbox.TENSOR_TYPE)) else self.fcell(xt, hf
  File ".../dl/lib/python3.6/site-packages/torch/nn/modules/module.py", line 491, in __call__
    result = self.forward(*input, **kwargs)
  File ".../dl/lib/python3.6/site-packages/torch/nn/modules/rnn.py", line 763, in forward
    self.bias_ih, self.bias_hh,
  File ".../dl/lib/python3.6/site-packages/torch/nn/_functions/rnn.py", line 54, in GRUCell
    return state(gi, gh, hidden) if b_ih is None else state(gi, gh, hidden, b_ih, b_hh)
  File ".../dl/lib/python3.6/site-packages/torch/nn/_functions/thnn/rnnFusedPointwise.py", line 24, in forward
    input_gate, hidden_gate, ibias, hbias, hx, hy, workspace)
TypeError: CudaGRUFused_updateOutput received an invalid combination of arguments - got (int, MaskedBatch, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor), but expected (int state, torch.cuda.FloatTensor input, torch.cuda.FloatTensor hidden, [torch.cuda.FloatTensor bias1 or None], [torch.cuda.FloatTensor bias2 or None], torch.cuda.FloatTensor hx, torch.cuda.FloatTensor hy, torch.cuda.FloatTensor storage)

Does it mean that matchbox requires another implementation of GRU for GPU? Is there some workarond?

MaskedBatch lacks type() method

I've got the following error:

      File ".../dl/lib/python3.6/site-packages/torch/nn/_functions/thnn/rnnFusedPointwise.py", line 9, in forward
    ctx.backend = type2backend[input_gate.type()]
AttributeError: 'MaskedBatch' object has no attribute 'type'

MaskedBatch lacks type() method

[Error] name 'matchbox' is not defined

Hi! I just following the tutorial from README and encounter this error

Traceback (most recent call last):
  File "/Users/akurniawan/Workspace/open-sources/bahasanet/test.py", line 9, in <module>
    class RNN(nn.Module):
  File "/Users/akurniawan/Workspace/open-sources/bahasanet/test.py", line 14, in RNN
    @matchbox.batch
  File "/Users/akurniawan/anaconda3/envs/experiment/lib/python3.6/site-packages/matchbox/macro.py", line 99, in batch
    return compile_function(node, fn.__globals__)
  File "/Users/akurniawan/anaconda3/envs/experiment/lib/python3.6/site-packages/matchbox/recompile/compile_function.py", line 83, in compile_function
    module = compile_file(node, globals_)
  File "/Users/akurniawan/anaconda3/envs/experiment/lib/python3.6/site-packages/matchbox/recompile/compile_function.py", line 58, in compile_file
    spec.loader.exec_module(m)
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/var/folders/nv/y8wxb1994wzb91b7r30lv5jw0000gn/T/tmpdwazarr1/matchbox_9b26.py", line 1, in <module>
    @matchbox.batch
NameError: name 'matchbox' is not defined

I am using python3.6 and pytorch 0.3.1 to run the code.

import matchbox
import matchbox.functional as F
import torch
from matchbox import MaskedBatch
from random import randint
from torch import nn


class RNN(nn.Module):
    def __init__(self, size):
        super().__init__()
        self.cell = nn.RNNCell(size, size)

    @matchbox.batch
    def forward(self, x):
        h = x.new_zeros(x.size(0), x.size(-1))
        for xt in x.unbind(1):
            h = self.fwd(xt, h)
        return h


b, t, c = 32, 10, 128
model = RNN(t)
x_unbatched = torch.rand(1, randint(1, t), c)  # a single random example
x_manual_batch = MaskedBatch.fromlist(
    [torch.rand(1, randint(1, t), c) for i in range(b)],  # list of examples
    (True, False))  # dimension 1 is dynamic and dimension 2 is static
h = model(x_unbatched)
h = model(x_manual_batch)

adding "from matchbox import functional as F" casue error

I am modifying seq2seq codes ( https://github.com/keon/seq2seq). It is very weird that when replace "from torch.nn import functional as F" using "from matchbox import functional as F" in train.py, error occurs. Removing "from torch.nn import functional as F", the code run successfully.
pytorch 0.4.0
File "/tmp/batchlatticelstm/bak/model.py", line 102, in forward
encoder_output, hidden = self.encoder(src)
File "/home/wds/anaconda3/envs/pytorch_0.4/lib/python3.6/site-packages/torch/nn/modules/module.py", line 491, in call
result = self.forward(*input, **kwargs)
File "/tmp/batchlatticelstm/bak/model.py", line 24, in forward
outputs, hidden = self.gru(embedded, hidden)
File "/home/wds/anaconda3/envs/pytorch_0.4/lib/python3.6/site-packages/torch/nn/modules/module.py", line 491, in call
result = self.forward(*input, **kwargs)
File "/home/wds/anaconda3/envs/pytorch_0.4/lib/python3.6/site-packages/torch/nn/modules/rnn.py", line 166, in forward
requires_grad=False)
TypeError: inner() got an unexpected keyword argument 'requires_grad'

Benchmarks for this?

Wondering if there any benchmarks on this. Does this add (or reduce) any overhead in computation time over normal control flow?

Convolutions

Fully general support will be difficult, but supporting convolutions along static dimensions is easy and supporting 1D conv along a single dynamic dimension shouldn't be too difficult either.

From Slack:
"Pytorcher [12:14 PM]
Yes, l'm doing 1d convolution along the same axis [that has dynamic length].
l'm doing graph classification; each graph has variable number of nodes and each node has 3 values.
For instance graph 1 has 18 nodes, each 3 values: graph_1=dim(18,3) graph_2=(78,3)
l do 1d convolution at each dimension separately"

Can't set attributes of built-in/extension type `torch.Tensor`

I'm running into an error when running any of your tests:

Traceback (most recent call last):
  File "test_control_flow.py", line 10, in <module>
    import matchbox
  File "/Users/psag/home/pytorch/pytorch/env/lib/python3.6/site-packages/matchbox-0.1.0-py3.6.egg/matchbox/__init__.py", line 93, in <module>
    from . import functional
  File "/Users/psag/home/pytorch/pytorch/env/lib/python3.6/site-packages/matchbox-0.1.0-py3.6.egg/matchbox/functional/__init__.py", line 9, in <module>
    from .nnet import dropout, linear, embedding, softmax, cross_entropy
  File "/Users/psag/home/pytorch/pytorch/env/lib/python3.6/site-packages/matchbox-0.1.0-py3.6.egg/matchbox/functional/nnet.py", line 20, in <module>
    TENSOR_TYPE.dropout = dropout
TypeError: can't set attributes of built-in/extension type 'torch.Tensor'

I'm using a fresh install of PyTorch master, installed astor and gast with pip and then ran python setup.py install to install matchbox. Any ideas what I'm doing wrong? Is this supposed to work?

Four undefined names

flake8 testing of https://github.com/salesforce/matchbox on Python 3.6.3

$ flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics

./matchbox/compat.py:33:16: F821 undefined name 'functional'
        return functional.embedding(
               ^
./matchbox/data.py:49:64: F821 undefined name 'six'
            batch = [numericalization_func(x) if isinstance(x, six.string_types)
                                                               ^
./matchbox/functional/tensor_shape.py:99:54: F821 undefined name 'args'
                               for i in range(1, len(args)))
                                                     ^
./matchbox/functional/tensor_shape.py:101:55: F821 undefined name 'args'
    dims = tuple(sizes[i] == -1 for i in range(1, len(args)))
                                                      ^
4     F821 undefined name 'functional'

Error while running example

It seems the following line hasn't followed the new API convention in the new version of torchtext?

def process(self, batch, device, train):

In new torchtext API, there is no train param anymore in def process and postprocessing.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.