1

My code is like below.

import numpy as np
import torch
import torch.nn as nn
import cupy as cp
from torchviz import make_dot
from torchinfo import summary
from torchsummary import summary as summary_
    
def get_filter_torch(*args, **kwargs):
    
    class TraversabilityFilter(nn.Module):
        def __init__(self, w1, w2, w3, w_out, device="cuda", use_bias=False):
            super(TraversabilityFilter, self).__init__()
            self.conv1 = nn.Conv2d(1, 4, 3, dilation=1, padding=0, bias=use_bias)
            self.conv2 = nn.Conv2d(1, 4, 3, dilation=2, padding=0, bias=use_bias)
            self.conv3 = nn.Conv2d(1, 4, 3, dilation=3, padding=0, bias=use_bias)
            self.conv_out = nn.Conv2d(12, 1, 1, bias=use_bias)

            # Set weights.
            self.conv1.weight = nn.Parameter(torch.from_numpy(w1).float())
            self.conv2.weight = nn.Parameter(torch.from_numpy(w2).float())
            self.conv3.weight = nn.Parameter(torch.from_numpy(w3).float())
            self.conv_out.weight = nn.Parameter(torch.from_numpy(w_out).float())

        def __call__(self, elevation_cupy):
            # Convert cupy tensor to pytorch.
            elevation_cupy = elevation_cupy.astype(cp.float32)
            elevation = torch.as_tensor(elevation_cupy, device=self.conv1.weight.device)
            print("input: ",elevation.shape)

            with torch.no_grad():
                out1 = self.conv1(elevation.view(-1, 1, elevation.shape[0], elevation.shape[1]))
                out2 = self.conv2(elevation.view(-1, 1, elevation.shape[0], elevation.shape[1]))
                out3 = self.conv3(elevation.view(-1, 1, elevation.shape[0], elevation.shape[1]))

                out1 = out1[:, :, 2:-2, 2:-2]
                out2 = out2[:, :, 1:-1, 1:-1]
                out = torch.cat((out1, out2, out3), dim=1)
                out = self.conv_out(out.abs())
                out = torch.exp(-out)
                print("output: ",out.shape)

            return out

    traversability_filter = TraversabilityFilter(*args, **kwargs).cuda().eval()
    return traversability_filter
   

# Define the weight values (you need to provide actual weight values)
w1 = np.random.randn(4, 1, 3, 3)  # Shape: (out_channels, in_channels, kernel_height, kernel_width)
w2 = np.random.randn(4, 1, 3, 3)  # Shape: (out_channels, in_channels, kernel_height, kernel_width)
w3 = np.random.randn(4, 1, 3, 3)  # Shape: (out_channels, in_channels, kernel_height, kernel_width)
w_out = np.random.randn(1, 12, 1, 1)  # Shape: (out_channels, in_channels, kernel_height, kernel_width)

model = get_filter_torch(w1, w2, w3, w_out)

cell_n = 200
x = cp.random.randn(cell_n, cell_n, dtype=cp.float32)
output = model(x)
print(model)

input_size=(200,200)
summary(model)
summary_(model, input_size)

When I run the code, the result is like below.

