深度学习7. 卷积的概念 |
您所在的位置:网站首页 › uint8_t转int › 深度学习7. 卷积的概念 |
一、卷积的概念卷积来源于英文的Convolution,其中Con是积分,vol是转、卷。 卷积是一种数学运算,常用于信号处理和图像处理等领域,它用简单的数学形式,描述了一个动态的过程。 卷积的定义如下(这个复杂的公式,在卷积神经网络中可能是用不到): 设 f 和 g 是两个定义在实数域上的函数,它们的卷积 f*g 定义为: (f * g)(t) = \int_{-\infty}^{\infty} f(\tau) g(t-\tau) d\tau 其中 t 为实数, \tau 是积分变量。 在离散形式下,如果 f 和 g 是两个长度为 n 的向量,它们的卷积 f*g 定义为: (f * g)[k] = \sum_{i=0}^{n-1} f[i] g[k-i] 其中 k 是整数,[i] 表示向量 f 的第 i 个元素。 二、神经网络中的卷积1. 神经网络卷积概念在卷积神经网络中,卷积操作是一种特殊的线性变换,卷积核(也称为滤波器)在输入数据上进行滑动,每次计算与卷积核重叠部分的点乘和。 这样的操作可以提取输入数据的局部特征,实现特征的共享和抽象,从而使得网络对输入数据的变化更加鲁棒和准确。 2. 卷积核卷积核是一种可学习的滤波器,用于对输入图像进行特征提取。卷积核通常是一个小的二维矩阵,其大小通常为 k\times k ,其中 k 是一个正整数,称为卷积核大小。卷积核的值通常是由神经网络自动学习得到的。 卷积核的作用是提取输入数据的局部特征。在卷积操作中,卷积核可以识别输入图像中的不同特征,例如边缘、纹理、角落等,从而提取更加高级的特征表示。通过使用多个卷积核,可以提取不同类型的特征,形成更加复杂的特征表示,进而提高模型的性能。 不同的卷积核(即采用不同的二维矩阵)可以实现不同的效果,常见的卷积核有: Sobel卷积核:边缘检测;Scharr卷积核:也是边缘检测卷积核,比Sobel更加平滑;Laplacian 卷积核:用于检测图像中的边缘和角点,具有旋转不变性和尺度不变性;高斯卷积核:用于图像平滑,减少图像中的噪声和细节信息;梯度卷积核:用于检测图像中的梯度信息,如水平和垂直方向的梯度;Prewitt 卷积核:用于检测图像中的边缘信息,与 Sobel 卷积核类似,但效果略差 ;Roberts 卷积核:用于检测图像中的边缘信息,与 Sobel 卷积核类似,但计算速度更快,精度稍低;LoG 卷积核:Laplacian of Gaussian 卷积核,是 Laplacian 卷积核和高斯卷积核的组合,用于检测图像中的边缘和斑点。3. 卷积核大小卷积核的大小是卷积神经网络中的一个超参数,通常与输入数据的尺寸以及需要提取的特征的大小有关。在卷积神经网络中,卷积核的大小通常比较小,例如常见的卷积核大小为 3 或 5,因为较小的卷积核可以更好地保留输入图像中的局部特征。 同时,卷积核的大小也需要根据卷积操作的步幅和填充等超参数进行选择。在后面例子中,卷积核大小为 3,步幅为 1,填充为 1,即每次卷积操作会对输入图像中的 3\times3 的区域进行处理,并生成一个相同大小的卷积特征。填充的目的是为了保留输入图像的边缘信息,以避免在卷积操作中丢失像素。 需要注意的是,卷积核大小的选择需要根据具体问题进行调整,通常需要通过实验来确定最佳的超参数。 三、实现一个简单的卷积功能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')示例的卷积核是一个简单的边缘检测器,用于检测图像中的边缘。 这里加载一张灰度图: 程序输出结果如下 : 输出结果: 下面用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\times3$。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\times28$ 的特征图。 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,加载时选择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()输入: 卷积结果: 输出: |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |