【深度学习-第5篇】使用Python快速实现CNN分类(模式识别)任务,含一维、二维、三维数据演示案例(使用pytorch框架)

news2024/11/20 23:15:46

在之前的文章中介绍了CNN的图解入门,CNN的MATLAB分类实现,CNN的MATLAB回归实现。

卷积神经网络(Convolutional Neural Networ,简称CNN)是一种广泛应用于图像识别领域的深度学习算法。它通过模拟人类视觉系统的层次结构,可以自动提取和学习图像的特征,在图像分类、目标检测、人脸识别等任务上取得了巨大成功。

当然了,CNN也可以扩展到非图像领域使用,比如对一组一维数据,也是同样可以实现分类的。本篇文章是之前CNN分类的MATLAB实现那篇文章的姊妹篇,通过这篇文章,大家将会快速掌握使用pytorch框架进行CNN分类的编程方法,另外对于主体代码流程,我也做了傻瓜化使用的封装,方便大家使用。

一、环境搭建

本篇使用的是Win10系统搭建VSCode+Anaconda+Pytorch+CUDA环境,当然如果你是用的是其他编辑器,没有使用anaconda,或者没有独立显卡,本文的程序也都是可以实现的(不过也需要正确配置好了相关环境)。

如果你还没有配置环境,或者配置的环境运行后边的代码有错误,那么推荐大家按照我之前的这篇文章操作来重新进行配置:

Mr.看海:【深度学习-番外1】Win10系统搭建VSCode+Anaconda+Pytorch+CUDA深度学习环境和框架全过程

环境搭建中遇到的问题,大家可以集中在上边这篇文章中留言反映。

二、一个简单的案例

这里我们以最常用的MNIST 数据集作为分类对象。MNIST 数据集包含 70,000 张手写数字的灰度图像 (0-9)。

0. 安装必要的库并导入

如果大家使用上述环境搭建方法,使用了conda的环境,则不需要再额外安装库。如果不是的话,你可能会需要安装numpy,torch和sklearn。

安装好之后,代码中导入必要的库:

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, recall_score, precision_score

1. 加载数据和预处理

1.1 加载数据

加载mnist.npz数据文件(这个文件文末可以获取),然后读取其中的输入和输出数据。输入就是手写图片数据,输出是标签,也就是图片中的真实数值。

# 1.1 加载MNIST数据集,并预处理
data = np.load('mnist.npz')
xdata, ydata = data['x_train'], data['y_train']

1.2 数据预处理

使用reshape()函数将xdata的形状调整为(样本数, 通道数, 高度, 宽度)的格式,以符合PyTorch中卷积神经网络的输入要求。这里的-1表示自动计算样本数,1表示单通道灰度图像,28表示图像的高度和宽度。

# 1.2 数据预处理:将图像数据reshape为(样本数, 通道数, 高度, 宽度)的形状
xdata = xdata.reshape(-1, 1, 28, 28) 

1.3 将数据转换为PyTorch张量

使用torch.tensor()函数将NumPy数组xdata和ydata转换为PyTorch张量,并指定数据类型。

之所以为了转化为张量,是因为PyTorch的模型和计算操作都基于PyTorch张量(torch.Tensor)进行,此步骤是必须的。

# 1.3 将NumPy数组转换为PyTorch张量,并指定数据类型
xdata = torch.tensor(xdata, dtype=torch.float32)
ydata = torch.tensor(ydata, dtype=torch.long)

2. 数据集划分

2.1 划分

使用train_test_split()函数将数据集划分为训练集和测试集,其中test_size=0.2表示测试集占总数据的20%,random_state=42用于固定随机种子以保证结果的可重复性。

将划分后的数据分别赋值给train_data(训练集图像)、test_data(测试集图像)、train_labels(训练集标签)和test_labels(测试集标签)。

# 2.1 使用train_test_split函数划分训练集和测试集
train_data, test_data, train_labels, test_labels = train_test_split(xdata, ydata, test_size=0.2, random_state=42)

2.2 创建数据加载器

数据划分完成后我们创建一下数据加载器,这么做的主要意义在于实现高效的数据批处理和数据迭代。数据加载器将数据集封装成可迭代的对象,并提供了许多有用的功能(比如数据打乱等),使得数据的读取、处理和输入到模型中更加方便和高效。具体到代码实现说明如下:

  • 使用TensorDataset()函数将训练集的图像和标签数据打包成数据集对象train_dataset,同样地,创建测试集数据集对象test_dataset。
  • 使用DataLoader()函数创建数据加载器train_loader和test_loader,用于批量加载和迭代数据。
  • batch_size=128表示每个批次包含128个样本,shuffle=True表示在每个epoch开始时打乱训练数据的顺序,以减少过拟合。
# 2.2 创建数据加载器DataLoader,用于批量加载数据
train_dataset = TensorDataset(train_data, train_labels)
test_dataset = TensorDataset(test_data, test_labels)

