计算机视觉:卷积神经网络(CNN)基本概念(一)

news/2025/2/21 12:33:46/

第一章:计算机视觉中图像的基础认知
第二章:计算机视觉卷积神经网络(CNN)基本概念(一)
第三章:计算机视觉卷积神经网络(CNN)基本概念(二)
第四章:搭建一个经典的LeNet5神经网络(附代码)
第五章:计算机视觉:神经网络实战之手势识别(附代码)
第六章:计算机视觉:目标检测从简单到容易(一)(附代码)

一、引言

卷积神经网络(Convolutional Neural Network, CNN)是一种专门设计用于处理图像数据的深度学习模型,是计算机视觉领域的核心技术,从人脸识别到自动驾驶,它的应用无处不在。

它在图像识别、分类、目标检测等领域表现出色,通过一系列组件如卷积层、池化层全连接层等,能够自动从图像中学习有用的特征,而无需手动设计。

它是如何从图像中“看见”并理解世界的?本文将解析CNN的核心组件、特征抽取原理,并通过代码展示其强大能力。

二、图像特征

在理解卷积神经网络概念之前,先理解图像中的特征是什么?这个很重要。

1. 什么是特征?
定义:特征是图像中可辨识的模式或结构,例如边缘角点纹理颜色分布等。

类比:假设你看到一只猫,你会注意到它的耳朵形状(边缘)、胡须(纹理)、眼睛位置(角点)——这些就是“特征”。

2. 特征分几个层次
低层特征:边缘、角点、颜色(由浅层卷积核提取),定位物体轮廓,区分前景与背景。
中层特征:纹理、简单形状(如圆形、线条组合),
高层特征:物体部件(如车轮、猫耳)或完整物体,检测复杂物体部件(如眼睛、鼻子)。

低层特征:边缘与角点检测示例(使用 OpenCV 的 Canny 算法):
边缘特征是指图像中灰度值变化较大的区域,通常表示物体的边界。
角点特征是指图像中两条边缘相交的点,通常表示物体的角点。

import cv2
import matplotlib.pyplot as plt# 读取图像并转灰度
img = cv2.imread("boy.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Canny 边缘检测
edges = cv2.Canny(gray, threshold1=100, threshold2=200)# 显示结果
plt.subplot(131), plt.imshow(img), plt.title('Original')
plt.subplot(132), plt.imshow(gray, cmap='gray'), plt.title('Original gray')
plt.subplot(133), plt.imshow(edges, cmap='gray'), plt.title('Edges')
plt.show()

效果:左侧为原图,中间为灰度图,右侧为边缘检测结果,白色线条表示边缘。
在这里插入图片描述
中层特征:纹理与形状,纹理提取示例:
纹理特征描述了图像中像素的排列方式,反映了表面的粗糙度、平滑度等特性
形状特征描述了物体的轮廓和几何形状。

from skimage.feature import local_binary_pattern
import numpy as np# 计算 LBP 纹理特征
radius = 3
n_points = 8 * radius
lbp = local_binary_pattern(gray, n_points, radius, method='uniform')# 显示纹理
plt.imshow(lbp, cmap='jet'), plt.title('LBP Texture')
plt.colorbar()
plt.show()

效果:不同颜色表示不同纹理模式,例如重复的斑纹或平滑区域。

cmap=‘jet’cmap=‘gray’
在这里插入图片描述在这里插入图片描述

高层特征:物体部件(以 CNN 特征图为例)可视化 CNN 卷积层的特征图(使用 PyTorch):

import torch
import torchvision.models as models
import torchvision.transforms as transforms
from PIL import Image# 加载预训练的 VGG16 模型
model = models.vgg16(pretrained=True).features[:4]  # 取前4层(第一个卷积块)
model.eval()# 读取图像并预处理
img = Image.open("boy.jpg")
transform = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
input_tensor = transform(img).unsqueeze(0)# 提取特征图
with torch.no_grad():features = model(input_tensor)# 可视化第1个卷积核的输出(第0通道)
plt.imshow(features[0, 0, :, :], cmap='viridis')
plt.title('CNN Feature Map (Channel 0)')
plt.colorbar()
plt.show()

在这里插入图片描述

三、什么是卷积神经网络

CNN_107">3.1 CNN基本概念

卷积神经网络CNN)是一种特别适用于图像处理任务的深度学习架构。其主要优势在于能够自动提取图像中的特征(边缘、纹理、形状等),而不是依赖于手工设计的特征提取方法。CNN包括卷积层、池化层全连接层等关键组件,每个组件都有特定的功能和作用。

