LFW人脸数据集测试协议及编程实现
1 简介
LFW人脸数据集主要用于测试模型准确率。数据库中共有13233张图像和5749人。 每张图片都是250x250 jpg。目前LFW数据集不用作训练,主要用于测试。因此本文主要讲解其测试协议。
程序使用pytorch
LFW官方网址:http://vis-www.cs.umass.edu/lfw/
本次实验测试MobileFaceNet模型在LFW上的准确率。
lfw数据集(已经对齐并且大小为112×112):
链接:https://pan.baidu.com/s/1Sy0EkKRfb0xHRgq1iao2qg
提取码:1rf3
2 数据集准备
2.1 LFW数据集下载
下载地址:http://vis-www.cs.umass.edu/lfw/lfw.tgz
2.2 人脸预处理
下载的LFW数据集是250×250的图像,我们需要将其进行人脸预处理操作(人脸检测+人脸对齐),具体方法可以看我另一个博客。
https://blog.csdn.net/qq_41684249/article/details/111302649
三、测试协议
3.1 pair.txt
下载地址:http://vis-www.cs.umass.edu/lfw/pairs.txt
该文件将数据库随机分为10组,我们随机选择300个匹配对和300个每组中不匹配的对。使用此拆分,可以使用10倍交叉验证得出数据库的性能。
pair.txt文件的格式如下:
- 第一行显示套数后跟每套匹配对的数量(相等到每组不匹配对的数量)。
- 匹配对格式如下:
name n1 n2
表示匹配对name人的第n1和第n2张图。 - 不匹配对格式如下:
name1 n1 name2 n2
表示不匹配对name1人的第n1张图和name2人的第n2张图。
3.2 具体流程
四、程序
4.1 MobileFaceNet
预训练模型见博客。
博客链接:https://blog.csdn.net/qq_41684249/article/details/115357756?spm=1001.2014.3001.5501
4.2 配置文件config.py
只需将这个配置文件对应修改就行。
img_list.txt和pairs.txt文件见第一部分。
# 测试协议txt文件 PAIRS_FILE_PATH = "/home/malidong/workspace/dataset/lfw/pairs.txt" # 预处理后的数据集地址 CROPPED_FACE_FOLDER = "/home/malidong/workspace/dataset/lfw/112×112" # 包含数据集所有相对路径的txt文件地址 IMAGE_LIST_FILE_PATH = "/home/malidong/workspace/dataset/lfw/img_list.txt" # 模型输出维度 FEAT_DIM = 512 # 深度卷积层核大小 OUT_H = 7 OUT_W = 7 # 预训练模型地址 MODEL_PATH = "/home/malidong/workspace/mobilefacenet/Epoch_17.pt" # 设备选择 DEVICE = "cuda"4.3 数据集加载文件test_dataset.py
import os import cv2 import numpy as np from torch.utils.data import Dataset import torchvision.transforms as transformsclass CommonTestDataset(Dataset):def __init__(self, image_root, image_list_file):'''普通测试数据集加载:param image_root: 数据集根目录:param image_list_file: txt文件,包含所有图片相对路径'''self.image_root = image_rootself.image_list = []image_list_buf = open(image_list_file) # 开始读取文件line = image_list_buf.readline().strip() # 读取一行while line:self.image_list.append(line)line = image_list_buf.readline().strip() # 继续读取下一行# 预处理self.transform = transforms.Compose([transforms.ToTensor(), # 0-1transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) # 归一化])def __len__(self):return len(self.image_list)def __getitem__(self, index):short_image_path = self.image_list[index] # 图片相对路径image_path = os.path.join(self.image_root, short_image_path) # 图片路径image = cv2.imdecode(np.fromfile(image_path, dtype=np.uint8), cv2.IMREAD_UNCHANGED) # 读取图片#image = cv2.resize(image, (128, 128))image = self.transform(image) # 预处理return image, short_image_path # 图片 相对路径(相当于图片身份,用于查询)4.4 lfw评估程序lfw_evaluator.py
import torch import numpy as np import torch.nn.functional as Fclass LFWEvaluator(object):def __init__(self, data_loader, pairs_file, device):'''LFW测试协议 先将所有的数据集进行特征提取,然后在测试:param data_loader: 数据集加载:param pairs_file: 测试txt文件地址:param device: 设备'''self.data_loader = data_loaderself.device = deviceself.pair_list = self.pairsParser(pairs_file)def pairsParser(self, pairs_file):'''读取pair.txt文件内容:param pairs_file: 测试txt文件地址:return: (图1 图2 标签),是一个list列表文件。标签为0表示不同,标签为1表示相同。'''test_pair_list = [] # 创建一个listpairs_file_buf = open(pairs_file) # 读取文件line = pairs_file_buf.readline() # 跳过第一行 因为第一行是无关的内容line = pairs_file_buf.readline().strip() # 读取一行,去除首尾空格while line: # 只要文件有内容,就会读取line_strs = line.split('\t') # 按空格分割if len(line_strs) == 3: # 如果是3元素,则表示两张人脸是同一个人person_name = line_strs[0] # 第一个元素是姓名image_index1 = line_strs[1] # 第二个元素是第一张图的索引image_index2 = line_strs[2] # 第三个元素是第二张图的索引image_name1 = person_name + '/' + person_name + '_' + image_index1.zfill(4) + '.jpg' # 得到第一张人脸的地址image_name2 = person_name + '/' + person_name + '_' + image_index2.zfill(4) + '.jpg' # 得到第二张人脸的地址label = 1 # 标签为1表示是同一个身份elif len(line_strs) == 4: # 表示两张人脸是不同的人person_name1 = line_strs[0] # 第一个人的姓名image_index1 = line_strs[1] # 第一个人的索引person_name2 = line_strs[2] # 第二个人的姓名image_index2 = line_strs[3] # 第二个人的索引image_name1 = person_name1 + '/' + person_name1 + '_' + image_index1.zfill(4) + '.jpg' # 得到第一张人脸的地址image_name2 = person_name2 + '/' + person_name2 + '_' + image_index2.zfill(4) + '.jpg' # 得到第二张人脸的地址label = 0 # 标签为0表示不同身份else:raise Exception('Line error: %s.' % line)test_pair_list.append((image_name1, image_name2, label)) # 存入list中line = pairs_file_buf.readline().strip() # 读取下一行return test_pair_listdef extract_feature(self, model, data_loader):'''提取所有测试集人脸特征:param model: 要测试的模型:param data_loader: 数据集:return: 字典形式 图片名字(相对路径):提取的特征'''model.eval() # 测试模式,如果不写,会对bn层有影响image_name2feature = {} # 字典with torch.no_grad(): # 不进行梯度下降for batch_idx, (images, filenames) in enumerate(data_loader): # 读取数据images = images.to(self.device) # 图片features = model(images) # 特征features = F.normalize(features) # 特征归一化 用于求余弦相似度features = features.cpu().numpy()for filename, feature in zip(filenames, features):image_name2feature[filename] = feature # 存入字典return image_name2featuredef test(self, model):'''测试程序:param model: 测试模型:return: 准确率和方差'''image_name2feature = self.extract_feature(model, self.data_loader)mean, std = self.test_one_model(self.pair_list, image_name2feature)return mean, stddef test_one_model(self, test_pair_list, image_name2feature, is_normalize = True):'''获取模型的LFW测试准确率:param test_pair_list: 测试pair.txt文件:param image_name2feature: 存入特征的字典:param is_normalize: 是否已经归一化,如果已经归一化,就True,无需在进行归一化。:return: 准确率、方差'''subsets_score_list = np.zeros((10, 600), dtype = np.float32) # 余弦相似度subsets_label_list = np.zeros((10, 600), dtype = np.int8) # 标签for index, cur_pair in enumerate(test_pair_list): # 图1 图2 标签cur_subset = index // 600 # 对600取整 一共6000对,分成10组cur_id = index % 600 # 对600取余image_name1 = cur_pair[0] # 图1image_name2 = cur_pair[1] # 图2label = cur_pair[2] # 标签 0表示不同 1表示相同subsets_label_list[cur_subset][cur_id] = label # 存入标签feat1 = image_name2feature[image_name1] # 图1的特征feat2 = image_name2feature[image_name2] # 图2的特征if not is_normalize: # 归一化feat1 = feat1 / np.linalg.norm(feat1)feat2 = feat2 / np.linalg.norm(feat2)cur_score = np.dot(feat1, feat2) # 余弦相似度(即得分)subsets_score_list[cur_subset][cur_id] = cur_score # 存入余弦相似度subset_train = np.array([True] * 10)accu_list = []for subset_idx in range(10): # 一组一组算test_score_list = subsets_score_list[subset_idx] # 一组的余弦相似度test_label_list = subsets_label_list[subset_idx] # 一组的标签subset_train[subset_idx] = False # 改为Falsetrain_score_list = subsets_score_list[subset_train].flatten() # 其余9组的余弦相似度train_label_list = subsets_label_list[subset_train].flatten() # 其余9组的标签subset_train[subset_idx] = True # 改为Truebest_thres = self.getThreshold(train_score_list, train_label_list) # 用其余9组得出阈值positive_score_list = test_score_list[test_label_list == 1] # 将标签为1的放入一个余弦相似度列表negtive_score_list = test_score_list[test_label_list == 0] # 将标签为0的放入一个余弦相似度列表true_pos_pairs = np.sum(positive_score_list > best_thres) # 计算结果正确的数量true_neg_pairs = np.sum(negtive_score_list < best_thres) # 计算结果正确的数量accu_list.append((true_pos_pairs + true_neg_pairs) / 600) # 计算每组最后结果mean = np.mean(accu_list) # 取平均值std = np.std(accu_list, ddof=1) / np.sqrt(10) # 方差return mean, stddef getThreshold(self, score_list, label_list, num_thresholds=1000):'''得到最佳阈值:param score_list: 余弦相似度list:param label_list: 标签list:param num_thresholds: 只需n次得到最优roc:return:'''pos_score_list = score_list[label_list == 1] # 将标签为1的放入一个余弦相似度列表neg_score_list = score_list[label_list == 0] # 将标签为0的放入一个余弦相似度列表pos_pair_nums = pos_score_list.size # 数量neg_pair_nums = neg_score_list.size # 数量score_max = np.max(score_list) # 最大余弦相似度score_min = np.min(score_list) # 最小余弦相似度score_span = score_max - score_min # 相减step = score_span / num_thresholds # 切分成num_thresholds个threshold_list = score_min + step * np.array(range(1, num_thresholds + 1)) # 得到一个切分后的阈值listfpr_list = []tpr_list = []for threshold in threshold_list: # 一个个试fpr = np.sum(neg_score_list > threshold) / neg_pair_nums # 错误/负样本数tpr = np.sum(pos_score_list > threshold) /pos_pair_nums # 正确/正样本数fpr_list.append(fpr)tpr_list.append(tpr)fpr = np.array(fpr_list)tpr = np.array(tpr_list)best_index = np.argmax(tpr-fpr) # 取最大值best_thres = threshold_list[best_index]return best_thres # 得到最大阈值4.5 主程序test_lfw.py
import os from prettytable import PrettyTable from torch.utils.data import DataLoader from test_dataset import CommonTestDataset from MobileFaceNets import MobileFaceNet import config from lfw_evaluator import LFWEvaluatorif __name__ == '__main__':# 参数预加载pairs_file_path = config.PAIRS_FILE_PATHcropped_face_folder = config.CROPPED_FACE_FOLDERimage_list_file_path = config.IMAGE_LIST_FILE_PATHfeat_dim = config.FEAT_DIMout_h = config.OUT_Hout_w = config.OUT_Wmodel_path = config.MODEL_PATHdevice = config.DEVICE# 设置gpuos.environ["CUDA_VISIBLE_DEVICES"] = "0, 1"# 测试数据集加载data_loader = DataLoader(CommonTestDataset(cropped_face_folder, image_list_file_path),batch_size=1024, num_workers=16, shuffle=False)# 模型加载model = MobileFaceNet(feat_dim, out_h, out_w)model = model.load_model(model, model_path)# lfw评估类加载lfw_evaluator = LFWEvaluator(data_loader, pairs_file_path, device)# 评估mean, std = lfw_evaluator.test(model)# 显示accu_list = [(os.path.basename(model_path), mean, std)]pretty_tabel = PrettyTable(["model_name", "mean accuracy", "standard error"])for accu_item in accu_list:pretty_tabel.add_row(accu_item)print(pretty_tabel)五、测试结果
总结
以上是生活随笔为你收集整理的LFW人脸数据集测试协议及编程实现的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: mysql 5.7 super_MySQ
- 下一篇: 如何确定VS编译器版本--_MSC_VE