train_loader = DataLoader(train_dataset, batch_size=128, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=128)

3. 定义CNN模型

我们在这里定义一个名为CNN的类,继承自nn.Module,这是一个PyTorch的神经网络模块。

# 3. 定义卷积神经网络(CNN)模型
class CNN(nn.Module):
    def __init__(self, num_classes):
        super(CNN, self).__init__()
        # 第一个卷积层:输入通道数为1,输出通道数为16,卷积核大小为3x3,步长为1,填充为1
        self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=1, padding=1)
        self.relu1 = nn.ReLU()  # ReLU激活函数
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)  # 最大池化层,池化核大小为2x2,步长为2
        
        # 第二个卷积层:输入通道数为16,输出通道数为32,卷积核大小为3x3,步长为1,填充为1
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)
        self.relu2 = nn.ReLU()  # ReLU激活函数
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)  # 最大池化层,池化核大小为2x2,步长为2
        
        # 全连接层:输入特征向量大小为32*7*7(经过卷积和池化后的特征图大小),输出大小为num_classes
        self.fc = nn.Linear(32 * 7 * 7, num_classes)

    def forward(self, x):
        # 前向传播过程:依次经过卷积层、ReLU激活函数、最大池化层,最后通过全连接层得到输出
        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 = x.view(x.size(0), -1)  # 将特征图展平成一维向量
        x = self.fc(x)
        return x

# 计算数据集中的类别数
num_classes = ydata.max().item() + 1
# 创建CNN模型实例
model = CNN(num_classes)

作为代码的核心部分,下边我详细讲解一下:

3.1 __init__(self, num_classes)部分

__init__(self, num_classes)部分实现CNN类的构造函数,在创建CNN实例时调用该方法。num_classes入口参数表示最终分类的类别数,即模型需要预测的不同类别的数量。

self.conv1 = nn.Conv2d(1,16, kernel_size=3, stride=1, padding=1)
  • 定义了第一个卷积层conv1,使用nn.Conv2d类。
  • 参数含义:输入通道数为1(灰度图像),输出通道数为16,卷积核大小为3x3,步长为1,填充为1。
  • 卷积层用于提取图像的局部特征,通过卷积操作将输入图像转换为特征图。
self.relu1 = nn.ReLU()
  • 定义了第一个ReLU激活函数relu1,使用nn.ReLU类。
  • ReLU激活函数用于引入非线性,提高模型的表达能力。
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
  • 定义了第一个最大池化层pool1,使用nn.MaxPool2d类。
  • 参数含义:池化核大小为2x2,步长为2。
  • 最大池化层用于降低特征图的空间维度,同时保留最显著的特征。
self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)
self.relu2 = nn.ReLU()
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)

上边这三行定义了第二个卷积层、激活函数和池化层。

self.fc = nn.Linear(32 * 7 * 7, num_classes)
  • 定义了全连接层fc,使用nn.Linear类。
  • 参数含义:输入特征向量的大小为32 * 7 * 7(经过卷积和池化后的特征图大小),输出大小为num_classes
  • 全连接层用于将提取的特征映射到最终的分类结果。

上述是一个简单的CNN网络结构,大家还可以根据实际需要,添加层或者修改其中的参数,如果不知道怎么修改,对CNN结构不熟悉,或者最后全连接层的特征图大小不知道怎么计算,都可以看这篇文章的讲解:

Mr.看海:【深度学习-第2篇】CNN卷积神经网络30分钟入门!足够通俗易懂了吧(图解)

CNN网络通用架构,来自上边的文章

3.2 forward(self, x)部分

forward(self, x)定义了前向传播的过程,描述了输入数据经过CNN的各个层的顺序和操作。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通过第一个卷积层、ReLU激活函数和最大池化层,得到第一次处理后的特征图。
  • 然后依次通过第二个卷积层、ReLU激活函数和最大池化层,得到进一步提取的特征图。
x = x.view(x.size(0), -1)
  • 将经过卷积和池化后的特征图展平成一个一维向量,以便输入到全连接层。
  • x.size(0)表示当前批次的样本数量,-1表示自动计算展平后的特征向量长度。
x = self.fc(x)
  • 将展平后的特征向量通过全连接层,得到最终的预测结果。

4. 训练模型

# 4. 训练模型
criterion = nn.CrossEntropyLoss()  # 定义交叉熵损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001)  # 定义Adam优化器,学习率为0.001
num_epochs = 10  # 训练的总轮数

