NNDL 作业5:卷积

目录

作业1

1. 图1使用卷积核(1 -1),输出特征图

2. 图1使用卷积核​编辑,输出特征图

3. 图2使用卷积核(1 -1),输出特征图

4. 图2使用卷积核​编辑,输出特征图 

5. 图3使用卷积核(1 -1),​编辑,​编辑 ,输出特征图 

作业2

 一、概念

二、探究不同卷积核的作用

 三、编程实现

1.实现灰度图的边缘检测、锐化、模糊。(必做)

2.调整卷积核参数,测试并总结。(必做)

3.使用不同尺寸图片,测试并总结。(必做)

4.尝试彩色图片边缘检测。(选做)

总结


作业1

NNDL 作业5:卷积_第1张图片
编程实现

  

1. 图1使用卷积核(1 -1),输出特征图

代码如下:

import torch
import matplotlib.pyplot as plt
import torch.nn.functional as F
import numpy as np
#生成图片
def create_pic():
    picture = torch.Tensor([[0,0,0,255,255,255],
                      [0,0,0,255,255,255],
                      [0,0,0,255,255,255],
                      [0,0,0,255,255,255],
                      [0,0,0,255,255,255]])
    return picture
#确定卷积网络
class MyNet(torch.nn.Module):
    def __init__(self,kernel,kshape):
        super(MyNet, self).__init__()
        kernel = torch.reshape(kernel,kshape)
        self.weight = torch.nn.Parameter(data=kernel, requires_grad=False)
    def forward(self, picture):
        picture = F.conv2d(picture,self.weight,stride=1,padding=0)
        return  picture
#确定卷积层
kernel = torch.tensor([-1.0,1.0])
#更改卷积层的形状适应卷积函数
kshape = (1,1,1,2)
#生成模型
model = MyNet(kernel=kernel,kshape=kshape)
#生成图片
picture = create_pic()
#更改图片的形状适应卷积层
picture = torch.reshape(picture,(1,1,5,6))
output = model(picture)
output = torch.reshape(output,(5,5))
plt.imshow(output,cmap='gray')
plt.show()

运行结果:

NNDL 作业5:卷积_第2张图片

2. 图1使用卷积核\binom{1}{-1},输出特征图

代码如下:

kernel = torch.tensor([-1.0,1.0])
#更改卷积和的形状为转置
kshape = (1,1,2,1)
model = MyNet(kernel=kernel,kshape=kshape)
picture = create_pic()
picture = torch.reshape(picture,(1,1,5,6))
output = model(picture)
output = torch.reshape(output,(6,4))
plt.imshow(output,cmap='gray')
plt.show()

运行结果:

NNDL 作业5:卷积_第3张图片

3. 图2使用卷积核(1 -1),输出特征图

代码如下:

#生成图像
def create_pic():
    picture = torch.Tensor([[0,0,0,255,255,255],
                      [0,0,0,255,255,255],
                      [0,0,0,255,255,255],
                      [255,255,255,0,0,0],
                      [255,255,255,0,0,0],
                      [255,255,255,0,0,0]])
    return picture
#确定卷积核
kernel = torch.tensor([-1.0,1.0])
kshape = (1,1,1,2)
#生成模型
model = MyNet(kernel=kernel,kshape=kshape)
picture = create_pic()
picture = torch.reshape(picture,(1,1,6,6))
print(picture)
output = model(picture)
output = torch.reshape(output,(6,5))
print(output)
plt.imshow(output,cmap='gray')
plt.show()

运行结果:

NNDL 作业5:卷积_第4张图片

4. 图2使用卷积核\binom{1}{-1},输出特征图 

代码如下:

def create_pic():
    picture = torch.Tensor([[0,0,0,255,255,255],
                      [0,0,0,255,255,255],
                      [0,0,0,255,255,255],
                      [255,255,255,0,0,0],
                      [255,255,255,0,0,0],
                      [255,255,255,0,0,0]])
    return picture

