<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 <div class="alert alert-block alert-info" style="margin-top
Step1:  <a id="ref0"></a>
<h2 align=center>Multiple Output Channels </h2>
In Pytroch, you can create a <code>Conv2d</code> 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
Step2:  Pytorch randomly assigns values to each kernel. However, use kernels that have  been developed to detect edges
Step3:  Each kernel has its own bias, so set them all to zero
Step4:  Print out each kernel
Step5:  Create an input <code>image</code> to represent the input X
Step6:  Plot it as an image
Step7:  Perform convolution using each channel
Step8:  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.
Step9:  Print out each channel as a tensor or an image
Step10:  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
Step11:  In this case, the second channel fluctuates, and the first and the third channels produce a constant value.
Step12:  The following figure summarizes the process
Step13:  Plot out each image
Step14:  Create a <code>Conv2d</code> object with two inputs
Step15:  Assign kernel values to make the math a little easier
Step16:  Perform the convolution
Step17:  The following images summarize the process. The object performs Convolution.
<img src = "https
Step18:  For each output, there is a bias, so set them all to zero
Step19:  Create a two-channel image and plot the results
Step20:  Perform the convolution
Step21:  The output of the first channel is given by
<ASSISTANT_TASK:>
Python Code:
import torch 
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np
from scipy import ndimage, misc
Explanation: <div class="alert alert-block alert-info" style="margin-top: 20px">
 <a href="http://cocl.us/pytorch_link_top"><img src = "http://cocl.us/Pytorch_top" width = 950, align = "center"></a>
<img src = "https://ibm.box.com/shared/static/ugcqz6ohbvff804xp84y4kqnvvk3bq1g.png" width = 200, align = "center">
<h1 align=center><font size = 5> Multiple Channel Convolution </font></h1>
# Table of Contents
In this lab, you will study convolution and review how the different operations change the relationship between input and output.
<div class="alert alert-block alert-info" style="margin-top: 20px">
<li><a href="#ref0">Multiple Output Channels </a></li>
<li><a href="#ref1">Multiple Inputs</a></li>
<li><a href="#ref2">Multiple Input and Multiple Output Channels </a></li>
<li><a href="#ref3">Practice Questions </a></li>
<br>
<p></p>
Estimated Time Needed: <strong>25 min</strong>
</div>
<hr>
Import the following libraries:
End of explanation
conv1 = nn.Conv2d(in_channels=1, out_channels=3,kernel_size=3)
Explanation: <a id="ref0"></a>
<h2 align=center>Multiple Output Channels </h2>
In Pytroch, you can create a <code>Conv2d</code> 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.
<img src = "https://ibm.box.com/shared/static/xtizse47geeewgb7st3ba2r5yo86oah9.png" width = 750, align = "center">
Symbolically, this can be represented as follows:
<img src = "https://ibm.box.com/shared/static/7dn8ci9uahkc93nu0uwd7vih4kfi8q0x.png" width = 500, align = "center">
Create a <code>Conv2d</code> with three channels:
End of explanation
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)
Explanation: Pytorch randomly assigns values to each kernel. However, use kernels that have  been developed to detect edges:
End of explanation
conv1.state_dict()['bias'][:]=torch.tensor([0.0,0.0,0.0])
conv1.state_dict()['bias']
Explanation: Each kernel has its own bias, so set them all to zero:
End of explanation
for x in conv1.state_dict()['weight']:
    print(x)
Explanation: Print out each kernel:
End of explanation
image=torch.zeros(1,1,5,5)
image[0,0,:,2]=1
image
Explanation: Create an input <code>image</code> to represent the input X:
End of explanation
plt.imshow(image[0,0,:,:].numpy(), interpolation='nearest', cmap=plt.cm.gray)
plt.colorbar()
plt.show()
Explanation: Plot it as an image:
End of explanation
out=conv1(image)
Explanation: Perform convolution using each channel:
End of explanation
out.shape
Explanation: 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.
End of explanation
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()
Explanation: Print out each channel as a tensor or an image:
End of explanation
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()
Explanation: 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:
<img src = "https://ibm.box.com/shared/static/rcldenz1cwak2ss3ixo61b7alqzqoudy.png" width = 750, align = "center">
If you use a different image, the result will be different:
End of explanation
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()
Explanation: In this case, the second channel fluctuates, and the first and the third channels produce a constant value.
End of explanation
image2=torch.zeros(1,2,5,5)
image2[0,0,2,:]=-2
image2[0,1,2,:]=1
image2
Explanation: The following figure summarizes the process:
<img src = "https://ibm.box.com/shared/static/vo4a5s3gj7ebjzfg5w3av0t0uxovfrlq.png" width = 750, align = "center">
<a id="ref1"></a>
<h2 align=center>Multiple Input Channels </h2>
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:  
<img src = "https://ibm.box.com/shared/static/gr0tvp5rdo1rfnl4pefrmlidanck11do.png" width = 750, align = "center">
Create an input with two channels:
End of explanation
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()
Explanation: Plot out each image:
End of explanation
conv3 = nn.Conv2d(in_channels=2, out_channels=1,kernel_size=3)
Explanation: Create a <code>Conv2d</code> object with two inputs:
End of explanation
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])
conv3.state_dict()['weight']
Explanation: Assign kernel values to make the math a little easier:
End of explanation
conv3(image2)
Explanation: Perform the convolution:
End of explanation
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]])
Explanation: The following images summarize the process. The object performs Convolution.
<img src = "https://ibm.box.com/shared/static/pzjm5916heeuqtvnlyfyd7x1stdxweqr.png" width = 750, align = "center">
Then, it adds the result: 
<img src = "https://ibm.box.com/shared/static/1mmvg1el62d7ufk93se5vfs2x4wovym4.png" width = 750, align = "center">
<a id="ref2"></a>
<h2>Multiple Input and Multiple Output Channels</h2>
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. 
<img src = "https://ibm.box.com/shared/static/gzmvz88y5o18joy8k2f6x3cqi5qsv2uf.png" width = 750, align = "center">
Create an example with two inputs and three outputs and assign the kernel values to make the math a little easier:
End of explanation
conv4.state_dict()['bias'][:]=torch.tensor([0.0,0.0,0.0])
Explanation: For each output, there is a bias, so set them all to zero:
End of explanation
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()
Explanation: Create a two-channel image and plot the results:
End of explanation
z=conv4(image4)
z
Explanation: Perform the convolution:
End of explanation
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])
Explanation: The output of the first channel is given by: 
<img src = "https://ibm.box.com/shared/static/dyylxgpqndz2d7ue5c34vfueiz9vk4ue.png" width = 750, align = "center">
The output of the second channel is given by:
<img src = "https://ibm.box.com/shared/static/di2k85fmskn6k1mz1bz46fimp0xpa6n6.png" width = 750, align = "center">
The output of the third channel is given by: 
<img src = "https://ibm.box.com/shared/static/4hrhcfk82vfvmg2rqddc03tou0tqnrl6.png" width = 750, align = "center">
<a id="ref3"></a>
<h2>Practice Questions </h2>
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:
End of explanation
<END_TASK>