for epoch in range(num_epochs):
    train_loss = 0.0
    train_preds = []
    train_true = []
    for data, labels in train_loader:
        optimizer.zero_grad()  # 梯度清零
        outputs = model(data)  # 前向传播
        loss = criterion(outputs, labels)  # 计算损失
        loss.backward()  # 反向传播
        optimizer.step()  # 更新模型参数
        train_loss += loss.item() * data.size(0)  # 累计训练损失
        _, preds = torch.max(outputs, 1)  # 获取预测结果
        train_preds.extend(preds.numpy())  # 收集训练集的预测结果
        train_true.extend(labels.numpy())  # 收集训练集的真实标签
    train_loss /= len(train_loader.dataset)  # 计算平均训练损失
    train_acc = accuracy_score(train_true, train_preds)  # 计算训练准确率
    
    print(f"Epoch [{epoch+1}/{num_epochs}], Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.4f}")

这一步将正式开始执行模型训练,其中比较关键的部分我详细讲解一下。

前三行是对损失函数、优化器、初始化学习率、总迭代次数的设定。这些参数很重要,不过相对比较容易理解,这里不再赘述。

我们主要看for循环里的内容:

 train_loss = 0.0
 train_preds = []
 train_true = []
  • 初始化变量,用于记录当前epoch的训练损失、预测结果和真实标签。
optimizer.zero_grad()# 梯度清零
  • 将优化器的梯度清零,以避免梯度累积。
  • 在每个批次开始前,需要清除上一批次的梯度信息,以确保每个批次的梯度计算是独立的。
outputs = model(data) # 前向传播
  • 将当前批次的数据data输入到模型中,进行前向传播,得到模型的预测输出outputs
loss = criterion(outputs, labels) # 计算损失
  • 使用损失函数criterion计算模型预测输出outputs和真实标签labels之间的损失。
  • 损失函数衡量了模型预测结果与真实标签之间的差异,用于指导模型参数的更新。
loss.backward() # 反向传播
  • 对损失函数进行反向传播,计算模型参数的梯度。
  • 反向传播算法通过链式法则,将损失函数对模型参数的梯度传递回网络的每一层,以便后续的参数更新。
optimizer.step() # 更新模型参数
  • 根据计算得到的梯度,使用优化器optimizer更新模型的参数。
  • 优化器根据梯度信息和学习率,调整模型参数的值,以最小化损失函数。
train_loss += loss.item() * data.size(0) # 累计训练损失
  • 累计当前批次的训练损失,用于后续计算平均训练损失。
  • loss.item()返回损失函数的标量值,data.size(0)表示当前批次的样本数。
_, preds = torch.max(outputs, 1) # 获取预测结果
  • 获取当前批次的预测结果,通过在维度1上取最大值得到预测的类别索引。
  • _表示忽略第一个返回值(最大值),preds表示预测的类别索引。
train_preds.extend(preds.numpy())  # 收集训练集的预测结果
train_true.extend(labels.numpy())  # 收集训练集的真实标签
  • 将当前批次的预测结果preds和真实标签labels分别添加到train_predstrain_true列表中。
  • 这些列表用于收集整个epoch的预测结果和真实标签,以便后续计算训练准确率。
train_loss /= len(train_loader.dataset)  # 计算平均训练损失
train_acc = accuracy_score(train_true, train_preds)  # 计算训练准确率
  • 计算当前epoch的平均训练损失,将累计的训练损失除以训练数据集的样本总数。
  • 使用accuracy_score函数计算当前epoch的训练准确率。
  • 训练准确率表示模型在训练数据集上的预测正确率,用于评估模型在训练过程中的表现。

5. 训练模型

# 5. 在测试集上评估模型
test_preds = []
test_true = []
with torch.no_grad():  # 禁用梯度计算
    for data, labels in test_loader:
        outputs = model(data)  # 前向传播
        _, preds = torch.max(outputs, 1)  # 获取预测结果
        test_preds.extend(preds.numpy())  # 收集测试集的预测结果
        test_true.extend(labels.numpy())  # 收集测试集的真实标签

accuracy = accuracy_score(test_true, test_preds)  # 计算测试准确率
recall = recall_score(test_true, test_preds, average='weighted')  # 计算加权平均召回率
precision = precision_score(test_true, test_preds, average='weighted')  # 计算加权平均精确率

print(f"测试集准确率: {accuracy:.4f}, 召回率: {recall:.4f}, 精确率: {precision:.4f}")

这部分是使用训练好的模型model实现对测试集数据的分类验证。

如果第4部分你看懂了,这部分一定也没啥问题。所以这部分不展开讲了。

运行上述全部程序,将会得到以下结果:

测试集准确率98.52%,这个数值不算特别高,因为上述仅仅作为算法流程的示例,没有专门对CNN网络参数进行调试,大家感兴趣的话,可以通过调整CNN网络结构、初始化学习率、迭代次数等参数,实现更高的分类准确率。

三、“一行代码”实现CNN分类任务(pytorch框架)