input:  torch.Size([200, 200])
output:  torch.Size([1, 1, 194, 194])
TraversabilityFilter(
  (conv1): Conv2d(1, 4, kernel_size=(3, 3), stride=(1, 1), bias=False)
  (conv2): Conv2d(1, 4, kernel_size=(3, 3), stride=(1, 1), dilation=(2, 2), bias=False)
  (conv3): Conv2d(1, 4, kernel_size=(3, 3), stride=(1, 1), dilation=(3, 3), bias=False)
  (conv_out): Conv2d(12, 1, kernel_size=(1, 1), stride=(1, 1), bias=False)
)
=================================================================
Layer (type:depth-idx)                   Param #
=================================================================
TraversabilityFilter                     --
├─Conv2d: 1-1                            36
├─Conv2d: 1-2                            36
├─Conv2d: 1-3                            36
├─Conv2d: 1-4                            12
=================================================================
Total params: 120
Trainable params: 120
Non-trainable params: 0
=================================================================
input:  torch.Size([2, 200, 200])
Traceback (most recent call last):
  File "/home/Documents/relay/temp/visual.py", line 72, in <module>
    summary_(model, input_size)
  File "/home/Documents/2levation_ws/venv/myenv/lib/python3.8/site-packages/torchsummary/torchsummary.py", line 72, in summary
    model(*x)
  File "/home/Documents/relay/temp/visual.py", line 34, in __call__
    out1 = self.conv1(elevation.view(-1, 1, elevation.shape[0], elevation.shape[1]))
  File "/home/Documents/2levation_ws/venv/myenv/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1212, in _call_impl
    result = forward_call(*input, **kwargs)
  File "/home/Documents/2levation_ws/venv/myenv/lib/python3.8/site-packages/torch/nn/modules/conv.py", line 463, in forward
    return self._conv_forward(input, self.weight, self.bias)
  File "/home/Documents/2levation_ws/venv/myenv/lib/python3.8/site-packages/torch/nn/modules/conv.py", line 459, in _conv_forward
    return F.conv2d(input, weight, bias, self.stride,
RuntimeError: Calculated padded input size per channel: (2 x 200). Kernel size: (3 x 3). Kernel size can't be greater than actual input size

The error occurs because the input size specified in the summary function doesn't match the actual input size used when applying the model. Even though the input size during usage is (200, 200), specifying input_size=(1, 200, 200) still results in the same error. How can this be resolved? My pytorch version is like below.

torch==1.13.1+cu116
torchaudio==0.13.1+cu116
torchinfo==1.8.0
torchsummary==1.5.1
torchvision==0.14.1+cu116
torchviz==0.0.2

And I use python 3.8.10.

1 Answer 1

0

Torchsummary works by running your model on a sample input and observing the shapes of intermediate results. Torchsummary does this by creating a random tensor of (2, your_shape). That is, it takes the shape you suggested and prepends a dimension of size 2, so an input shape of (200, 200) becomes (2, 200, 200).

Also, note that while your model expects a cupy.ndarray as an input, Torchsummary will pass a torch.Tensor. This is perfectly fine, because by convention classes that inherit from nn.Module should accept torch.Tensors rather than other types.

Finally, elevation.view(-1, 1, elevation.shape[0], elevation.shape[1]) assumes that elevation.shape[0] and elevation.shape[1] will be your input_size, so (200, 200). This is a very strong assumption and it is better to assume that the last two dimensions will be (200, 200).

Here's a working version of your code but modified to take into account what I just described.

import numpy as np
import torch
import torch.nn as nn
import cupy as cp
from torchviz import make_dot
from torchinfo import summary
from torchsummary import summary as summary_


def get_filter_torch(*args, **kwargs):
    class TraversabilityFilter(nn.Module):
        def __init__(self, w1, w2, w3, w_out, device="cuda", use_bias=False):
            super(TraversabilityFilter, self).__init__()
            self.conv1 = nn.Conv2d(1, 4, 3, dilation=1, padding=0, bias=use_bias)
            self.conv2 = nn.Conv2d(1, 4, 3, dilation=2, padding=0, bias=use_bias)
            self.conv3 = nn.Conv2d(1, 4, 3, dilation=3, padding=0, bias=use_bias)
            self.conv_out = nn.Conv2d(12, 1, 1, bias=use_bias)

            # Set weights.
            self.conv1.weight = nn.Parameter(torch.from_numpy(w1).float())
            self.conv2.weight = nn.Parameter(torch.from_numpy(w2).float())
            self.conv3.weight = nn.Parameter(torch.from_numpy(w3).float())
            self.conv_out.weight = nn.Parameter(torch.from_numpy(w_out).float())

        def __call__(self, elevation_cupy):
            # Convert cupy tensor to pytorch IF NEEDED
            if isinstance(elevation_cupy, cp.ndarray):
                elevation_cupy = elevation_cupy.astype(cp.float32)
                elevation = torch.as_tensor(elevation_cupy, device=self.conv1.weight.device)
            elif isinstance(elevation_cupy, torch.Tensor):
                elevation = elevation_cupy
            else:
                raise TypeError()
            print("input: ", elevation.shape)
            # use last two axes 
            with torch.no_grad():
                out1 = self.conv1(elevation.view(-1, 1, elevation.shape[-2], elevation.shape[-1]))
                out2 = self.conv2(elevation.view(-1, 1, elevation.shape[-2], elevation.shape[-1]))
                out3 = self.conv3(elevation.view(-1, 1, elevation.shape[-2], elevation.shape[-1]))

                out1 = out1[:, :, 2:-2, 2:-2]
                out2 = out2[:, :, 1:-1, 1:-1]
                out = torch.cat((out1, out2, out3), dim=1)
                out = self.conv_out(out.abs())
                out = torch.exp(-out)
                print("output: ", out.shape)

            return out

    traversability_filter = TraversabilityFilter(*args, **kwargs).cuda().eval()
    return traversability_filter


# Define the weight values (you need to provide actual weight values)
w1 = np.random.randn(4, 1, 3, 3)  # Shape: (out_channels, in_channels, kernel_height, kernel_width)
w2 = np.random.randn(4, 1, 3, 3)  # Shape: (out_channels, in_channels, kernel_height, kernel_width)
w3 = np.random.randn(4, 1, 3, 3)  # Shape: (out_channels, in_channels, kernel_height, kernel_width)
w_out = np.random.randn(1, 12, 1, 1)  # Shape: (out_channels, in_channels, kernel_height, kernel_width)

model = get_filter_torch(w1, w2, w3, w_out)

cell_n = 200
x = cp.random.randn(cell_n, cell_n, dtype=cp.float32)
output = model(x)
print(model)

input_size = (200, 200)
summary(model)
summary_(model, input_size)
Sign up to request clarification or add additional context in comments.

1 Comment

Wow. I understood why it doesn't work. Thanks for your kind and detailed explanation!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.