Welcome to our new Data Science resource site!

logo
Programming for Data Science
Multiple Channels
Initializing search
    • Home
    • Python
    • Data Collection and Visualization
    • Machine Learning
    • Deep Learning
    • Time Series
    • Maths & Statistics
    • Extras
    • About
    • Home
      • Overview
      • End of Course Exercise
        • Outline
        • Introduction
        • Variables
        • Numbers
        • Strings
        • Operators
        • Containers
        • Flow Control
        • Advanced
        • Modules
        • File Handling
        • End of Course Exercise
        • Filter Function
        • Map Function
        • Reduce Function
        • NumPy Crash Course
        • Pandas Crash Course
        • Matplotlib Crash Course
      • NumPy Crash Course
      • Pandas Crash Course
      • Weather Data
      • Matplotlib Crash Course
      • Data Exploration Exercise
      • Handling Missing Data
      • Overview
      • Training Models
        • Introduction
        • Advanced
        • Feature Selection
        • Why Scaling
        • Feature Scaling (FPL)
        • Normalization and Standardization
      • Handling Missing Data
        • Classification Metrics
        • Regression Metrics
        • Pipelines
        • Hyperparameter Tuning
      • Introduction
        • 1D Tensors
        • 2D Tensors
        • Derivatives & Graphs
        • Simple Datasets
        • Pre-Built Datasets
        • Exercise
        • 1D Regression
        • One Parameter
        • Slope & Bias
        • Exercise
        • SGD
        • Mini-Batch GD
        • PyTorch Way
        • Training & Validation
        • Exercise
        • Multiple LR Prediction
        • Multiple LR Training
        • Multi-Target LR
        • Training Multiple Output
        • Exercise
        • Prediction
        • MSE Issues
        • Cross Entropy
        • Softmax
        • Exercise
        • Custom Datasets
        • DataLoaders
        • Transforms
        • Simple Hidden Layer
        • this is for exercises
        • XOR Problem
        • MNIST
        • Activation Functions
        • MNIST One Layer
        • MNIST Two Layer
        • Multiclass Spiral
        • Dropout Prediction
        • Dropout Regression
        • Initialization
        • Xavier Init
        • He Init
        • Momentum
        • NN with Momentum
        • Batch Normalization
        • Convolution Basics
        • Activation & Pooling
        • Multiple Channels
        • Simple CNN
        • CNN Small Image
        • CNN Batch Processing
      • Introduction
      • Analysis
      • Forecasting
      • Python Example
      • Overview
      • Eigen Values and Vectors
      • Descriptive Statistics
      • Inferential Statistics
      • Statistical Models
      • Hypothesis Testing
      • Customer Analysis
      • How KNN Works
      • Handling Imbalanced Data
      • Classification Metrics
      • License
      • ReadMe

    author: Juma Shafara date: "2024-08-12" title: Multiple Channel Convolutional Network keywords: [Training Two Parameter, Mini-Batch Gradient Decent, Training Two Parameter Mini-Batch Gradient Decent] description: In this lab, you will learn two important components in building a convolutional neural network.¶

    Photo by DATAIDEA

    Objective for this Notebook

    1. Learn on Multiple Input and Multiple Output Channels.

    Table of Contents¶

    In this lab, you will study convolution and review how the different operations change the relationship between input and output.

  1. Multiple Output Channels
  2. Multiple Inputs
  3. Multiple Input and Multiple Output Channels
  4. Practice Questions

  5. Estimated Time Needed: 25 min

    Don't Miss Any Updates!

    Before we continue, I have a humble request, to be among the first to hear about future updates of the course materials, simply enter your email below, follow us on (formally Twitter), or subscribe to our YouTube channel.

    Import the following libraries:

    In [1]:
    Copied!
    import torch 
    import torch.nn as nn
    import matplotlib.pyplot as plt
    import numpy as np
    from scipy import ndimage, misc
    
    import torch import torch.nn as nn import matplotlib.pyplot as plt import numpy as np from scipy import ndimage, misc

    Multiple Output Channels

    In Pytroch, you can create a Conv2d object with multiple outputs. For each channel, a kernel is created, and each kernel performs a convolution independently. As a result, the number of outputs is equal to the number of channels. This is demonstrated in the following figure. The number 9 is convolved with three kernels: each of a different color. There are three different activation maps represented by the different colors.

    No description has been provided for this image

    Symbolically, this can be represented as follows:

    No description has been provided for this image

    Create a Conv2d with three channels:

    In [2]:
    Copied!
    conv1 = nn.Conv2d(in_channels=1, out_channels=3,kernel_size=3)
    
    conv1 = nn.Conv2d(in_channels=1, out_channels=3,kernel_size=3)

    Pytorch randomly assigns values to each kernel. However, use kernels that have been developed to detect edges:

    In [3]:
    Copied!
    Gx=torch.tensor([[1.0,0,-1.0],[2.0,0,-2.0],[1.0,0.0,-1.0]])
    Gy=torch.tensor([[1.0,2.0,1.0],[0.0,0.0,0.0],[-1.0,-2.0,-1.0]])
    
    conv1.state_dict()['weight'][0][0]=Gx
    conv1.state_dict()['weight'][1][0]=Gy
    conv1.state_dict()['weight'][2][0]=torch.ones(3,3)
    
    Gx=torch.tensor([[1.0,0,-1.0],[2.0,0,-2.0],[1.0,0.0,-1.0]]) Gy=torch.tensor([[1.0,2.0,1.0],[0.0,0.0,0.0],[-1.0,-2.0,-1.0]]) conv1.state_dict()['weight'][0][0]=Gx conv1.state_dict()['weight'][1][0]=Gy conv1.state_dict()['weight'][2][0]=torch.ones(3,3)

    Each kernel has its own bias, so set them all to zero:

    In [4]:
    Copied!
    conv1.state_dict()['bias'][:]=torch.tensor([0.0,0.0,0.0])
    conv1.state_dict()['bias']
    
    conv1.state_dict()['bias'][:]=torch.tensor([0.0,0.0,0.0]) conv1.state_dict()['bias']
    Out[4]:
    tensor([0., 0., 0.])

    Print out each kernel:

    In [5]:
    Copied!
    for x in conv1.state_dict()['weight']:
        print(x)
    
    for x in conv1.state_dict()['weight']: print(x)
    tensor([[[ 1.,  0., -1.],
             [ 2.,  0., -2.],
             [ 1.,  0., -1.]]])
    tensor([[[ 1.,  2.,  1.],
             [ 0.,  0.,  0.],
             [-1., -2., -1.]]])
    tensor([[[1., 1., 1.],
             [1., 1., 1.],
             [1., 1., 1.]]])
    

    Create an input image to represent the input X:

    In [6]:
    Copied!
    image=torch.zeros(1,1,5,5)
    image[0,0,:,2]=1
    image
    
    image=torch.zeros(1,1,5,5) image[0,0,:,2]=1 image
    Out[6]:
    tensor([[[[0., 0., 1., 0., 0.],
              [0., 0., 1., 0., 0.],
              [0., 0., 1., 0., 0.],
              [0., 0., 1., 0., 0.],
              [0., 0., 1., 0., 0.]]]])

    Plot it as an image:

    In [7]:
    Copied!
    plt.imshow(image[0,0,:,:].numpy(), interpolation='nearest', cmap=plt.cm.gray)
    plt.colorbar()
    plt.show()
    
    plt.imshow(image[0,0,:,:].numpy(), interpolation='nearest', cmap=plt.cm.gray) plt.colorbar() plt.show()
    No description has been provided for this image

    Perform convolution using each channel:

    In [8]:
    Copied!
    out=conv1(image)
    
    out=conv1(image)

    The result is a 1x3x3x3 tensor. This represents one sample with three channels, and each channel contains a 3x3 image. The same rules that govern the shape of each image were discussed in the last section.

    In [9]:
    Copied!
    out.shape
    
    out.shape
    Out[9]:
    torch.Size([1, 3, 3, 3])

    Print out each channel as a tensor or an image:

    In [10]:
    Copied!
    for channel,image in enumerate(out[0]):
        plt.imshow(image.detach().numpy(), interpolation='nearest', cmap=plt.cm.gray)
        print(image)
        plt.title("channel {}".format(channel))
        plt.colorbar()
        plt.show()
    
    for channel,image in enumerate(out[0]): plt.imshow(image.detach().numpy(), interpolation='nearest', cmap=plt.cm.gray) print(image) plt.title("channel {}".format(channel)) plt.colorbar() plt.show()
    tensor([[-4.,  0.,  4.],
            [-4.,  0.,  4.],
            [-4.,  0.,  4.]], grad_fn=<UnbindBackward0>)
    
    No description has been provided for this image
    tensor([[0., 0., 0.],
            [0., 0., 0.],
            [0., 0., 0.]], grad_fn=<UnbindBackward0>)
    
    No description has been provided for this image
    tensor([[3., 3., 3.],
            [3., 3., 3.],
            [3., 3., 3.]], grad_fn=<UnbindBackward0>)
    
    No description has been provided for this image

    Different kernels can be used to detect various features in an image. You can see that the first channel fluctuates, and the second two channels produce a constant value. The following figure summarizes the process:

    No description has been provided for this image

    If you use a different image, the result will be different:

    In [11]:
    Copied!
    image1=torch.zeros(1,1,5,5)
    image1[0,0,2,:]=1
    print(image1)
    plt.imshow(image1[0,0,:,:].detach().numpy(), interpolation='nearest', cmap=plt.cm.gray)
    plt.show()
    
    image1=torch.zeros(1,1,5,5) image1[0,0,2,:]=1 print(image1) plt.imshow(image1[0,0,:,:].detach().numpy(), interpolation='nearest', cmap=plt.cm.gray) plt.show()
    tensor([[[[0., 0., 0., 0., 0.],
              [0., 0., 0., 0., 0.],
              [1., 1., 1., 1., 1.],
              [0., 0., 0., 0., 0.],
              [0., 0., 0., 0., 0.]]]])
    
    No description has been provided for this image

    In this case, the second channel fluctuates, and the first and the third channels produce a constant value.

    In [12]:
    Copied!
    out1=conv1(image1)
    for channel,image in enumerate(out1[0]):
        plt.imshow(image.detach().numpy(), interpolation='nearest', cmap=plt.cm.gray)
        print(image)
        plt.title("channel {}".format(channel))
        plt.colorbar()
        plt.show()
    
    out1=conv1(image1) for channel,image in enumerate(out1[0]): plt.imshow(image.detach().numpy(), interpolation='nearest', cmap=plt.cm.gray) print(image) plt.title("channel {}".format(channel)) plt.colorbar() plt.show()
    tensor([[0., 0., 0.],
            [0., 0., 0.],
            [0., 0., 0.]], grad_fn=<UnbindBackward0>)
    
    No description has been provided for this image
    tensor([[-4., -4., -4.],
            [ 0.,  0.,  0.],
            [ 4.,  4.,  4.]], grad_fn=<UnbindBackward0>)
    
    No description has been provided for this image
    tensor([[3., 3., 3.],
            [3., 3., 3.],
            [3., 3., 3.]], grad_fn=<UnbindBackward0>)
    
    No description has been provided for this image

    The following figure summarizes the process:

    No description has been provided for this image

    Multiple Input Channels

    For two inputs, you can create two kernels. Each kernel performs a convolution on its associated input channel. The resulting output is added together as shown:

    No description has been provided for this image

    Create an input with two channels:

    In [13]:
    Copied!
    image2=torch.zeros(1,2,5,5)
    image2[0,0,2,:]=-2
    image2[0,1,2,:]=1
    image2
    
    image2=torch.zeros(1,2,5,5) image2[0,0,2,:]=-2 image2[0,1,2,:]=1 image2
    Out[13]:
    tensor([[[[ 0.,  0.,  0.,  0.,  0.],
              [ 0.,  0.,  0.,  0.,  0.],
              [-2., -2., -2., -2., -2.],
              [ 0.,  0.,  0.,  0.,  0.],
              [ 0.,  0.,  0.,  0.,  0.]],
    
             [[ 0.,  0.,  0.,  0.,  0.],
              [ 0.,  0.,  0.,  0.,  0.],
              [ 1.,  1.,  1.,  1.,  1.],
              [ 0.,  0.,  0.,  0.,  0.],
              [ 0.,  0.,  0.,  0.,  0.]]]])

    Plot out each image:

    In [14]:
    Copied!
    for channel,image in enumerate(image2[0]):
        plt.imshow(image.detach().numpy(), interpolation='nearest', cmap=plt.cm.gray)
        print(image)
        plt.title("channel {}".format(channel))
        plt.colorbar()
        plt.show()
    
    for channel,image in enumerate(image2[0]): plt.imshow(image.detach().numpy(), interpolation='nearest', cmap=plt.cm.gray) print(image) plt.title("channel {}".format(channel)) plt.colorbar() plt.show()
    tensor([[ 0.,  0.,  0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.],
            [-2., -2., -2., -2., -2.],
            [ 0.,  0.,  0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.]])
    
    No description has been provided for this image
    tensor([[0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0.],
            [1., 1., 1., 1., 1.],
            [0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0.]])
    
    No description has been provided for this image

    Create a Conv2d object with two inputs:

    In [15]:
    Copied!
    conv3 = nn.Conv2d(in_channels=2, out_channels=1,kernel_size=3)
    
    conv3 = nn.Conv2d(in_channels=2, out_channels=1,kernel_size=3)

    Assign kernel values to make the math a little easier:

    In [16]:
    Copied!
    Gx1=torch.tensor([[0.0,0.0,0.0],[0,1.0,0],[0.0,0.0,0.0]])
    conv3.state_dict()['weight'][0][0]=1*Gx1
    conv3.state_dict()['weight'][0][1]=-2*Gx1
    conv3.state_dict()['bias'][:]=torch.tensor([0.0])
    
    Gx1=torch.tensor([[0.0,0.0,0.0],[0,1.0,0],[0.0,0.0,0.0]]) conv3.state_dict()['weight'][0][0]=1*Gx1 conv3.state_dict()['weight'][0][1]=-2*Gx1 conv3.state_dict()['bias'][:]=torch.tensor([0.0])
    In [17]:
    Copied!
    conv3.state_dict()['weight']
    
    conv3.state_dict()['weight']
    Out[17]:
    tensor([[[[ 0.,  0.,  0.],
              [ 0.,  1.,  0.],
              [ 0.,  0.,  0.]],
    
             [[-0., -0., -0.],
              [-0., -2., -0.],
              [-0., -0., -0.]]]])

    Perform the convolution:

    In [18]:
    Copied!
    conv3(image2)
    
    conv3(image2)
    Out[18]:
    tensor([[[[ 0.,  0.,  0.],
              [-4., -4., -4.],
              [ 0.,  0.,  0.]]]], grad_fn=<ConvolutionBackward0>)

    The following images summarize the process. The object performs Convolution.

    No description has been provided for this image

    Then, it adds the result:

    No description has been provided for this image

    Multiple Input and Multiple Output Channels

    When using multiple inputs and outputs, a kernel is created for each input, and the process is repeated for each output. The process is summarized in the following image.

    There are two input channels and 3 output channels. For each channel, the input in red and purple is convolved with an individual kernel that is colored differently. As a result, there are three outputs.

    No description has been provided for this image

    Create an example with two inputs and three outputs and assign the kernel values to make the math a little easier:

    In [19]:
    Copied!
    conv4 = nn.Conv2d(in_channels=2, out_channels=3,kernel_size=3)
    conv4.state_dict()['weight'][0][0]=torch.tensor([[0.0,0.0,0.0],[0,0.5,0],[0.0,0.0,0.0]])
    conv4.state_dict()['weight'][0][1]=torch.tensor([[0.0,0.0,0.0],[0,0.5,0],[0.0,0.0,0.0]])
    
    
    conv4.state_dict()['weight'][1][0]=torch.tensor([[0.0,0.0,0.0],[0,1,0],[0.0,0.0,0.0]])
    conv4.state_dict()['weight'][1][1]=torch.tensor([[0.0,0.0,0.0],[0,-1,0],[0.0,0.0,0.0]])
    
    conv4.state_dict()['weight'][2][0]=torch.tensor([[1.0,0,-1.0],[2.0,0,-2.0],[1.0,0.0,-1.0]])
    conv4.state_dict()['weight'][2][1]=torch.tensor([[1.0,2.0,1.0],[0.0,0.0,0.0],[-1.0,-2.0,-1.0]])
    
    conv4 = nn.Conv2d(in_channels=2, out_channels=3,kernel_size=3) conv4.state_dict()['weight'][0][0]=torch.tensor([[0.0,0.0,0.0],[0,0.5,0],[0.0,0.0,0.0]]) conv4.state_dict()['weight'][0][1]=torch.tensor([[0.0,0.0,0.0],[0,0.5,0],[0.0,0.0,0.0]]) conv4.state_dict()['weight'][1][0]=torch.tensor([[0.0,0.0,0.0],[0,1,0],[0.0,0.0,0.0]]) conv4.state_dict()['weight'][1][1]=torch.tensor([[0.0,0.0,0.0],[0,-1,0],[0.0,0.0,0.0]]) conv4.state_dict()['weight'][2][0]=torch.tensor([[1.0,0,-1.0],[2.0,0,-2.0],[1.0,0.0,-1.0]]) conv4.state_dict()['weight'][2][1]=torch.tensor([[1.0,2.0,1.0],[0.0,0.0,0.0],[-1.0,-2.0,-1.0]])

    For each output, there is a bias, so set them all to zero:

    In [20]:
    Copied!
    conv4.state_dict()['bias'][:]=torch.tensor([0.0,0.0,0.0])
    
    conv4.state_dict()['bias'][:]=torch.tensor([0.0,0.0,0.0])

    Create a two-channel image and plot the results:

    In [21]:
    Copied!
    image4=torch.zeros(1,2,5,5)
    image4[0][0]=torch.ones(5,5)
    image4[0][1][2][2]=1
    for channel,image in enumerate(image4[0]):
        plt.imshow(image.detach().numpy(), interpolation='nearest', cmap=plt.cm.gray)
        print(image)
        plt.title("channel {}".format(channel))
        plt.colorbar()
        plt.show()
    
    image4=torch.zeros(1,2,5,5) image4[0][0]=torch.ones(5,5) image4[0][1][2][2]=1 for channel,image in enumerate(image4[0]): plt.imshow(image.detach().numpy(), interpolation='nearest', cmap=plt.cm.gray) print(image) plt.title("channel {}".format(channel)) plt.colorbar() plt.show()
    tensor([[1., 1., 1., 1., 1.],
            [1., 1., 1., 1., 1.],
            [1., 1., 1., 1., 1.],
            [1., 1., 1., 1., 1.],
            [1., 1., 1., 1., 1.]])
    
    No description has been provided for this image
    tensor([[0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0.],
            [0., 0., 1., 0., 0.],
            [0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0.]])
    
    No description has been provided for this image

    Perform the convolution:

    In [22]:
    Copied!
    z=conv4(image4)
    z
    
    z=conv4(image4) z
    Out[22]:
    tensor([[[[ 0.5000,  0.5000,  0.5000],
              [ 0.5000,  1.0000,  0.5000],
              [ 0.5000,  0.5000,  0.5000]],
    
             [[ 1.0000,  1.0000,  1.0000],
              [ 1.0000,  0.0000,  1.0000],
              [ 1.0000,  1.0000,  1.0000]],
    
             [[-1.0000, -2.0000, -1.0000],
              [ 0.0000,  0.0000,  0.0000],
              [ 1.0000,  2.0000,  1.0000]]]], grad_fn=<ConvolutionBackward0>)

    The output of the first channel is given by:

    No description has been provided for this image

    The output of the second channel is given by:

    No description has been provided for this image

    The output of the third channel is given by:

    No description has been provided for this image

    Practice Questions

    Use the following two convolution objects to produce the same result as two input channel convolution on imageA and imageB as shown in the following image:

    In [23]:
    Copied!
    imageA=torch.zeros(1,1,5,5)
    imageB=torch.zeros(1,1,5,5)
    imageA[0,0,2,:]=-2
    imageB[0,0,2,:]=1
    
    
    conv5 = nn.Conv2d(in_channels=1, out_channels=1,kernel_size=3)
    conv6 = nn.Conv2d(in_channels=1, out_channels=1,kernel_size=3)
    
    
    Gx1=torch.tensor([[0.0,0.0,0.0],[0,1.0,0],[0.0,0.0,0.0]])
    conv5.state_dict()['weight'][0][0]=1*Gx1
    conv6.state_dict()['weight'][0][0]=-2*Gx1
    conv5.state_dict()['bias'][:]=torch.tensor([0.0])
    conv6.state_dict()['bias'][:]=torch.tensor([0.0])
    
    imageA=torch.zeros(1,1,5,5) imageB=torch.zeros(1,1,5,5) imageA[0,0,2,:]=-2 imageB[0,0,2,:]=1 conv5 = nn.Conv2d(in_channels=1, out_channels=1,kernel_size=3) conv6 = nn.Conv2d(in_channels=1, out_channels=1,kernel_size=3) Gx1=torch.tensor([[0.0,0.0,0.0],[0,1.0,0],[0.0,0.0,0.0]]) conv5.state_dict()['weight'][0][0]=1*Gx1 conv6.state_dict()['weight'][0][0]=-2*Gx1 conv5.state_dict()['bias'][:]=torch.tensor([0.0]) conv6.state_dict()['bias'][:]=torch.tensor([0.0])
    No description has been provided for this image
    No description has been provided for this image

    Double-click here for the solution.

    What's on your mind? Put it in the comments!

    June 3, 2025 June 3, 2025

    © 2025 DATAIDEA. All rights reserved. Built with ❤️ by Juma Shafara.