[23-24 秋学期]NNDL 作业6 卷积 [HBU]

目录

 一、概念

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

后接:关于使用pycharm输出卷积图像后图片仍然不清晰的可能原因以及解决方法

总结:


前言:卷积常用于特征提取 

实验过程中注意认真体会“特征提取”,弄清楚为什么卷积能够提取特征。


 一、概念

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

        大致看了一遍邱锡鹏《神经网络与深度学习》的卷积一节。谈谈我对这些名词概念的理解(理解不足 描述不准请见谅)。

个人理解:

卷积: 卷积可以将图像(二维)或者信号(一维) f(.)矩阵函数,通过另一个矩阵函数g(.)的操作,进行一些数学关系上的运算而得到新的函数矩阵关系h(.)。不同的函数矩阵关系即不同的卷积关系,可以得到不同的矩阵关系h(.),也就是可以提取出不同的特征。要将图像变得平滑、模糊、锐化,或者只提取边缘、局部特征等,都需要经过不同的卷积关系来提取得到。

卷积核:卷积核又称作滤波器,卷积核是一种矩阵函数,它的大小通常小于需要被卷积的矩阵。卷积核是用来从输入数据中提取特征的。比如在处理图像时,一个卷积核可能会关注图像的某个特定部分,比如边缘或颜色变化。通过滑动这个卷积核并与其覆盖的图像部分进行运算,网络可以学习到如何识别图像中的这些特征。每个卷积核都有一些可调整的参数,这些参数是通过训练网络来学习的。这样,每个卷积核都可以根据它所处理的输入数据来调整自己,从而更好地提取特征。

特征图:特征图也叫特征映射,即卷积操作后被提取出来的特征的输出结果。它描述了网络对输入数据的特征理解。特征图是一个二维矩阵,其中每个像素位置的值表示该位置的特征强度或权重。

特征选择:特征选择是卷积过程中根据卷积核的参数(想要提取的特征)而进行的特征偏好保留的过程。通过它选择输入数据中最相关的特征,而忽略不相关或冗余的特征。

步长:卷积神经网络中的步长不同于前馈神经网络反向更新中的学习率,卷积神经网络的步长表示卷积核在输入数据上每次滑动过的距离。

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第1张图片

填充:在一维矩阵中,填充即在输入数据的左右两端各添加额外的P个0;在二维矩阵中,填充是指在矩阵周围一圈添加0。填充零可以保护一些不想丢失的特征参数。

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第2张图片

感受野:感受野是指卷积核可以感知到的输入数据的区域。感受野描述了卷积核对输入数据的感知范围。感受野的大小可以通过改变卷积核的大小和步长来调整。

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第3张图片


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

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第4张图片

https://blog.csdn.net/superdont/article/details/127124819

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第5张图片

从左至右依次为 图1 图2 图3 .

1. 图1分别使用卷积核\begin{pmatrix} 1 & -1 \end{pmatrix}​,\begin{pmatrix} 1\\ -1\\ \end{pmatrix}输出特征图

代码:

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

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

#定义卷积核w1,w2
#w1是形状为1*2的卷积核,数据类型为float32,元素为[1,-1]
#w2是形状为2*1的卷积核,元素为[1,-1]
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])
print('w1为:\n',w1,'\n',type(w1),'\n','形状shape为:',w1.shape)
print('w2为:\n',w2)

#将卷积核转化为Tensor类型
w1 = torch.Tensor(w1)
w2 = torch.Tensor(w2)
# print('w1为:\n',w1,'\n',type(w1),'\n','形状shape为:',w1.shape)
# print('w2为:\n',w2)
print('=========')

#创建两个卷积层conv1,conv2. 分别将w1,w2参数值传入到卷积层中
conv1 = torch.nn.Conv2d(1, 1, [1, 2])
conv1.weight = torch.nn.Parameter(w1)
print('卷积层conv1为:',conv1,'\n','权重w为:',conv1.weight)

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

#创建图像
#创建4*6的浮点数矩阵,其中每个元素都是1
img = np.ones([4, 6], dtype='float32')
img[:, 3:] = 255. #将矩阵的第4列-第6列所有元素设置为255白色
img[:, :3] = 0. #将矩阵的前三列(索引值为0-2)的所有元素设置为0黑色
x = img.reshape([1, 1, 4, 6])#将二维图像矩阵转化为四维张量,即批次大小(Batch size)通道数
x = torch.Tensor(x)
print('转换为Tensor后的x为:\n',x)

#进行卷积操作
#detach():使用detach()确保y只是简单的成为数值,而不需要梯度计算,不希望该节点反向传播
y1 = conv1(x).detach().numpy()
y2 = conv2(x).detach().numpy()

plt.subplot(131).set_title('图1原图')
plt.imshow(img, cmap='gray')

#squeeze()用于从数组中移除尺寸为1的维度
plt.subplot(132).set_title('图1使用卷积核为(1,-1)结果')
plt.imshow(y1.squeeze(), cmap='gray') #以灰度图像的形式显示出来
plt.subplot(133).set_title('图1使用卷积核为(1,-1)T结果')
plt.imshow(y2.squeeze(), cmap='gray')
plt.show()

运行结果:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第6张图片

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第7张图片

图1原图位于最左端,可见卷积核(1,-1)提取了边缘特征,即提取表现了黑白颜色变化最剧烈的特征(也就是边缘处);卷积核(1,-1)T则将颜色对换,黑色和白色互换。


2. 图2分别使用卷积核\begin{pmatrix} 1 & -1 \end{pmatrix}​,\begin{pmatrix} 1\\ -1\\ \end{pmatrix}输出特征图

将图1矩阵像素值分布改为图2矩阵的像素值分布,title名称换一下即可,其余部分的代码和第一问一样:

img = np.ones([6, 6], dtype='float32')
img[:3, 3:] = 255.
img[3:, :3] = 255
img[:3, :3] = 0.

输出结果:
[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第8张图片


3. 图3分别使用卷积核\begin{pmatrix} 1 & -1 \end{pmatrix}​,\begin{pmatrix} 1\\ -1\\ \end{pmatrix}​,\begin{pmatrix} 1 &-1 \\ -1&1 \end{pmatrix}​ ,输出特征图 

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

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

#创建参数w 卷积核
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为:\n',w3)

#转换为Tensor形式
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('卷积核为(1,-1)')
plt.imshow(y1.squeeze(), cmap='gray')
plt.subplot(223).set_title('卷积核为(1,-1)T')
plt.imshow(y2.squeeze(), cmap='gray')
plt.subplot(224).set_title('卷积核为[[1 -1],[-1 1]]')
plt.imshow(y3.squeeze(), cmap='gray')
plt.show()

运行结果:
[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第9张图片

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第10张图片


4. 实现灰度图边缘检测、锐化、模糊

使用特定卷积核,这些都是预定义的图像处理算子,例如Sobel算子、Sharpen算子、Blur算子、Emboss算子和Outline算子。它们被广泛应用于图像处理和计算机视觉任务中。

  • Sobel算子:用于边缘检测,通过计算像素点周围像素的加权差值,得到边缘强度。
  • Sharpen算子:用于图像增强,通过增加图像边缘的对比度来增强图像的清晰度。
  • Blur算子:用于图像平滑,减少图像中的噪声和细节信息。
  • Emboss算子:用于图像突出轮廓,通过增强图像边缘的对比度来突出图像的轮廓。
  • Outline算子:用于检测边缘轮廓。
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  #用来正常显示负号

# 加载图片
file_path = 'C:\\Users\\27513\\Pictures\\Camera Roll\\cat12.jpg'
im = Image.open(file_path).convert('L') #读取图片。.convert('L')表示转换为灰度图像
im = np.array(im, dtype='float32') #将图片转换为numpy矩阵

#先展示原图 cmap='gray'表示以灰度图的样式展现出来
plt.subplot(331).set_title('大橘原图')
plt.imshow(im.astype('uint8'), cmap='gray')

#再将原图转换为torch张量
im = torch.from_numpy(im.reshape((1, 1, im.shape[0], im.shape[1])))
print('转换为Tensor后的im为:\n',im,'\n','im.shape为:\n',im.shape)

#定义卷积核 输入、输出通道为1,卷积核大小为3*3,并且不使用偏置项
conv1 = nn.Conv2d(1, 1, 3, bias=False)
conv2 = nn.Conv2d(1, 1, 3, bias=False)
conv3 = nn.Conv2d(1, 1, 3, bias=False)
conv4 = nn.Conv2d(1, 1, 3, bias=False)
conv5 = nn.Conv2d(1, 1, 3, bias=False)
conv6 = nn.Conv2d(1, 1, 3, bias=False)
conv7 = nn.Conv2d(1, 1, 3, bias=False)
conv8 = nn.Conv2d(1, 1, 3, bias=False)

#卷积核1,使用bottom_sobel底部边缘卷积核。注意形状必须匹配
bottom_sobel = np.array([[-1, -2, -1],
                         [0, 0, 0],
                         [1, 2, 1]], dtype='float32').reshape((1, 1, 3, 3))
conv1.weight.data = torch.from_numpy(bottom_sobel)

#卷积核2,使用left_sobel左部边缘卷积核
left_sobel = np.array([[1, 0, -1],
                       [2, 0, -2],
                       [1, 0, -1]], dtype='float32').reshape((1, 1, 3, 3))
conv2.weight.data = torch.from_numpy(left_sobel)

#卷积核3,使用right_sobel右部边缘卷积核
right_sobel = np.array([[-1, 0, 1],
                        [-2, 0, 2],
                        [-1, 0, 1]], dtype='float32').reshape((1, 1, 3, 3))
conv3.weight.data = torch.from_numpy(right_sobel)

#卷积核4,使用top_sobel顶部边缘卷积核
top_sobel = np.array([[-1, 2, 1],
                      [0, 0, 0],
                      [-1, -2, -1]], dtype='float32').reshape((1, 1, 3, 3))
conv4.weight.data = torch.from_numpy(top_sobel)

#卷积核5,使用sharpen锐化卷积核
sharpen = np.array([[0, -1, 0],
                    [-1, 5, -1],
                    [0, -1, 0]], dtype='float32').reshape((1, 1, 3, 3))
conv5.weight.data = torch.from_numpy(sharpen)

#卷积核6,使用blur模糊卷积核
blur = np.array([[0.0625, 0.125, 0.0625],
                 [0.125, 0.25, 0.125],
                 [0.0625, 0.125, 0.0625]], dtype='float32').reshape((1, 1, 3, 3))
conv6.weight.data = torch.from_numpy(blur)

#卷积核7,使用emboss突出图像轮廓卷积核
emboss = np.array([[-2, -1, 0],
                   [-1, 1, 1],
                   [0, 1, 2]], dtype='float32').reshape((1, 1, 3, 3))
conv7.weight.data = torch.from_numpy(emboss)

#卷积核8,使用outline检测边缘轮廓卷积核
outline = np.array([[-1, -1, -1],
                    [-1, 8, -1],
                    [-1, -1, -1]], dtype='float32').reshape((1, 1, 3, 3))
conv8.weight.data = torch.from_numpy(outline)

#卷积核定义完成,使用pytorch进行卷积操作,并把结果转换为numpy数组
#Variable把张量im包装成pytorch变量。
#.data 获取到张量的原始数据
#.squeeze()用于消除大小为1的维度
y1 = conv1(Variable(im)).data.squeeze().numpy()
y2 = conv2(Variable(im)).data.squeeze().numpy()
y3 = conv3(Variable(im)).data.squeeze().numpy()
y4 = conv4(Variable(im)).data.squeeze().numpy()
y5 = conv5(Variable(im)).data.squeeze().numpy()
y6 = conv6(Variable(im)).data.squeeze().numpy()
y7 = conv7(Variable(im)).data.squeeze().numpy()
y8 = conv8(Variable(im)).data.squeeze().numpy()

# 可视化
plt.axis('off') #关闭xy轴

plt.subplot(332).set_title('bottom_sobel')
plt.imshow(y1, cmap='gray')
plt.axis('off')

plt.subplot(333).set_title('left_sobel')
plt.imshow(y2, cmap='gray')
plt.axis('off')

plt.subplot(334).set_title('right_sobel')
plt.imshow(y3, cmap='gray')
plt.axis('off')

plt.subplot(335).set_title('top_sobel')
plt.imshow(y4, cmap='gray')
plt.axis('off')

plt.subplot(336).set_title('锐化sharpen')
plt.imshow(y5, cmap='gray')
plt.axis('off')

plt.subplot(337).set_title('模糊blur')
plt.imshow(y6, cmap='gray')
plt.axis('off')

plt.subplot(338).set_title('emboss')
plt.imshow(y7, cmap='gray')
plt.axis('off')

plt.subplot(339).set_title('outline')
plt.imshow(y8, cmap='gray')
plt.axis('off')
plt.show()

运行结果:(感觉效果不是特别明显啊)

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第11张图片


后接:关于使用pycharm输出卷积图像后图片仍然不清晰的可能原因以及解决方法

由上方我得到的卷积结果图可以看到,卷积的效果并没有很清晰。完全看不到模糊blur和锐化sharpen的效果(我甚至觉得它俩的卷积核是不是设置反了,检查代码后发现并没有弄反卷积核)。

       对于这个问题,我最初写完这篇博客只是认为这是pycharm显示图像性能不好的原因,就把这个问题放下了。但是魏老师关注到了这一点,给我分享了一篇博客,帮助我解决这个问题。所以我要对 卷积后效果不清晰 的这个问题进行一些解决尝试。

       魏老师分享的解决方案:

【精选】【NNDL作业】图像锐化后,为什么“蒙上了一层灰色”?_在matlab图像处理过程中,double型图像灰度值出现负数的原因-CSDN博客

简单来讲,就是图像中的像素点出现了 <0 和 >255 的情况,灰白图像像素点的值域很大(说白话就是黑、暗的地方更暗了,白、亮的的地方更亮了)进行卷积时,需要将图像的像素点值全部归一(也可以理解为映射)到[0,255]这个范围内。

在代码中若要实现像素点归一化,可以采取以下两种方式:

1. 遍历图像中的所有像素点,把>255的像素点设置为255,<0的像素点设置为0

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第12张图片

2.使用imshow,设置imshow中参数  vmax,vmin  ,限制像素点范围

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第13张图片

使用这两种方式,分别查看效果:

代码1: 遍历图像中的所有像素点,把>255的像素点设置为255,<0的像素点设置为0

#卷积NNDL 去灰操作
import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
import torch.nn.functional as F
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 = 'C:\\Users\\27513\\Pictures\\Camera Roll\\NNDL.png'
im = Image.open(file_path).convert('L')  # 读入一张灰度图的图片
im = np.array(im, dtype='float32')  # 将其转换为一个矩阵
print('im的形状为:\n', im.shape[0], im.shape[1])
print('im为(图像处理前的矩阵):\n',im)

plt.subplot(121).set_title('原始灰度图')
plt.imshow(im.astype('uint8'), cmap='gray')  # 可视化图片
plt.axis('off') #关闭xy轴

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([[0, -1, 0],
                         [-1, 5, -1],
                         [0, -1, 0]], dtype='float32')  # 定义轮廓检测算子
sobel_kernel = sobel_kernel.reshape((1, 1, 3, 3))  # 适配卷积的输入输出
conv1.weight.data = torch.from_numpy(sobel_kernel)  # 给卷积的 kernel 赋值

edge1 = conv1(Variable(im))  # 作用在图片上
print('edge1:\n', edge1)
print('edge1.shape[2]为:\n',edge1.shape[2])
print('edge1.shape[3]为:\n',edge1.shape[3])

for i in range(edge1.shape[2]):
    for j in range(edge1.shape[3]):
        if edge1[0][0][i][j] > 255:
            edge1[0][0][i][j] = 255
        if edge1[0][0][i][j] < 0:
            edge1[0][0][i][j] = 0

x = edge1.data.squeeze().numpy()
print('x的形状为:\n', x.shape)  # 输出大小
print('x为(图像去灰处理后的矩阵):\n', x)

plt.subplot(122).set_title('去除灰色')
plt.imshow(x, cmap='gray')
plt.axis('off')
plt.show()

代码2:使用imshow,设置imshow中参数  vmax,vmin  ,限制像素点范围

#深度学习  卷积后图片去灰
import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
import torch.nn.functional as F
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 = 'C:\\Users\\27513\\Pictures\\Camera Roll\\NNDL.png'
im = Image.open(file_path).convert('L')  # 读入一张灰度图的图片
im = np.array(im, dtype='float32')  # 将其转换为一个矩阵
print('im的形状为:\n', im.shape[0], im.shape[1])

plt.subplot(131).set_title('原始灰度图')
plt.imshow(im.astype('uint8'), cmap='gray')  # 可视化图片
plt.axis('off') #关闭xy轴

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([[0, -1, 0],
                         [-1, 5, -1],
                         [0, -1, 0]], dtype='float32')  # 定义轮廓检测算子
sobel_kernel = sobel_kernel.reshape((1, 1, 3, 3))  # 适配卷积的输入输出
conv1.weight.data = torch.from_numpy(sobel_kernel)  # 给卷积的 kernel 赋值

edge1 = conv1(Variable(im))  # 作用在图片上
print('edge1:\n', edge1)

x = edge1.data.squeeze().numpy()
print('x的形状为:\n', x.shape)  # 输出大小
print('x为:\n', x)

plt.subplot(132).set_title('不添加vmin vmax/卷积后')
plt.imshow(x, cmap='gray')
plt.axis('off') #关闭xy轴

plt.subplot(133).set_title('添加vmin vmax')
plt.imshow(x, cmap='gray', vmin=0, vmax=255)
plt.axis('off')
plt.show()

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第14张图片

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第15张图片

效果与代码1做出来的一样。 卷积后的图片 成功的去除掉了蒙了一层灰色的感觉,而且细节更加明显了。

多尝试几张图片,感受一下效果:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第16张图片

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第17张图片

现在将橘猫九宫图 全部进行去灰操作,看看效果:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第18张图片

我把没有进行去灰操作的大橘九宫图放在下面,对比一下:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第19张图片

对比之下可以看到,进行的去灰操作后,sobel  emboss 和outline的卷积效果更加明显了!这三个卷积核的效果有一个共同点:增强图像边缘、轮廓特征。(尤其是对于sobel和outline卷积核的效果最为明显,图片变为了黑白对比。)

也许是大橘猫照片的边缘轮廓特征不明显,所以我又换了一张本身边缘轮廓特征就比较明显的图片进行输出(这张照片是一个表情包):

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第20张图片

各个卷积核的效果都十分的明显!进行去灰操作的效果是显著的。不仅去除了灰蒙蒙的图片效果,还增强了图片细节!


5. 总结不同卷积核的特征和作用。

老师分享的网址:直观地解释图像内核 (setosa.io)

进入之后可以自己调整参数、卷积核、图片,进行可视化的直观体验。示例图:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第21张图片

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第22张图片

可以看到 卷积的过程为对应的像素位置相乘再求和,计算出新的像素值。这个网站真的很直观很好用!!不仅详细的展示了3*3卷积核的参数内容,而且还给出了图片进行卷积后的不同效果。

现在我以河北大学校猫-大橘学长的照片为例子,总结常用卷积核矩阵以及效果:

河北大学校猫-大橘学长 原图

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第23张图片

1.模糊效果:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第24张图片

2.浮雕效果:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第25张图片

3.锐化:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第26张图片

4.轮廓:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第27张图片

5.边缘sobel:(效果和上一个差不多,但是卷积核矩阵里的参数不同)

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第28张图片

6.indentity 不改变:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第29张图片


6. 代码解读:

老师给出了参考的代码,我先读取了自家猫咪的图片,放入代码中试着运行了一次,进行了观察:

 可以看到卷积后的图像对原图像进行了边缘特征的提取(不是很清晰,可以调高亮度)。猫咪身体的边缘轮廓、胡子、眼鼻嘴的轮廓形状特征都被提取了出来。

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第30张图片[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第31张图片

我print了其中几个重要的参数值,用于更好的理解卷积网络运行的过程:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第32张图片

老师提供代码+自己的解读注释:

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 = 'C:\\Users\\27513\\Pictures\\Camera Roll\\cat10.jpg'

# 读入一张灰度图的图片,convert('L')表示这张图像已经自动转换为灰度图
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()

#将numpy数据的图像重塑为torch张量,以适应卷积操作
im = torch.from_numpy(im.reshape((1, 1, im.shape[0], im.shape[1])))

#定义卷积层conv1,该卷积层输入通道数为1,输出通道数为1,卷积核大小为3*3,且无偏置项
conv1 = nn.Conv2d(1, 1, 3, bias=False)
print('卷积conv1的内容:', conv1)

#定义轮廓检测算子,为numpy数组类型(定义卷积核),用于轮廓检测
sobel_kernel = np.array([[-1, -1, -1],
                         [-1, 8, -1],
                         [-1, -1, -1]], dtype='float32')
#适配卷积的输入输出
sobel_kernel = sobel_kernel.reshape((1, 1, 3, 3))
print('改变适配输入输出形状的卷积核:\n',sobel_kernel,'\n',type(sobel_kernel))
#给卷积的 kernel 赋值w
conv1.weight.data = torch.from_numpy(sobel_kernel)
print('w参数值:',conv1.weight.data)

# 作用在图片上 Variable(im)是将输入图像转换为Variable类型,方便计算
edge1 = conv1(Variable(im))

#squeeze()消除大小为1的维度,使数据更便于处理。
x = edge1.data.squeeze().numpy()
print('卷积后图像形状:',x.shape)  # 输出大小

plt.imshow(x, cmap='gray')
plt.title('卷积后图像')
plt.show()

>卷积层conv1的建立

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第33张图片

输出的卷积层conv1信息为:
                      Conv2d(1, 1, kernel_size=(3, 3), stride=(1, 1), bias=False)

> sobel_kernel边缘检测算子(卷积核)的建立

   np.array([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]], dtype='float32')是在创建一个3x3的浮点数数组,这个数组就是Sobel算子,通过与图像进行卷积,可以用来检测图像的边缘。这个算子在中心位置(也就是8的位置)有一个峰值,而在其他位置的值都是负数。这样,当这个算子和图像进行卷积时,会在边缘的位置产生较大的正数响应。

    sobel_kernel = sobel_kernel.reshape((1, 1, 3, 3))是将这个2D数组重塑为一个4D数组。在PyTorch中,卷积操作需要输入具有四维的张量,这四维分别是:批次大小(batch size),通道数量(channel数量),高度(height),宽度(width)。在这段代码中,批次大小和通道数量都为1,所以需要将2D的Sobel算子重塑为3D的张量,这样就可以将其用于卷积操作。

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第34张图片


总结:

本次作业的心得体会,重点谈谈卷积能够提取特征的原理。

1.在代码部分,我了解到了在pytorch中,卷积操作需要输入具有4维张量,这四维张量分别是

  1. 批次大小(Batch Size):表示一次输入的样本数量。
  2. 通道数量(Channel Count):表示输入的每个样本具有多少个通道。对于彩色图像,这个                                                通道数量通常为3(RGB)。对于灰度图像,这个通道数量为1。
  3. 高度(Height):表示输入的每个样本的高度。
  4. 宽度(Width):表示输入的每个样本的宽度。

因此,一个典型的四维卷积输入张量形状可能为(Batch Size, Channel Count, Height, Width)。例如,如果有一个包含32个样本,每个样本都是3通道的彩色图像,图像大小为64x64,那么这个输入张量的形状将会是(32, 3, 64, 64)。

2.pytorch中的Variable类型是什么?

文心一言中我得到了如下解答:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第35张图片

pytorch官网中我得到了如下解答:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第36张图片

               意思就是Tensor已经替代了Variable类,新的pytorch已经剔除Variable类了。

一位博主的回答:
[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第37张图片

现在对我Variable有了更深一步的了解啦!
 

3.在网站中,获得了直观的卷积神经网络作用效果的体验:

   进入网站后,首先需要了解灰度图像是如何在计算机中展现出来的,灰度图像在计算机中以二维矩阵的方式存储,可以通过滑动鼠标到不同的像素点,观察对应灰度图上展示出来的效果。这些像素点的范围在[0,255],0展现为黑色,255展现为白色。

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第38张图片

之后便可以通过调整卷积核(3*3矩阵)来展现不同的图像卷积后的效果:

[23-24 秋学期]NNDL 作业6 卷积 [HBU]_第39张图片


对于文章中引用的博客链接和网络资料,在此鸣谢:

魏老师原博客:【23-24 秋学期】NNDL 作业6 卷积-CSDN博客

【精选】NNDL 作业5:卷积_笼子里的薛定谔的博客-CSDN博客

浅谈Pytorch中的Variable的使用方法_pytorch variable-CSDN博客

直观地解释图像内核 (setosa.io)

你可能感兴趣的:(深度学习,深度学习,人工智能,卷积神经网络)