上边章节演示了使用pytorch实现CNN分类的基础代码演示,不过说实话,对于新手来说使用起来还是有一定难度的。而且我们在实际研究中可能会面临更为复杂的困境:

  • 导入自己的数据后,网络结构一改就频频报错
  • 代码被改得乱七八糟,看的头大
  • 不知道该画哪些图、怎么画图
  • 一维数据分类不知道怎么搞
  • ……

按照本专栏的惯例,笔者参照MATLAB封装函数的样式,封装了pytorch快速实现CNN分类的函数,在设定好相关参数后,只需要一行代码,就可以实现数据集训练集/验证集/测试集快速划分、绘制混淆矩阵、计算分类准确度、自动选取GPU/CPU训练、导出训练过程数据等等常用功能,而且这个封装函数可以适用于一维/二维/三维数据,这个函数的介绍如下:

def FunClassCNNs(dataX, dataY, divideR, cLayer, poolingLayer, fcLayer, options, setting):
    """
    使用CNN进行模式识别(分类)的快速实现函数,程序会优先使用GPU进行加速,如果没有GPU则使用CPU
    
    参数:
    - dataX: 输入数据,形状为(num_samples, num_channels, height, width)的numpy数组
    - dataY: 标签值,形状为(num_samples,)的numpy数组,可以是向量型或索引型
    - divideR: 数据集划分比例,形如[train_ratio, val_ratio, test_ratio]的列表
    - cLayer: 卷积层结构,形状为(num_conv_layers, 5)的numpy数组,每一行代表一个卷积层的参数[filter_height, filter_width, num_filters, stride, padding]
    - poolingLayer: 池化层结构,形状为(num_conv_layers, 5)的列表,每一行代表一个池化层的参数['pool_type', pool_height, pool_width, stride, padding],其中pool_type可以是'maxPooling2dLayer'或'averagePooling2dLayer'或'none'
    - fcLayer: 全连接层结构,形状为(num_fc_layers,)的列表,每一个元素代表一个全连接层的输出维度,如果为空列表则只有一个输出维度等于类别数的全连接层
    - options: 网络训练相关的选项,字典类型,包含以下键值对:
      - 'solverName': 优化器类型,可以是'sgdm'或'rmsprop'或'adam',默认为'adam'
      - 'MaxEpochs': 最大迭代次数,默认为30
      - 'MiniBatchSize': 批量大小,默认为128
      - 'InitialLearnRate': 初始学习率,默认为0.005
      - 'ValidationFrequency': 验证频率,即每多少次迭代进行一次验证,默认为50
      - 'LearnRateSchedule': 学习率调度方式,可以是'piecewise'或'none',默认为'none'
      - 'LearnRateDropPeriod': 学习率下降周期,默认为10
      - 'LearnRateDropFactor': 学习率下降因子,默认为0.95
    - setting: 其他选项,字典类型,包含以下键值对:
      - figflag: 是否绘制图像,'on'为绘制,'off'为不绘制
      - deviceSel: 训练设备选择,可以是'cpu'或'gpu',默认为'gpu',当设置为'gpu'时,如果gpu硬件不可用,则会自动切换到cpu
      - seed: 随机种子,整数,设置为0时不启用,设置为其他整数时启用,不同的整数为不同的种子值,变换种子值会影响结果,相同种子的计算结果是一致的,缺省时为不设置随机种子
      - minmax: 是否进行归一化,布尔值,默认为True
      
    返回值:
    - accuracy: 测试集上的准确率
    - recall: 测试集上的召回率
    - precision: 测试集上的精确率
    - model: 训练好的PyTorch模型
    - info: 包含训练过程中的损失和准确率信息的字典
    """

看注释写的蛮多的似乎有点唬人,其实使用起来蛮简单。

下边我使用三个公开数据集,分别演示这个函数在一维、二维、三维数据中的应用效果,以及能够得出的一系列有用的图片和其他结果。

1.MNIST手写数据集

这就是上个章节中用到过的数据集。

MNIST手写数据集,每张图片是28*28的数据矩阵

现在我们实现分类任务,只需要执行下边这些代码即可(全套运行程序下载链接见文末):

import numpy as np
from khCNN import FunClassCNNs

# 1.加载MNIST数据集
data = np.load('mnist.npz')  # 从文件中加载MNIST数据集
xData, yData = data['x_train'], data['y_train']  # 获取训练集的特征和标签
xData= xData.reshape(-1, 1, 28, 28)  # 将训练集特征重塑为(样本数, 通道数, 高度, 宽度)的形状

