How to get a 5 dimensional input with a 3D array tensor?

username: nestlee
cooked: <p>I want to test if my computer can train a 3D array with 3D UNET.<br>
I want to test it by using tensor.zeros of shape (64,192,192) like input to my model. The code (uncomplete) is the following :</p>
<p>array_3D = torch.zeros(64, 192, 192)<br>
model = UNet()<br>
loss_function = DiceLoss()<br>
optimizer = optim.Adam(model.parameters(), lr=1e-4)<br>
epochs = 10</p>
<p>for epoch in range(epochs):</p>
<pre><code>batch_size = 1
model.train()

for data in range(1):
    batch_train_source_numpy = array_3D
    batch_train_logits_cuda = model(batch_train_source_numpy)
</code></pre>
<p>but then I get this error :</p>
<p>RuntimeError: Expected 5-dimensional input for 5-dimensional weight [16, 1, 3, 3, 3], but got 3-dimensional input of size [64, 192, 192] instead</p>
<p>how can I put a batch size and how many channels should I have to get a 5dimensionnal input ?</p>

username: Andrei_Cristea
cooked: <p>Hi nestlee,</p>
<p>Cannot reproduce without seeing how you defined UNet and DiceLoss, but I’m going to guess that the model expects an input shaped like: (N, C, H, W, D) where:</p>
<ul>
<li>N = minibatch size</li>
<li>C = number of channels, 3 if image is expected to be RGB, 1 if black-and-white</li>
<li>H, W, D = height, width, depth – <strong>note that these could be in some other order, look up your model’s documentation to be sure</strong>
</li>
</ul>
<p>However, your input passed was just shaped (H, W, D). So you probably need to unsqueeze along the first (minibatch dimension) and add an extra channel. Something like:</p>
<pre><code class="lang-auto">array_3D = torch.zeros(3, 64, 192, 192)
...
batch_train_source_numpy = array_3D.view(-1, 3, 64, 192, 192)
</code></pre>
<p>Hope this helps!</p>

username: nestlee
cooked: <p>Hi Andrei_Cristea</p>
<p>I have Black and White images<br>
here is the code for the 3D UNET :</p>
<pre><code>  def double_conv(in_channels, out_channels):
      conv = nn.Sequential(
          nn.Conv3d(in_channels,out_channels, kernel_size=3, padding=1),
          nn.ReLU(inplace=True),
          nn.Conv3d(out_channels, out_channels, kernel_size=3, padding=1),
          nn.ReLU(inplace=True)
      )
      return conv
  
  def crop_image(tensor, target_tensor):
      target_size = target_tensor.size()[2]
      tensor_size = tensor.size()[2]
      delta = tensor_size - target_size
      delta = delta//2
      return tensor[:, :, delta:tensor_size-delta, delta:tensor_size-delta]
  
  class UNet(nn.Module):
      def __init__(self):
          super(UNet, self).__init__()
  
          self.max_pool_2x2_1 = nn.MaxPool3d(kernel_size=2, stride=2)
          self.max_pool_2x2_2 = nn.MaxPool3d(kernel_size=2, stride=2)
          self.max_pool_2x2_3 = nn.MaxPool3d(kernel_size=2, stride=2)
          self.max_pool_2x2_4 = nn.MaxPool3d(kernel_size=2, stride=2)
          self.max_pool_2x2_5 = nn.MaxPool3d(kernel_size=2, stride=2)
          self.down_conv_1 = double_conv(1, 16)
          self.down_conv_2 = double_conv(16, 32)
          self.down_conv_3 = double_conv(32, 64)
          self.down_conv_4 = double_conv(64, 128)
          self.down_conv_5 = double_conv(128, 256)
  
          self.up_conv_trans2 = nn.ConvTranspose3d(in_channels=256, out_channels=128, kernel_size=2, stride=2)
          self.up_conv_2 = double_conv(256, 128)
  
          self.up_conv_trans3 = nn.ConvTranspose3d(in_channels=128, out_channels=64, kernel_size=2, stride=2)
          self.up_conv_3 = double_conv(128, 64)
  
          self.up_conv_trans4 = nn.ConvTranspose3d(in_channels=64, out_channels=32, kernel_size=2, stride=2)
          self.up_conv_4 = double_conv(64, 32)
  
          self.up_conv_trans5 = nn.ConvTranspose3d(in_channels=32, out_channels=16, kernel_size=2, stride=2)
          self.up_conv_5 = double_conv(32, 16)
  
  
          self.out = nn.Conv3d(in_channels=16, out_channels=1, kernel_size=1)
  
  
      def forward(self, image):
          out_conv1 = self.down_conv_1(image)
          out_pool1 = self.max_pool_2x2_1(out_conv1)
          out_conv2 = self.down_conv_2(out_pool1)
          out_pool2 = self.max_pool_2x2_2(out_conv2)
          out_conv3 = self.down_conv_3(out_pool2)
          out_pool3 = self.max_pool_2x2_3(out_conv3)
          out_conv4 = self.down_conv_4(out_pool3)
          out_pool4 = self.max_pool_2x2_4(out_conv4)
          out_conv5 = self.down_conv_5(out_pool4)
  
          #decoder part
          out_up_conv = self.up_conv_trans2(out_conv5)
  
          y = crop_image(out_conv4, out_up_conv)
          out_up_conv = self.up_conv_2(torch.cat([out_up_conv, y], 1))
  
          out_up_conv = self.up_conv_trans3(out_up_conv)
          y = crop_image(out_conv3, out_up_conv)
          out_up_conv = self.up_conv_3(torch.cat([out_up_conv, y], 1))
  
          out_up_conv = self.up_conv_trans4(out_up_conv)
          y = crop_image(out_conv2, out_up_conv)
          out_up_conv = self.up_conv_4(torch.cat([out_up_conv, y], 1))
  
          out_up_conv = self.up_conv_trans5(out_up_conv)
          y = crop_image(out_conv1, out_up_conv)
          out_up_conv = self.up_conv_5(torch.cat([out_up_conv, y], 1))
  
  
          out_up_conv = self.out(out_up_conv)
          return out_up_conv</code></pre>

