python卷积神经网络人脸识别示例实现详解

目录

一、准备

1)使用pytorch

2)安装pytorch

3)准备训练和测试资源

二、卷积神经网络的基本结构

三、代码实现

1)导入库

2)数据预处理

3)加载数据

4)构建一个卷积神经网络

 5)模型训练

6)模型测试

四、测试结果 

五、模型导出

1)保存模型的状态参数

 2)保存完整模型

 六、总结


一、准备

1)使用pytorch

为什么建议使用pytorch来构建卷积神经网络呢?因为pytorch是基于python开发的一个神经网络工具包,它已经实现了激活函数定义、权重矩阵定义、卷积计算、正向传播、反向传播、权重矩阵更新等神经网络的基本操作,而不需要我们再去编写代码实现这些功能,只需调用相应的函数就可以搭建好我们所需的网络结构。pytorch极大方便了我们构建神经网络,加快了神经网络开发速度,我们只需要关注网络的结构层次,而不用关心所建立的网络具体训练和预测过程。

2)安装pytorch

pytorch有CPU版和GPU版,GPU版需要使用到英伟达的显卡来加快网络速度,安装过程也稍显复杂。本文重点是对python卷积神经网络示例解析,因此安装CPU版。在pycharm开发工具的终端中直接执行命令:pip install torch torchvision torchaudio,即可完成pytorch的CPU版本安装。

3)准备训练和测试资源

因为使用pytorch开发卷积神经网络实现人脸识别,pytorch对数据存放有一定要求,因此需要将相关资源放在特定的目录结构下。训练目录和测试目录结构如下图所示,在文件夹中放入相应的图片资源即可。文件路径可以自行定义,但是同一个人的照片必须放在同一个文件夹下,pytorch根据该文件夹的名称自动将相应的图片资源归于一类。

二、卷积神经网络的基本结构

一个简单的卷积神经网络包括输入层、卷积层、池化层、扁平化层和全连接层。网络的复杂度体现在卷积、池化层的大小和数量上。卷积神经网络主要用于处理有大量数据输入的情景,如图像识别是最好的例子,可以极大减少运算量。下面以一个人脸识别的实际例子,详细讲解卷积神经网络的搭建流程。

三、代码实现

1)导入库

使用PyCharm工具新建一个.py文件,在文件中导入下面需要使用到的相关库。在导入下面的库前,需要确保已经安装好了pytorch相关依赖包。

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

2)数据预处理

以下定义了一组针对图片进行预处理的方法,包括3个。第一个是将图像调整为100*100像素大小,这样我们就可以输入任何大小图片,并能保证数据输入到网络中的大小是一致的。第二个是将图像数据转换为张量格式,这时pytorch自定义的一种数据格式,可以简单理解为类似于多维向量。第三个是对图像进行归一化处理,其目的是使数据的均值为 0,标准差为 1。这样做可以加速模型的训练过程,提高模型的稳定性和收敛速度。函数参数如下:

  • mean:一个长度为 3 的列表,分别代表图像三个通道(RGB)的均值。这里 [0.485, 0.456, 0.406] 是在 ImageNet 数据集上统计得到的三个通道的均值。

  • std:一个长度为 3 的列表,分别代表图像三个通道(RGB)的标准差。[0.229, 0.224, 0.225] 是在 ImageNet 数据集上统计得到的三个通道的标准差。

 参数的取值可以直接使用上面提供的经验值。对于输入图像的每个像素RGB值 ,归一化后的像素RGB值 按照以下公式计算:

其中,mean 和 std 分别是对应通道的均值和标准差。

transform = transforms.Compose([
    transforms.Resize((100, 100)),  # 调整图像大小
    transforms.ToTensor(),  # 将图像转换为张量
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 归一化
])

3)加载数据