3.2 主要组件

  1. 卷积层(Convolutional Layer)
    • 功能:卷积层通过应用一系列卷积核(也称为滤波器)来提取图像的局部特征。
    • 工作原理:每个卷积核在输入图像上滑动,计算局部区域的加权和,生成一个特征图(Feature Map)。这些特征图捕捉了图像的边缘、纹理、形状等低级特征。
    • 关键参数
      • 卷积核尺寸(如3×3)
      • 步长(Stride):滑动步长(影响输出尺寸)
      • 填充(Padding):边缘补零(控制输出尺寸)

使用一个形象一点的动图来展示卷积,蓝色边框组成的矩阵,就代表一张图片,因为一张图片可以转换成为一个矩阵,红色的矩阵就是卷积核,卷积核就是一个矩阵,取名叫卷积核,红色的矩阵在蓝色图片矩阵上滑动,每滑动一步,就做一次矩阵乘法,就是 2 个9乘9 的矩阵相乘,得到一个数(黑色的点),所有的像素点都被滑动完了,就得到一个右边的新的红色的矩阵,这个红色矩阵代表的是一张新的图像,新的图像也代表了左边原图的一个特征图。这个过程就是卷积,你细品!
在这里插入图片描述
下面这张图,是经典的卷积神经网络,convolutions 就是卷积操作。红色箭头指的地方,可以形象看到,卷积之后的结果,使图像变多了,这些多出来的图像就是上一层图像的特征图。
在这里插入图片描述

代码示例
这段代码展示了如何手动实现一个简单的二维卷积操作,使用NumPy库来处理图像和卷积核。

import numpy as np
# 图像
# 生成一个 64 × 64 的二维数组(模拟灰度图像),其元素是从标准正态分布中随机抽取的。
img = np.random.randn(64, 64)
# 卷积核
# 生成一个 3×3 的二维数组(模拟卷积核或滤波器),其元素也是从标准正态分布中随机抽取的。
kernel = np.random.randn(3, 3)
# 初始尺寸
# 获取输入图像的高度和宽度。
H, W = img.shape
# 获取卷积核的高度和宽度。
k, k = kernel.shape
# 结果
# 根据输入图像和卷积核的尺寸,初始化一个与输出大小相匹配的全零矩阵。
# 输出的尺寸是 (H-k+1, W-k+1),这是由于卷积过程中卷积核在图像上滑动时的有效区域大小。
result = np.zeros(shape=(H-k+1, W-k+1))
"""卷积的基本计算
"""
# 遍历 H 方向,遍历图像的高度方向,确保卷积核可以在该位置完全覆盖图像的部分。
for h_idx in range(H-k+1):# 遍历 W 方向,遍历图像的宽度方向,同样确保卷积核可以在此位置完全覆盖图像的部分。for w_idx in range(W-k+1):# 图像块,提取当前卷积核位置对应的图像块。img_block = img[h_idx: h_idx + k, w_idx: w_idx + k]# 填充结果# 对提取出的图像块和卷积核进行逐元素相乘后求和,得到的结果存入结果矩阵对应的位置。result[h_idx, w_idx] = (img_block * kernel).sum()
# 结果矩阵 result 包含了所有卷积操作的结果,每个值代表了原图中相应位置经过卷积运算后的响应值。
result

