返回

利用NumPy的优势,从零开始搭建卷积神经网络

人工智能

踏上构建CNN的旅程

当我们谈到机器学习中的图像识别和处理领域,卷积神经网络(CNN)可谓是耀眼的明星。CNN因其出色的图像处理能力,成为了图像处理领域不可或缺的利器。如果你想更深入地理解CNN,仅仅了解它的原理是不够的,亲自动手搭建一个CNN才是最好的学习方法。在这篇文章中,我们将使用纯NumPy代码,从零开始搭建CNN,让你对CNN的实现过程和原理有更深刻的理解。

NumPy:构建CNN的得力助手

NumPy作为一款强大的科学计算库,在构建CNN时会成为你的得力助手。NumPy提供了一系列强大的数组操作函数和线性代数函数,可以帮助你轻松处理CNN中的大量数据和复杂计算。通过利用NumPy的这些优势,你可以将更多的精力放在理解CNN的实现原理上,而不是陷入繁琐的计算细节中。

CNN的基本架构:认识卷积层、池化层和全连接层

CNN的架构主要由卷积层、池化层和全连接层组成。卷积层负责提取图像中的特征,池化层负责对卷积层的输出进行降维,全连接层负责将降维后的数据分类。接下来,我们将详细介绍这三个层的实现细节。

1. 卷积层:特征提取的利器

卷积层是CNN的核心组成部分,负责从图像中提取特征。卷积层由一个称为卷积核的过滤器组成,该过滤器在图像上滑动,提取图像中的特征。卷积核的大小和形状决定了提取特征的方式,不同的卷积核可以提取不同的特征。

2. 池化层:降维和特征增强

池化层的作用是对卷积层的输出进行降维,减少数据的计算量,同时还可以增强特征的鲁棒性。池化层通常使用最大池化或平均池化两种方式。最大池化选择卷积层输出中的最大值作为池化后的输出,平均池化选择卷积层输出中的平均值作为池化后的输出。

3. 全连接层:分类的终结者

全连接层是CNN的最后一层,负责将降维后的数据分类。全连接层由一个权重矩阵和一个偏置向量组成,权重矩阵将降维后的数据映射到新的空间,偏置向量对映射后的数据进行调整。最后,通过激活函数对映射后的数据进行非线性变换,得到分类结果。

CNN的训练过程:让网络学习识别图像

搭建好CNN之后,我们需要对它进行训练,让它学会识别图像。CNN的训练过程主要包括正向传播和反向传播两个步骤。正向传播是将输入数据通过网络层层传递,得到输出结果。反向传播是根据输出结果计算误差,然后将误差反向传播到网络中,调整网络中的权重和偏置。通过不断地重复正向传播和反向传播,CNN可以逐渐学习识别图像。

使用NumPy构建CNN:一步步实现

现在,让我们开始使用NumPy代码一步步构建CNN。首先,我们需要导入必要的NumPy库。然后,我们将定义卷积层、池化层和全连接层。接下来,我们将把这些层组合成一个CNN模型,并定义损失函数和优化器。最后,我们将加载训练数据和测试数据,并对CNN模型进行训练。

1. 导入NumPy库

import numpy as np

2. 定义卷积层

class Conv2D:
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
        # 初始化卷积层的参数
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.kernel_size = kernel_size
        self.stride = stride
        self.padding = padding

        # 初始化卷积核和偏置
        self.weight = np.random.randn(out_channels, in_channels, kernel_size, kernel_size)
        self.bias = np.zeros((out_channels,))

    def forward(self, x):
        # 计算卷积层的输出
        out = np.zeros((x.shape[0], self.out_channels,
                        int((x.shape[1] - self.kernel_size + 2 * self.padding) / self.stride) + 1,
                        int((x.shape[2] - self.kernel_size + 2 * self.padding) / self.stride) + 1))

        for i in range(x.shape[0]):
            for j in range(self.out_channels):
                for k in range(int((x.shape[1] - self.kernel_size + 2 * self.padding) / self.stride) + 1):
                    for l in range(int((x.shape[2] - self.kernel_size + 2 * self.padding) / self.stride) + 1):
                        out[i, j, k, l] = np.sum(x[i, :, k*self.stride:k*self.stride+self.kernel_size, l*self.stride:l*self.stride+self.kernel_size] *
                                                self.weight[j, :, :, :]) + self.bias[j]

        return out

3. 定义池化层

class MaxPool2D:
    def __init__(self, kernel_size, stride=1, padding=0):
        # 初始化池化层的参数
        self.kernel_size = kernel_size
        self.stride = stride
        self.padding = padding

    def forward(self, x):
        # 计算池化层的输出
        out = np.zeros((x.shape[0], x.shape[1],
                        int((x.shape[2] - self.kernel_size + 2 * self.padding) / self.stride) + 1,
                        int((x.shape[3] - self.kernel_size + 2 * self.padding) / self.stride) + 1))

        for i in range(x.shape[0]):
            for j in range(x.shape[1]):
                for k in range(int((x.shape[2] - self.kernel_size + 2 * self.padding) / self.stride) + 1):
                    for l in range(int((x.shape[3] - self.kernel_size + 2 * self.padding) / self.stride) + 1):
                        out[i, j, k, l] = np.max(x[i, j, k*self.stride:k*self.stride+self.kernel_size, l*self.stride:l*self.stride+self.kernel_size])

        return out

4. 定义全连接层

class FullyConnected:
    def __init__(self, in_features, out_features):
        # 初始化全连接层的参数
        self.in_features = in_features
        self.out_features = out_features

        # 初始化权重和偏置
        self.weight = np.random.randn(in_features, out_features)
        self.bias = np.zeros((out_features,))

    def forward(self, x):
        # 计算全连接层的输出
        out = np.dot(x, self.weight) + self.bias
        return out

5. 组合成CNN模型

class CNN:
    def __init__(self):
        # 定义CNN的结构
        self.conv1 = Conv2D(1, 32, 3, 1, 1)
        self.pool1 = MaxPool2D(2, 2)
        self.conv2 = Conv2D(32, 64, 3, 1, 1)
        self.pool2 = MaxPool2D(2, 2)
        self.fc1 = FullyConnected(64 * 4 * 4, 128)
        self.fc2 = FullyConnected(128, 10)

    def forward(self, x):
        # 计算CNN的输出
        x = self.conv1.forward(x)
        x = self.pool1.forward(x