kernel = torch.tensor([-1.0,1.0])
kshape = (1,1,2,1)
model = MyNet(kernel=kernel,kshape=kshape)
picture = create_pic()
picture = torch.reshape(picture,(1,1,6,6))
print(picture)
output = model(picture)
output = torch.reshape(output,(5,6))
print(output)
plt.imshow(output,cmap='gray')
plt.show()

运行结果:

NNDL 作业5:卷积_第5张图片

5. 图3使用卷积核(1 -1),\binom{1}{-1}\binom{1 -1}{-1 1} ,输出特征图 

代码如下:

import numpy as np
import torch
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

w1 = np.array([1, -1], dtype='float32').reshape([1, 1, 1, 2])
w2 = np.array([1, -1], dtype='float32').T.reshape([1, 1, 2, 1])
w3 =  np.array([[1,-1,-1,1]],dtype='float32').reshape([1,1,2,2])

print(w3)
w1 = torch.Tensor(w1)
w2 = torch.Tensor(w2)
w3 = torch.Tensor(w3)

conv1 = torch.nn.Conv2d(1, 1, [1, 2])
conv1.weight = torch.nn.Parameter(w1)

conv2 = torch.nn.Conv2d(1, 1, [2, 1])
conv2.weight = torch.nn.Parameter(w2)

conv3 = torch.nn.Conv2d(1,1,[2,2])
conv3.weight = torch.nn.Parameter(w3)
# 创建图像
img = np.ones([9, 9], dtype='float32')
for i in range(7):
    img[i+1,i+1]=255.
    img[i+1,7-i]=255.

x = img.reshape([1, 1, 9, 9])
x = torch.Tensor(x)

y1 = conv1(x).detach().numpy()
y2 = conv2(x).detach().numpy()
y3 = conv3(x).detach().numpy()
plt.subplot(221).set_title('图3')
plt.imshow(img, cmap='gray')
plt.subplot(222).set_title('图3使用卷积核为(1,-1)结果')
plt.imshow(y1.squeeze(),cmap='gray')
plt.subplot(223).set_title('图3使用卷积核为(1,-1)T结果')
plt.imshow(y2.squeeze(),cmap='gray')
plt.subplot(224).set_title('图3使用卷积核为[[1 -1],[-1 1]]结果')
plt.imshow(y3.squeeze(),cmap='gray')
plt.show()

运行结果:

NNDL 作业5:卷积_第6张图片

作业2


 一、概念


用自己的语言描述“卷积、卷积核、特征图、特征选择、步长、填充、感受野”。

卷积:所谓两个函数的卷积,本质上就是先将一个函数翻转,然后进行滑动叠加。

在连续情况下,叠加指的是对两个函数的乘积求积分,在离散情况下就是加权求和,为简单起见就统一称为叠加。

整体看来是这么个过程:

翻转——>滑动——>叠加——>滑动——>叠加——>滑动——>叠加.....

多次滑动得到的一系列叠加值,构成了卷积函数。

卷积的“卷”,指的的函数的翻转,从 g(t) 变成 g(-t) 的这个过程;同时,“卷”还有滑动的意味在里面

卷积的“积”,指的是积分/加权求和。

卷积核:就相当于数字图像处理中的滤波算子。

卷积核就是图像处理时,给定输入图像,输入图像中一个小区域中像素加权平均后成为输出图像中的每个对应像素,其中权值由一个函数定义,这个函数称为卷积核。

卷积核的深度和卷积核的个数是不同的概念,卷积核的深度是单个卷积核的属性,其数值需与输入的图像或特征图深度保持一致,而卷积核的个数则定义为对输入图像或特征进行处理,需要多少个单个卷积核可以较为充分的提取特征,其数值可以自行定义。

特征图:神经网络中,卷积(函数)的作用是找特征。而且经常有多个需要找的特征。多个找出的特征就是特征图。比如一个二维的像素图,每个特征是略小于原图的二维图,特征图就是n个特征叠加的一个三维的图。 

特征选择:顾名思义,就是要选择一部分的特征用于训练,而不使用全部的特征。

