当前位置: 首页 > news >正文

Paddle实现人脸对比

人脸对比

人脸对比,顾名思义,就是对比两个人脸的相似度。本文将用Paddle实现这一功能。

PS:作者肝了整整3天才稍微搞明白实现方法

数据集准备

这里使用百度AI Studio的开源数据集:

人脸数据_数据集-飞桨AI Studio星河社区 (baidu.com)

这个数据集提供了500个人的人脸各100张,比较适合我们的项目。

根据这个数据集,很容易写出数据集类,文件名为face_dataset.py:

import numpy as np
from PIL import Image
import paddle
from random import shuffleclass FaceData(paddle.io.Dataset):def __init__(self, mode, num):super().__init__()# 训练集/测试集file = 'facecap/train_list.txt' if mode == 'train' else 'facecap/test_list.txt'self.imgs1 = []self.imgs2 = []self.labels = []# 控制相同人脸个数与不同人脸个数各占一半_1_count = 0with open(file) as f:# 读取数据集文件信息数据并洗牌lines = f.readlines()shuffle(lines)lines = lines[:num]print('read down')# 加载数据集for line1 in lines:line1 = line1.strip()img1, label1 = line1.split(' ')pil_img1 = Image.open(f'facecap\\{img1}').convert('RGB').resize((96, 96))for line2 in lines:line2 = line2.strip()img2, label2 = line2.split(' ')if label1 == label2:_1_count += 1pil_img2 = Image.open(f'facecap\\{img2}').convert('RGB').resize((96, 96))self.imgs1.append(np.array(pil_img1).transpose((2, 0, 1)) / 255.0)self.imgs2.append(np.array(pil_img2).transpose((2, 0, 1)) / 255.0)self.labels.append(1)elif _1_count > 0:_1_count -= 1pil_img2 = Image.open(f'facecap\\{img2}').convert('RGB').resize((96, 96))self.imgs1.append(np.array(pil_img1).transpose((2, 0, 1)) / 255.0)self.imgs2.append(np.array(pil_img2).transpose((2, 0, 1)) / 255.0)self.labels.append(0)self.imgs1 = np.array(self.imgs1, dtype=np.float32)self.imgs2 = np.array(self.imgs2, dtype=np.float32)self.labels = np.array(self.labels, dtype=np.float32)print('load down')def __getitem__(self, idx):return self.imgs1[idx], self.imgs2[idx], self.labels[idx]def __len__(self):return len(self.labels)

需要注意的是,PIL的图片维度与paddle CNN的维度不一样,需要使用transpose改变 

当然,使用这个数据集类读取数据是非常漫长的,因此我们创建了一个face_create_dataset.py,创建数据集对象并保存到本地:

from face_dataset import FaceData
import pickletrain_dataset = FaceData(mode='train', num=2000)
test_dataset = FaceData(mode='test', num=200)pickle.dump(train_dataset, open('./database/train.data', 'wb'), protocol=4)
pickle.dump(test_dataset, open('./database/test.data', 'wb'), protocol=4)

 这里我们使用pickle保存对象,注意这里要指定protocol=4,以保证可以存储超过4G的大文件

最后,这个脚本会在本地的database文件夹下生成两个data文件,使用时只需要加载即可

孪生网络

既然要输入两张图片,就自然需要使用两张卷积网络,分别处理两张图片。但是人脸对比与输入顺序无关,这就要求两个网络对于同一张图片的输出是相同的,也就是这两个网络是相同的。即共享权重的网络。因此我们可以定义网络如下:

class FaceNet(paddle.nn.Layer):def __init__(self):super().__init__()# 共享权重的cnn网络self.cnn = paddle.nn.Sequential(paddle.nn.Conv2D(3, 16, 3, padding=1),paddle.nn.ReLU(),paddle.nn.MaxPool2D(2, 2),paddle.nn.Conv2D(16, 32, 3, padding=1),paddle.nn.ReLU(),paddle.nn.MaxPool2D(2, 2),paddle.nn.Conv2D(32, 64, 3, padding=1),paddle.nn.ReLU(),paddle.nn.MaxPool2D(2, 2),paddle.nn.Conv2D(64, 128, 3, padding=1),paddle.nn.ReLU(),paddle.nn.MaxPool2D(2, 2),paddle.nn.Flatten(),paddle.nn.Linear(4608, 5))def forward(self, face1, face2):# 前向传播:使用cnn网络分别输出两个结果并返回n1r = self.cnn(face1)n2r = self.cnn(face2)return n1r, n2r

