MindSpore开发之路(十):构建卷积神经网络(CNN):核心层详解

在前面的文章中,我们已经掌握了MindSpore的基础知识,包括Tensor、nn.Cell、数据处理等。从本篇开始,我们将正式进入激动人心的模型构建部分,首先要学习的就是在计算机视觉(CV)领域大放异彩的------卷积神经网络(Convolutional Neural Network, CNN)

1. CNN是什么

把CNN想象成一个拥有"火眼金睛"的图像识别专家。它不像我们之前接触的简单网络那样"一视同仁"地看待所有像素,而是通过模仿人类视觉系统的方式,逐层地从图像中提取特征,从最基础的边缘、颜色,到更复杂的纹理、形状,最终识别出图像的内容。

本篇文章将作为您进入CNN世界的第一站,详细拆解构成CNN的几个最核心的"零件"------即MindSpore中的关键神经网络层。

2. CNN的核心"零件"

在MindSpore中,构建一个CNN网络就像搭乐高积木一样,我们需要从mindspore.nn模块中拿出各种功能的"积木块"(神经网络层),然后将它们有序地拼接起来。下面,我们就来逐一认识这些核心"积木块"。

2.1 卷积层 (nn.Conv2d):特征提取器

卷积层是CNN的灵魂,它负责从输入图像中提取特征。

  • 工作原理 :想象你有一个"滤镜"(称为卷积核滤波器 ),这个滤镜非常小,比如3x3大小。你将这个滤镜覆盖在输入图像的左上角,计算滤镜覆盖区域的像素与滤镜自身值的加权和,得到一个输出值。然后,你将滤镜向右移动一个"步长"(stride),重复计算,直到扫过整行。接着,下移一个步长,继续扫描,最终生成一张新的、尺寸更小的图像,这张图就叫做特征图(Feature Map)。这个过程就模拟了大脑识别物体边缘、角落等局部特征的方式。

  • 关键参数

    • in_channels (int): 输入通道数。对于RGB彩色图像,就是3。
    • out_channels (int): 输出通道数,也等于卷积核的数量。每个卷积核可以学习提取一种不同的特征,所以输出通道数越多,提取的特征就越丰富。
    • kernel_size (int or tuple): 卷积核的大小。常用的有 3 (代表3x3) 或 (3, 5)
    • stride (int): 卷积核移动的步长。默认为1。
    • pad_mode (str): 填充模式。'same'模式会在图像周围自动填充0,使得输出特征图的尺寸与输入大致相同;'valid'模式则不填充,输出尺寸会变小。
  • 代码示例

python 复制代码
import mindspore
from mindspore import nn, Tensor
import numpy as np

# 假设我们有一个 1x1x5x5 的单通道图像 (N, C, H, W)
# N: 批量大小, C: 通道数, H: 高度, W: 宽度
input_image = Tensor(np.ones([1, 1, 5, 5]), mindspore.float32)

# 定义一个卷积层:输入1通道,输出2通道,卷积核3x3,步长1
# pad_mode='valid'表示不填充
conv_layer = nn.Conv2d(in_channels=1, out_channels=2, kernel_size=3, stride=1, pad_mode='valid')

# 将图像输入卷积层
output_feature_map = conv_layer(input_image)

print("输入图像尺寸:", input_image.shape)
print("输出特征图尺寸:", output_feature_map.shape)
# (5-3)/1 + 1 = 3, 所以输出尺寸是 1x2x3x3

2.2 激活函数 (nn.ReLU):引入非线性

如果只有卷积层,无论叠加多少层,其效果都等同于一个线性变换,这无法让网络学习复杂的数据模式。因此,我们需要激活函数来引入非线性。

  • 工作原理ReLU (Rectified Linear Unit) 是目前最常用的激活函数之一。它的规则极其简单:对于输入的每个值,如果大于0,则保持不变;如果小于或等于0,则直接变为0。这个简单的操作却能极大地提升网络的表达能力。

  • 代码示例