使用dataset.ImageFolder()加载需要的训练图像和测试凸显数据。这里的数据路径需要注意,并没有到具体的文件名。比如在D:/zhaopian/train目录下,程序会自动读取该目录下的子文件夹,并将子文件夹中的图像数据归集到以该子文件夹命名的类别中。参数transform=transform就是调用上述第二步定义的数据预处理方法集,在读取每一张图像数据时将自动依次执行上述3个方法。并将最终转换好的数据返回。

创建数据加载器是为了便于网络训练时分批次加载数据,避免一次加载所有图像数据导致内存不足。

  • batch_size=32:指定每个批次中包含的数据样本数量。这里设置为 32,表示每次从 train_dataset 中取出 32 个样本组成一个批次进行训练。batch_size 的选择会影响模型的训练速度和性能,较大的 batch_size 可以加快训练速度,但可能会导致内存不足;较小的 batch_size 可以增加模型的泛化能力,但训练速度会变慢。

  • shuffle=True:一个布尔值,用于指定是否在每个训练周期(epoch)开始时打乱数据集的顺序。设置为 True 可以增加数据的随机性,避免模型学习到数据的特定顺序,有助于提高模型的泛化能力。

train_dataset = datasets.ImageFolder(root='D:/zhaopian/train', transform=transform)
test_dataset = datasets.ImageFolder(root='D:/zhaopian/test', transform=transform)
all_samples = test_dataset.samples
print("数据集中的类别:", train_dataset.classes)

# 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)

4)构建一个卷积神经网络

自定义一个卷积神经网络类FaceCNN,继承自nn.Module。该网络包含两个卷积层,两个池化层,两个全连接层。在卷积层和全连接层之间均使用了相同的激活函数ReLU:

函数 nn.Conv2d(3, 16, kernel_size=3, padding=1) 的作用是定义一个二维卷积层,参数3表示输入的数据是3个通道,这里对应的是彩色图像的RGB三组数据,即每张图会有3个100*100的矩阵输入到该卷积层中。参数16表示该卷积层有16个卷积核,每个核的大小均是3*3(参数kernel_size=3确定的)的矩阵,这16个3*3的卷积核初始值是随机的,它的值是通过模型训练最终确定的,也就是神经网络中的权重矩阵。定义16个卷积核意味着什么呢?当一幅彩色图像输入该卷积层时,该图像有3组矩阵,每组矩阵与一个卷积核进行卷积,得到3个卷积后的矩阵,然后在将这3个卷积后的矩阵对应位置取平均值,得到一个最终的卷积均值矩阵。16个卷积核依次执行上述操作,则经过该卷积层后,会得到16个卷积均值矩阵。参数padding=1表示对输入数据进行扩充,1表示在数据四周加一行,填充的目的通常是为了保持输入和输出特征图的尺寸一致。那么,经过该卷积层后,最终会得到16个100*100的卷积均值矩阵。

函数nn.MaxPool2d(2)的作用是定义一个2*2的二维池化层,该池化层输出池中的最大值。在最大池化操作中,池化窗口会在输入特征图上滑动,对每个窗口内的元素进行操作。这里设置为 2,表示使用一个 2x2 的池化窗口。当池化窗口在特征图上滑动时,会选取每个 2x2 区域内的最大值作为该区域的输出值。此处需要注意的是,经过2*2的二维池化层后,输出的矩阵变为了50*50,因为池化层的步幅等于它的大小,这与卷积不同,卷积默认步幅为1。

函数nn.Conv2d(16, 32, kernel_size=3, padding=1) 用于定义第二个二维卷积层。在该卷积层中,前面两个参数分别是16和32,16对应的是前面的16个卷积核,因为一幅图像经过第一层卷积后会输出16组数据,所以此处第二层卷积时,输入的数据就是16组。32表示第二层卷积后输出32组50*50的矩阵。

函数nn.Linear(32 * 25 * 25, 128)的作用是定义一个全连接层,全连接层的每个神经元都与前一层的所有神经元相连接,通过对输入进行线性变换(加权求和)并加上偏置,实现从输入特征到输出特征的映射。第一个参数32 * 25 * 25=20000,表示输入数据的数量,128表示输出数据的数量。全连接层线性变换公式为:

 其中, x是输入向量,W 是权重矩阵,b 是偏置向量, y是输出向量。权重矩阵W的形状为 (20000, 128), b的形状为 (128,1)。W和b都是在模型训练中需要更新的矩阵。