username: nestlee
cooked: <p>Hi Andrei_Cristea,</p>
<p>I have black and white images</p>
<p>3D UNET :</p>
<pre><code>  def double_conv(in_channels, out_channels):
      conv = nn.Sequential(
          nn.Conv3d(in_channels,out_channels, kernel_size=3, padding=1),
          nn.ReLU(inplace=True),
          nn.Conv3d(out_channels, out_channels, kernel_size=3, padding=1),
          nn.ReLU(inplace=True)
      )
      return conv
  
  def crop_image(tensor, target_tensor):
      target_size = target_tensor.size()[2]
      tensor_size = tensor.size()[2]
      delta = tensor_size - target_size
      delta = delta//2
      return tensor[:, :, delta:tensor_size-delta, delta:tensor_size-delta]
  
  class UNet(nn.Module):
      def __init__(self):
          super(UNet, self).__init__()
  
          #definition of max pooling :
          self.max_pool_2x2_1 = nn.MaxPool3d(kernel_size=2, stride=2)
          self.max_pool_2x2_2 = nn.MaxPool3d(kernel_size=2, stride=2)
          self.max_pool_2x2_3 = nn.MaxPool3d(kernel_size=2, stride=2)
          self.max_pool_2x2_4 = nn.MaxPool3d(kernel_size=2, stride=2)
          self.max_pool_2x2_5 = nn.MaxPool3d(kernel_size=2, stride=2)
          self.down_conv_1 = double_conv(1, 16)
          self.down_conv_2 = double_conv(16, 32)
          self.down_conv_3 = double_conv(32, 64)
          self.down_conv_4 = double_conv(64, 128)
          self.down_conv_5 = double_conv(128, 256)
          #self.down_conv_6 = double_conv(256, 512)
  
          self.up_conv_trans2 = nn.ConvTranspose3d(in_channels=256, out_channels=128, kernel_size=2, stride=2)
          self.up_conv_2 = double_conv(256, 128)
  
          self.up_conv_trans3 = nn.ConvTranspose3d(in_channels=128, out_channels=64, kernel_size=2, stride=2)
          self.up_conv_3 = double_conv(128, 64)
  
          self.up_conv_trans4 = nn.ConvTranspose3d(in_channels=64, out_channels=32, kernel_size=2, stride=2)
          self.up_conv_4 = double_conv(64, 32)
  
          self.up_conv_trans5 = nn.ConvTranspose3d(in_channels=32, out_channels=16, kernel_size=2, stride=2)
          self.up_conv_5 = double_conv(32, 16)
  
  
          self.out = nn.Conv3d(in_channels=16, out_channels=1, kernel_size=1)
  
  
      def forward(self, image):
          #encoder part
          out_conv1 = self.down_conv_1(image)
          out_pool1 = self.max_pool_2x2_1(out_conv1)
          out_conv2 = self.down_conv_2(out_pool1)
          out_pool2 = self.max_pool_2x2_2(out_conv2)
          out_conv3 = self.down_conv_3(out_pool2)
          out_pool3 = self.max_pool_2x2_3(out_conv3)
          out_conv4 = self.down_conv_4(out_pool3)
          out_pool4 = self.max_pool_2x2_4(out_conv4)
          out_conv5 = self.down_conv_5(out_pool4)
          # out_pool5 = self.max_pool_2x2_5(out_conv5)
          # out_conv6 = self.down_conv_6(out_pool5)
          #print(f"outconv6 = {out_conv6.size()}")
  
          #decoder part
          out_up_conv = self.up_conv_trans2(out_conv5)
  
          y = crop_image(out_conv4, out_up_conv)
          #print(y.size())
          out_up_conv = self.up_conv_2(torch.cat([out_up_conv, y], 1))
  
          out_up_conv = self.up_conv_trans3(out_up_conv)
          y = crop_image(out_conv3, out_up_conv)
          out_up_conv = self.up_conv_3(torch.cat([out_up_conv, y], 1))
  
          out_up_conv = self.up_conv_trans4(out_up_conv)
          y = crop_image(out_conv2, out_up_conv)
          out_up_conv = self.up_conv_4(torch.cat([out_up_conv, y], 1))
  
          out_up_conv = self.up_conv_trans5(out_up_conv)
          y = crop_image(out_conv1, out_up_conv)
          out_up_conv = self.up_conv_5(torch.cat([out_up_conv, y], 1))
  
          out_up_conv = self.out(out_up_conv)
          return out_up_conv