python 复制代码
# 定义一个ReLU激活函数层
relu_layer = nn.ReLU()

# 假设有一个包含正数和负数的Tensor
input_tensor = Tensor(np.array([[-1.0, 4.0, -8.0], [2.0, -5.0, 9.0]]), mindspore.float32)

# 应用ReLU
output_tensor = relu_layer(input_tensor)

print("应用ReLU前:
", input_tensor)
print("应用ReLU后:
", output_tensor)

2.3 池化层 (nn.MaxPool2d):信息浓缩与降维

池化层通常紧跟在卷积层和激活层之后,它有两个主要作用:

  1. 降维:显著减小特征图的尺寸,从而减少网络后续的参数数量和计算量。
  2. 保持特征不变性:通过取一个区域内的最大值(Max Pooling)或平均值(Avg Pooling),使得网络对特征的微小位移不那么敏感,增强了模型的鲁棒性。
  • 工作原理MaxPool2d(最大池化)与卷积类似,也是用一个窗口在特征图上滑动,但它不进行加权计算,而是简单地取窗口内的最大值作为输出。

  • 关键参数

    • kernel_size (int): 池化窗口的大小。
    • stride (int): 窗口移动的步长。
  • 代码示例

python 复制代码
# 假设我们有一个 1x2x4x4 的特征图
feature_map = Tensor(np.arange(1 * 2 * 4 * 4).reshape(1, 2, 4, 4), mindspore.float32)

# 定义一个最大池化层:窗口2x2,步长2
maxpool_layer = nn.MaxPool2d(kernel_size=2, stride=2)

# 应用池化
output_pooled = maxpool_layer(feature_map)

print("池化前尺寸:", feature_map.shape)
print("池化后尺寸:", output_pooled.shape) # (4-2)/2 + 1 = 2, 所以输出尺寸是 1x2x2x2

2.4 展平层 (nn.Flatten):从三维到一维的"压平"

在经过多轮"卷积-激活-池化"操作后,我们得到了一系列高度浓缩的特征图。但在将这些特征用于最终分类之前,需要将它们"压平",变成一个一维的向量。这就是nn.Flatten层的工作。

  • 工作原理 :它会保留batch_size(N),然后将后面的所有维度(C, H, W)的数值全部拉伸成一个长长的一维向量。

  • 代码示例

python 复制代码
# 假设我们有一个 1x2x2x2 的池化后特征
pooled_map = Tensor(np.ones([1, 2, 2, 2]), mindspore.float32)

# 定义一个展平层
flatten_layer = nn.Flatten()

# 应用展平
output_vector = flatten_layer(pooled_map)

print("展平前尺寸:", pooled_map.shape)
print("展平后尺寸:", output_vector.shape) # 2 * 2 * 2 = 8, 所以输出尺寸是 1x8

2.5 全连接层 (nn.Dense):最终分类器

全连接层(在MindSpore中称为Dense)通常位于CNN的末端。在特征被展平后,这个一维向量会被送入一个或多个全连接层,进行最终的分类或回归。

  • 工作原理:它的每一个神经元都与前一层的所有神经元相连接,通过学习到的权重对特征进行加权求和,最终映射到指定的输出维度(例如,在10分类任务中,输出维度就是10)。

  • 关键参数

    • in_channels (int): 输入神经元的数量(即展平后向量的长度)。
    • out_channels (int): 输出神经元的数量(即分类的类别数)。
  • 代码示例

python 复制代码
# 假设我们有一个长度为8的展平向量
flatten_vector = Tensor(np.ones([1, 8]), mindspore.float32)

# 定义一个全连接层:输入8个特征,输出到10个类别
dense_layer = nn.Dense(in_channels=8, out_channels=10)

# 应用全连接层
output_logits = dense_layer(flatten_vector)

print("全连接层输入尺寸:", flatten_vector.shape)
print("全连接层输出尺寸:", output_logits.shape)

3. 组装一个简单的CNN

现在我们已经认识了所有的"零件",让我们把它们组装起来,构建一个简单的CNN模型。这个模型将遵循经典的卷积 -> 激活 -> 池化 -> 展平 -> 全连接的结构。

python 复制代码
import mindspore
from mindspore import nn

class SimpleCNN(nn.Cell):
    def __init__(self, num_classes=10):
        super(SimpleCNN, self).__init__()
        # 定义第一组卷积、激活、池化
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, pad_mode='valid')
        self.relu1 = nn.ReLU()
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)

        # 定义第二组卷积、激活、池化
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5, pad_mode='valid')
        self.relu2 = nn.ReLU()
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)

        # 定义展平层和全连接层
        self.flatten = nn.Flatten()
        # 假设输入是32x32的图像,经过两轮卷积池化后,尺寸需要计算
        # 这里我们先假设一个值,后续实战中会精确计算
        self.fc1 = nn.Dense(in_channels=16 * 5 * 5, out_channels=120)
        self.relu3 = nn.ReLU()
        self.fc2 = nn.Dense(in_channels=120, out_channels=84)
        self.relu4 = nn.ReLU()
        self.fc3 = nn.Dense(in_channels=84, out_channels=num_classes)

    def construct(self, x):
        # 按照顺序将输入x通过各个层
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.pool1(x)
        
        x = self.conv2(x)
        x = self.relu2(x)
        x = self.pool2(x)
        
        x = self.flatten(x)
        
        x = self.fc1(x)
        x = self.relu3(x)
        x = self.fc2(x)
        x = self.relu4(x)
        x = self.fc3(x)
        
        return x