总结:这段代码演示了如何使用纯Python和NumPy实现基本的二维卷积操作。它首先生成了一个模拟的图像和卷积核,然后通过遍历图像的每一个可能放置卷积核的位置,计算卷积核与图像局部区域的点积之和,从而形成一个新的特征图。这个过程是许多计算机视觉任务中的基础,比如边缘检测、特征提取等。通过这种方式,可以直观地理解卷积操作是如何工作的。

上面讲解的是灰度图的卷积过程,灰度图只有一个通道,数据形状[N, C, H, W]是[1, 1, H, W],N代表图片的数量,C 代表图片的通道数,彩色图片通道数就是 3(红、绿、蓝),灰度图的通道数是 1,所以灰度图的数据形状是[1, 1, H, W]。如果是多张图片一起卷积,数据形状就是[N, 1, H, W]。

下面这段代码,是使用PyTorch实现卷积操作

import torch
import torch.nn as nn# 定义输入:1张3通道的5x5图像
input = torch.randn(1, 3, 5, 5)  # [N, C, H, W]# 定义卷积层:3输入通道,2输出通道,3x3卷积核,步长1,填充1
conv = nn.Conv2d(in_channels=3, out_channels=2, kernel_size=3, stride=1, padding=1)# 执行卷积
output = conv(input)
print(output.shape)  # 输出形状:[1, 2, 5, 5]

input的数据形状是[N, C, H, W]=[1, 3, 5, 5],使用torch.randn随机生成的数据,模拟一张彩色图片,C=3代表图片具有 3 个通道。最终输出的形状output.shape [N, C, H, W]=[1, 2, 5, 5],N代表图片的数量,一张图片输入,卷积之后,输出一张图片。C 代表通道数,输入 3 个通道,卷积之后,输出 2 个通道,这是怎么计算的呢?下面是从网络上复制的一张图,来说明,
在这里插入图片描述
第一排 3 个 矩阵代表的是一张图片的 3 个通道(红、绿、蓝)的数据,第二排是 3 个卷积核组成一组,分别对应第一排的通道,本质上就是卷积核矩阵在原图上滑动,卷积核矩阵和原图对应的通道矩阵,做矩阵乘法,得到了第三排的数字,把这 3 个数字相加,在加上一个 Bias 偏执,最终输出 Output 矩阵,就是卷积之后的结果,这个过程就是把一张有 3 个通道的图,通过卷积计算之后,输出为一个通道。

回到上面的问题:输入 3 个通道,卷积之后,输出 2 个通道,这是怎么计算的?,在看下面另外一张从网上复制的图:
在这里插入图片描述
最左边输入是 3 个通道,最右边输出 2 个矩阵,中间有 2 组卷积核,每一组卷积核和原图做卷积计算之后,就会输出一个矩阵,2 组卷积核,就会输出 2 个矩阵。

  1. 激活函数(Activation Function)
    • 功能:引入非线性因素,使网络能够学习更复杂的模式,在二维平面里简单理解,一条直线y=wx+b,不管你的参数w和 b做什么样的变化,y 始终是一条直线,x 和 y 始终是线性关系,为了把直线掰弯,就得在y上面增加一个函数,这个使直线弯曲的函数,就是激活函数
    • 常用类型:ReLU、Sigmoid 和 Tanh。

下面是三个函数在二维坐标系里的图像,感受一下,函数都很简单,比如ReLU:f(x)= max(0,x),即对于输入x,如果x大于0,则输出x;如果x小于或等于0,则输出0。上面的例子y=wx+b,增加一个ReLU,y=max(0,wx+b),y就不再是一条直线了,y 和 x 的关系就不再是线性的了,
在这里插入图片描述
在这里插入图片描述
你可能会说,这样做了那不就改变了原本的表达含义了吗,其实我们要处理的任务本来就不是一个线性关系,比如下面这个图像分类,把黄色的点和蓝色的点分开,就不会是一条线性的线。
在这里插入图片描述

我们在训练模型的时候,计算机要从一推数据中去找规律,本来就很复杂,不可能是线性的规律。无激活函数的神经网络仅是线性变换的堆叠,无法学习复杂模式。后面在讲激活函数的威力,现在只需要有个初步认识。

