import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np
from scipy import ndimage, misc
Multiple Channel Convolutional Network
Training Two Parameter, Mini-Batch Gradient Decent, Training Two Parameter Mini-Batch Gradient Decent
Objective for this Notebook
- Learn on Multiple Input and Multiple Output Channels.
Import the following libraries:
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.
Symbolically, this can be represented as follows:
Create a Conv2d
with three channels:
= nn.Conv2d(in_channels=1, out_channels=3,kernel_size=3) conv1
Pytorch randomly assigns values to each kernel. However, use kernels that have been developed to detect edges:
=torch.tensor([[1.0,0,-1.0],[2.0,0,-2.0],[1.0,0.0,-1.0]])
Gx=torch.tensor([[1.0,2.0,1.0],[0.0,0.0,0.0],[-1.0,-2.0,-1.0]])
Gy
'weight'][0][0]=Gx
conv1.state_dict()['weight'][1][0]=Gy
conv1.state_dict()['weight'][2][0]=torch.ones(3,3) conv1.state_dict()[
Each kernel has its own bias, so set them all to zero:
'bias'][:]=torch.tensor([0.0,0.0,0.0])
conv1.state_dict()['bias'] conv1.state_dict()[
tensor([0., 0., 0.])
Print out each kernel:
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:
=torch.zeros(1,1,5,5)
image0,0,:,2]=1
image[ image
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:
0,0,:,:].numpy(), interpolation='nearest', cmap=plt.cm.gray)
plt.imshow(image[
plt.colorbar() plt.show()
Perform convolution using each channel:
=conv1(image) out
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.
out.shape
torch.Size([1, 3, 3, 3])
Print out each channel as a tensor or an image:
for channel,image in enumerate(out[0]):
='nearest', cmap=plt.cm.gray)
plt.imshow(image.detach().numpy(), interpolationprint(image)
"channel {}".format(channel))
plt.title(
plt.colorbar() plt.show()
tensor([[-4., 0., 4.],
[-4., 0., 4.],
[-4., 0., 4.]], grad_fn=<UnbindBackward0>)
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]], grad_fn=<UnbindBackward0>)
tensor([[3., 3., 3.],
[3., 3., 3.],
[3., 3., 3.]], grad_fn=<UnbindBackward0>)
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:
If you use a different image, the result will be different:
=torch.zeros(1,1,5,5)
image10,0,2,:]=1
image1[print(image1)
0,0,:,:].detach().numpy(), interpolation='nearest', cmap=plt.cm.gray)
plt.imshow(image1[ 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.]]]])
In this case, the second channel fluctuates, and the first and the third channels produce a constant value.
=conv1(image1)
out1for channel,image in enumerate(out1[0]):
='nearest', cmap=plt.cm.gray)
plt.imshow(image.detach().numpy(), interpolationprint(image)
"channel {}".format(channel))
plt.title(
plt.colorbar() plt.show()
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]], grad_fn=<UnbindBackward0>)
tensor([[-4., -4., -4.],
[ 0., 0., 0.],
[ 4., 4., 4.]], grad_fn=<UnbindBackward0>)
tensor([[3., 3., 3.],
[3., 3., 3.],
[3., 3., 3.]], grad_fn=<UnbindBackward0>)
The following figure summarizes the process:
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:
Create an input with two channels:
=torch.zeros(1,2,5,5)
image20,0,2,:]=-2
image2[0,1,2,:]=1
image2[ image2
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:
for channel,image in enumerate(image2[0]):
='nearest', cmap=plt.cm.gray)
plt.imshow(image.detach().numpy(), interpolationprint(image)
"channel {}".format(channel))
plt.title(
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.]])
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.]])
Create a Conv2d
object with two inputs:
= nn.Conv2d(in_channels=2, out_channels=1,kernel_size=3) conv3
Assign kernel values to make the math a little easier:
=torch.tensor([[0.0,0.0,0.0],[0,1.0,0],[0.0,0.0,0.0]])
Gx1'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'] conv3.state_dict()[
tensor([[[[ 0., 0., 0.],
[ 0., 1., 0.],
[ 0., 0., 0.]],
[[-0., -0., -0.],
[-0., -2., -0.],
[-0., -0., -0.]]]])
Perform the convolution:
conv3(image2)
tensor([[[[ 0., 0., 0.],
[-4., -4., -4.],
[ 0., 0., 0.]]]], grad_fn=<ConvolutionBackward0>)
The following images summarize the process. The object performs Convolution.
Then, it adds the result:
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.
Create an example with two inputs and three outputs and assign the kernel values to make the math a little easier:
= nn.Conv2d(in_channels=2, out_channels=3,kernel_size=3)
conv4 '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.state_dict()[
For each output, there is a bias, so set them all to zero:
'bias'][:]=torch.tensor([0.0,0.0,0.0]) conv4.state_dict()[
Create a two-channel image and plot the results:
=torch.zeros(1,2,5,5)
image40][0]=torch.ones(5,5)
image4[0][1][2][2]=1
image4[for channel,image in enumerate(image4[0]):
='nearest', cmap=plt.cm.gray)
plt.imshow(image.detach().numpy(), interpolationprint(image)
"channel {}".format(channel))
plt.title(
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.]])
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.]])
Perform the convolution:
=conv4(image4)
z z
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:
The output of the second channel is given by:
The output of the third channel is given by:
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:
=torch.zeros(1,1,5,5)
imageA=torch.zeros(1,1,5,5)
imageB0,0,2,:]=-2
imageA[0,0,2,:]=1
imageB[
= nn.Conv2d(in_channels=1, out_channels=1,kernel_size=3)
conv5 = nn.Conv2d(in_channels=1, out_channels=1,kernel_size=3)
conv6
=torch.tensor([[0.0,0.0,0.0],[0,1.0,0],[0.0,0.0,0.0]])
Gx1'weight'][0][0]=1*Gx1
conv5.state_dict()['weight'][0][0]=-2*Gx1
conv6.state_dict()['bias'][:]=torch.tensor([0.0])
conv5.state_dict()['bias'][:]=torch.tensor([0.0]) conv6.state_dict()[
Double-click here for the solution.