在建立的神经网络中还定义了前向传播函数forward(),它由开发者自行设计数据在网络中传播的方向。图像数据首先经过第一个卷积层,然后通过激活函数,再进入第一个池化层,随后依次进入第二个卷积层,第二个激活函数,第二个池化层。x = x.view(-1, 32 * 25 * 25)的作用是将得到的数据展平为一维数据。从前面的输出可知,x是一组由32个25*25矩阵组成的数据集,x.view会将该数据集整合为一个一维数组,数组的元素总量保持不变。

数据在经过展平后,进入了第一全连接层,由网络结构可知,第一个全连接层输出的是一个128个元素的数组,数据量被极大压缩了。然后再次经过激活函数,随后进入第二个全连接层,第二个全连接层输出的数组由n个元素组成,n=len(train_dataset.classes),就是我们训练模型时提供的n个人数。但需要注意的是,此处输出的结果与人脸识别并无直接关系,我们需要对该结果进行进一步处理,让它与人脸对应起来。

class FaceCNN(nn.Module):
    def __init__(self):
        super(FaceCNN, self).__init__()
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1)  # 定义第一个二维卷积层
        self.relu1 = nn.ReLU()  # 定义第一个激活函数
        self.pool1 = nn.MaxPool2d(2)  # 定义第一个二维池化层
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)  # 定义第二个二维卷积层
        self.relu2 = nn.ReLU()  # 定义第二个激活函数
        self.pool2 = nn.MaxPool2d(2)  # 定义第二个二维池化层
        self.fc1 = nn.Linear(32 * 25 * 25, 128)  # 定义第一个全连接层,展平后输出128个特征量
        self.relu3 = nn.ReLU()  # 定义第三个激活函数
        self.fc2 = nn.Linear(128, len(train_dataset.classes))  # 定义第二个全连接层,展平后输出n个特征量,n为人数

    def forward(self, x):
        x = self.pool1(self.relu1(self.conv1(x)))
        x = self.pool2(self.relu2(self.conv2(x)))
        x = x.view(-1, 32 * 25 * 25)
        x = self.relu3(self.fc1(x))
        x = self.fc2(x)
        return x

 5)模型训练

首先用自己定义卷积神经网络类实例化一个对象。然后定义一个损失函数,这里直接使用了pytorch库中提供的交叉熵损失函数对象,交叉熵损失函数(Cross-Entropy Loss)是分类问题中常用的一种损失函数,尤其适用于多分类任务。它能够有效衡量模型预测的概率分布与真实标签的概率分布之间的差异。该函数的具体实现比较复杂,如果不做算法研究,可以暂时不用管其具体实现,只需要知道在分类问题中适用该函数。

optimizer = optim.Adam(model.parameters(), lr=0.001) 这行代码在 PyTorch 中用于创建一个 Adam 优化器对象 optimizer,该优化器将用于更新模型 model 的参数。Adam(Adaptive Moment Estimation)是一种常用的优化算法,它结合了 AdaGrad 和 RMSProp 两种算法的优点。Adam 算法能够自适应地调整每个参数的学习率,同时利用梯度的一阶矩估计(均值)和二阶矩估计(方差)来更新参数。它具有收敛速度快、对不同类型的数据集和模型都有较好表现等优点,因此在深度学习中得到了广泛应用。我们同样也不需要去关心它的具体实现,PyTorch已经帮我们完成了相应的操作。其中的参数定义如下:

  • model.parameters():这是 optim.Adam 的第一个参数,它是一个生成器,用于返回模型 model 中所有需要更新的参数。在 PyTorch 中,模型的参数通常是可学习的张量,通过调用 model.parameters() 可以获取这些参数,优化器将根据这些参数的梯度信息来更新它们。

  • lr=0.001lr 是学习率(learning rate)的缩写,它是 optim.Adam 的一个重要超参数,用于控制每次参数更新的步长。学习率决定了模型在训练过程中朝着损失函数最小值前进的速度。这里将学习率设置为 0.001,表示每次更新参数时,参数的变化量是梯度乘以 0.001。如果学习率设置得过大,模型可能会跳过损失函数的最小值,导致无法收敛;如果学习率设置得过小,模型的训练速度会变得很慢。

