Error while creating train transform using torch vision

Viewed 1107

I am using torch vision to create the following train transformation. I do not understand what's wrong and how I can fix it?

train_transform = torch.nn.Sequential(
    transforms.ToTensor(), 
    transforms.RandomApply([
         transforms.RandomApply([transforms.RandomRotation(15)], 0.6),
         transforms.RandomApply([transforms.RandomAffine(0, shear=0.2)], 0.3),
         transforms.RandomApply([transforms.RandomResizedCrop(size = 224, scale=(0.8, 1.4))], 0.7)
        ], p= 0.5)
)

This is the error:

TypeError Traceback (most recent call last) in () 5 transforms.RandomApply([transforms.RandomAffine(0, shear=0.2)], 0.3), 6 transforms.RandomApply([transforms.RandomResizedCrop(size = 224, scale=(0.8, 1.4))], 0.7) ----> 7 ], p= 0.5) 8 )

1 frames /usr/local/lib/python3.7/dist-packages/torch/nn/modules/module.py in add_module(self, name, module) 371 if not isinstance(module, Module) and module is not None: 372 raise TypeError("{} is not a Module subclass".format( --> 373 torch.typename(module))) 374 elif not isinstance(name, torch._six.string_classes): 375 raise TypeError("module name should be a string. Got {}".format(

TypeError: torchvision.transforms.transforms.ToTensor is not a Module subclass

1 Answers

torch.nn.Sequential scripts your transformations. You can only use scriptable transformations in torch.nn.Sequential and transforms.ToTensor() is not a scriptable transformation. A scriptable transformation only takes a Tensor as an input. This is why you cannot use transforms.ToTensor() in the torch.nn.Sequential function, as you take an some data as input and transform it to a tensor.

You can find this information in the PyTorch documentation here under Scriptable Transformations. The transforms.RandomApply transformation is also not scriptable but it says here in the transforms.visions docu how to use it in a scriptable way anyway (see the definition of the class RandomApply). In the source code it says: " In order to script the transformation, please use torch.nn.ModuleList as input instead of list/tuple of transforms"

You have to wrap all your random transformations with torch.nn.ModuleList.

my_transforms = transforms.RandomApply(torch.nn.ModuleList([
     transforms.RandomApply( torch.nn.ModuleList([ transforms.RandomRotation(15)]), p=0.6),
     transforms.RandomApply( torch.nn.ModuleList([ transforms.RandomAffine(0, shear=0.2)]), p=0.3),
     transforms.RandomApply( torch.nn.ModuleList([ transforms.RandomResizedCrop(size = 224, scale=(0.8, 1.4))]), p=0.7),

 ]), p=0.3)
scripted_transforms = torch.jit.script(my_transforms)

If you want to, you can also wrap these transformations with torch.nn.Sequential but it is not necessary. Then the code should be this:

my_transforms = torch.nn.Sequential(transforms.RandomApply(torch.nn.ModuleList([
     transforms.RandomApply( torch.nn.ModuleList([ transforms.RandomRotation(15)]), p=0.6),
     transforms.RandomApply( torch.nn.ModuleList([ transforms.RandomAffine(0, shear=0.2)]), p=0.3),
     transforms.RandomApply( torch.nn.ModuleList([ transforms.RandomResizedCrop(size = 224, scale=(0.8, 1.4))]), p=0.7),

 ]), p=0.3))
scripted_transforms = torch.jit.script(my_transforms)

As mentioned, the tensor transformation cannot be added to this, since it is not scriptable.

Another solution is to use the transforms.Compose instead of torch.nn.Sequential like this:

from torchvision import transforms
train_transform = transforms.Compose([
    transforms.ToTensor(), 
    transforms.RandomApply([
         transforms.RandomApply([transforms.RandomRotation(15)], 0.6),
         transforms.RandomApply([transforms.RandomAffine(0, shear=0.2)], 0.3),
         transforms.RandomApply([transforms.RandomResizedCrop(size = 224, scale=(0.8, 1.4))], 0.7)
        ], p= 0.5)
])

In both cases the complete list of transformations will be randomly applied to your data, exactly in the order as you have specified the transformations in the list. You can see this from the source code of Random.Apply, where the forward pass looks like this

def forward(self, img):
        if self.p < torch.rand(1):
            return img
        for t in self.transforms:
            img = t(img)
        return img

The function loops over all transformations t in self.transforms exactly in the order you have specified in the list. So technically it is not necessary that you use torch.nn.Sequential.

Related