代码示例:ReLU激活效果可视化

import matplotlib.pyplot as plt# 生成一个从-5到5之间均匀分布的100个数值的张量。这将作为ReLU激活函数的输入值范围。
x = torch.linspace(-5, 5, 100)
# 将ReLU激活函数应用于每个元素的 x 张量,得到一个新的张量 y,其中所有负数都被替换为0。
y = nn.ReLU()(x)
# 将PyTorch张量转换为NumPy数组后进行绘图。
# x.numpy() 和 y.numpy() 分别表示ReLU函数的输入和对应的输出值。
plt.plot(x.numpy(), y.numpy())
plt.title("ReLU Activation")
plt.xlabel("Input")
plt.ylabel("Output")
plt.show()

在这里插入图片描述
直观地看到ReLU是如何工作的——它将所有的负输入值变为0,而正输入值保持不变。

  • 在 (x<0) 的区域,图形与X轴重合,因为ReLU对所有负输入返回0。
  • 在 (x>0) 的区域,图形是一条斜率为1的直线,表明ReLU对所有正输入返回其本身值。

激活函数如何发挥作用,可以看看这个视频,比较形象,《10分钟直观展示神经网络是怎样学会任何东西的!》

  1. 池化层(Pooling Layer)
    • 功能:通过降采样操作减少特征图的尺寸,即宽度和高度,同时保留重要的信息,从而降低计算复杂度并防止过拟合。就是特征图太大了,为了减少计算量,把特征图的像素变小,假如特征图是 500 x 500 像素的图,通过某种方式,把特征图变成 100 x 100像素。
    • 常用类型:最大池化(Max Pooling)和平均池化(Average Pooling)。

最大池化(Max Pooling)

定义:最大池化操作是在输入特征图上滑动一个固定大小的窗口(通常称为池化核或滤波器),并在每个窗口内选择最大的值作为输出特征图对应位置的值。还是拿这个动图举例,看它是不是把一张大图,滑动+计算之后,就变成了右边的图,右边的图比左边的小。
在这里插入图片描述
还是看看经典的 CNN 网络,红色箭头指的地方,可以形象看到,这就是池化之后的结果,图像的尺寸相比前一层的图片尺寸变小了。
在这里插入图片描述

工作原理

  • 假设有一个 n x n 的局部区域,最大池化会选择这个区域内最大的数值。
  • 例如,在 (2 x 2) 的池化窗口中,如果输入值为
    在这里插入图片描述
    那么最大池化的结果将是 (4),因为它是该窗口中的最大值。

特点

  • 保持显著特征:最大池化倾向于保留最突出的特征(即具有最高激活值的特征),这对于识别图像中的关键部分特别有用。
  • 减少噪声影响:由于它只选择最强信号,因此可以帮助减少背景噪声的影响。
  • 空间不变性:有助于模型对输入的小变化不敏感,增加模型的鲁棒性。

示例代码

import torch
import torch.nn as nn# 创建一个简单的2D张量作为输入
input_tensor = torch.tensor([[[[1, 2, 3, 0],[4, 5, 6, 0],[7, 8, 9, 0],[0, 0, 0, 0]
]]], dtype=torch.float32)# 定义最大池化层
# kernel_size=2, 表示卷积核的大小是2乘2的矩阵
# stride=2,表示每次移动 2 个像素
max_pool = nn.MaxPool2d(kernel_size=2, stride=2)# 应用最大池化
output_tensor = max_pool(input_tensor)
print("最大池化结果:\n", output_tensor)

输出:

最大池化结果:tensor([[[[5., 6.],[8., 9.]]]])

平均池化(Average Pooling)

定义:平均池化操作同样是在输入特征图上滑动一个固定大小的窗口,但它计算的是窗口内所有数值的平均值,而不是最大值。

工作原理

  • 对于同样的 (2 x 2) 区域,如果输入值为
    在这里插入图片描述
    平均池化的结果将是 (1+3+4+2) / 4 = 2.5。