# 初始化模型
model = FaceCNN()

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
num_epochs = 20  # 训练10次
for epoch in range(num_epochs):
    model.train()  # 将模型设置为训练模式。
    running_loss = 0.0
    for i, (images, labels) in enumerate(train_loader):
        optimizer.zero_grad()  # 每次反向传播之前,将优化器中所有参数的梯度清零
        outputs = model(images)  # 自动调用模型的 forward 方法
        loss = criterion(outputs, labels)  # 计算损失值
        loss.backward()  # 调用 loss.backward() 开始反向传播过程,计算每个可训练参数的梯度,并将这些梯度存储在参数的 .grad 属性中。
        optimizer.step()  # 优化器会根据存储在参数 .grad 属性中的梯度,按照指定的优化算法更新模型的参数。
        running_loss += loss.item()

6)模型测试

模型测试的代码实现如下。测试时首先将模型设置为评估模式,在这里同样使用批量传入测试图像数据的方式。

_, predicted = torch.max(outputs.data, 1)用于找出在模型输出的结果outputs.data中,每一行数据的最大值在该行中的索引位置,torch.max 函数的第二个参数,指定了在哪个维度上进行最大值查找。这里设置为 1,表示在每一行(即每个样本)上查找最大值。需要进一步说明的是,由于我们采用批量处理的方式,outputs.data应是一个二维数组,每一行表示模型对一张图的处理结果。根据前面网络结构定义可知,若是输入3个人的人脸照片,则每一行应该有3个元素。这里取最大值,是因为模型输出的结果表征的是模型预测的概率,概率越大说明输入图像是对应人脸的概率越大。而对应的人脸在labels中表征,在本例中输入3个人的照片,则它是一个包含3个元素[0,1,2]的数组,每个值对应一个人。因此, torch.max找到最大值在每一行中的索引位置后,该位置也就表征了对应的人。

# 测试模型
model.eval()  # 将模型切换到评估模式。
correct = 0
total = 0
with torch.no_grad():  # 不计算导数
    for index, (images, labels) in enumerate(test_loader):
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
        labels_array = labels.numpy()
        predicted_array = predicted.numpy()
        # 计算当前批次的起始索引
        start_idx = index * test_loader.batch_size
        # 计算当前批次的结束索引
        end_idx = start_idx + images.size(0)
        # 获取当前批次的图像文件路径
        batch_samples = all_samples[start_idx:end_idx]
        # 提取图像文件名称
        batch_image_names = [sample[0].split('\\')[2] for sample in batch_samples]
        for i in range(len(predicted_array)):
            print(f'图名: {batch_image_names[i]}', f'实际名字: {train_dataset.classes[labels_array[i]]}',
                  f'预测名字: {train_dataset.classes[predicted_array[i]]}')
print(f'总测试数量: {total}', f'正确识别数量: {correct}')
print(f'Test Accuracy: {100 * correct / total}%')

四、测试结果 

从网络上随机下载了一些公开图片,对网路进行了训练和测试,得到结果如下。实际看神经网络的识别准确率还是比较高的。模型的完整代码和使用的资源可在这里下载。

五、模型导出

在完成神经网络模型训练后,需要将训练好的模型导出,以供后续使用,具体方法有两种。

1)保存模型的状态参数

在训练后的模型下加入下列代码,则模型中的所有参数均会保存到对应的.pth文件中。

torch.save(model.state_dict(), 'model_state_dict.pth')

在重新使用模型时,需要再次实例化模型对象,然后加载保存的模型参数。因此,重新使用时我们需要知道模型的结构定义,即重写FaceCNN类。

# 加载模型的状态字典
loaded_model = FaceCNN()
loaded_model.load_state_dict(torch.load('model_state_dict.pth'))
loaded_model.eval()  # 设置为评估模式

 2)保存完整模型

我们也可以直接保存完整的模型文件,在重新使用时直接加载该模型即可。这种方式简单,但缺少灵活性。

# 保存整个模型
torch.save(model, 'whole_model.pth')

# 加载整个模型
loaded_model = torch.load('whole_model.pth')
loaded_model.eval()  # 设置为评估模式

 六、总结

通过一个人脸识别示例,详细说明了利用pytorch模块搭建卷积神经网络的实现流程,并对代码进行了逐行解释。pytorch极大方便了神经网络开发,让开发人员可以不用关注网络中具体的算法实现,而更加侧重在网络模型搭建上。在本例试验测试中,模型的训练次数和网络结构参数的调整均会对图像识别的准确造成大幅度影响,仍需要通过大量测试优化网络结构参数。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Ollama安装deepseek-r1模型搭建本地知识库

大模型 安装Ollama使用win系统安装使用sh脚本安装使用docker安装 下载大模型搭建本地知识库安装Dify对比参数模型 本实验主要使用win系统安装ollama部署deepseek-r1(1.5b、7b、8b、14b、32b等参数)并搭建本地知识库(个人学习研究为主&#xf…

【R语言】t检验

一、基本介绍 t检验(t-test)是用于比较两个样本均值是否存在显著差异的一种统计方法。 t.test()函数的调用格式: t.test(x, yNULL, alternativec("two.sided", "less", "greater"), mu0, pairFALSE, var.eq…

Uniapp 原生组件层级过高问题及解决方案

文章目录 一、引言🏅二、问题描述📌三、问题原因❓四、解决方案💯4.1 使用 cover-view 和 cover-image4.2 使用 subNVue 子窗体4.3 动态隐藏原生组件4.4 使用 v-if 或 v-show 控制组件显示4.5 使用 position: fixed 布局 五、总结&#x1f38…

x_xor_md5

x_xor_md5 打开附件,用010打开 发现有一行值是重复的。 根据题目提示我们把全文与重复行进行异或 str1 [0x01,0x78,0x0C,0x4C,0x10,0x9E,0x32,0x37,0x12,0x0C,0xFB,0xBA,0xCB,0x8F,0x6A,0x53]result[]txt [0x69,0x35,0x41,0x01,0x1C,0x9E,0x75,0x78,0x5D,0x48,0xF…

【C语言标准库函数】标准输入输出函数详解[6]:字符文件读取写入

目录 一、字符文件写入函数 1.1. fputc函数 1.1.1. fputc函数简介 1.1.2. 使用场景 1.1.3. 注意事项 1.1.4. 示例 1.2. fputs函数 1.2.1. fputs函数简介 1.2.2. 使用场景 1.2.3. 注意事项 1.2.4. 示例 二、字符文件读取函数 2.1. fgetc函数 2.1.1. 函数简介 2.1…

基于深度学习的半导体故障诊断与寿命预测算法研究

摘要 半导体产业在现代科技领域占据着核心地位,其设备的稳定性和可靠性至关重要。传统的半导体故障诊断和寿命预测方法存在效率低、准确性差等问题。本文聚焦于深度学习算法在半导体故障诊断与寿命预测方面的应用,详细阐述了卷积神经网络(CN…

制药行业 BI 可视化数据分析方案

一、行业背景 随着医药行业数字化转型的深入,企业积累了海量的数据,包括销售数据、生产数据、研发数据、市场数据等。如何利用这些数据,挖掘其价值,为企业决策提供支持,成为医药企业面临的重大挑战。在当今竞争激烈的…

基于Python flask-sqlalchemy的SQLServer数据库管理平台

适应场景: 主要用于帮助DBA自动化很多日常工作,包括: 数据库状态监控 性能问题诊断 日志分析 自动巡检 问题告警 系统截图: main.py from flask import Blueprint, render_template, request, flash, redirect, url_for f…

TensorRT 8.6.1教程1-TensorRT简介

区分计算节点和数据节点 视频 TensorRT 教程 | 基于 8.6.1 版本 | 第一部分_哔哩哔哩_bilibili cookbook

MySQL创建存储过程和存储函数

【图书推荐】《MySQL 9从入门到性能优化(视频教学版)》-CSDN博客 《MySQL 9从入门到性能优化(视频教学版)(数据库技术丛书)》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

【项目总结】易到家家政服务平台 —— 派单调度(7)

派单调度需求分析 在抢单业务中,用户下单成功由服务人员或机构进行抢单,抢单成功服务人员上门服务,除了抢单业务系统还设计了派单业务,由系统根据用户订单的特点自动派给合适的服务人员。 流程如下: 首先获取待分配…

visual studio 在kylin v10上跨平台编译时c++标准库提示缺少无法打开的问题解决

情况1:提示无法打开 源文件 "string"之类导致无法编译 情况2:能编译,但无法打开这些库文件或标准库使用提示下划红色问题 解决方案: 一、通过工具->选项->跨平台里,在“远程标头IntelliSense管理器”更新下载一下…

SpringCould+vue3项目的后台用户管理的CURD【Taurus教育平台】

文章目录 一.SpringCouldvue3项目的后台用户管理的CURD【Taurus教育平台】 1.1 背景 二.用户列表(分页查询) 2.1 前端Vue3 (Vue3-Element-Admin)2.2 后端SpringCould 处理 三. 用户信息删除 3.1 前端Vue3 (Vue3-Eleme…

Eclipse 插件开发相关概念

整理了Eclipse插件开发的概念,用于熟悉入门 SWT(Standard Widget Toolkit)标准图形工具箱 Java开发的GUI程序技术,由Eclipse开发,相比AWT、Swing更美观;对于目标平台上已经有的控件,SWT会直接使…

算法之 数论

文章目录 质数判断质数3115.质数的最大距离 质数筛选204.计数质数2761.和等于目标值的质数对 2521.数组乘积中的不同质因数数目 质数 质数的定义:除了本身和1,不能被其他小于它的数整除,最小的质数是 2 求解质数的几种方法 法1,根…

AndroidStudio查看Sqlite和SharedPreference

1.查看Sqlite 使用App Inspection,这是个好东西 打开方式:View → Tool Windows → App Inspection 界面如图: App inspection不但可以看Sqlite还可以抓包network和background task连抓包工具都省了。 非常好使 2.查看sharedPreference 使…

谈一谈数据库中的死锁问题

文章目录 死锁是什么?死锁的四个必要条件避免死锁的策略 本篇文章是基于《MySQL45讲》来写的个人理解与感悟。 死锁是什么? 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。若无外力作用&a…

网络工程师 (31)VLAN

前言 VLAN(Virtual Local Area Network)即虚拟局域网,是一种将物理局域网划分成多个逻辑上独立的虚拟网络的技术。 一、定义与特点 定义:VLAN是对连接到的第二层交换机端口的网络用户的逻辑分段,不受网络用户的物理位置…

从深入理解 netty——》AI

想了很久,准备写一个系列从深入理解 netty——》AI。 先说下为啥要从netty开始,看看netty的重要性 rocketmq异步消息组件nacos微服务注册中心spring cloud gateway网关redission分布式缓存es全文检索sentinel流量控制,服务保护seata分布式…

从 0 开始本地部署 DeepSeek:详细步骤 + 避坑指南 + 构建可视化(安装在D盘)

个人主页:chian-ocean 前言: 随着人工智能技术的迅速发展,大语言模型在各个行业中得到了广泛应用。DeepSeek 作为一个新兴的 AI 公司,凭借其高效的 AI 模型和开源的优势,吸引了越来越多的开发者和企业关注。为了更好地…