# 实例化网络
net = SimpleCNN()
print(net)

注意 :上述代码中 fc1in_channels 是一个估算值。在实际项目中,你需要根据输入图像的尺寸和卷积/池化层的参数精确计算出展平后的向量长度。我们将在后续的LeNet-5实战文章中详细演示这个计算过程。

4. 总结

在本篇文章中,我们详细学习了构建卷积神经网络(CNN)所需的几个核心层:

  • nn.Conv2d:用于提取局部特征。
  • nn.ReLU:用于引入非线性,增强模型表达力。
  • nn.MaxPool2d:用于降低维度,减少计算量。
  • nn.Flatten:用于将多维特征"压平"成一维向量。
  • nn.Dense:用于根据提取的特征进行最终分类。

通过将这些"零件"有序地组合,我们成功搭建了一个简单的CNN模型。这为您后续学习更复杂的网络(如LeNet-5、ResNet等)并进行端到端实战打下了坚实的基础。

在下一篇文章中,我们将学习如何构建循环神经网络(RNN),敬请期待!

相关推荐
Clank的游戏栈2 小时前
AI游戏开发全自动编程课程体系(Cursor版,支持Unity/Cocos, Laya后续支持)
人工智能·unity·游戏引擎
雍凉明月夜2 小时前
深度学习网络笔记Ⅱ(常见网络分类1)
人工智能·笔记·深度学习
北岛寒沫2 小时前
北京大学国家发展研究院 经济学辅修 经济学原理课程笔记(第十三课 垄断竞争)
人工智能·经验分享·笔记
AI营销实验室2 小时前
AI 工具何高质量的为销售线索打分?
大数据·人工智能
Wang201220132 小时前
RNN和LSTM对比
人工智能·算法·架构
xueyongfu2 小时前
从Diffusion到VLA pi0(π0)
人工智能·算法·stable diffusion
jackylzh3 小时前
配置pytorch环境,并调试YOLO
人工智能·pytorch·yolo
杜子不疼.3 小时前
AI Ping双款新模型同步免费解锁:GLM-4.7与MiniMax M2.1实测
人工智能
打码人的日常分享3 小时前
企业数据资产管控和数据治理解决方案
大数据·运维·网络·人工智能·云计算