这个网络还需要有特殊的损失函数。这个损失函数将会使相同的人脸距离相近,不同的人脸距离更远。我们采用勾股定理计算距离,这样的距离也叫欧氏距离。

因此,对于一个在n维空间上的两个点(x1, x2, x3, ..., xn), (y1, y2, y3, ..., yn),就有:

d = \sqrt{(x_1-y_1)^2+(x_2-y_2)^2+...+(x_n-y_n)^2}

因此,如果人脸相同,损失函数将会输出的损失值是:

\left | 0-d \right |

这样的话,如果距离过远,损失值就会偏大 ,从而使输出更接近0

如果人脸不同,输出的损失值是:

max(m-d, 0)

这样的话,只有当距离比设定阈值远时,损失才为0.

另外,损失最好使用乘方而不是绝对值,这样的话就可以增加远离时的梯度。

我们定义其损失函数如下:

# 损失函数定义
class FaceLoss(paddle.nn.Layer):def __init__(self, margin=(512 ** 0.5)):super(FaceLoss, self).__init__()self.margin = margindef forward(self, output1, output2, label):# 计算欧式距离(勾股定理)euclidean_distance = paddle.norm(output1 - output2, axis=1)# 损失值# 在数据集中,1为相同,0为不同。但是输出要求相似的图片距离更近loss_contrastive = label * paddle.square(euclidean_distance) + \(1 - label) * paddle.square(paddle.maximum(self.margin - euclidean_distance, paddle.to_tensor(0.0)))# 损失函数应对同一批次取一个损失值return paddle.mean(loss_contrastive)

在paddle中,可以使用paddle.norm计算距离。axis=1表示只对第1维度计算距离,因为第0维度是数据批次。

在数据集中,我们定义1为相同,0为不同。根据我们之前的分析,很容易算出损失值的公式。

接下来就可以把这两个整合在一个py文件中,起名face_layers.py:

import paddleclass FaceNet(paddle.nn.Layer):def __init__(self):super().__init__()# 共享权重的cnn网络self.cnn = paddle.nn.Sequential(paddle.nn.Conv2D(3, 16, 3, padding=1),paddle.nn.ReLU(),paddle.nn.MaxPool2D(2, 2),paddle.nn.Conv2D(16, 32, 3, padding=1),paddle.nn.ReLU(),paddle.nn.MaxPool2D(2, 2),paddle.nn.Conv2D(32, 64, 3, padding=1),paddle.nn.ReLU(),paddle.nn.MaxPool2D(2, 2),paddle.nn.Conv2D(64, 128, 3, padding=1),paddle.nn.ReLU(),paddle.nn.MaxPool2D(2, 2),paddle.nn.Flatten(),paddle.nn.Linear(4608, 5))def forward(self, face1, face2):# 前向传播:使用cnn网络分别输出两个结果并返回n1r = self.cnn(face1)n2r = self.cnn(face2)return n1r, n2r# 损失函数定义
class FaceLoss(paddle.nn.Layer):def __init__(self, margin=(512 ** 0.5)):super(FaceLoss, self).__init__()self.margin = margindef forward(self, output1, output2, label):# 计算欧式距离(勾股定理)euclidean_distance = paddle.norm(output1 - output2, axis=1)# 损失值# 在数据集中,1为相同,0为不同。但是输出要求相似的图片距离更近loss_contrastive = label * paddle.square(euclidean_distance) + \(1 - label) * paddle.square(paddle.maximum(self.margin - euclidean_distance, paddle.to_tensor(0.0)))# 损失函数应对同一批次取一个损失值return paddle.mean(loss_contrastive)

训练

接下来我们需要编写训练脚本face.py:

import paddle
from face_dataset import FaceData
from face_layers import FaceNet, FaceLoss
import pickle# 加载数据集
train_dataset = pickle.load(open('./database/train.data', 'rb'))
test_dataset = pickle.load(open('./database/test.data', 'rb'))# 输出数据集信息
print(f'加载数据完毕,训练集数据个数:{len(train_dataset)};测试集数据个数:{len(test_dataset)}')count = 0
for context1, context2, label in train_dataset:if label == 1:count += 1print(f'训练集相同人脸个数{count}')count = 0
for context1, context2, label in test_dataset:if label == 1:count += 1print(f'测试集相同人脸个数{count}')# 指定设备
paddle.device.set_device('gpu')# 创建模型
model = paddle.Model(FaceNet())# 打印模型信息
print(model.summary(((1, 3, 96, 96), (1, 3, 96, 96))))# 模型训练的配置准备,准备损失函数,优化器和评价指标
model.prepare(paddle.optimizer.Adam(parameters=model.parameters(), learning_rate=0.00001),FaceLoss())# 模型训练
model.fit(train_dataset, epochs=50, batch_size=64, verbose=1)
# 模型评估
model.evaluate(test_dataset, batch_size=64, verbose=1)# 保存模型
model.save('./output/face-compare')

