多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

基于PyTorch与CelebA数据集的人脸识别工程实践全流程解析

基于PyTorch与CelebA数据集的人脸识别工程实践全流程解析 简介本资源是一个基于CelebA数据集与PyTorch框架实现的人脸识别神经网络完整项目面向深度学习初学者、计算机视觉方向学生及人脸识别技术实践者旨在解决人脸检测与特征提取的基础建模问题适用于课程设计、科研入门与模型复现等场景。压缩包共30个文件含10个核心Python脚本如train_model.py、test_model.py、net_model.py、6个XML配置与标注文件、6个TXT格式的训练/测试样本列表如train1000.txt、test1000.txt以及PKL模型参数、MD说明文档等结构清晰模块化程度高涵盖数据加载Data_Loader、网络定义Net、训练与测试全流程。目前已有115人学习下载。读者可直接运行代码完成端到端训练与推理获取已组织好的CelebA子集划分逻辑、自定义CNN/ResNet风格网络实现、摄像头实时捕获检测示例catch_camera.py及配套README说明大幅降低从数据准备到模型部署的学习门槛。1. 项目缘起从“认识人脸”到“认出人脸”的工程实践几年前我接手过一个智能门禁系统的原型开发核心需求很简单摄像头拍到人脸系统能判断这是不是“自己人”。当时市面上成熟的SDK很多但要么是闭源黑盒出了问题无从排查要么是授权费用高昂对于想深入理解技术本质的开发者来说总感觉隔着一层纱。于是我决定自己动手从零开始构建一个可解释、可控制、能学习的人脸识别模型。这就是“FaceDetectionByTorch”这个项目的起点——它不是一个追求极致SOTA最先进的学术研究而是一个旨在打通从数据到部署全流程的、面向工程实践的“教学级”项目。这个项目的核心是基于PyTorch深度学习框架和CelebA人脸属性数据集构建一个能够完成人脸检测与识别任务的神经网络。你可能听过很多关于人脸识别的“神话”比如准确率99.9%但在实际工程中从一张原始的RGB图片到最终输出“这是张三”中间要经历数据预处理、特征提取、模型设计、训练调优、部署推理等多个环节每一个环节都藏着无数细节和“坑”。本项目将带你完整地走一遍这个流程你会亲手处理CelebA数据设计一个卷积神经网络CNN用PyTorch实现它并训练它学会区分不同的人脸。最终你将得到一个完整的、可运行的代码包即FaceDetectionByTorch.zip以及更重要的是一套解决此类计算机视觉问题的系统性方法论。2. 环境搭建与数据准备万事开头“细”在开始写第一行模型代码之前有两件“脏活累活”必须搞定搭建一个稳定、高效的PyTorch开发环境以及把原始的CelebA数据集“驯服”成模型能“吃”的格式。很多教程对这部分一笔带过但这恰恰是新手最容易卡住、老手也常翻车的地方。2.1 PyTorch环境搭建避开版本依赖的“雷区”PyTorch的安装看似简单pip install torch一行命令的事。但如果你需要GPU加速或者你的项目依赖特定版本的CUDANVIDIA的并行计算平台事情就复杂了。我见过太多人因为版本不匹配导致import torch成功但torch.cuda.is_available()返回False训练速度慢如蜗牛。我的建议是先去PyTorch官网pytorch.org的“Get Started”页面。那里有一个交互式选择器你需要根据你的操作系统、包管理工具pip或conda、CUDA版本如果使用GPU来选择安装命令。这里的关键是CUDA版本必须与你的NVIDIA显卡驱动兼容。你可以通过命令行nvidia-smi查看驱动支持的CUDA最高版本。例如驱动版本为545.xx通常支持CUDA 12.3及以下。那么你在PyTorch官网就应选择CUDA 12.1或11.8的安装命令。注意如果你使用Anaconda强烈建议为这个项目创建一个独立的虚拟环境conda create -n face_torch python3.9然后在其中安装PyTorch。这能完美隔离不同项目间的依赖冲突。对于本项目一个典型的环境配置如下以Linux系统、CUDA 12.1为例# 创建并激活虚拟环境 conda create -n face_torch python3.9 conda activate face_torch # 从PyTorch官网获取的安装命令 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 # 安装其他必要的科学计算和图像处理库 pip install numpy pandas matplotlib opencv-python pillow scikit-learn tqdm安装完成后运行一个简单的测试脚本验证GPU是否可用import torch print(fPyTorch版本: {torch.__version__}) print(fCUDA是否可用: {torch.cuda.is_available()}) print(f可用GPU数量: {torch.cuda.device_count()}) print(f当前GPU名称: {torch.cuda.get_device_name(0) if torch.cuda.is_available() else None})2.2 CelebA数据集解析与预处理从原始图片到训练样本CelebACelebFaces Attributes Dataset是一个大规模的人脸属性数据集包含超过20万张名人图像每张图像有40个属性标注如是否微笑、是否戴眼镜等和5个关键点位置。对于人脸识别任务我们主要利用其丰富的身份标签即“这个人是谁”。第一步下载与解压。数据集通常是一个压缩包解压后你会看到一个img_align_celeba文件夹存放图片和几个文本文件如identity_CelebA.txt,list_attr_celeba.txt。文件结构管理清晰是后续高效编程的基础。第二步理解数据标注。identity_CelebA.txt文件是核心每一行格式如000001.jpg 1表示图片000001.jpg对应的人物ID是1。CelebA有超过1万个独立身份但每个身份的图像数量极不均衡有的明星有几十张图片有的只有一两张。这直接引出了人脸识别中的一个经典难题长尾分布。我们的项目为了简化通常会选取出现次数最多的前N个身份例如N1000来构建一个相对均衡的子集这能保证模型训练稳定快速见到效果。第三步构建自定义Dataset类。这是PyTorch数据处理的精髓。我们不能直接扔给模型一堆图片路径而是要通过一个torch.utils.data.Dataset的子类告诉PyTorch如何读取一张图片、如何对其进行变换、如何返回图片张量和对应的标签。import os from PIL import Image import torch from torch.utils.data import Dataset from torchvision import transforms class CelebADataset(Dataset): def __init__(self, img_dir, label_file, transformNone, selected_idsNone): Args: img_dir (string): 图片目录路径。 label_file (string): 身份标签文件路径。 transform (callable, optional): 应用于图片的变换/增强。 selected_ids (set, optional): 指定要包含的人物ID集合。 self.img_dir img_dir self.transform transform self.img_paths [] self.labels [] # 读取标签文件并筛选身份 with open(label_file, r) as f: for line in f: img_name, label_str line.strip().split() label int(label_str) if selected_ids is None or label in selected_ids: self.img_paths.append(os.path.join(img_dir, img_name)) self.labels.append(label) # 构建一个从原始标签到连续索引0,1,2,...的映射 # 这对于分类任务的交叉熵损失函数是必须的 unique_labels sorted(set(self.labels)) self.label_to_idx {orig: new for new, orig in enumerate(unique_labels)} self.labels [self.label_to_idx[l] for l in self.labels] def __len__(self): return len(self.img_paths) def __getitem__(self, idx): img_path self.img_paths[idx] # 使用PIL或OpenCV读取图片。PIL与torchvision兼容性更好。 image Image.open(img_path).convert(RGB) # 确保是三通道 label self.labels[idx] if self.transform: image self.transform(image) return image, torch.tensor(label, dtypetorch.long)第四步设计数据变换Transform。这是提升模型泛化能力的关键。训练集和验证集的变换通常不同。# 训练集变换增强 归一化 train_transform transforms.Compose([ transforms.RandomHorizontalFlip(p0.5), # 随机水平翻转简单有效的增强 transforms.RandomRotation(degrees10), # 小幅随机旋转 transforms.Resize((128, 128)), # 统一缩放到固定尺寸 transforms.ToTensor(), # 将PIL图像或NumPy数组转换为张量并缩放到[0,1] transforms.Normalize(mean[0.485, 0.456, 0.406], # ImageNet的均值 std[0.229, 0.224, 0.225]) # ImageNet的标准差 ]) # 验证集/测试集变换仅保留必要的 resize 和归一化不做增强 val_transform transforms.Compose([ transforms.Resize((128, 128)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ])这里归一化使用的均值和标准差是ImageNet数据集的统计值。虽然CelebA不是ImageNet但在实践中使用预训练模型如果后续采用或将其作为通用初始化值效果通常不错。如果你想更精确可以计算自己数据集的均值和标准差。第五步创建DataLoader。Dataset负责定义如何读取单个样本DataLoader则负责批量加载、打乱数据、使用多进程加速。from torch.utils.data import DataLoader, random_split # 假设我们已创建了完整的dataset full_dataset CelebADataset(img_dir./img_align_celeba, label_file./identity_CelebA.txt, transformtrain_transform, selected_idsset(range(1000))) # 选取前1000个身份 # 划分训练集和验证集例如8:2 train_size int(0.8 * len(full_dataset)) val_size len(full_dataset) - train_size train_dataset, val_dataset random_split(full_dataset, [train_size, val_size]) # 注意random_split会返回子集但它们的transform是共享的。我们需要为验证集重置transform。 val_dataset.dataset.transform val_transform # 这里需要访问原dataset属性 train_loader DataLoader(train_dataset, batch_size64, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_dataset, batch_size64, shuffleFalse, num_workers4, pin_memoryTrue)pin_memoryTrue在GPU训练时能加速数据从CPU到GPU的传输num_workers指定用于数据加载的子进程数通常设置为CPU核心数。3. 神经网络模型设计构建特征提取的“管道”人脸识别模型通常分为两部分一个骨干网络用于从原始像素中提取抽象的特征向量也称为“嵌入”或“特征脸”一个分类头用于根据特征向量判断属于哪个已知身份。在训练阶段两者一起训练在推理识别阶段我们只使用骨干网络来提取特征然后通过比较特征向量的相似度如余弦距离来判断是否为同一个人。3.1 骨干网络选择从零搭建还是站在巨人肩上对于骨干网络你有两个主要选择从零搭建一个简单的CNN优点是结构透明易于理解和修改非常适合学习。缺点是性能有限需要更多的数据和训练时间才能达到较好效果。使用预训练模型进行微调例如ResNet、MobileNet、EfficientNet等。这些模型在ImageNet上预训练过已经学会了提取通用图像特征的强大能力。我们只需将其最后的全连接层原用于1000类ImageNet分类替换为适合我们身份数量的新层然后进行微调。这是工业界和科研的常见做法能实现“快、好、省”。在本项目中为了深入理解原理我们先从零搭建一个CNN。一个典型的结构如下import torch.nn as nn import torch.nn.functional as F class SimpleFaceCNN(nn.Module): def __init__(self, num_classes1000): super(SimpleFaceCNN, self).__init__() # 特征提取部分 (Backbone) self.conv1 nn.Conv2d(3, 32, kernel_size3, padding1) # 输入3通道(RGB)输出32通道 self.pool1 nn.MaxPool2d(2, 2) # 尺寸减半 self.conv2 nn.Conv2d(32, 64, kernel_size3, padding1) self.pool2 nn.MaxPool2d(2, 2) self.conv3 nn.Conv2d(64, 128, kernel_size3, padding1) self.pool3 nn.MaxPool2d(2, 2) # 全连接分类头 (Head) # 经过3次pooling(2x2)128x128的图片变为 128 / (2^3) 16即16x16的特征图 self.fc1 nn.Linear(128 * 16 * 16, 512) # 展平后接入全连接层 self.fc2 nn.Linear(512, num_classes) # Dropout层用于防止过拟合 self.dropout nn.Dropout(p0.5) def forward(self, x): # 卷积激活池化序列 x self.pool1(F.relu(self.conv1(x))) x self.pool2(F.relu(self.conv2(x))) x self.pool3(F.relu(self.conv3(x))) # 展平特征图 x x.view(-1, 128 * 16 * 16) # 全连接层 x F.relu(self.fc1(x)) x self.dropout(x) # 只在训练时生效 x self.fc2(x) # 输出logits未归一化的分数 return x这个网络虽然简单但包含了CNN的核心要素卷积、激活函数、池化、全连接。Conv2d是局部感知和权值共享的体现MaxPool2d进行下采样以增加感受野并减少参数Dropout在训练时随机“关闭”一部分神经元是一种有效的正则化手段。3.2 分类头与损失函数如何教会模型“分辨”模型输出的是每个类别的“得分”logits。我们需要一个损失函数来度量预测得分与真实标签之间的差距并引导模型更新参数以减少这个差距。对于多分类任务交叉熵损失是标准选择。在PyTorch中nn.CrossEntropyLoss将Softmax归一化和负对数似然损失结合在了一起因此我们模型的最后一层不需要加Softmax激活。model SimpleFaceCNN(num_classes1000) # 假设我们有1000个身份 criterion nn.CrossEntropyLoss() # 交叉熵损失函数在训练循环中我们会计算loss criterion(outputs, labels)其中outputs是模型直接输出的logitslabels是形状为(batch_size,)的整数张量。3.3 模型初始化与可视化好的开始是成功的一半神经网络的参数初始化很重要不恰当的初始化可能导致梯度消失或爆炸。PyTorch的卷积层和全连接层默认使用Kaiming均匀初始化针对ReLU激活函数这对于我们的网络通常是有效的。但了解如何手动初始化是有益的def init_weights(m): if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear): nn.init.kaiming_normal_(m.weight, modefan_out, nonlinearityrelu) if m.bias is not None: nn.init.constant_(m.bias, 0) model.apply(init_weights)使用torchsummary库可以方便地查看模型结构和参数数量pip install torchsummaryfrom torchsummary import summary summary(model, input_size(3, 128, 128), devicecpu)这会输出每一层的输出形状和参数量帮助你确认网络结构是否符合预期以及模型复杂度是否适合你的硬件。4. 模型训练与验证在迭代中逼近最优训练一个神经网络本质上是求解一个高维非凸优化问题。我们通过反向传播计算梯度并用优化器更新参数使损失函数值不断下降。4.1 优化器选择Adam vs SGDAdam优化器自适应地调整每个参数的学习率结合了动量和自适应学习率的优点通常收敛速度快是很多场景下的默认选择。SGD随机梯度下降配合动量Momentum和学习率调度虽然可能收敛慢一些但最终解的性能泛化能力有时更好尤其在计算机视觉任务中。对于本项目初学者我推荐从Adam开始import torch.optim as optim optimizer optim.Adam(model.parameters(), lr0.001, weight_decay1e-5) # weight_decay是L2正则化防止过拟合lr学习率是最重要的超参数之一。太大可能导致震荡不收敛太小则收敛缓慢。weight_decay通过对大权值进行惩罚来抑制模型复杂度。4.2 训练循环的完整实现一个标准的训练循环包括将模型设置为训练模式、清空梯度、前向传播、计算损失、反向传播、更新参数。def train_one_epoch(model, train_loader, criterion, optimizer, device, epoch): model.train() # 设置为训练模式启用Dropout等 running_loss 0.0 correct 0 total 0 for batch_idx, (inputs, labels) in enumerate(train_loader): inputs, labels inputs.to(device), labels.to(device) # 清零梯度 optimizer.zero_grad() # 前向传播 outputs model(inputs) loss criterion(outputs, labels) # 反向传播与优化 loss.backward() optimizer.step() # 统计 running_loss loss.item() * inputs.size(0) _, predicted outputs.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() # 每N个batch打印一次进度 if batch_idx % 50 0: print(fEpoch: {epoch} [{batch_idx * len(inputs)}/{len(train_loader.dataset)} f({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.4f}) epoch_loss running_loss / total epoch_acc 100. * correct / total return epoch_loss, epoch_acc4.3 验证与早停防止模型“学傻了”模型在训练集上表现好不代表在没见过的新数据上也好。我们需要一个独立的验证集来监控模型的泛化能力。早停是一种有效的正则化技术当验证集损失在连续多个epoch不再下降甚至上升时就停止训练并回滚到验证损失最低的那个epoch的模型参数。def validate(model, val_loader, criterion, device): model.eval() # 设置为评估模式关闭Dropout等 val_loss 0.0 correct 0 total 0 with torch.no_grad(): # 关闭梯度计算节省内存和计算 for inputs, labels in val_loader: inputs, labels inputs.to(device), labels.to(device) outputs model(inputs) loss criterion(outputs, labels) val_loss loss.item() * inputs.size(0) _, predicted outputs.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() val_loss / total val_acc 100. * correct / total return val_loss, val_acc # 训练主循环 device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) num_epochs 50 best_val_acc 0.0 best_model_state None patience 5 # 容忍验证集准确率不提升的epoch数 counter 0 for epoch in range(num_epochs): train_loss, train_acc train_one_epoch(model, train_loader, criterion, optimizer, device, epoch) val_loss, val_acc validate(model, val_loader, criterion, device) print(fEpoch {epoch}: Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%, fVal Loss: {val_loss:.4f}, Val Acc: {val_acc:.2f}%) # 早停逻辑 if val_acc best_val_acc: best_val_acc val_acc best_model_state model.state_dict().copy() # 深拷贝最佳状态 torch.save(best_model_state, fbest_model_epoch{epoch}.pth) counter 0 else: counter 1 if counter patience: print(fEarly stopping at epoch {epoch}) break # 训练结束后加载最佳模型 model.load_state_dict(best_model_state)这个循环里model.train()和model.eval()的切换至关重要它控制了Dropout、BatchNorm等层在不同阶段的行为。4.4 学习率调度动态调整学习步伐固定学习率可能不是最优的。当损失下降变慢时适当降低学习率有助于模型更精细地收敛到局部最优点。torch.optim.lr_scheduler提供了多种调度器。scheduler optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemax, factor0.5, patience3, verboseTrue) # 在验证阶段后调用 scheduler.step(val_acc)这里使用了ReduceLROnPlateau当验证准确率modemax在连续3个epoch不再提升时学习率乘以0.5。5. 模型评估、推理与部署从数字到应用训练完成后我们需要系统地评估模型性能并知道如何用它进行实际的人脸识别。5.1 性能评估超越准确率对于分类任务除了整体的准确率查看混淆矩阵能揭示模型在哪些类别上容易混淆。对于人脸识别1:1比对我们更关心特征空间的质量。一个好的特征提取器应该让同一个人的不同图片的特征向量非常接近类内距离小而不同人的特征向量相距甚远类间距离大。我们可以从测试集中提取所有图片的特征即骨干网络在分类头之前的输出然后计算类内距离分布随机抽取同一个人的多张图片计算它们特征之间的余弦距离或欧氏距离的平均值。类间距离分布随机抽取不同人的图片对计算特征距离。 理想情况下类内距离分布应明显小于类间距离分布。两者的重叠区域越小模型区分能力越强。def extract_features(model, data_loader, device): 提取数据集中所有图片的特征向量和标签 model.eval() features_list [] labels_list [] with torch.no_grad(): for inputs, labels in data_loader: inputs inputs.to(device) # 假设我们修改了模型有一个方法能返回特征 # 或者我们手动获取倒数第二层的输出 feats model.extract_features(inputs) # 你需要实现这个方法 features_list.append(feats.cpu()) labels_list.append(labels) return torch.cat(features_list, dim0), torch.cat(labels_list, dim0) # 计算余弦相似度 from sklearn.metrics.pairwise import cosine_similarity import numpy as np features, labels extract_features(model, test_loader, device) features_np features.numpy() labels_np labels.numpy() intra_similarities [] inter_similarities [] # 简化计算抽样比较 for i in range(1000): # 找同类样本 same_class_idx np.where(labels_np labels_np[i])[0] if len(same_class_idx) 1: j np.random.choice(same_class_idx[same_class_idx ! i]) intra_similarities.append(cosine_similarity([features_np[i]], [features_np[j]])[0][0]) # 找不同类样本 diff_class_idx np.where(labels_np ! labels_np[i])[0] j np.random.choice(diff_class_idx) inter_similarities.append(cosine_similarity([features_np[i]], [features_np[j]])[0][0]) print(f平均类内相似度: {np.mean(intra_similarities):.4f}) print(f平均类间相似度: {np.mean(inter_similarities):.4f})5.2 单张图片推理流程训练好的模型如何识别一张新图片流程如下人脸检测与对齐使用OpenCV的Haar级联分类器或更先进的MTCNN、RetinaFace等模型从图片中框出人脸区域并进行对齐使眼睛处于水平位置。这是人脸识别前的关键预处理步骤能大幅提升识别稳定性。本项目为简化假设输入已经是裁剪对齐好的人脸。预处理与验证集相同的变换Resize, ToTensor, Normalize。特征提取将预处理后的图片张量输入模型的骨干网络得到特征向量。特征比对将提取的特征与已知人脸库Gallery中所有特征进行相似度计算如余弦相似度。决策如果最高相似度超过预设阈值则判定为库中对应的人否则判定为“未知人员”。import cv2 from PIL import Image def recognize_face(model, face_img_path, gallery_features, gallery_labels, threshold0.6): 识别单张人脸图片 Args: model: 训练好的模型仅骨干部分 face_img_path: 人脸图片路径 gallery_features: 已知人脸库的特征向量集合 [N, feature_dim] gallery_labels: 已知人脸库对应的标签 [N] threshold: 判定阈值 Returns: predicted_label (int or str): 预测的标签若为-1或Unknown则表示未知 max_similarity (float): 最大相似度 # 1. 读取并预处理图片假设已对齐 img Image.open(face_img_path).convert(RGB) transform val_transform # 使用验证集变换 img_tensor transform(img).unsqueeze(0) # 增加batch维度 - [1, C, H, W] # 2. 提取特征 model.eval() with torch.no_grad(): feature model.extract_features(img_tensor.to(device)) # [1, feature_dim] feature feature.cpu().numpy().squeeze() # [feature_dim] # 3. 与库中特征比对 similarities cosine_similarity([feature], gallery_features)[0] # [N] max_idx np.argmax(similarities) max_sim similarities[max_idx] # 4. 决策 if max_sim threshold: return gallery_labels[max_idx], max_sim else: return Unknown, max_sim5.3 模型保存、加载与部署考虑保存与加载PyTorch通常保存模型的state_dict参数字典而非整个模型对象这样更灵活。# 保存 torch.save({ epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), val_acc: best_val_acc, }, face_recog_checkpoint.pth) # 加载 checkpoint torch.load(face_recog_checkpoint.pth, map_locationdevice) model.load_state_dict(checkpoint[model_state_dict])部署将PyTorch模型部署到生产环境如服务器、边缘设备时需要考虑模型格式转换使用TorchScripttorch.jit.trace或torch.jit.script或ONNX格式以实现与语言无关的推理。性能优化使用半精度FP16推理、TensorRT加速、模型剪枝、量化等技术来减少模型大小、提升推理速度。服务化使用Flask、FastAPI等框架封装模型为REST API服务。6. 项目总结与进阶思考通过这个项目我们完成了一个完整的人脸识别模型生命周期从环境配置、数据处理、模型构建、训练调优到评估推理。这个基于CelebA和简单CNN的FaceDetectionByTorch项目其核心价值不在于达到多高的识别精度而在于提供了一个清晰、可复现的实践框架。在实际操作中我深刻体会到几个容易被忽视却至关重要的点数据质量决定上限CelebA图片质量较高且已对齐。但在真实场景中光照、角度、遮挡、分辨率变化极大。数据增强的策略需要更有针对性例如模拟不同光照、添加随机遮挡等。骨干网络的选择是性能关键当你想提升效果时将简单的CNN替换为ResNet-18/34、MobileNetV2等预训练模型进行微调准确率往往会有质的飞跃。这背后的原因是迁移学习利用了在大规模数据集上学到的通用特征。损失函数的设计是前沿方向我们使用了最基础的交叉熵损失。但在人脸识别领域为了获得判别力更强的特征度量学习方法更为流行如Triplet Loss、ArcFace、CosFace等。这些损失函数直接优化特征空间迫使类内更紧凑、类间更疏远是达到工业级精度的重要手段。阈值是业务逻辑的一部分识别阈值threshold不是一个固定值。在安防场景高安全要求可能需要调高如0.8减少误识False Accept在便捷打卡场景可能调低如0.5减少拒识False Reject。需要在验证集上绘制错误接受率FAR和错误拒绝率FRR的曲线根据业务需求确定最佳工作点。这个项目压缩包FaceDetectionByTorch.zip里应该包含完整的代码、一份详细说明如何准备数据和运行训练的README以及一个预训练的模型文件。你可以把它作为一个坚实的起点去探索更复杂的网络结构、更先进的损失函数或者尝试将其应用到你自己收集的人脸数据集上。记住在深度学习项目中亲手调试代码、观察损失曲线波动、分析错误案例比任何理论都更能让你成长。本文还有配套的精品资源点击获取
返回列表