# 2. 调用方法进行分类
# 2.1 设置数据集划分比例
divideR = [0.8, 0.1, 0.1]  # 训练集:验证集:测试集 = 8:1:1
# 2.2 设置卷积层参数
cLayer = np.array([
    [3, 3, 16, 1, 1],  # 第一个卷积层: 卷积核大小为3x3, 16个卷积核, 步长为1, 填充为1
    [3, 3, 32, 1, 0]   # 第二个卷积层: 卷积核大小为3x3, 32个卷积核, 步长为1, 填充为0
])
# 2.3 设置池化层参数
poolingLayer = [
    ['maxPooling2dLayer', 2, 2, 2, 0],  # 第一个池化层: 最大池化, 池化核大小为2x2, 步长为2, 填充为0
    ['averagePooling2dLayer', 2, 2, 2, 0]  # 第二个池化层: 平均池化, 池化核大小为2x2, 步长为2, 填充为0
]
# 2.4 设置全连接层参数
fcLayer = [128, 64]  # 两个全连接层, 输出维度分别为128和64
# 2.5 设置训练选项
options = {
    'MaxEpochs': 30,  # 最大迭代次数为30
    'MiniBatchSize': 128,  # 批量大小为128
    'InitialLearnRate': 0.001,  # 初始学习率为0.001
    'ValidationFrequency': 3,  # 每3次迭代进行一次验证
    'LearnRateSchedule': 'piecewise',  # 学习率调度方式为分段常数衰减
    'LearnRateDropPeriod': 10,  # 学习率下降周期为10
    'LearnRateDropFactor': 0.95  # 学习率下降因子为0.95
}
# 2.6 设置随机种子和设备选择
setting = {
    'seed': 42,  # 随机种子为42
    'deviceSel': 'gpu',  # 优先使用GPU进行训练
    'figflag': 'on',  # 绘制图像
    'minmax': True  # 进行归一化
}

# 2.7 调用函数“一行代码”实现训练和测试
accuracy, recall, precision, model, info = FunClassCNNs(dataX=xData, dataY=yData, divideR=divideR, cLayer=cLayer, poolingLayer=poolingLayer,  fcLayer=fcLayer, options=options, setting=setting )

上述代码在保留了比较丰富且必要的设置的前提下,基本已经精简得无法再精简了。

运行完上述代码后,可以得到以下结果:

(1)混淆矩阵图片。

混淆矩阵(Confusion Matrix)是一种常用的评估分类模型性能的工具。就像下图,结果是一个正方形矩阵。其中每一行对应一个实际类别,每一列对应一个预测类别。对角线部分代表预测结果与实际类别相同(即预测正确)的数量,其余部分则代表预测错误的数量。

比如第3行第4列方框中的数字3,代表对于本次分类,有3个手写数字“3”被错误分类成了“2”。

解读混淆矩阵的关键是观察对角线元素和非对角线元素。在对角线上的元素表示正确分类的样本数量,而非对角线上的元素表示被误分类的样本数量。

需要注意的是,这个图针对的是测试集数据。

混淆矩阵可以全面地描述分类网络的特性,属于写论文必备图片。

混淆矩阵

下边这张图可以反应预测标签被错误分类的整体态势,图中蓝色点是真实标签,红色标签是预测标签。完美情况是预测标签与真实标签完全重合,如果有被错误分类的情况,就会像下图这样出现很多散点。这张图现在在论文中也比较常见。

(2)训练过程图。

下边两张图分别是loss值和分类准确度的收敛过程。其中蓝色线条是训练集结果,橙色线条是验证集结果。

此图也是论文必画图之一。

(3)网络结构图、表。

网络结构图中有每层网络的类型、输入和输出数据尺寸、网络结构等信息,方便大家论文中使用。

网络结构表中有网络中各个层的类型以及尺寸等信息,写论文时也用得到。

(4)训练过程表。

在模型的训练过程中,将在终端打印出训练集和测试集的实时Loss值和准确率值,就像下边这样:

上边这个MNIST数据集测试集正确率是98.7%,这个是随意调了调网络和参数的结果,如果花时间进一步优化网络,可以得到更好的结果。

2.猫狗大战数据集

猫狗大战数据集中包含了不同尺寸的猫、狗彩色图像。

类别只有猫和狗两类。

下图是其中随机抽取的一些示意图片:

这个数据集比较大,完整的有将近1G的图片,本案例中为了轻量化程序文件,从其中选取了2000张图片(猫、狗各1000)。

这个案例主要是为了向大家展示,如果数据文件是图片(而不是数据文件),要怎样加载图片并导入程序;并且演示图片大小不同的时候应该怎么处理。

这里我们同样调用封装好的函数。

此时我们只需要运行以下这段代码(全套运行程序下载链接见文末):

import os
import numpy as np
import torch
from PIL import Image
from torchvision import transforms
from sklearn.model_selection import train_test_split
from khCNN import FunClassCNNs

## 1. 加载图片数据
# 1.1 设置数据集路径
data_dir = 'catdogFig'

# 1.2 定义图像预处理操作
transform = transforms.Compose([
    transforms.Resize((224, 224)),  # 调整图片大小为224x224
    transforms.ToTensor(),  # 将图片转换为张量
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 归一化
])

