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

第一章:计算机视觉中图像的基础认知
第二章:计算机视觉:卷积神经网络(CNN)基本概念(一)
第三章:计算机视觉:卷积神经网络(CNN)基本概念(二)
第四章:搭建一个经典的LeNet5神经网络

一、引言

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

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

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

二、图像特征

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

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

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

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

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

python 复制代码
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()

效果 :左侧为原图,中间为灰度图,右侧为边缘检测结果,白色线条表示边缘。

中层特征:纹理与形状,纹理提取示例:
纹理特征描述了图像中像素的排列方式,反映了表面的粗糙度、平滑度等特性
形状特征描述了物体的轮廓和几何形状。

python 复制代码
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):

python 复制代码
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()

三、什么是卷积神经网络?

3.1 CNN基本概念

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

3.2 主要组件

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

使用一个形象一点的动图来展示卷积,蓝色边框组成的矩阵,就代表一张图片,因为一张图片可以转换成为一个矩阵,红色的矩阵就是卷积核,卷积核就是一个矩阵,取名叫卷积核,红色的矩阵在蓝色图片矩阵上滑动,每滑动一步,就做一次矩阵乘法,就是 2 个9乘9 的矩阵相乘,得到一个数(黑色的点),所有的像素点都被滑动完了,就得到一个右边的新的红色的矩阵,这个红色矩阵代表的是一张新的图像,新的图像也代表了左边原图的一个特征图。这个过程就是卷积,你细品!

下面这张图,是经典的卷积神经网络,convolutions 就是卷积操作。红色箭头指的地方,可以形象看到,卷积之后的结果,使图像变多了,这些多出来的图像就是上一层图像的特征图。

代码示例

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

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

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

python 复制代码
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]
  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激活效果可视化

python 复制代码
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),因为它是该窗口中的最大值。

特点

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

示例代码

python 复制代码
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)

输出:

powershell 复制代码
最大池化结果:
 tensor([[[[5., 6.],
          [8., 9.]]]])

平均池化(Average Pooling)

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

工作原理

  • 对于同样的 (2 x 2) 区域,如果输入值为

    平均池化的结果将是 (1+3+4+2) / 4 = 2.5。

特点

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

示例代码

python 复制代码
# 使用相同的输入张量
# 定义平均池化层
avg_pool = nn.AvgPool2d(kernel_size=2, stride=2)

# 应用平均池化
output_tensor_avg = avg_pool(input_tensor)
print("平均池化结果:\n", output_tensor_avg)

输出结果:

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

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

具体解释

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

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

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

示例代码

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

python 复制代码
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个像素点。

python 复制代码
原始特征图尺寸: 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)基本概念(二)

相关推荐
hahaha60162 分钟前
农业机器人的开发
人工智能·计算机视觉
xiaoli23276 分钟前
机器学习——SVM
人工智能·机器学习·支持向量机
智驱力人工智能11 分钟前
高密爆炸警钟长鸣:AI为化工安全戴上“智能护盾”
人工智能·算法·安全·重构·边缘计算·高密爆炸·高密化工厂
元闰子22 分钟前
AI Agent需要什么样的数据库?
数据库·人工智能·后端
蚂蚁数据AntData23 分钟前
⼤模型驱动的DeepInsight Copilot在蚂蚁的技术实践
大数据·人工智能·数据分析·copilot·数据库架构
LeonDL16825 分钟前
HALCON 深度学习训练 3D 图像的几种方式优缺点
人工智能·python·深度学习·3d·halcon·halcon训练3d图像·深度学习训练3d图像
jmsail26 分钟前
Dynamics 365 Business Central AI Sales Order Agent Copilot
人工智能·microsoft·copilot·dynamics 365·d365 bc erp
lingxiao1688829 分钟前
测量3D翼片的距离与角度
计算机视觉·halcon·3d视觉
要养家的程序猿41 分钟前
RagFlow优化&代码解析(一)
人工智能·ai