这里需要注意,我们需要使用FaceLoss作为损失函数

训练完毕后,训练数据将被存储在本地的output文件夹下,使用时加载即可

接下来我们可以编写face_use.py使用这个模型:

import paddle
from face_dataset import FaceData
from face_layers import FaceNet
from PIL import Image
import numpy as np# 加载模型
model = paddle.Model(FaceNet())
model.load('./output/face-compare')print('加载模型完毕')# 打开图片
pil_img1 = Image.open(f'facecap\\003\\30.jpg').convert('RGB').resize((96, 96))
pil_img2 = Image.open(f'facecap\\003\\27.jpg').convert('RGB').resize((96, 96))# 转np数组
np_img1 = np.array(pil_img1, dtype=np.float32).transpose((2, 0, 1)) / 255.0
np_img2 = np.array(pil_img2, dtype=np.float32).transpose((2, 0, 1)) / 255.0# 预测
pred = model.predict_batch((np.array([np_img1], dtype=np.float32), np.array([np_img2], dtype=np.float32)))# 计算距离
euclidean_distance = paddle.norm(paddle.to_tensor([pred[0]]) - paddle.to_tensor([pred[1]]))
print(euclidean_distance.numpy())

这里只以两张相同人的人脸的图片做测试,最后输出:

加载模型完毕
[0.1978856]

改用两张不同人的人脸做测试,最后输出:

加载模型完毕
[1.1059165]

可以看到,这个模型的效果还不错。但是经过我的多次测试,发现这个模型还有一定的提升空间。这需要更大的数据集、更深的模型和更多的训练次数

总结

我们使用孪生网络技术,成功实现了人脸对比模型,并有一定的准确性,可以应用于人脸比对等场景。但是,由于数据集、模型和训练次数有限,还难以实现更准确的人脸对比

相关文章:

Paddle实现人脸对比

