Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature suggestion] Add a simple shape assertion in einops-style notation #168

Open
zplizzi opened this issue Jan 21, 2022 · 5 comments
Open

Comments

@zplizzi
Copy link

zplizzi commented Jan 21, 2022

I find that when I use einops, I end up with a mix of einops operations and regular asserts like this:

x = repeat(target, "b t h w c -> b t k h w c", k=K)
x = do_thing(x)
assert x.shape == (batch_size, T, H, W, C)

I love the einops notation, and I can remove shape assertions immediately before or after an einops operation. But when I don't need to do a reshape/repeat/etc, I have to fall back to the assert notation to check the shape. I like to include lots of shape asserts in general both to make sure I haven't accidentally included a bug, but also for improving readability, so the reader always knows the shape of tensors. Asserts are superior to comments, as they will fail if you forget to update them, ensuring that they're always accurate.

So I propose a new einops "operation", which does nothing except check shapes, and would raise an assertion error if the shape is incorrect. It would have a notation analogous to other einops operations:

from einops import check_shape
check_shape(x, "b t h w c", t=T, h=H, c=3)

This is preferable to the normal assert for a few reasons:

  • if we're already using einops, it's nice to have a standard notation format, rather than mixing two notation formats. It makes the code more readable.
  • i like the "b t h w c" style notation better than the assert-style notation, it allows you to give a "name" to each axis as opposed to just specifying its value.
  • this notation allows you to only check certain axes. eg I don't normally care to check the batch_size dim, but doing assert x.shape[1:] = (T, H, W, C) is kinda yucky - and worse for axes not at the beginning or end.
@arogozhnikov
Copy link
Owner

Hi @zplizzi,
I has similar thoughts, but I see no good reason why

assert x.shape == (batch_size, T, H, W, C)

is worse than

check_shape(x, "b t h w c", t=T, h=H, c=3)

As for skip dimensions, I'd prefer a function like

check_shape(x, [None, T, H, W, C])

... which does not require einops style

@zplizzi
Copy link
Author

zplizzi commented Jan 21, 2022

Well, supposing you only want to check a couple axes,

check_shape(x, "b t h w c", t=T)

is certainly more descriptive than
check_shape(x, [None, T, None, None, None])
or
assert x.shape[1] == T

Taken further, I could imagine just using
check_shape(x, "b t h w c")

which would just check that there are 5 axes, but primarily serves as documentation of the shape.

@zplizzi
Copy link
Author

zplizzi commented Jan 21, 2022

It also allows for usage like

shapes = parse_shape(x, "b t k h w c")
check_shape(y, "b t k h w", **shapes)
check_shape(z, "b t h w c", **shapes)

(depending on if we strictly enforced that all kwargs must exist in the shape specifier or not)

@sebastianjaszczur
Copy link

sebastianjaszczur commented Feb 7, 2022

I've personally found useful assert_shape as a decorator for a function/layer. Basically something like

@assert_shape('...a->...b')
class Dense(nn.Layer):
    (...)

The above means that Dense should have the same signature as einsum('...a->...b'). It should check that the last dimension is indeed of the same size etc.

I've implemented this idea in Trax, here (there are aksi sine examples and documentation): https://github.com/google/trax/blob/master/trax/layers/assert_shape.py
This is certainly implementable also in PyTorch; I'm not sure about other frameworks.

@nils-werner
Copy link

I didn't check the performance implications, but for now you may be able to get away with

def check_shape(tensor, pattern, **kwargs):
    return einops.rearrange(tensor, f"{pattern} -> {pattern}", **kwargs)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants