深度学习7. 卷积的概念
ztj100 2024-12-01 07:00 39 浏览 0 评论
一、卷积的概念
卷积来源于英文的Convolution,其中Con是积分,vol是转、卷。
卷积是一种数学运算,常用于信号处理和图像处理等领域,它用简单的数学形式,描述了一个动态的过程。
卷积的定义如下(这个复杂的公式,在卷积神经网络中可能是用不到):
设 f 和 g 是两个定义在实数域上的函数,它们的卷积 f?g 定义为:
其中 t 为实数,τ 是积分变量。
在离散形式下,如果 f 和 g 是两个长度为 n 的向量,它们的卷积 f?g 定义为:
其中 k 是整数,[i] 表示向量 f 的第 i 个元素。
二、神经网络中的卷积
1. 神经网络卷积概念
在卷积神经网络中,卷积操作是一种特殊的线性变换,卷积核(也称为滤波器)在输入数据上进行滑动,每次计算与卷积核重叠部分的点乘和。
这样的操作可以提取输入数据的局部特征,实现特征的共享和抽象,从而使得网络对输入数据的变化更加鲁棒和准确。
2. 卷积核
卷积核是一种可学习的滤波器,用于对输入图像进行特征提取。卷积核通常是一个小的二维矩阵,其大小通常为 k×k,其中 k 是一个正整数,称为卷积核大小。卷积核的值通常是由神经网络自动学习得到的。
卷积核的作用是提取输入数据的局部特征。在卷积操作中,卷积核可以识别输入图像中的不同特征,例如边缘、纹理、角落等,从而提取更加高级的特征表示。通过使用多个卷积核,可以提取不同类型的特征,形成更加复杂的特征表示,进而提高模型的性能。
不同的卷积核(即采用不同的二维矩阵)可以实现不同的效果,常见的卷积核有:
- Sobel卷积核:边缘检测;
- Scharr卷积核:也是边缘检测卷积核,比Sobel更加平滑;
- Laplacian 卷积核:用于检测图像中的边缘和角点,具有旋转不变性和尺度不变性;
- 高斯卷积核:用于图像平滑,减少图像中的噪声和细节信息;
- 梯度卷积核:用于检测图像中的梯度信息,如水平和垂直方向的梯度;
- Prewitt 卷积核:用于检测图像中的边缘信息,与 Sobel 卷积核类似,但效果略差 ;
- Roberts 卷积核:用于检测图像中的边缘信息,与 Sobel 卷积核类似,但计算速度更快,精度稍低;
- LoG 卷积核:Laplacian of Gaussian 卷积核,是 Laplacian 卷积核和高斯卷积核的组合,用于检测图像中的边缘和斑点。
3. 卷积核大小
卷积核的大小是卷积神经网络中的一个超参数,通常与输入数据的尺寸以及需要提取的特征的大小有关。在卷积神经网络中,卷积核的大小通常比较小,例如常见的卷积核大小为 3 或 5,因为较小的卷积核可以更好地保留输入图像中的局部特征。
同时,卷积核的大小也需要根据卷积操作的步幅和填充等超参数进行选择。在后面例子中,卷积核大小为 3,步幅为 1,填充为 1,即每次卷积操作会对输入图像中的 3 × 3 3\times33×3 的区域进行处理,并生成一个相同大小的卷积特征。填充的目的是为了保留输入图像的边缘信息,以避免在卷积操作中丢失像素。
需要注意的是,卷积核大小的选择需要根据具体问题进行调整,通常需要通过实验来确定最佳的超参数。
三、实现一个简单的卷积功能
1. 卷积函数
import numpy as np
from PIL import Image
def convolve(image, kernel):
# 获取图像和卷积核的大小
image_rows, image_cols = image.shape
kernel_rows, kernel_cols = kernel.shape
# 计算输出图像的大小
output_rows = image_rows - kernel_rows + 1
output_cols = image_cols - kernel_cols + 1
# 初始化输出图像矩阵,全零的矩阵
output = np.zeros((output_rows, output_cols))
# 执行卷积操作
for row in range(output_rows):
for col in range(output_cols):
output[row, col] = np.sum(image[row:row + kernel_rows, col:col + kernel_cols] * kernel)
return output
自定义的卷积函数接收两个参数:
- image: 输入图像
- kernel: 卷积核
卷积使用 valid 卷积的方式,在进行卷积操作时,输出图像的尺寸会变小,计算公式是:
(image_rows - kernel_rows + 1, image_cols - kernel_cols + 1)
程序使用两个嵌套的循环遍历输出图像的每个像素,并计算该像素对应的卷积结果。
np.sum函数中的参数 image 对输入图像进行切片,矩阵会进行逐元素相乘(Hadamard乘积或元素级乘积)。image[row:row + kernel_rows, col:col + kernel_cols] 和kernel的大小都是 kernel_rows x kernel_cols, 相乘结果返回一个相同形状的矩阵。
2. 边缘检测卷积核调用示例
# 加载图像
img = np.array(Image.open('lena_gray.jpg').convert('L'))
# 定义卷积核
kernel = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]])
# 执行卷积操作
output = convolve(img, kernel)
# 保存输出图像
output_img = Image.fromarray(np.uint8(output))
output_img.save('lena_gray_convolved.jpg')
示例的卷积核是一个简单的边缘检测器,用于检测图像中的边缘。
这里加载一张灰度图:
程序输出结果如下 :
3. 高斯卷积核示例
# 加载图像
img = np.array(Image.open('lena_gray.jpg').convert('L'))
# 定义卷积核
def gaussian_kernel(size, sigma):
x, y = np.mgrid[-size:size+1, -size:size+1]
g = np.exp(-((x**2 + y**2)/(2.0*sigma**2)))
return g / g.sum()
kernel = gaussian_kernel(3, 1.5)
# 执行卷积操作
output = convolve(img, kernel)
# 保存输出图像
output_img = Image.fromarray(np.uint8(output))
output_img.save('lena_gray_convolved.jpg')
输出结果:
四、PyTorch计算卷积
1. 生成单通道图像调用卷积
(1)生成单通道图像torch.randn(1, 1, 28, 28)
下面用torch.randn(1, 1, 28, 28) 来生成随机数的 PyTorch 函数,它返回一个大小为 (1, 1, 28, 28) 的张量。其中每个参数的具体含义如下:
- 第一个参数 1 表示生成的张量的 batch size(批大小)为 1。
- 第二个参数 1 表示生成的张量的通道数为 1(单通道图像)。
- 第三个参数 28 表示生成的张量的高度为 28。
- 第四个参数 28 表示生成的张量的宽度为 28。
torch.randn(1, 1, 28, 28) 返回的张量可以看作是大小为 1x28x28 的单通道图像,每个像素的值是从标准正态分布(均值为 0,方差为 1)中随机采样得到的。
(2)卷积层
nn.Conv2d 是 PyTorch 中用于定义卷积层的类。
代码nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, padding=1) 表示创建一个卷积层对象 conv_layer,参数的含义如下:
- in_channels=1 表示输入通道数为 1,即输入的是单通道的图像。
- out_channels=16 表示输出通道数为 16,即卷积核的数量为 16;卷积核的数量是一个经验值,需要根据实际情况进行调整,并且会对模型的运行速度和内存占用等方面产生影响。过多的卷积核会导致模型更加复杂,需要更多的计算和存储资源,而过少的卷积核可能无法充分提取输入数据的特征。。
- kernel_size=3 表示卷积核大小为 3×3。
- padding=1 表示在输入的每个边缘填充 1 个零。这样做的目的是为了保持输入输出大小相同,即输出特征图的大小与输入特征图的大小相同。如果不进行填充操作,则卷积核会“越过”图像的边缘像素,从而导致输出特征图的大小减小。
最终,可以通过调用 conv_layer(input_data) 来实现卷积操作,其中 input_data 是输入的数据,卷积操作的结果将作为函数返回值。
import torch
import torch.nn as nn
# 创建一个大小为 28*28 的单通道图像
input_data = torch.randn(1, 1, 28, 28) # 一个大小为28x28的单通道图像
# 创建卷积层,输入通道数为 1
# 输出通道数16
# 卷积核大小3*3
# 1个0填充
conv_layer = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, padding=1)
# 对输入数据进行卷积操作
output_data = conv_layer(input_data)
# 输出结果
print(output_data.shape) # (1, 16, 28, 28)
卷积后得到了 1个批次、16 个大小为 28 × 28 28\times2828×28 的特征图。
2. 加载灰度图像进行卷积操作
下面示例中,卷积结果 [batch_size, channel,height,width] 会进行降维操作,以便于可视化显示。
最后会使用 Image.fromarray ,将数组转为图片显示出来。
import torch.nn as nn
import torchvision.transforms as transforms
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# 读入示例图片
img = Image.open('lena_gray.jpg').convert('L') # 将示例图片转换为灰度图
plt.imshow(img, cmap='gray')
plt.show()
# 将图片转换为张量并增加一个维度作为批次维度
img_tensor = transforms.ToTensor()(img).unsqueeze(0)
# 创建卷积层,输入通道数为 1,输出通道数1,卷积核大小3*3,1个0填充
conv_layer = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=1)
# 对输入数据进行卷积操作
output_tensor = conv_layer(img_tensor)
print(output_tensor.shape)
# 输出 torch.Size([1, 1, 426, 397])
# 将卷积结果转换为numpy数组并移除批次维度
output_np = output_tensor.squeeze(0).squeeze(0).detach().numpy()
print(output_np.shape)
# 输出 (426, 397)
# 将卷积结果转换为灰度图像
output_img = Image.fromarray(np.uint8(output_np * 255), mode='L')
# 将卷积结果保存为图片
output_img.save('output.jpg')
# 使用Matplotlib库展示卷积结果
output_mat = plt.imread('output.jpg')
plt.imshow(output_mat, cmap='gray')
plt.show()
输出:
3. 对彩色图片卷积,输出1通道
对彩色图片进行卷积,要把输入通道数改为3,加载时选择RGB:
import torch.nn as nn
import torchvision.transforms as transforms
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# 读入示例彩色图片
img = Image.open('lena_color.png').convert('RGB')
plt.imshow(img)
plt.show()
# 将图片转换为张量并增加一个维度作为批次维度
img_tensor = transforms.ToTensor()(img).unsqueeze(0)
# 创建卷积层,输入通道数为 3,输出通道数1,卷积核大小3*3,1个0填充
conv_layer = nn.Conv2d(in_channels=3, out_channels=1, kernel_size=3, padding=1)
# 对输入数据进行卷积操作
output_tensor = conv_layer(img_tensor)
print(output_tensor.shape)
# 将卷积结果转换为numpy数组并移除批次维度
#output_np = output_tensor.squeeze(0).squeeze(0).detach().numpy()
#print(output_np.shape)
# 将卷积结果转换为灰度图像
#output_img = Image.fromarray(np.uint8(output_np * 255), mode='L')
output_np = output_tensor.squeeze(0).detach().numpy() # 形状为 (C, H, W)
output_np = np.repeat(output_np, 3, axis=0) # 将通道数由 1 改为 3
output_np = np.expand_dims(output_np, axis=1) # 添加一个新的维度
output_img = transforms.ToPILImage()(output_np) # 转换为 PIL.Image 对象
# 将卷积结果保存为图片
output_img.save('output.jpg')
# 使用Matplotlib库展示卷积结果
output_mat = plt.imread('output.jpg')
plt.imshow(output_mat, cmap='gray')
plt.show()
输入:
卷积结果:
4. 输出3通道的卷积操作
import torch.nn as nn
import torchvision.transforms as transforms
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# 读入示例图片
img = Image.open('lena_color.png').convert('RGB')
plt.imshow(img)
plt.show()
# 将图片转换为张量并增加一个维度作为批次维度
img_tensor = transforms.ToTensor()(img).unsqueeze(0)
# 创建卷积层,输入通道数为3,输出通道数为3,卷积核大小3*3,1个0填充
conv_layer = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, padding=1)
# 对输入数据进行卷积操作
output_tensor = conv_layer(img_tensor)
# 这时的形状是 torch.Size([1, 3, 726, 724])
# # 将卷积结果转换为图像
output_np = output_tensor.squeeze(0).detach().numpy() # 形状为 (C, H, W)
# 这时的形状 (3, 726, 724)
output_np = np.transpose(output_np, (1, 2, 0)) # 转置使得颜色通道在最后一个维度
# 这时的形状 (726, 724, 3)
# 为了转为图像,下面要对数据处理,传入的 numpy 数组中的数据类型不是 uint8 类型。
# 由于 transforms.ToPILImage() 只支持 uint8 类型的数据,要把 float32 类型的 numpy 数组缩放到 0-255 的范围,并转换为 uint8 类型。 np.clip 函数和 np.uint8 来实现此功能
# 如果输出1通道,ToPILImage会转成unit8数据,但输出3通道时候是转成float32,需要自己加转换
output_np = np.clip(output_np * 255, 0, 255).astype(np.uint8)
# 这时形状没有发生变化 (726, 724, 3)
output_img = transforms.ToPILImage()(output_np) # 转换为 PIL.Image 对象
# 展示卷积结果
plt.imshow(output_img)
plt.show()
输出:
相关推荐
- sharding-jdbc实现`分库分表`与`读写分离`
-
一、前言本文将基于以下环境整合...
- 三分钟了解mysql中主键、外键、非空、唯一、默认约束是什么
-
在数据库中,数据表是数据库中最重要、最基本的操作对象,是数据存储的基本单位。数据表被定义为列的集合,数据在表中是按照行和列的格式来存储的。每一行代表一条唯一的记录,每一列代表记录中的一个域。...
- MySQL8行级锁_mysql如何加行级锁
-
MySQL8行级锁版本:8.0.34基本概念...
- mysql使用小技巧_mysql使用入门
-
1、MySQL中有许多很实用的函数,好好利用它们可以省去很多时间:group_concat()将取到的值用逗号连接,可以这么用:selectgroup_concat(distinctid)fr...
- MySQL/MariaDB中如何支持全部的Unicode?
-
永远不要在MySQL中使用utf8,并且始终使用utf8mb4。utf8mb4介绍MySQL/MariaDB中,utf8字符集并不是对Unicode的真正实现,即不是真正的UTF-8编码,因...
- 聊聊 MySQL Server 可执行注释,你懂了吗?
-
前言MySQLServer当前支持如下3种注释风格:...
- MySQL系列-源码编译安装(v5.7.34)
-
一、系统环境要求...
- MySQL的锁就锁住我啦!与腾讯大佬的技术交谈,是我小看它了
-
对酒当歌,人生几何!朝朝暮暮,唯有己脱。苦苦寻觅找工作之间,殊不知今日之事乃我心之痛,难道是我不配拥有工作嘛。自面试后他所谓的等待都过去一段时日,可惜在下京东上的小金库都要见低啦。每每想到不由心中一...
- MySQL字符问题_mysql中字符串的位置
-
中文写入乱码问题:我输入的中文编码是urf8的,建的库是urf8的,但是插入mysql总是乱码,一堆"???????????????????????"我用的是ibatis,终于找到原因了,我是这么解决...
- 深圳尚学堂:mysql基本sql语句大全(三)
-
数据开发-经典1.按姓氏笔画排序:Select*FromTableNameOrderByCustomerNameCollateChinese_PRC_Stroke_ci_as//从少...
- MySQL进行行级锁的?一会next-key锁,一会间隙锁,一会记录锁?
-
大家好,是不是很多人都对MySQL加行级锁的规则搞的迷迷糊糊,一会是next-key锁,一会是间隙锁,一会又是记录锁。坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何如何用命令分析加...
- 一文讲清怎么利用Python Django实现Excel数据表的导入导出功能
-
摘要:Python作为一门简单易学且功能强大的编程语言,广受程序员、数据分析师和AI工程师的青睐。本文系统讲解了如何使用Python的Django框架结合openpyxl库实现Excel...
- 用DataX实现两个MySQL实例间的数据同步
-
DataXDataX使用Java实现。如果可以实现数据库实例之间准实时的...
- MySQL数据库知识_mysql数据库基础知识
-
MySQL是一种关系型数据库管理系统;那废话不多说,直接上自己以前学习整理文档:查看数据库命令:(1).查看存储过程状态:showprocedurestatus;(2).显示系统变量:show...
- 如何为MySQL中的JSON字段设置索引
-
背景MySQL在2015年中发布的5.7.8版本中首次引入了JSON数据类型。自此,它成了一种逃离严格列定义的方式,可以存储各种形状和大小的JSON文档,例如审计日志、配置信息、第三方数据包、用户自定...
你 发表评论:
欢迎- 一周热门
-
-
MySQL中这14个小玩意,让人眼前一亮!
-
旗舰机新标杆 OPPO Find X2系列正式发布 售价5499元起
-
【VueTorrent】一款吊炸天的qBittorrent主题,人人都可用
-
面试官:使用int类型做加减操作,是线程安全吗
-
C++编程知识:ToString()字符串转换你用正确了吗?
-
【Spring Boot】WebSocket 的 6 种集成方式
-
PyTorch 深度学习实战(26):多目标强化学习Multi-Objective RL
-
pytorch中的 scatter_()函数使用和详解
-
与 Java 17 相比,Java 21 究竟有多快?
-
基于TensorRT_LLM的大模型推理加速与OpenAI兼容服务优化
-
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)