人脸对比 人脸对比,顾名思义,就是对比两个人脸的相似度。本文将用Paddle实现这一功能。 PS:作者肝了整整3天才稍微搞明白实现方法 数据集准备 这里使用百度AI Studio的开源数据集: 人脸数据_数据集-飞桨AI Studio星河社区 (b…...

挖一挖:PostgreSQL Java里的double类型存储到varchar精度丢失问题

前言 大概故事是这样的,PostgreSQL数据库,表结构: create table t1(a varchar);然后使用标准的Java jdbc去插入数据,其基本代码如下: import java.sql.*; public class PgDoubleTest {public static void main(Stri…...

函数对象基本使用

一、函数对象概念 1.重载函数调用操作符的类,其对象常称为函数对象 2.函数对象使用重载的()时,行为类似函数调用,也叫仿函数 本质: 函数对象(仿函数)是一个类,不是一个函数 二、函数对象使用 特点: 函…...

浅谈HTTP

浅谈HTTP 要通过netty实现HTTP服务器(或者客户端),首先你要了解HTTP协议。 HTTP在客户端 - 服务器计算模型中用作请求 - 响应协议。 例如,web浏览器可以是客户端,并且在托管网站的计算机上运行的应用程序可以是服务器。 客户端向服务器提交…...

HarmonyOS NEXT应用开发之@Provide装饰器和\@Consume装饰器:与后代组件双向同步

Provide和Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,Provide和Consume摆脱参数传递机制的束缚,实现跨层级传递。 其中Provide装饰的变…...

Docker 安装 | 部署MySQL 8.x 初始设置

1、准备工作 如果不想看前面的废话请直接右边目录跳到 运行容器 处 默认你已经有 docker 环境。 Windows 推荐 Docker Desktop (下载地址)并基于 WSL2 运行 Docker 环境 mac 推荐 Orbstack (下载地址)(这个很节省资源&…...

linux三剑客之流编辑器sed

sed(stream editor)是Linux和Unix系统中一个非常强大的文本处理工具。它主要用于对文本数据进行过滤和转换。sed 可以在不打开文件的情况下,直接对输入流进行操作,并且可以将结果输出到标准输出或文件。 基本语法: s…...

【Android Studio】上位机-安卓系统手机-蓝牙调试助手

【Android Studio】上位机-安卓系统手机-蓝牙调试助手 文章目录 前言AS官网一、手机配置二、移植工程三、配置四、BUG五、Java语言总结 前言 提示:以下是本篇文章正文内容,下面案例可供参考 AS官网 AS官网 一、手机配置 Android Studio 下真机调试 …...

怎样把学浪购买的课程下载下来

如何把学浪已购买的课程下载下来?这里就教大家一个方法,利用一个工具轻轻松松把视频下载下来 这个工具我打包成压缩包了,有需要的自己取一下 链接:https://pan.baidu.com/s/1y7vcqILToULrYApxfEzj_Q?pwdkqvj 提取码:kqvj --来自百度网盘超级会员V1…...

SD-WAN如何解决更有性价比地跨境网络问题

云桥通SD-WAN利用智能路由和负载均衡技术,优化数据传输路径,提高网络性能和可靠性。这意味着数据在跨国传输时可以更快到达目的地,减少延迟和丢包率。跨境SD-WAN提高了网络连接速度和质量,使用户能够更快地访问跨国业务所需的资源…...

第15章 File类与IO流

一 java.io.File类的使用 1.1 概述 File类及本章下的各种流,都定义在java.io包下。一个File对象代表硬盘或网络中可能存在的一个文件或者文件目录(俗称文件夹),与平台无关。(体会万事万物皆对象)File 能新…...

C语言基础语法-教案16(从小白到劝退之结构体初阶)

最近给大家争取到一个 深夜福利 保证你在深夜手机刷到 嘎嘎香~ 那就是 大流量卡 缺点:月租太便宜 185GB~ 100分钟通话时长~ 长期套餐~ 畅想自由的气息 流量自由的同时还拥有超长通话,而且免费领取。 名额有限,咱们废话不多说直接上…...

Linux:ip和ip协议的初步认识

文章目录 ip协议基本认识ip协议的报头网段划分ip的类型划分 ip协议基本认识 前面对于TCP的内容已经基本结束了,那么这也就意味着在传输层也已经结束了,那么下一步要进入的是的是网络层,网络层中也有很多种协议,这里主要进行解析的…...

Android12 简单的共享内存驱动实现 参考Ashmem

Android12 共享内存驱动实现 SOC:RK3568 system:Android12 概述: 1. 概述 Ashmem(Anonymous Shared Memory,Android 匿名共享内存),它基于 mmap 系统调用,可以让不同进程将同一段…...

物理安全和逻辑安全在信息安全中的重要作用

在信息时代,信息安全已经成为企业和个人不可或缺的重要组成部分。物理安全和逻辑安全作为信息安全的两大支柱,发挥着至关重要的作用。 什么是物理安全和逻辑安全? 物理安全是指通过技术手段,对计算机设备、网络设备、数据中心等…...

每日一题 --- 滑动窗口最大值[力扣][Go]

滑动窗口最大值 题目:239. 滑动窗口最大值 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1…...

TensorBoard可视化+Confustion Matrix Drawing

for later~ 代码阅读 1. 加载trainset import argparse import logging import os import numpy as npimport torch from torch import distributed from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriterfrom backbones import get_…...

012——LED模块驱动开发(基于I.MX6uLL)

目录 一、 硬件原理图 二、 驱动程序 三、 应用程序 四、 Makefile 五、操作 一、 硬件原理图 又是非常经典的点灯环节 ,每次学新语言第一步都是hello world,拿到新板子或者学习新的操作系统,第一步就是点灯。 LED 的驱动方式&#xff0…...

基于springboot实现房屋租赁管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现房屋租赁系统演示 摘要 房屋是人类生活栖息的重要场所,随着城市中的流动人口的增多,人们对房屋租赁需求越来越高,为满足用户查询房屋、预约看房、房屋租赁的需求,特开发了本基于Spring Boot的房屋租赁系统。 …...

168.乐理基础-中古调式概述

如果到这五线谱还没记住还不认识的话去看102.五线谱-高音谱号与103.五线谱-低音谱号这两个里,这里面有五线谱对应的音名,对比着看 如果不认识调号去看112.五线谱的调号(一)、113.五线谱的调号(二)、114.快…...

XML Group端口详解

在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...