特点

  • 平滑过渡:平均池化通过取平均值的方式可以产生更加平滑的结果,有助于减小特征图中的突变。
  • 信息保留:与最大池化相比,平均池化可能保留更多的原始信息,因为它考虑了区域内所有像素的贡献。

示例代码

# 使用相同的输入张量
# 定义平均池化层
avg_pool = nn.AvgPool2d(kernel_size=2, stride=2)# 应用平均池化
output_tensor_avg = avg_pool(input_tensor)
print("平均池化结果:\n", output_tensor_avg)

输出结果:

平均池化结果:tensor([[[[3.0000, 2.2500],[3.7500, 2.2500]]]])
  1. 全连接层(Fully Connected Layer)
    • 功能:将前面层提取的特征图展平成一维向量,然后通过多层全连接层进行分类或回归,简单理解就是将一个多维度的矩阵,转换成为一个一维的矩阵。全连接层通常位于网络的末端,用于输出最终的预测结果。

在看看经典的 CNN 网络,红色箭头指的地方,可以形象看到,这就是全连接层。在简单点理解就是一个一个的像素点,组成一个一维向量。在这里插入图片描述

具体解释

  1. 特征提取:通过一系列卷积层和池化层,从输入图像中提取出有用的特征。每一层都会生成一个或多个特征图,表示不同层次的抽象特征。

  2. 展平操作:在进入全连接层之前,必须将这些特征图转换为一维向量。这是因为全连接层中的每个神经元都与前一层的所有神经元相连,要求输入是一个一维向量而不是多维矩阵。

  3. 全连接层:展平后的向量被馈送到一个或多个全连接层中。每个全连接层由许多神经元组成,每个神经元接收前一层所有神经元的输出,并通过加权求和和激活函数来计算自己的输出。最后一层通常用于分类或回归任务,其输出维度取决于具体的应用场景(例如,在分类任务中,输出层的神经元数量等于类别的数量)。

示例代码

这里有一个简单的示例,展示如何使用PyTorch实现这个过程:

import torch
import torch.nn as nn# 假设我们有一个经过卷积和池化后的特征图,尺寸为 (batch_size, channels, height, width)
# 这里假设 batch_size=64, channels=32, height=7, width=7
feature_map = torch.randn((64, 32, 7, 7))  # 展平特征图:将 (batch_size, channels, height, width) 转换为 (batch_size, channels*height*width)
flattened_vector = feature_map.view(feature_map.size(0), -1)print("原始特征图尺寸:", feature_map.size())
print("展平后的向量尺寸:", flattened_vector.size())# 定义全连接层 全连接层的输入是in_features=1568 = 32 * 7 * 7个数,输出是out_features=128个数,
# 全连接层要做的,就是怎么把1568个数,映射到128个数上,out_features的每一个数,都和in_features的所有数,都有关系
fc_layer = nn.Linear(in_features=32 * 7 * 7, out_features=128)# 将展平后的向量输入到全连接层
output = fc_layer(flattened_vector)print("全连接层输出尺寸:", output.size())

输出内容看到,展平后的向量尺寸是[64, 1568],这里 64 代表 64 张图片,因为是多张图片同时处理,不是一张一张图片处理,每一张图片展平后的尺寸是1568个像素点。

原始特征图尺寸: torch.Size([64, 32, 7, 7])
展平后的向量尺寸: torch.Size([64, 1568])
展平后的向量: tensor([[ 0.0468,  0.2119,  1.0045,  ...,  0.2380, -0.6989,  0.2017],[-1.2644, -0.4565,  0.2545,  ..., -0.6224,  0.3993,  1.0740],[ 1.5705, -0.5679, -1.4569,  ...,  1.6291,  0.6159, -0.6800],...,[-0.3710,  0.0769, -2.0269,  ...,  0.0668,  2.4325,  0.6908],[ 1.7032, -0.3882,  0.0301,  ...,  0.4000,  0.8739,  0.2931],[-1.9210, -1.7849, -0.4754,  ..., -1.5891, -1.2453,  1.2085]])
全连接层输出尺寸: torch.Size([64, 128])