# 1.3 加载图片数据
def load_data(data_dir):
    images = []
    labels = []
    for label in ['cat', 'dog']:
        label_dir = os.path.join(data_dir, label)
        for filename in os.listdir(label_dir):
            if filename.endswith('.jpg') or filename.endswith('.png'):  # 支持.jpg和.png格式的图片
                image_path = os.path.join(label_dir, filename)
                image = Image.open(image_path).convert('RGB')  # 打开图片并转换为RGB模式
                image = transform(image)  # 对图片进行预处理
                images.append(image)
                labels.append(0 if label == 'cat' else 1)  # 猫的标签为0,狗的标签为1
    return torch.stack(images), torch.tensor(labels)  # 将图片和标签转换为张量

# 加载数据集
dataX, dataY = load_data(data_dir)

# 将dataX转换为NumPy数组
dataX = dataX.numpy()

# 2. 调用方法进行分类
# 2.1 划分数据集
divideR = [0.7, 0.15, 0.15]  # 训练集、验证集、测试集的比例

# 2.2 定义CNN模型结构
cLayer = np.array([[8, 8, 16, 1, 1], [6, 6, 32, 1, 1], [4, 4, 64, 1, 1]])  # 卷积层结构
poolingLayer = [['maxPooling2dLayer', 2, 2, 2, 0], ['maxPooling2dLayer', 2, 2, 2, 0], ['maxPooling2dLayer', 2, 2, 2, 0]]  # 池化层结构
fcLayer = [128, 64]  # 全连接层结构

# 2.3 设置训练选项
options = {
    'MaxEpochs': 20,  # 最大训练轮数
    'MiniBatchSize': 64,  # 小批量数据大小
    'InitialLearnRate': 0.001,  # 初始学习率
    'ValidationFrequency': 5  # 验证频率
}

# 2.4 设置其他选项
setting = {
    'figflag': 'on',  # 是否显示图形
    'deviceSel': 'gpu'  # 设备选择(gpu或cpu)
}

# 进行训练和测试
accuracy, recall, precision, model, info = FunClassCNNs(dataX, dataY, divideR, cLayer, poolingLayer, fcLayer, options, setting)

运行程序,可以得到如下结果,这个和MNIST数据集类似,就不展开说了。

大致调了调,得到的正确率大概在68%,这个结果比较差强人意,主要是因为我们的数据集比较小,难以很好地泛化模型,如果用完整数据集结果就不一样了。

3.iris鸢尾花数据集

这里介绍一下鸢尾花数据集,鸢尾花在机器学习里是常客之一。数据集由具有150个实例组成,其特征数据包括四个:萼片长、萼片宽、花瓣长、花瓣宽。数据集中一共包括三种鸢尾花,分别叫做Setosa、Versicolor、Virginica,就像下图:

鸢尾花

也就是说这组数据每组的维度是1*4,也就是一维数据,总共有150组数据。

在这个例子里我们还演示了当数据文件为表格(CSV)时的处理方法。

此时我们只需要运行以下这段代码(全套运行程序下载链接见文末):

from sklearn.datasets import load_iris
import numpy as np
import pandas as pd
from khCNN import FunClassCNNs

# 加载鸢尾花数据集
# 从CSV文件中读取数据
data = pd.read_csv('iris.csv')
X = data.iloc[:, :-1].values  # 提取特征数据
y = data.iloc[:, -1].values   # 提取标签数据

# 将数据转换为(num_samples, num_channels, height, width)的形状
X = X.reshape(X.shape[0], 1, 1, X.shape[1])

# 设置网络结构参数
cLayer = np.array([[1, 3, 8, 1, 0]])  # 卷积层参数

poolingLayer = [['maxPooling2dLayer', 1, 2, 2, 0]]  # 池化层参数

fcLayer = [32]  # 全连接层参数

options = {
    'solverName': 'adam',  # 优化器名称
    'MaxEpochs': 50,  # 最大训练轮数
    'MiniBatchSize': 16,  # 小批量数据大小
    'InitialLearnRate': 0.005,  # 初始学习率
    'ValidationFrequency': 10,  # 验证频率
    'LearnRateSchedule': 'piecewise',  # 学习率调度方式
    'LearnRateDropPeriod': 20,  # 学习率下降周期
    'LearnRateDropFactor': 0.5  # 学习率下降因子
}

setting = {
    'figflag': 'on',  # 是否显示图形
    'deviceSel': 'gpu',  # 设备选择(gpu或cpu)
    'seed': 42,  # 随机种子
    'minmax': True  # 数据已经标准化,不需要再进行归一化
}

# 调用函数进行训练和评估
accuracy, recall, precision, model, info = FunClassCNNs(
    dataX=X,  # 输入特征数据
    dataY=y,  # 输入标签数据
    divideR=[0.7, 0.15, 0.15],  # 数据划分比例(训练集、验证集、测试集)
    cLayer=cLayer,  # 卷积层参数
    poolingLayer=poolingLayer,  # 池化层参数
    fcLayer=fcLayer,  # 全连接层参数
    options=options,  # 训练选项
    setting=setting  # 设置参数
)

需要注意此时,设置滤波器的高与宽,方向要与输入数据dataX保持一致。也就是说dataX的维度是150*1*1*4,滤波器就得设置成1*3,而不能是3*1,池化层也同理。

这个数据集运行得到的准确率是99%。

三、总结

总的来说,自己编程的方法可以快速实现简单的功能,但是用于工程和研究还是欠缺一些必要的图表。

使用封装函数对复杂的CNN训练和评估流程进行了高度封装,大家只需要提供数据和指定参数,就可以轻松进行模型的训练和评估,大大减轻了同学们负担;另外函数接收多个参数作为输入,包括网络结构和训练选项等,使得用户可以根据自己的需求灵活地定制和配置模型,适应各种不同的应用场景;此函数不仅实现了CNN模型的训练,还对模型的性能进行了全面评估,包括准确度等指标,并返回了训练过程中的详细信息,助力用户快速理解模型的性能,并进行后续的优化调整。

需要上述三个案例的代码和封装函数的代码,同学们可以在公众号 khscience(看海的城堡)中回复“CNN分类”获取。

扩展阅读:

3.1 Mr.看海:神经网络15分钟入门!足够通俗易懂了吧

3.2 Mr.看海:神经网络15分钟入门!——反向传播到底是怎么传播的?

3.3 Mr.看海:神经网络15分钟入门!使用python从零开始写一个两层神经网络

3.4 Mr.看海:用深度学习做了下中国股市预测,结果是...

3.5 Mr.看海:使用MATLAB快速搭建神经网络实现分类任务(模式识别)

3.6 Mr.看海:【深度学习-第1篇】深度学习是什么、能干什么、要怎样学?

3.7 Mr.看海:【深度学习-第2篇】CNN卷积神经网络30分钟入门!足够通俗易懂了吧(图解)

3.8 Mr.看海:【深度学习-第3篇】使用MATLAB快速实现CNN分类(模式识别)任务,含一维、二维、三维数据演示案例

3.9 Mr.看海:【深度学习-第4篇】使用MATLAB快速实现CNN多变量回归预测

3.10 Mr.看海:【深度学习-番外1】Win10系统搭建VSCode+Anaconda+Pytorch+CUDA深度学习环境和框架全过程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1624002.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker NetWork (网络)

Docker 为什么需要网络管理 容器的网络默认与宿主机及其他容器都是相互隔离的,但同时我们也要考虑下面的一些问题, 比如 多个容器之间是如何通信的容器和宿主机是如何通信的容器和外界主机是如何通信的容器中要运行一些网络应用(如 nginx、web 应用、数…

HarmonyOS hsp制作与引用

1. HarmonyOS hsp制作与引用 1.1 介绍 HSP动态共享包(模块),应用内HSP指的是专门为某一应用开发的HSP,只能被该应用内部其他HAP/HSP使用,用于应用内部代码、资源的共享。应用内HSP跟随其宿主应用的APP包一起发布,与该…

「deepin生态共建小组」正式启动招募!三大生态共建项目,速来 !

基于社区开源精神,为提高大家对deepin生态建设的参与感,应用商店将正式开放众多软件给广大开源爱好者进行维护。参与小组工作可获得多项专属小组福利,工作项目分为玲珑格式迁移、wine应用打包、deb原生应用维护。 招募条件 1)不限…

【C/C++笔试练习】OSI分层模型、源端口和目的端口、网段地址、SNMP、状态码、tcp报文、域名解析、HTTP协议、计算机网络、美国节日、分解因数

文章目录 C/C笔试练习选择部分(1)OSI分层模型(2)源端口和目的端口(3)网段地址(4)SNMP(5)状态码(6)tcp报文(7)域…

使用python setup.py报错:Upload failed (403) / Upload failed (400)

当前报错的环境 Python 3.9.19twine1.15.0 本地~/.pypirc已正确配置了用户名和密码,用在pypi.org注册: [pypi]username skylerhupassword ${password}执行 python setup.py sdist upload -r pypi 打包上传到仓库报错。 在不久之前同样的环境&#…

C语言扫雷游戏完整实现(上)

文章目录 前言一、新建好头文件和源文件二、实现游戏菜单选择功能三、定义游戏函数四、初始化棋盘五、 打印棋盘函数六、布置雷函数七、玩家排雷菜单八、标记功能的菜单九、标记功能菜单的实现总结 前言 C语言从新建文件到游戏菜单,游戏函数,初始化棋盘…

网工交换基础——生成树协议(01)