</code></pre>
<p>and my dice loss function :</p>
<pre><code>  class DiceLoss(nn.Module):
      def __init__(self, weight=None, size_average=True):
          super(DiceLoss, self).__init__()
  
      def forward(self, inputs, targets):
  
          inputs = torch.sigmoid(inputs)
  
          # flatten label and prediction tensors
          inputs = inputs.view(-1)
          targets = targets.view(-1)
  
          intersection = (inputs * targets).sum()
          dice = (2. * intersection) / (inputs.sum() + targets.sum())
  
          return 1 - dice
</code></pre>
<p>I tried your suggestion but I get this error :</p>
<pre><code>RuntimeError: Sizes of tensors must match except in dimension 1. Expected size 24 but got size 8 for tensor number 1 in the list.
</code></pre>
<p>what sould I do ?<br>
Thank you for your help</p>

username: Andrei_Cristea
cooked: <p>Your model expects an input where the lengths of the 3 dimensions are equal. This works for me:</p>
<pre><code class="lang-auto">array_3D = torch.zeros(1, 192, 192, 192)
batch_train_source_numpy = array_3D.view(-1, 1, 192, 192, 192)
model = UNet()
print(model(batch_train_source_numpy))
</code></pre>

username: KFrank
cooked: <p>Hi Nestlee!</p>
<aside class="quote no-group" data-username="nestlee" data-post="4" data-topic="148257" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/n/9e8a1a/48.png" class="avatar"> nestlee:</div>
<blockquote>
<pre data-code-wrap="python"><code class="lang-python">      def crop_image(tensor, target_tensor):
          target_size = target_tensor.size()[2]
          tensor_size = tensor.size()[2]
          delta = tensor_size - target_size
          delta = delta//2
          return tensor[:, :, delta:tensor_size-delta, delta:tensor_size-delta]
</code></pre>
</blockquote>
</aside>
<p>Andrei’s observation that your model expects three equal dimensions<br>
is partially correct, but your <code>crop_image()</code> has additional problems.</p>
<p><code>crop_image()</code> does crop the first and second dimensions of your<br>
three-dimensional “image” to the same size, and if the two uncropped<br>
dimensions differ, it will crop the second dimension incorrectly.</p>
<p>Furthermore, it doesn’t crop the third dimension of your “image” (which<br>
is the fifth dimension of your 5d input) at all.</p>
<p>Lastly, <code>delta = delta//2</code> rounds down, so if <code>delta</code> (before dividing) is<br>
only 1, you won’t crop at all, when you should probably crop one pixel<br>
from one side of the “image” and none from the other.</p>
<p>All three of these issues can cause cropping to not work correctly so that<br>
your cropped upstream “image” and downstream image won’t have the<br>
same shape and <code>torch.cat([out_up_conv, y]</code> will fail with the error<br>
message you posted.</p>
<p>Andrei’s test case of <code>array_3D = torch.zeros(1, 192, 192, 192)</code><br>
worked because every downsampling (<code>MaxPool3d</code>) happens to get fed<br>
a tensor whose dimensions are divisible by 2, so that no actual cropping<br>
ever had to happen.</p>
<p>Try running your model on examples such as:</p>
<pre data-code-wrap="python"><code class="lang-python">batch_array_3D = torch.zeros (1, 1, 190, 190, 190)
# or
batch_array_3D = torch.zeros (1, 1, 192, 192, 196)
</code></pre>
<p>Best.</p>
<p>K. Frank</p>