代码解释

  • feature_map: 输入的特征图,假定大小为 (64 x 32 x 7 x 7)(批次大小为64,通道数为32,高度和宽度均为7)。
  • view 方法: 将特征图展平成形状为 (64 x (32 x 7 x 7)) 的一维向量。这里的 -1 参数告诉PyTorch自动计算该维度的大小。
  • fc_layer: 定义了一个全连接层,它接受一个大小为 (32 x 7 x 7) 的输入,并输出一个大小为128的向量。

通过这种方式,您可以将任意大小的多维特征图转换为适合全连接层处理的一维向量,从而进行进一步的分类或回归任务。

四、什么是灰度图像、灰度值?
五、特征抽取的具体过程
六、CNN的简单案例

接下一篇《计算机视觉卷积神经网络(CNN)基本概念(二)》


http://www.ppmy.cn/news/1573505.html

相关文章

深度学习中的知识蒸馏

知识蒸馏&#xff08;Knowledge Distillation&#xff09;是一种模型压缩技术&#xff0c;旨在将大型、复杂的模型&#xff08;通常称为教师模型&#xff09;的知识迁移到小型、简单的模型&#xff08;学生模型&#xff09;中。通过这种方式&#xff0c;学生模型可以在保持较高…

使用Java爬虫获取京东商品SKU信息的完整指南

在电商领域&#xff0c;商品SKU&#xff08;Stock Keeping Unit&#xff09;信息是商家和消费者都非常关注的内容。SKU信息不仅包括商品的基本属性&#xff08;如价格、库存、规格等&#xff09;&#xff0c;还涉及到商品的动态数据&#xff08;如促销信息、库存状态等&#xf…

深入解析 Flutter GetX

深入解析 Flutter GetX&#xff1a;从原理到实战 GetX 是 Flutter 中一个轻量级且功能强大的状态管理、路由管理和依赖注入框架。它以简单、快速、高效著称&#xff0c;适合从小型到大型项目的开发需求。GetX 的设计理念是一体化解决方案&#xff0c;通过一个框架解决状态管理…

【ArcGIS Pro二次开发】(87):样式_Style的用法

.Stylx类型的文件即为样式库文件&#xff0c;保存了符号样式。 1、根据名字获取当前工程中的style //获取当前工程中的所有style var ProjectStyles Project.Current.GetItems<StyleProjectItem>();//根据名字找出指定的style StyleProjectItem style ProjectStyles.F…

Linux中安装open-webui报sqlite版本低的解决办法

almalinux中安装好open-webui&#xff0c;启动服务时报如下错&#xff1a; RuntimeError: [91mYour system has an unsupported version of sqlite3. Chroma requires sqlite3 > 3.35.0.[0m [94mPlease visit https://docs.trychr…

Nat. Genet | 单细胞多组回归模型识别功能和疾病相关增强子,并实现染色质潜力分析

Nat. Genet | 单细胞多组回归模型识别功能和疾病相关增强子&#xff0c;并实现染色质潜力分析 本文提出了一种名为SCARlink的基因调控模型&#xff0c;通过结合单细胞RNA测序&#xff08;scRNA-seq&#xff09;和单细胞开放染色质测序&#xff08;scATAC-seq&#xff09;数据&…

可变列二维数组【C语言】

废话不多说&#xff0c;直接看代码。 可能在这题上用的上&#xff1a; P2058 [NOIP 2016 普及组] 海港https://www.luogu.com.cn/problem/P2058 int N;scanf("%d", &N); //确定行数int **a malloc(N * sizeof(int*));for (int i 0; i < N; i) {int size;…

Postman如何流畅使用DeepSeek

上次写了一篇文章是用chatBox调用api的方式使用DeepSeek&#xff0c;但是实际只能请求少数几次就不再能给回响应。这回我干脆用最原生的方法Postman调用接口请求好了。 1. 通过下载安装Postman软件 postman下载(https://pan.quark.cn/s/c8d1c7d526f3)&#xff0c;包含7.0和10…