一、生成树的技术概述 1、技术背景 二层交换机网络的冗余性导致出现二层环路: 人为因素导致的二层环路问题: 二层环路带来的网络问题: 生成树协议的概念: STP(Spanning Tree Protocol)是生成树协议的英文缩写。该协议可应用于在网…

如何分析和优化慢sql语句

前言 sql查询速度比较慢容易成为性能瓶颈,这时我们可以优化我们的sql语句或数据库表 一般sql语句执行很慢的种类分为: 1.聚合查询 2.多表查询 3.表数据量过大查询 4.深度分页查询 这四种的前三种都可以通过优化sql语句来优化sql查询速度 正文 聚合查询 我们可以通过尝…

机器人视觉教学实训平台

一:功能概述 1.1、功能简介 机器人视觉教学实训平台基于睿尔曼机器人与海康机器视觉产品,面向机器人视觉系统应用而开发设计,产品涵盖机器人系统、工业视觉系统、自动化控制系统、计算机编程系统,可以在一台设备上进行多种与机器…

C++初阶学习第三弹——类与对象(上)——初始类与对象

前言: 在前面,我们已经初步学习了C的一些基本语法,比如内敛函数、函数重载、缺省参数、引用等等,接下来我们就将正式步入C的神圣殿堂,首先,先给你找个对象 目录 一、类与对象是什么? 二、类的各…

Git 工作原理

Git 工作原理 | CoderMast编程桅杆https://www.codermast.com/dev-tools/git/git-workspace-index-repo.html Workspace:工作区Index / Stage:暂存区Repository:仓库区(或本地仓库)Remote:远程仓库 Git 一…

如何优雅的实现 iframe 多层级嵌套通讯

前言 在前端开发项目中,不可避免的总会和 iframe 进行打交道,我们通常会使用 postMessage 实现消息通讯。 如果存在下面情况: iframe 父子通讯iframe 同层级通讯iframe 嵌套层级通讯 当面对这种复杂的情况的时候,通讯不可避免…

Uptime Kuma 使用指南:一款简单易用的站点监控工具

我平时的工作会涉及到监控,而站点是一个很重要的监控项。项目上线后,我们通常会将站点监控配置到云平台上,以检测各站点的连通性。但随着项目不断增多,云平台上的配额就有点捉急了。针对这个情况,我们可以试试这个开源…

李沐49_样式迁移——自学笔记

样式迁移 将样式图片中的样式迁移到内容图片上,合成图片,例如将照片转换成漫画形式或者是油画风。 基于CNN的样式迁移 读取图片和样式风格 %matplotlib inline import torch import torchvision from torch import nn from d2l import torch as d2ld…

Facebook的魅力魔法:探访数字社交的奇妙世界

1. 社交媒体的演变与Facebook的角色 在数字化时代,社交媒体已经成为我们日常生活中不可或缺的一部分。而在众多的社交媒体平台中,Facebook 以其深厚的历史和广泛的影响力,成为了全球数亿用户沟通、分享和互动的主要场所。从其初创之时起&…

【学习AI-相关路程-自我总结-相关入门-自我学习-NVIDIA-Jetson】

【学习AI-相关路程-自我总结-相关入门-自我学习】 1、前言2、思考前进方向3、学习路线1、基础知识阶段2、初级准备阶段3、中级学习阶段4、高级实战阶段 4、自我的努力5、学习平台6、自己总结 1、前言 最近AI相关比较火的,对于程序员,或者走这行的人来说…

Flutter开发好用插件url_launcher详解-启动 URL

文章目录 url_launcher介绍安装用法错误处理自定义行为其他功能 url_launcher介绍 url_launcher 是一个 Flutter 插件,用于启动 URL。它支持网络、电话、短信和电子邮件方案。您可以使用它从您的 Flutter 应用程序中打开网站、拨打号码、发送短信或撰写电子邮件。 …

群组分析方法

目录 1.什么是群组分析方法 2.基本原理 3.群组分析方法分类 3.1.层次方法 3.2.划分方法 3.3.密度基方法 ​​​​​​​3.4.模型基方法 4.群组评估 5.应用步骤 1.什么是群组分析方法 群组分析(Cluster Analysis)是数据分析中的一种重要方法&…

git lab 2.7版本修改密码命令

1.gitlab-rails console -e production Ruby: ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [x86_64-linux] GitLab: 14.9.0-jh (51fb4a823f6) EE GitLab Shell: 13.24.0 PostgreSQL: 12.7 2根据用户名修改密码 user User.find_by(username: ‘username’) # 替换’use…

ABAP 遗传算法求解

本文无文本解析,结尾处有简单装箱问题的示例,该算法收敛结果较慢,仅供ABAP爱好者参考,实践,实际应用建议使用线性规划。可直接复制后在系统中使用。 对象自定义逻辑版本-截图 对象自定义逻辑版本-对象描述 INIT I…