那么为什么要进行特征选择?

  • 有一些特征之间的相关度太高了,会大量消耗计算资源。
  • 有一些特征可能会对预测结果有负影响。

特征选择是剔除一部分冗余的特征,剔除的这些特征和预测的关系很小。

步长:滑动卷积核时,我们会先从输入的左上角开始,每次往左滑动一列或者往下滑动一行逐一计算输出,我们将每次滑动的行数和列数称为Stride。卷积过程中,有时需要通过padding来避免信息损失,有时也要在卷积时通过设置的步长(Stride)来压缩一部分信息,或者使输出的尺寸小于输入的尺寸。

填充:输入图像与卷积核进行卷积后的结果中损失了部分值,输入图像的边缘被“修剪”掉了。这是因为边缘上的像素永远不会位于卷积核中心,而卷积核也没法扩展到边缘区域以外。
这个结果我们是不能接受的,有时我们还希望输入和输出的大小应该保持一致。为解决这个问题,可以在进行卷积操作前,对原矩阵进行边界填充,也就是在矩阵的边界上填充一些值,以增加矩阵的大小。

感受野:是指输出特征图上某个像素对应到输入空间(原图)中的区域范围。所以感受野可以理解为特征图像素到输入区域的映射。
感受野的作用:

一般task要求感受野越大越好,如图像分类中最后卷积层的感受野要大于输入图像,网络深度越深感受野越大性能越好;
密集预测task要求输出像素的感受野足够的大,确保做出决策时没有忽略重要信息,一般也是越深越好;
目标检测task中设置anchor要严格对应感受野,anchor太大或偏离感受野都会严重影响检测性能。
 

二、探究不同卷积核的作用

1.边缘检测
边缘检测的目的就是找到图像中亮度变化剧烈的像素点构成的集合,表现出来往往是轮廓。
NNDL 作业5:卷积_第7张图片
2.锐化
图像锐化也称边缘增强。锐化技术用于加强图像中的边界和细节信息。由于边界和细节信息对应频域中的高频部分,所以在频域中通常对图像进行高通滤波,在空间域则进行微分处理。
NNDL 作业5:卷积_第8张图片 

3.模糊
NNDL 作业5:卷积_第9张图片

4.浮雕
浮雕滤波器可以给图像一种3D阴影的效果。

NNDL 作业5:卷积_第10张图片

 三、编程实现


1.实现灰度图的边缘检测、锐化、模糊。(必做)

代码如下:

import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
from PIL import Image
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号 #有中文出现的情况,需要u'内容
# https://blog.csdn.net/weixin_40123108/article/details/83510592
file_path = 'deer.jpg'
im = Image.open(r'C:\Users\86155\Desktop\dog.jpg').convert('L')  # 读入一张灰度图的图片
im = np.array(im, dtype='float32')  # 将其转换为一个矩阵
print(im.shape[0], im.shape[1])
plt.imshow(im.astype('uint8'), cmap='gray')  # 可视化图片
plt.title('原图')
plt.show()


#边缘检测
im = torch.from_numpy(im.reshape((1, 1, im.shape[0], im.shape[1])))
conv1 = nn.Conv2d(1, 1, 3, bias=False)  # 定义卷积

sobel_kernel = np.array([[-1, -1, -1],
                         [-1, 8, -1],
                         [-1, -1, -1]], dtype='float32')  # 定义轮廓检测算子
sobel_kernel = sobel_kernel.reshape((1, 1, 3, 3))  # 适配卷积的输入输出
conv1.weight.data = torch.from_numpy(sobel_kernel)  # 给卷积的 kernel 赋值

edge1 = conv1(Variable(im))  # 作用在图片上

x = edge1.data.squeeze().numpy()
print(x.shape)  # 输出大小

plt.imshow(x, cmap='gray')
plt.show()


#锐化
import cv2
import numpy as np
import matplotlib.pyplot as plt

#读取图像
img = cv2.imread(r'C:\Users\86155\Desktop\dog.jpg')
lenna_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

#灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#高斯滤波
gaussianBlur = cv2.GaussianBlur(grayImage, (3,3), 0)

#阈值处理
ret, binary = cv2.threshold(gaussianBlur, 127, 255, cv2.THRESH_BINARY)

#Roberts算子
kernelx = np.array([[-1,0],[0,1]], dtype=int)
kernely = np.array([[0,-1],[1,0]], dtype=int)
x = cv2.filter2D(binary, cv2.CV_16S, kernelx)
y = cv2.filter2D(binary, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)

#Prewitt算子
kernelx = np.array([[1,1,1],[0,0,0],[-1,-1,-1]], dtype=int)
kernely = np.array([[-1,0,1],[-1,0,1],[-1,0,1]], dtype=int)
x = cv2.filter2D(binary, cv2.CV_16S, kernelx)
y = cv2.filter2D(binary, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Prewitt = cv2.addWeighted(absX,0.5,absY,0.5,0)

#Sobel算子
x = cv2.Sobel(binary, cv2.CV_16S, 1, 0)
y = cv2.Sobel(binary, cv2.CV_16S, 0, 1)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)

#拉普拉斯算法
dst = cv2.Laplacian(binary, cv2.CV_16S, ksize = 3)
Laplacian = cv2.convertScaleAbs(dst)

#效果图
titles = ['Source Image', 'Binary Image', 'Roberts Image',
          'Prewitt Image','Sobel Image', 'Laplacian Image']
images = [lenna_img, binary, Roberts, Prewitt, Sobel, Laplacian]
for i in np.arange(6):
    plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()


#模糊
if __name__ == "__main__":
    image = cv2.imread(r'C:\Users\86155\Desktop\dog.jpg')

    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 此为均值模糊
    # (30,1)为一维卷积核,指在x,y方向偏移多少位
    dst1 = cv2.blur(image, (30, 1))

    # 此为中值模糊,常用于去除椒盐噪声
    dst2 = cv2.medianBlur(image, 15)

    # 自定义卷积核,执行模糊操作,也可定义执行锐化操作
    kernel = np.ones([5, 5], np.float32) / 25
    dst3 = cv2.filter2D(image, -1, kernel=kernel)

    plt.subplot(2, 2, 1)
    plt.imshow(image)
    plt.axis('off')
    plt.title('Offical')

    plt.subplot(2, 2, 2)
    plt.imshow(dst1)
    plt.axis('off')
    plt.title('Box blur')

    plt.subplot(2, 2, 3)
    plt.imshow(dst2)
    plt.axis('off')
    plt.title('median blur')

    plt.subplot(2, 2, 4)
    plt.imshow(dst3)
    plt.axis('off')
    plt.title('defined blur')

    plt.show()

运行结果:

NNDL 作业5:卷积_第11张图片

边缘检测:

NNDL 作业5:卷积_第12张图片

锐化:

NNDL 作业5:卷积_第13张图片

模糊:

NNDL 作业5:卷积_第14张图片


2.调整卷积核参数,测试并总结。(必做)

 以锐化为例:

NNDL 作业5:卷积_第15张图片

 当步长设置为1时:

 NNDL 作业5:卷积_第16张图片

当步长设置为10时: 

NNDL 作业5:卷积_第17张图片

 当步长设置为20时:

 NNDL 作业5:卷积_第18张图片

步长小,提取的特征会更全面,但同时可能造成计算量增大,甚至过拟合等问题。步长大,计算量会下降,但很有可能错失一些有用的特征。随着步长的变大,像素点逐渐减少,并且图像提取的边界越来越模糊。


3.使用不同尺寸图片,测试并总结。(必做)

原图:

NNDL 作业5:卷积_第19张图片

边缘检测:

NNDL 作业5:卷积_第20张图片 

锐化:

NNDL 作业5:卷积_第21张图片 

模糊:

NNDL 作业5:卷积_第22张图片 

4.尝试彩色图片边缘检测。(选做)

import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
from PIL import Image
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号 #有中文出现的情况,需要u'内容
# https://blog.csdn.net/weixin_40123108/article/details/83510592
file_path = 'a.webp'
im = Image.open(file_path).convert('L')  # 读入一张灰度图的图片
im = np.array(im, dtype='float32')  # 将其转换为一个矩阵
print(im.shape[0], im.shape[1])
plt.imshow(im.astype('uint8'), cmap='gray')  # 可视化图片
plt.title('原图')
plt.show()

im = torch.from_numpy(im.reshape((1, 1, im.shape[0], im.shape[1])))
conv1 = nn.Conv2d(1, 1, 3, bias=False)  # 定义卷积

sobel_kernel = np.array([[-1, -1, -1],
                         [-1, 8, -1],
                         [-1, -1, -1]], dtype='float32')  # 定义轮廓检测算子
sobel_kernel = sobel_kernel.reshape((1, 1, 3, 3))  # 适配卷积的输入输出
conv1.weight.data = torch.from_numpy(sobel_kernel)  # 给卷积的 kernel 赋值

edge1 = conv1(Variable(im))  # 作用在图片上

x = edge1.data.squeeze().numpy()
print(x.shape)  # 输出大小

plt.imshow(x, cmap='gray')
plt.title('边缘检测')
plt.show()

NNDL 作业5:卷积_第23张图片

NNDL 作业5:卷积_第24张图片  


总结
 

为什么卷积能够提取图像上的特征?

信号与系统的角度看,卷积很多时候出现在一个系统的单位脉冲响应与输入信号上,用于求出系统在一定输入下所对应的输出。

神经网络的多层结构从一开始的设想是模仿生物的神经元的一层一层传递的结构,从一开始的神经元判断角和边等等到大的局部特征最终得到所看到的目标是否是与神经元中记忆的某一个模式所匹配来进行目标的判断,这一点从反卷积出的图像可以看出。

 卷积神经网络CNN实际上是通过带有label的训练数据来train由一个个特征判断系统中的运算法则,对于图像特征提取而言,系统的运算法则可能比较复杂,通过卷积的方式将复杂判断系统的运算法则变为一个个易于计算的卷积核,从而更方便地提取图像特征,完成对图像的分析与处理。

本次作业主要实现了卷积的应用,更加深刻的了解了卷积等一系列的概念,以及如何实现图像的边缘检测和锐化和模糊。调整卷积核参数,可以通过改变卷积核的步长使图片发生变化。

参考代码:

import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
from PIL import Image
import matplotlib.pyplot as plt
 
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号 #有中文出现的情况,需要u'内容
# https://blog.csdn.net/weixin_40123108/article/details/83510592
file_path = 'deer.jpg'
im = Image.open(file_path).convert('L')  # 读入一张灰度图的图片
im = np.array(im, dtype='float32')  # 将其转换为一个矩阵
print(im.shape[0], im.shape[1])
plt.imshow(im.astype('uint8'), cmap='gray')  # 可视化图片
plt.title('原图')
plt.show()
 
im = torch.from_numpy(im.reshape((1, 1, im.shape[0], im.shape[1])))
conv1 = nn.Conv2d(1, 1, 3, bias=False)  # 定义卷积
 
sobel_kernel = np.array([[-1, -1, -1],
                         [-1, 8, -1],
                         [-1, -1, -1]], dtype='float32')  # 定义轮廓检测算子
sobel_kernel = sobel_kernel.reshape((1, 1, 3, 3))  # 适配卷积的输入输出
conv1.weight.data = torch.from_numpy(sobel_kernel)  # 给卷积的 kernel 赋值
 
edge1 = conv1(Variable(im))  # 作用在图片上
 
x = edge1.data.squeeze().numpy()
print(x.shape)  # 输出大小
 
plt.imshow(x, cmap='gray')
plt.show()

参考链接:

卷积为什么能提取特征

特征选择

卷积神经网络

卷积

Image Kernels explained visually (setosa.io)

你可能感兴趣的:(深度学习,python,人工智能)